Dynamic Types - TypeBuilder
Sometimes you have output schemas that change at runtime — for example if you have a list of Categories that you need to classify that come from a database, or your schema is user-provided.
TypeBuilder is used to create or modify dynamic types at runtime to achieve this.
Dynamic BAML Enums
Imagine we want to make a categorizer prompt, but the list of categories to output come from a database.
- Add
@@dynamicto the class or enum definition to mark it as dynamic in BAML.
- Import the
TypeBuilderfrom baml_client in your runtime code and modifyCategory. All dynamic types you define in BAML will be available as properties ofTypeBuilder. Think of the typebuilder as a registry of modified runtime types that the baml function will read from when building the output schema in the prompt.
Python
TypeScript
Ruby
Go
OpenAPI
Dynamic BAML Classes
Now we’ll add some properties to a User class at runtime using @@dynamic.
We can then modify the User schema at runtime. Since we marked User with @@dynamic, it’ll be available as a property of TypeBuilder.
Python
TypeScript
Ruby
Go
Add existing BAML types to a property (e.g. you want to add a subset of tools)
Imagine you have a ChatResponse type in a function that you want to modify with a set of tools.
You want to add a tool_calls property to the ChatResponse type that can be a list of GetWeather or GetNews types, that are completely defined in BAML.
You can modify the set of tools that can be used in the ChatResponse type at runtime like this:
Python
TypeScript
Ruby
Go
Creating new dynamic classes or enums not in BAML
The previous examples showed how to modify existing types. Here we create a new Hobbies enum, and a new class called Address without having them defined in BAML.
Note that you must attach the new types to the existing Return Type of your BAML function(in this case it’s User).
Python
TypeScript
Ruby
Go
TypeBuilder provides methods for building different kinds of types:
Adding descriptions to dynamic types
Python
TypeScript
Ruby
Go
Creating dynamic classes and enums at runtime with BAML syntax
Ok, what if you just want to write some actual baml code to modify the types at runtime?
The TypeBuilder has a higher level API add_baml to do this:
Python
TypeScript
Ruby
Go
Building dynamic types from JSON schema
JSON Schema is a declarative language for validating JSON data structures, often derived from language-native type definitions such as Python classes, TypeScript interfaces, or Java classes.
BAML supports converting JSON schemas into dynamic BAML types, allowing you to automatically use your existing data models with BAML’s LLM functions. This feature enables seamless integration between your application’s type system and BAML’s structured output capabilities.
We have a working implementation of this feature, but are waiting for concrete use cases to merge it into the main codebase. For a detailed explanation of this functionality, see our article on dynamic JSON schemas. You can also explore the source code and examples to understand how to implement this in your projects.
Please chime in on the GitHub issue if this is something you’d like to use.
Testing dynamic types in BAML
When testing dynamic types there are two different cases:
- Injecting properties into dynamic types returned by the tested function.
- Injecting values into dynamic types received as arguments by the tested function.
The first case requires using the type_builder and dynamic blocks in the
test, whereas the second case only requires specifying the values in the args
block.
Testing return types
Dynamic classes
Suppose we have a dynamic class Resume and we want to add a property that
stores the user’s work experience when we testing a specific function. We can
do that by specifying the types and properties that we need in the
type_builder block.
The rendered prompt for ExtractResume will now include the experience field
defined in the dynamic block and the LLM will correctly extract the experience
in the input text.
Dynamic enums
Dynamic enums can be included in the type_builder block just like classes. The
only difference is that we inject new variants in the dynamic block instead of
properties.
The Feedback variant will be rendered in the prompt for ClassifyMessage
during the test execution.
Testing parameter types
When a dynamic type is used as an input parameter of a function, we can simply
pass any value in the args block of the test and the value will be rendered in
the prompt.
Dynamic classes
Dynamic enums
Enums work the same way, any variant defined in the args block will be
rendered normally.
For more information about dynamic types, see Type Builder.