Skip to content

Wrong json schema generation on enum for tools. #2997

@Vonfry

Description

@Vonfry

Describe the bug
When creating a new tool from function in python by client.tools.create_from_function, server will parse sources if no args_schema is provided. In this case, function type with Literal is parsed in wrong format.
If args_schema is provided by a pydantic model, the client slide convert it into json schema, but the server side ignore enum and some others in properties.

Please describe your setup

  • How are you running Letta?
    • Docker
    • From source
  • Describe your setup
    • What's your OS (Windows/MacOS/Linux)? Linux
    • What is your docker run ... command (if applicable) uv sync --all-packages --all-groups --all-extras; letta --port ... --debug

Example codes
The following function is function to create.

def custom_tool_for_test(foo: Literal['a', 'b']) -> str:
    """
    Simulate the roll of a 20-sided die (d20).

    This function generates a random integer between 1 and 20, inclusive,

    which represents the outcome of a single roll of a d20.

    Args:
       foo: a value from given range.

    Returns:

        str: The result of the die roll.
    """
    import random

    dice_role_outcome = random.randint(1, 20)

    output_string = f"You rolled a {dice_role_outcome}"

    return output_string

And the generate schema is

{
  "name": "custom_tool_for_test",
  "description": "Simulate the roll of a 20-sided die (d20).\n\nThis function generates a random integer between 1 and 20, inclusive,\n\nwhich represents the outcome of a single roll of a d20.",
  "parameters": {
    "type": "object",
    "properties": {
      "foo": {
        "type": "string",
        "enum": [
          "('a', 'b')"
        ],
        "description": "a value from given range."
      }
    },
    "required": [
      "foo"
    ]
  }
}

The "enum" field should be ["a", "b"] instead.

Issue trace
It is caused by _parse_type_annotation in letta/functions/functions.py called by derive_openai_json_schema and Tool.refresh_source_code_and_json_schema in letta/schemas/tool.py.
_parse_type_annotation handles ast.Name only for ast.Subscript, but it doesn't handle ast.Tuple, which is parsed from `Literal["foo", "bar"].
It can fix by

        elif isinstance(annotation_node.slice, ast.Tuple):
            # Type like as Literal["foo", "bar"]
            return [_parse_type_annotation(elt, imports_map) for elt in annotation_node.slice.elts]

However, the return type is checked generate_schema, so the patch cannot work directly.

If args_schema is passed, generate_model_from_args_json_schema and generate_schema_from_args_schema_v2 are used. enum and some others are omited in these functions during rebuilding schema.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions