> Version notice: This site documents BAML v0, the legacy DSL. BAML v1 is a separate, fully featured programming language in public beta. If the user intends to use BAML v1, ignore this site's content and use the BAML v1 documentation instead. If the intended version is unclear, clarify it before using this page.
> For clean Markdown of any page, append .md to the page URL. For the complete documentation index, see https://docs.boundaryml.com/llms.txt. For MCP access, connect to https://docs.boundaryml.com/_mcp/server.

# TypeBuilder

`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**

```baml {4}
enum Category {
  RED
  BLUE
  @@dynamic  // Makes this enum modifiable at runtime
}

function Categorize(text: string) -> Category {
  prompt #"
    Categorize this text:
    {{ text }}

    {{ ctx.output_format }}
  "#
}
```

**Runtime Usage**

#### Python

```python
from baml_client.type_builder import TypeBuilder
from baml_client import b

# Create a TypeBuilder instance
tb = TypeBuilder()

# Add new values to the Category enum
tb.Category.add_value('GREEN')
tb.Category.add_value('YELLOW')

# Pass the typebuilder when calling the function
result = b.Categorize("The sun is bright", {"tb": tb})
# result can now be RED, BLUE, GREEN, or YELLOW
```

#### TypeScript

```typescript
import { TypeBuilder } from '../baml_client/type_builder'
import { b } from '../baml_client'

// Create a TypeBuilder instance
const tb = new TypeBuilder()

// Add new values to the Category enum
tb.Category.addValue('GREEN')
tb.Category.addValue('YELLOW')

// Pass the typebuilder when calling the function
const result = await b.Categorize("The sun is bright", { tb })
// result can now be RED, BLUE, GREEN, or YELLOW
```

#### Go

```go
package main

import (
    "context"
    "fmt"
    
    b "example.com/myproject/baml_client"
)

func main() {
    ctx := context.Background()
    
    // Create a TypeBuilder instance
    tb, err := b.NewTypeBuilder()
    if err != nil {
        panic(err)
    }
    
    // Get the Category enum and add new values
    category, err := tb.Category()
    if err != nil {
        panic(err)
    }
    
    _, err = category.AddValue("GREEN")
    if err != nil {
        panic(err)
    }
    
    _, err = category.AddValue("YELLOW")
    if err != nil {
        panic(err)
    }
    
    // Pass the typebuilder when calling the function
    result, err := b.Categorize(ctx, "The sun is bright", b.WithTypeBuilder(tb))
    if err != nil {
        panic(err)
    }
    
    // result can now be RED, BLUE, GREEN, or YELLOW
    fmt.Printf("Result: %+v\n", result)
}
```

#### Ruby

```ruby
require_relative 'baml_client/client'

# Create a TypeBuilder instance
tb = Baml::TypeBuilder.new

# Add new values to the Category enum
tb.Category.add_value('GREEN')
tb.Category.add_value('YELLOW')

# Pass the typebuilder when calling the function
result = Baml::Client.categorize(text: "The sun is bright", baml_options: { tb: tb })
# result can now be RED, BLUE, GREEN, or YELLOW
```

#### Rust

```rust
use myproject::baml_client::sync_client::B;
use myproject::baml_client::type_builder::TypeBuilder;

fn main() {
    // Create a TypeBuilder instance
    let tb = TypeBuilder::new();

    // Add new values to the Category enum
    tb.Category().inner().add_value("GREEN").unwrap();
    tb.Category().inner().add_value("YELLOW").unwrap();

    // Pass the typebuilder when calling the function
    let result = B.Categorize
        .with_type_builder(&tb)
        .call("The sun is bright")
        .unwrap();
    // 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`

```baml
class User {
  name string
  age int
  @@dynamic  // Allow adding more properties
}
```

**Runtime Usage**

#### Python

```python
tb = TypeBuilder()
tb.User.add_property('email', tb.string())
tb.User.add_property('address', tb.string())
```

#### TypeScript

```typescript
const tb = new TypeBuilder()
tb.User.addProperty('email', tb.string())
tb.User.addProperty('address', tb.string())
```

#### Ruby

```ruby
tb = Baml::TypeBuilder.new
tb.User.add_property('email', tb.string)
tb.User.add_property('address', tb.string)
```

#### Rust

```rust
let tb = TypeBuilder::new();
tb.User().inner().add_property("email", tb.string()).unwrap();
tb.User().inner().add_property("address", tb.string()).unwrap();
```

---

**`Enums`** `example`

```baml
enum Category {
  VALUE1
  VALUE2
  @@dynamic  // Allow adding more values
}
```

**Runtime Usage**

#### Python

```python
tb = TypeBuilder()
tb.Category.add_value('VALUE3')
tb.Category.add_value('VALUE4')
```

#### TypeScript

```typescript
const tb = new TypeBuilder()
tb.Category.addValue('VALUE3')
tb.Category.addValue('VALUE4')
```

#### Ruby

```ruby
tb = Baml::TypeBuilder.new
tb.Category.add_value('VALUE3')
tb.Category.add_value('VALUE4')
```

#### Rust

```rust
let tb = TypeBuilder::new();
tb.Category().inner().add_value("VALUE3").unwrap();
tb.Category().inner().add_value("VALUE4").unwrap();
```

---

### Creating New Types

You can also create entirely new types at runtime:

#### Python

```python
tb = TypeBuilder()

# Create a new enum
hobbies = tb.add_enum("Hobbies")
hobbies.add_value("Soccer")
hobbies.add_value("Reading")

# Create a new class
address = tb.add_class("Address")
address.add_property("street", tb.string())
address.add_property("city", tb.string())

# Attach new types to existing BAML type
tb.User.add_property("hobbies", hobbies.type().list())
tb.User.add_property("address", address.type())
```

#### TypeScript

```typescript
const tb = new TypeBuilder()

// Create a new enum
const hobbies = tb.addEnum("Hobbies")
hobbies.addValue("Soccer")
hobbies.addValue("Reading")

// Create a new class
const address = tb.addClass("Address")
address.addProperty("street", tb.string())
address.addProperty("city", tb.string())

// Attach new types to existing BAML type
tb.User.addProperty("hobbies", hobbies.type().list())
tb.User.addProperty("address", address.type())
```

#### Ruby

```ruby
tb = Baml::TypeBuilder.new

# Create a new enum
hobbies = tb.add_enum("Hobbies")
hobbies.add_value("Soccer")
hobbies.add_value("Reading")

# Create a new class
address = tb.add_class("Address")
address.add_property("street", tb.string)
address.add_property("city", tb.string)

# Attach new types to existing BAML type
tb.User.add_property("hobbies", hobbies.type.list)
tb.User.add_property("address", address.type)
```

#### Rust

```rust
let tb = TypeBuilder::new();

// Create a new enum
let hobbies = tb.add_enum("Hobbies").unwrap();
hobbies.add_value("Soccer").unwrap();
hobbies.add_value("Reading").unwrap();

// Create a new class
let address = tb.add_class("Address").unwrap();
address.add_property("street", tb.string()).unwrap();
address.add_property("city", tb.string()).unwrap();

// Attach new types to existing BAML type
tb.User().inner().add_property("hobbies", hobbies.as_type().unwrap().list()).unwrap();
tb.User().inner().add_property("address", address.as_type().unwrap()).unwrap();
```

## Type Builders

TypeBuilder provides methods for building different kinds of types:

| Method                                  | Returns        | Description                      | Example                             |
| --------------------------------------- | -------------- | -------------------------------- | ----------------------------------- |
| `string()`                              | `FieldType`    | Creates a string type            | `tb.string()`                       |
| `int()`                                 | `FieldType`    | Creates an integer type          | `tb.int()`                          |
| `float()`                               | `FieldType`    | Creates a float type             | `tb.float()`                        |
| `bool()`                                | `FieldType`    | Creates a boolean type           | `tb.bool()`                         |
| `literal_string(value: string)`         | `FieldType`    | Creates a literal string type    | `tb.literal_string("hello")`        |
| `literal_int(value: int)`               | `FieldType`    | Creates a literal integer type   | `tb.literal_int(123)`               |
| `literal_bool(value: boolean)`          | `FieldType`    | Creates a literal boolean type   | `tb.literal_bool(true)`             |
| `list(type: FieldType)`                 | `FieldType`    | Makes a type into a list         | `tb.list(tb.string())`              |
| `union(types: FieldType[])`             | `FieldType`    | Creates a union of types         | `tb.union([tb.string(), tb.int()])` |
| `map(key: FieldType, value: FieldType)` | `FieldType`    | Creates a map type               | `tb.map(tb.string(), tb.int())`     |
| `add_class(name: string)`               | `ClassBuilder` | Creates a new class              | `tb.add_class("User")`              |
| `add_enum(name: string)`                | `EnumBuilder`  | Creates a new enum               | `tb.add_enum("Category")`           |
| `MyClass`                               | `FieldType`    | Reference an existing BAML class | `tb.MyClass.type()`                 |

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

```baml {4}
class User {
  name string
  age int
  @@dynamic  // Allow adding more properties
}
```

```python {2}
tb = TypeBuilder()
tb.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.

| Method       | Returns     | Description              | Example                  |
| ------------ | ----------- | ------------------------ | ------------------------ |
| `list()`     | `FieldType` | Makes a type into a list | `tb.string().list()`     |
| `optional()` | `FieldType` | Makes a type optional    | `tb.string().optional()` |

### ClassBuilder

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

| Method                                        | Returns                | Description                     | Example                                     |
| --------------------------------------------- | ---------------------- | ------------------------------- | ------------------------------------------- |
| `add_property(name: string, type: FieldType)` | `ClassPropertyBuilder` | Adds a property to the class    | `my_cls.add_property("email", tb.string())` |
| `description(description: string)`            | `ClassBuilder`         | Adds a description to the class | `my_cls.description("A user class")`        |
| `type()`                                      | `FieldType`            | Returns the type of the class   | `my_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.

| Method                             | Returns                | Description                              | Example                                   |
| ---------------------------------- | ---------------------- | ---------------------------------------- | ----------------------------------------- |
| `description(description: string)` | `ClassPropertyBuilder` | Adds a description to the property       | `my_prop.description("An email address")` |
| `alias(alias: string)`             | `ClassPropertyBuilder` | Adds the alias attribute to the property | `my_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.

| Method                             | Returns            | Description                          | Example                                      |
| ---------------------------------- | ------------------ | ------------------------------------ | -------------------------------------------- |
| `add_value(value: string)`         | `EnumValueBuilder` | Adds a value to the enum             | `my_enum.add_value("VALUE1")`                |
| `description(description: string)` | `EnumBuilder`      | Adds a description to the enum value | `my_enum.description("A value in the enum")` |
| `type()`                           | `FieldType`        | Returns the type of the enum         | `my_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.

| Method                             | Returns            | Description                                | Example                                       |
| ---------------------------------- | ------------------ | ------------------------------------------ | --------------------------------------------- |
| `description(description: string)` | `EnumValueBuilder` | Adds a description to the enum value       | `my_value.description("A value in the enum")` |
| `alias(alias: string)`             | `EnumValueBuilder` | Adds the alias attribute to the enum value | `my_value.alias("VALUE1")`                    |
| `skip()`                           | `EnumValueBuilder` | Adds the skip attribute to the enum value  | `my_value.skip()`                             |

## Adding Descriptions

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

#### Python

```python
tb = TypeBuilder()

# Add description to a property
tb.User.add_property("email", tb.string()) \
   .description("User's primary email address")

# Add description to an enum value
tb.Category.add_value("URGENT") \
   .description("Needs immediate attention")
```

#### TypeScript

```typescript
const tb = new TypeBuilder()

// Add description to a property
tb.User.addProperty("email", tb.string())
   .description("User's primary email address")

// Add description to an enum value
tb.Category.addValue("URGENT")
   .description("Needs immediate attention")
```

#### Ruby

```ruby
tb = Baml::TypeBuilder.new

# Add description to a property
tb.User.add_property("email", tb.string)
   .description("User's primary email address")

# Add description to an enum value
tb.Category.add_value("URGENT")
   .description("Needs immediate attention")
```

#### Rust

```rust
let tb = TypeBuilder::new();

// Add description to a property
tb.User().inner().add_property("email", tb.string()).unwrap()
    .description("User's primary email address");

// Add description to an enum value
tb.Category().inner().add_value("URGENT").unwrap()
    .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:

#### Python

```python
tb = TypeBuilder()
tb.add_baml("""
  // Creates a new class Address that does not exist in the BAML source.
  class Address {
    street string
    city string
    state string
  }

  // Modifies the existing @@dynamic User class to add the new address property.
  dynamic class User {
    address Address
  }

  // Modifies the existing @@dynamic Category enum to add a new variant.
  dynamic enum Category {
    PURPLE
  }
""")
```

#### TypeScript

```typescript
const tb = new TypeBuilder()
tb.addBaml(`
  // Creates a new class Address that does not exist in the BAML source.
  class Address {
    street string
    city string
    state string
  }

  // Modifies the existing @@dynamic User class to add the new address property.
  dynamic class User {
    address Address
  }

  // Modifies the existing @@dynamic Category enum to add a new variant.
  dynamic enum Category {
    PURPLE
  }
`)
```

#### Ruby

```ruby
tb = Baml::TypeBuilder.new
tb.add_baml("
  // Creates a new class Address that does not exist in the BAML source.
  class Address {
    street string
    city string
    state string
  }

  // Modifies the existing @@dynamic User class to add the new address property.
  dynamic class User {
    address Address
  }

  // Modifies the existing @@dynamic Category enum to add a new variant.
  dynamic enum Category {
    PURPLE
  }
")
```

#### Rust

```rust
let tb = TypeBuilder::new();
tb.add_baml(r#"
  // Creates a new class Address that does not exist in the BAML source.
  class Address {
    street string
    city string
    state string
  }

  // Modifies the existing @@dynamic User class to add the new address property.
  dynamic class User {
    address Address
  }

  // Modifies the existing @@dynamic Category enum to add a new variant.
  dynamic enum Category {
    PURPLE
  }
"#).unwrap();
```

## Common Patterns

Here are some common patterns when using TypeBuilder:

1. **Dynamic Categories**: When categories come from a database or external source

**`Python`**

```python Python
categories = fetch_categories_from_db()
tb = TypeBuilder()
for category in categories:
    tb.Category.add_value(category)
```

**`TypeScript`**

```typescript TypeScript
const categories = await fetchCategoriesFromDb()
const tb = new TypeBuilder()
categories.forEach(category => {
    tb.Category.addValue(category)
})
```

**`Ruby`**

```ruby Ruby
categories = fetch_categories_from_db
tb = Baml::TypeBuilder.new
categories.each do |category|
    tb.Category.add_value(category)
end
```

2. **Form Fields**: When extracting dynamic form fields

**`Python`**

```python Python
fields = get_form_fields()
tb = TypeBuilder()
form = tb.add_class("Form")
for field in fields:
    form.add_property(field.name, tb.string())
```

**`TypeScript`**

```typescript TypeScript
const fields = getFormFields()
const tb = new TypeBuilder()
const form = tb.addClass("Form")
fields.forEach(field => {
    form.addProperty(field.name, tb.string())
})
```

**`Ruby`**

```ruby Ruby
fields = get_form_fields
tb = Baml::TypeBuilder.new
form = tb.add_class("Form")
fields.each do |field|
    form.add_property(field.name, tb.string)
end
```

3. **Optional Properties**: When some fields might not be present

**`Python`**

```python Python
tb = TypeBuilder()
tb.User.add_property("middle_name", tb.string().optional())
```

**`TypeScript`**

```typescript TypeScript
const tb = new TypeBuilder()
tb.User.addProperty("middle_name", tb.string().optional())
```

**`Ruby`**

```ruby Ruby
tb = Baml::TypeBuilder.new
tb.User.add_property("middle_name", tb.string.optional)
```

> **Warning**
>
> 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](/guide/baml-advanced/dynamic-runtime-types#testing-dynamic-types-in-baml)
for examples of testing functions that use dynamic types. See also the
[reference](/ref/baml/test) 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.