TypeBuilder is used to create or modify output schemas at runtime. It’s particularly useful when you have dynamic output structures that can’t be determined at compile time - like categories from a database or user-provided schemas.

Here’s a simple example of using TypeBuilder to add new enum values before calling a BAML function:

BAML Code

1enum Category {
2 RED
3 BLUE
4 @@dynamic // Makes this enum modifiable at runtime
5}
6
7function Categorize(text: string) -> Category {
8 prompt #"
9 Categorize this text:
10 {{ text }}
11
12 {{ ctx.output_format }}
13 "#
14}

Runtime Usage

1from baml_client.type_builder import TypeBuilder
2from baml_client import b
3
4# Create a TypeBuilder instance
5tb = TypeBuilder()
6
7# Add new values to the Category enum
8tb.Category.add_value('GREEN')
9tb.Category.add_value('YELLOW')
10
11# Pass the typebuilder when calling the function
12result = b.Categorize("The sun is bright", {"tb": tb})
13# result can now be RED, BLUE, GREEN, or YELLOW

Dynamic Types

There are two ways to use TypeBuilder:

  1. Modifying existing BAML types marked with @@dynamic
  2. Creating entirely new types at runtime

Modifying Existing Types

To modify an existing BAML type, mark it with @@dynamic:

Classes
example
1class User {
2 name string
3 age int
4 @@dynamic // Allow adding more properties
5}

Runtime Usage

1tb = TypeBuilder()
2tb.User.add_property('email', tb.string())
3tb.User.add_property('address', tb.string())
Enums
example
1enum Category {
2 VALUE1
3 VALUE2
4 @@dynamic // Allow adding more values
5}

Runtime Usage

1tb = TypeBuilder()
2tb.Category.add_value('VALUE3')
3tb.Category.add_value('VALUE4')

Creating New Types

You can also create entirely new types at runtime:

1tb = TypeBuilder()
2
3# Create a new enum
4hobbies = tb.add_enum("Hobbies")
5hobbies.add_value("Soccer")
6hobbies.add_value("Reading")
7
8# Create a new class
9address = tb.add_class("Address")
10address.add_property("street", tb.string())
11address.add_property("city", tb.string())
12
13# Attach new types to existing BAML type
14tb.User.add_property("hobbies", hobbies.type().list())
15tb.User.add_property("address", address.type())

Type Builders

TypeBuilder provides methods for building different kinds of types:

MethodReturnsDescriptionExample
string()FieldTypeCreates a string typetb.string()
int()FieldTypeCreates an integer typetb.int()
float()FieldTypeCreates a float typetb.float()
bool()FieldTypeCreates a boolean typetb.bool()
list(type: FieldType)FieldTypeMakes a type into a listtb.list(tb.string())
union(types: FieldType[])FieldTypeCreates a union of typestb.union([tb.string(), tb.int()])
add_class(name: string)ClassBuilderCreates a new classtb.add_class("User")
add_enum(name: string)EnumBuilderCreates a new enumtb.add_enum("Category")

In addition to the methods above, all types marked with @@dynamic will also appear in the TypeBuilder.

1class User {
2 name string
3 age int
4 @@dynamic // Allow adding more properties
5}
1tb = TypeBuilder()
2tb.User.add_property("email", tb.string())

FieldType

FieldType is a type that represents a field in a type. It can be used to add descriptions, constraints, and other metadata to a field.

MethodReturnsDescriptionExample
list()FieldTypeMakes a type into a listtb.string().list()
optional()FieldTypeMakes a type optionaltb.string().optional()

ClassBuilder

ClassBuilder is a type that represents a class in a type. It can be used to add properties to a class.

MethodReturnsDescriptionExample
add_property(name: string, type: FieldType)ClassPropertyBuilderAdds a property to the classmy_cls.add_property("email", tb.string())
description(description: string)ClassBuilderAdds a description to the classmy_cls.description("A user class")
type()FieldTypeReturns the type of the classmy_cls.type()

ClassPropertyBuilder

ClassPropertyBuilder is a type that represents a property in a class. It can be used to add descriptions, constraints, and other metadata to a property.

MethodReturnsDescriptionExample
description(description: string)ClassPropertyBuilderAdds a description to the propertymy_prop.description("An email address")
alias(alias: string)ClassPropertyBuilderAdds the alias attribute to the propertymy_prop.alias("email_address")

EnumBuilder

EnumBuilder is a type that represents an enum in a type. It can be used to add values to an enum.

MethodReturnsDescriptionExample
add_value(value: string)EnumValueBuilderAdds a value to the enummy_enum.add_value("VALUE1")
description(description: string)EnumBuilderAdds a description to the enum valuemy_enum.description("A value in the enum")
type()FieldTypeReturns the type of the enummy_enum.type()

EnumValueBuilder

EnumValueBuilder is a type that represents a value in an enum. It can be used to add descriptions, constraints, and other metadata to a value.

MethodReturnsDescriptionExample
description(description: string)EnumValueBuilderAdds a description to the enum valuemy_value.description("A value in the enum")
alias(alias: string)EnumValueBuilderAdds the alias attribute to the enum valuemy_value.alias("VALUE1")
skip()EnumValueBuilderAdds the skip attribute to the enum valuemy_value.skip()

Adding Descriptions

You can add descriptions to properties and enum values to help guide the LLM:

1tb = TypeBuilder()
2
3# Add description to a property
4tb.User.add_property("email", tb.string()) \
5 .description("User's primary email address")
6
7# Add description to an enum value
8tb.Category.add_value("URGENT") \
9 .description("Needs immediate attention")

Creating/modyfing dynamic types with the add_baml method

The TypeBuilder has a higher level API for creating dynamic types at runtime. Here’s an example:

1tb = TypeBuilder()
2tb.add_baml("""
3 // Creates a new class Address that does not exist in the BAML source.
4 class Address {
5 street string
6 city string
7 state string
8 }
9
10 // Modifies the existing @@dynamic User class to add the new address property.
11 dynamic class User {
12 address Address
13 }
14
15 // Modifies the existing @@dynamic Category enum to add a new variant.
16 dynmic enum Category {
17 PURPLE
18 }
19""")

Common Patterns

Here are some common patterns when using TypeBuilder:

  1. Dynamic Categories: When categories come from a database or external source
1categories = fetch_categories_from_db()
2tb = TypeBuilder()
3for category in categories:
4 tb.Category.add_value(category)
  1. Form Fields: When extracting dynamic form fields
1fields = get_form_fields()
2tb = TypeBuilder()
3form = tb.add_class("Form")
4for field in fields:
5 form.add_property(field.name, tb.string())
  1. Optional Properties: When some fields might not be present
1tb = TypeBuilder()
2tb.User.add_property("middle_name", tb.string().optional())

All types added through TypeBuilder must be connected to the return type of your BAML function. Standalone types that aren’t referenced won’t affect the output schema.

Testing Dynamic Types

See the advanced dynamic types tests guide for examples of testing functions that use dynamic types. See also the reference for syntax.

Future Features

We’re working on additional features for TypeBuilder:

  • JSON Schema support (awaiting use cases)
  • OpenAPI schema integration
  • Pydantic model support

If you’re interested in these features, please join the discussion in our GitHub issues.

Built with