mirror of
https://github.com/meta-llama/llama-stack.git
synced 2025-10-05 20:27:35 +00:00
fix(responses): type aliasing not supported for pydantic code generation and discrimintated unions
This commit is contained in:
parent
8fb17ba18e
commit
80b82c070c
5 changed files with 287 additions and 73 deletions
158
docs/_static/llama-stack-spec.html
vendored
158
docs/_static/llama-stack-spec.html
vendored
|
@ -8993,9 +8993,163 @@
|
||||||
"title": "OpenAIResponsesTool"
|
"title": "OpenAIResponsesTool"
|
||||||
},
|
},
|
||||||
"OpenAIResponsesToolChoice": {
|
"OpenAIResponsesToolChoice": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/ToolChoiceOptions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/ToolChoiceTypes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/ToolChoiceAllowed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/ToolChoiceFunction"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/ToolChoiceMcp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/ToolChoiceCustom"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"discriminator": {
|
||||||
|
"propertyName": "type",
|
||||||
|
"mapping": {
|
||||||
|
"allowed_tools": "#/components/schemas/ToolChoiceAllowed",
|
||||||
|
"function": "#/components/schemas/ToolChoiceFunction",
|
||||||
|
"mcp": "#/components/schemas/ToolChoiceMcp",
|
||||||
|
"custom": "#/components/schemas/ToolChoiceCustom"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ToolChoiceAllowed": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"title": "OpenAIResponsesToolChoice",
|
"properties": {
|
||||||
"description": "Type alias.\nType aliases are created through the type statement::\n\n type Alias = int\n\nIn this example, Alias and int will be treated equivalently by static\ntype checkers.\n\nAt runtime, Alias is an instance of TypeAliasType. The __name__\nattribute holds the name of the type alias. The value of the type alias\nis stored in the __value__ attribute. It is evaluated lazily, so the\nvalue is computed only if the attribute is accessed.\n\nType aliases can also be generic::\n\n type ListOrSet[T] = list[T] | set[T]\n\nIn this case, the type parameters of the alias are stored in the\n__type_params__ attribute.\n\nSee PEP 695 for more information."
|
"mode": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"auto",
|
||||||
|
"required"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"tools": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "object",
|
||||||
|
"title": "object",
|
||||||
|
"description": "The base class of the class hierarchy.\nWhen called, it accepts no arguments and returns a new featureless\ninstance that has no instance attributes and cannot be given any."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"const": "allowed_tools",
|
||||||
|
"default": "allowed_tools"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": [
|
||||||
|
"mode",
|
||||||
|
"tools",
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"title": "ToolChoiceAllowed"
|
||||||
|
},
|
||||||
|
"ToolChoiceCustom": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"const": "custom",
|
||||||
|
"default": "custom"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": [
|
||||||
|
"name",
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"title": "ToolChoiceCustom"
|
||||||
|
},
|
||||||
|
"ToolChoiceFunction": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"const": "function",
|
||||||
|
"default": "function"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": [
|
||||||
|
"name",
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"title": "ToolChoiceFunction"
|
||||||
|
},
|
||||||
|
"ToolChoiceMcp": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"server_label": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"const": "mcp",
|
||||||
|
"default": "mcp"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": [
|
||||||
|
"server_label",
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"title": "ToolChoiceMcp"
|
||||||
|
},
|
||||||
|
"ToolChoiceOptions": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"none",
|
||||||
|
"auto",
|
||||||
|
"required"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ToolChoiceTypes": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"file_search",
|
||||||
|
"web_search_preview",
|
||||||
|
"computer_use_preview",
|
||||||
|
"web_search_preview_2025_03_11",
|
||||||
|
"image_generation",
|
||||||
|
"code_interpreter"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": [
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"title": "ToolChoiceTypes"
|
||||||
},
|
},
|
||||||
"OpenAIResponseContentPart": {
|
"OpenAIResponseContentPart": {
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
|
|
137
docs/_static/llama-stack-spec.yaml
vendored
137
docs/_static/llama-stack-spec.yaml
vendored
|
@ -6589,39 +6589,118 @@ components:
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
title: OpenAIResponsesTool
|
title: OpenAIResponsesTool
|
||||||
OpenAIResponsesToolChoice:
|
OpenAIResponsesToolChoice:
|
||||||
|
oneOf:
|
||||||
|
- $ref: '#/components/schemas/ToolChoiceOptions'
|
||||||
|
- $ref: '#/components/schemas/ToolChoiceTypes'
|
||||||
|
- oneOf:
|
||||||
|
- $ref: '#/components/schemas/ToolChoiceAllowed'
|
||||||
|
- $ref: '#/components/schemas/ToolChoiceFunction'
|
||||||
|
- $ref: '#/components/schemas/ToolChoiceMcp'
|
||||||
|
- $ref: '#/components/schemas/ToolChoiceCustom'
|
||||||
|
discriminator:
|
||||||
|
propertyName: type
|
||||||
|
mapping:
|
||||||
|
allowed_tools: '#/components/schemas/ToolChoiceAllowed'
|
||||||
|
function: '#/components/schemas/ToolChoiceFunction'
|
||||||
|
mcp: '#/components/schemas/ToolChoiceMcp'
|
||||||
|
custom: '#/components/schemas/ToolChoiceCustom'
|
||||||
|
ToolChoiceAllowed:
|
||||||
type: object
|
type: object
|
||||||
title: OpenAIResponsesToolChoice
|
properties:
|
||||||
|
mode:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- auto
|
||||||
|
- required
|
||||||
|
tools:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: object
|
||||||
|
additionalProperties:
|
||||||
|
type: object
|
||||||
|
title: object
|
||||||
description: >-
|
description: >-
|
||||||
Type alias.
|
The base class of the class hierarchy.
|
||||||
|
|
||||||
Type aliases are created through the type statement::
|
When called, it accepts no arguments and returns a new featureless
|
||||||
|
|
||||||
type Alias = int
|
instance that has no instance attributes and cannot be given any.
|
||||||
|
type:
|
||||||
In this example, Alias and int will be treated equivalently by static
|
type: string
|
||||||
|
const: allowed_tools
|
||||||
type checkers.
|
default: allowed_tools
|
||||||
|
additionalProperties: false
|
||||||
|
required:
|
||||||
At runtime, Alias is an instance of TypeAliasType. The __name__
|
- mode
|
||||||
|
- tools
|
||||||
attribute holds the name of the type alias. The value of the type alias
|
- type
|
||||||
|
title: ToolChoiceAllowed
|
||||||
is stored in the __value__ attribute. It is evaluated lazily, so the
|
ToolChoiceCustom:
|
||||||
|
type: object
|
||||||
value is computed only if the attribute is accessed.
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
Type aliases can also be generic::
|
type:
|
||||||
|
type: string
|
||||||
type ListOrSet[T] = list[T] | set[T]
|
const: custom
|
||||||
|
default: custom
|
||||||
In this case, the type parameters of the alias are stored in the
|
additionalProperties: false
|
||||||
|
required:
|
||||||
__type_params__ attribute.
|
- name
|
||||||
|
- type
|
||||||
|
title: ToolChoiceCustom
|
||||||
See PEP 695 for more information.
|
ToolChoiceFunction:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
type:
|
||||||
|
type: string
|
||||||
|
const: function
|
||||||
|
default: function
|
||||||
|
additionalProperties: false
|
||||||
|
required:
|
||||||
|
- name
|
||||||
|
- type
|
||||||
|
title: ToolChoiceFunction
|
||||||
|
ToolChoiceMcp:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
server_label:
|
||||||
|
type: string
|
||||||
|
type:
|
||||||
|
type: string
|
||||||
|
const: mcp
|
||||||
|
default: mcp
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
additionalProperties: false
|
||||||
|
required:
|
||||||
|
- server_label
|
||||||
|
- type
|
||||||
|
title: ToolChoiceMcp
|
||||||
|
ToolChoiceOptions:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- none
|
||||||
|
- auto
|
||||||
|
- required
|
||||||
|
ToolChoiceTypes:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
type:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- file_search
|
||||||
|
- web_search_preview
|
||||||
|
- computer_use_preview
|
||||||
|
- web_search_preview_2025_03_11
|
||||||
|
- image_generation
|
||||||
|
- code_interpreter
|
||||||
|
additionalProperties: false
|
||||||
|
required:
|
||||||
|
- type
|
||||||
|
title: ToolChoiceTypes
|
||||||
OpenAIResponseContentPart:
|
OpenAIResponseContentPart:
|
||||||
oneOf:
|
oneOf:
|
||||||
- $ref: '#/components/schemas/OpenAIResponseContentPartOutputText'
|
- $ref: '#/components/schemas/OpenAIResponseContentPartOutputText'
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
# This source code is licensed under the terms described in the LICENSE file in
|
# This source code is licensed under the terms described in the LICENSE file in
|
||||||
# the root directory of this source tree.
|
# the root directory of this source tree.
|
||||||
|
|
||||||
from typing import Annotated, Any, Literal, Union
|
from typing import Annotated, Any, Literal
|
||||||
|
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
from typing_extensions import TypedDict
|
from typing_extensions import TypedDict
|
||||||
|
@ -14,21 +14,20 @@ from llama_stack.apis.tools.openai_tool_choice import (
|
||||||
ToolChoiceCustom,
|
ToolChoiceCustom,
|
||||||
ToolChoiceFunction,
|
ToolChoiceFunction,
|
||||||
ToolChoiceMcp,
|
ToolChoiceMcp,
|
||||||
|
ToolChoiceOptions,
|
||||||
ToolChoiceTypes,
|
ToolChoiceTypes,
|
||||||
)
|
)
|
||||||
from llama_stack.apis.vector_io import SearchRankingOptions as FileSearchRankingOptions
|
from llama_stack.apis.vector_io import SearchRankingOptions as FileSearchRankingOptions
|
||||||
from llama_stack.schema_utils import json_schema_type, register_schema
|
from llama_stack.schema_utils import json_schema_type, register_schema
|
||||||
|
|
||||||
type OpenAIResponsesToolChoice = Annotated[
|
OpenAIResponsesToolChoice = (
|
||||||
Union[
|
ToolChoiceOptions
|
||||||
ToolChoiceTypes,
|
| ToolChoiceTypes # Multiple type values - can't use a discriminator here
|
||||||
ToolChoiceAllowed,
|
| Annotated[
|
||||||
ToolChoiceFunction,
|
ToolChoiceAllowed | ToolChoiceFunction | ToolChoiceMcp | ToolChoiceCustom,
|
||||||
ToolChoiceMcp,
|
|
||||||
ToolChoiceCustom
|
|
||||||
],
|
|
||||||
Field(discriminator="type"),
|
Field(discriminator="type"),
|
||||||
]
|
]
|
||||||
|
)
|
||||||
register_schema(OpenAIResponsesToolChoice, name="OpenAIResponsesToolChoice")
|
register_schema(OpenAIResponsesToolChoice, name="OpenAIResponsesToolChoice")
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ from pydantic import BaseModel
|
||||||
|
|
||||||
from llama_stack.schema_utils import json_schema_type, register_schema
|
from llama_stack.schema_utils import json_schema_type, register_schema
|
||||||
|
|
||||||
type ToolChoiceOptions = Literal["none", "auto", "required"]
|
ToolChoiceOptions = Literal["none", "auto", "required"]
|
||||||
register_schema(ToolChoiceOptions, name="ToolChoiceOptions")
|
register_schema(ToolChoiceOptions, name="ToolChoiceOptions")
|
||||||
|
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ class ToolChoiceTypes(BaseModel):
|
||||||
"image_generation",
|
"image_generation",
|
||||||
"code_interpreter",
|
"code_interpreter",
|
||||||
]
|
]
|
||||||
"""The type of hosted tool the model should to use.
|
"""The type of hosted tool the model should use.
|
||||||
|
|
||||||
Allowed values are:
|
Allowed values are:
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ class ToolChoiceAllowed(BaseModel):
|
||||||
```
|
```
|
||||||
"""
|
"""
|
||||||
|
|
||||||
type: Literal["allowed_tools"]
|
type: Literal["allowed_tools"] = "allowed_tools"
|
||||||
"""Allowed tool configuration type. Always `allowed_tools`."""
|
"""Allowed tool configuration type. Always `allowed_tools`."""
|
||||||
|
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ class ToolChoiceFunction(BaseModel):
|
||||||
name: str
|
name: str
|
||||||
"""The name of the function to call."""
|
"""The name of the function to call."""
|
||||||
|
|
||||||
type: Literal["function"]
|
type: Literal["function"] = "function"
|
||||||
"""For function calling, the type is always `function`."""
|
"""For function calling, the type is always `function`."""
|
||||||
|
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@ class ToolChoiceMcp(BaseModel):
|
||||||
server_label: str
|
server_label: str
|
||||||
"""The label of the MCP server to use."""
|
"""The label of the MCP server to use."""
|
||||||
|
|
||||||
type: Literal["mcp"]
|
type: Literal["mcp"] = "mcp"
|
||||||
"""For MCP tools, the type is always `mcp`."""
|
"""For MCP tools, the type is always `mcp`."""
|
||||||
|
|
||||||
name: str | None = None
|
name: str | None = None
|
||||||
|
@ -91,5 +91,5 @@ class ToolChoiceCustom(BaseModel):
|
||||||
name: str
|
name: str
|
||||||
"""The name of the custom tool to call."""
|
"""The name of the custom tool to call."""
|
||||||
|
|
||||||
type: Literal["custom"]
|
type: Literal["custom"] = "custom"
|
||||||
"""For custom tool calling, the type is always `custom`."""
|
"""For custom tool calling, the type is always `custom`."""
|
||||||
|
|
|
@ -93,14 +93,7 @@ def get_class_property_docstrings(
|
||||||
"""
|
"""
|
||||||
|
|
||||||
result = {}
|
result = {}
|
||||||
# Check if the type has __mro__ (method resolution order)
|
for base in inspect.getmro(data_type):
|
||||||
if hasattr(data_type, "__mro__"):
|
|
||||||
bases = inspect.getmro(data_type)
|
|
||||||
else:
|
|
||||||
# For TypeAliasType or other types without __mro__, just use the type itself
|
|
||||||
bases = [data_type] if hasattr(data_type, "__doc__") else []
|
|
||||||
|
|
||||||
for base in bases:
|
|
||||||
docstr = docstring.parse_type(base)
|
docstr = docstring.parse_type(base)
|
||||||
for param in docstr.params.values():
|
for param in docstr.params.values():
|
||||||
if param.name in result:
|
if param.name in result:
|
||||||
|
@ -512,24 +505,13 @@ class JsonSchemaGenerator:
|
||||||
(concrete_type,) = typing.get_args(typ)
|
(concrete_type,) = typing.get_args(typ)
|
||||||
return self.type_to_schema(concrete_type)
|
return self.type_to_schema(concrete_type)
|
||||||
|
|
||||||
# Check if this is a TypeAliasType (Python 3.12+) which doesn't have __mro__
|
|
||||||
if hasattr(typ, "__mro__"):
|
|
||||||
# dictionary of class attributes
|
# dictionary of class attributes
|
||||||
members = dict(inspect.getmembers(typ, lambda a: not inspect.isroutine(a)))
|
members = dict(inspect.getmembers(typ, lambda a: not inspect.isroutine(a)))
|
||||||
|
|
||||||
property_docstrings = get_class_property_docstrings(typ, self.options.property_description_fun)
|
property_docstrings = get_class_property_docstrings(typ, self.options.property_description_fun)
|
||||||
else:
|
|
||||||
# TypeAliasType or other types without __mro__
|
|
||||||
members = {}
|
|
||||||
property_docstrings = {}
|
|
||||||
properties: Dict[str, Schema] = {}
|
properties: Dict[str, Schema] = {}
|
||||||
required: List[str] = []
|
required: List[str] = []
|
||||||
# Only process properties if the type supports class properties
|
for property_name, property_type in get_class_properties(typ):
|
||||||
if hasattr(typ, "__mro__"):
|
|
||||||
class_properties = get_class_properties(typ)
|
|
||||||
else:
|
|
||||||
class_properties = []
|
|
||||||
|
|
||||||
for property_name, property_type in class_properties:
|
|
||||||
# rename property if an alias name is specified
|
# rename property if an alias name is specified
|
||||||
alias = get_annotation(property_type, Alias)
|
alias = get_annotation(property_type, Alias)
|
||||||
if alias:
|
if alias:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue