From f676c48a97360463b5d47fbe62bddc714401b100 Mon Sep 17 00:00:00 2001 From: Ashwin Bharambe Date: Mon, 29 Sep 2025 20:14:39 -0700 Subject: [PATCH] Add Form() annotations and fix OpenAPI generation for multipart params - Add Form() annotations to purpose and expires_after parameters in file upload endpoints - Add support for optional multipart form parameters in OpenAPI generator - Generated spec now properly mirrors OpenAI format with schema refs --- docs/openapi_generator/pyopenapi/generator.py | 14 +- docs/static/llama-stack-spec.html | 656 ++++++++++++------ docs/static/llama-stack-spec.yaml | 359 ++++++---- llama_stack/apis/files/files.py | 7 +- .../providers/inline/files/localfs/files.py | 6 +- .../providers/remote/files/s3/files.py | 6 +- llama_stack/strong_typing/inspection.py | 14 + llama_stack/strong_typing/schema.py | 5 + 8 files changed, 705 insertions(+), 362 deletions(-) diff --git a/docs/openapi_generator/pyopenapi/generator.py b/docs/openapi_generator/pyopenapi/generator.py index 758fe7e8f..a38e02e7f 100644 --- a/docs/openapi_generator/pyopenapi/generator.py +++ b/docs/openapi_generator/pyopenapi/generator.py @@ -5,6 +5,7 @@ # the root directory of this source tree. import hashlib +import inspect import ipaddress import types import typing @@ -12,6 +13,7 @@ from dataclasses import make_dataclass from typing import Annotated, Any, Dict, get_args, get_origin, Set, Union from fastapi import UploadFile +from pydantic import BaseModel from llama_stack.apis.datatypes import Error from llama_stack.strong_typing.core import JsonType @@ -632,14 +634,22 @@ class Generator: base_type = get_args(param_type)[0] else: base_type = param_type + + # Check if the type is optional + is_optional = is_type_optional(base_type) + if is_optional: + base_type = unwrap_optional_type(base_type) + if base_type is UploadFile: # File upload properties[name] = {"type": "string", "format": "binary"} else: - # Form field + # All other types - generate schema reference + # This includes enums, BaseModels, and simple types properties[name] = self.schema_builder.classdef_to_ref(base_type) - required_fields.append(name) + if not is_optional: + required_fields.append(name) multipart_schema = { "type": "object", diff --git a/docs/static/llama-stack-spec.html b/docs/static/llama-stack-spec.html index c0c1168d8..616ebb4fc 100644 --- a/docs/static/llama-stack-spec.html +++ b/docs/static/llama-stack-spec.html @@ -6081,10 +6081,17 @@ "file": { "type": "string", "format": "binary" + }, + "purpose": { + "$ref": "#/components/schemas/OpenAIFilePurpose" + }, + "expires_after": { + "$ref": "#/components/schemas/ExpiresAfter" } }, "required": [ - "file" + "file", + "purpose" ] } } @@ -6203,10 +6210,17 @@ "file": { "type": "string", "format": "binary" + }, + "purpose": { + "$ref": "#/components/schemas/OpenAIFilePurpose" + }, + "expires_after": { + "$ref": "#/components/schemas/ExpiresAfter" } }, "required": [ - "file" + "file", + "purpose" ] } } @@ -7926,7 +7940,25 @@ "type": "object", "properties": { "strategy": { - "$ref": "#/components/schemas/SamplingStrategy", + "oneOf": [ + { + "$ref": "#/components/schemas/GreedySamplingStrategy" + }, + { + "$ref": "#/components/schemas/TopPSamplingStrategy" + }, + { + "$ref": "#/components/schemas/TopKSamplingStrategy" + } + ], + "discriminator": { + "propertyName": "type", + "mapping": { + "greedy": "#/components/schemas/GreedySamplingStrategy", + "top_p": "#/components/schemas/TopPSamplingStrategy", + "top_k": "#/components/schemas/TopKSamplingStrategy" + } + }, "description": "The sampling strategy." }, "max_tokens": { @@ -7954,27 +7986,6 @@ "title": "SamplingParams", "description": "Sampling parameters." }, - "SamplingStrategy": { - "oneOf": [ - { - "$ref": "#/components/schemas/GreedySamplingStrategy" - }, - { - "$ref": "#/components/schemas/TopPSamplingStrategy" - }, - { - "$ref": "#/components/schemas/TopKSamplingStrategy" - } - ], - "discriminator": { - "propertyName": "type", - "mapping": { - "greedy": "#/components/schemas/GreedySamplingStrategy", - "top_p": "#/components/schemas/TopPSamplingStrategy", - "top_k": "#/components/schemas/TopKSamplingStrategy" - } - } - }, "SystemMessage": { "type": "object", "properties": { @@ -8557,7 +8568,25 @@ "description": "Type of the event" }, "delta": { - "$ref": "#/components/schemas/ContentDelta", + "oneOf": [ + { + "$ref": "#/components/schemas/TextDelta" + }, + { + "$ref": "#/components/schemas/ImageDelta" + }, + { + "$ref": "#/components/schemas/ToolCallDelta" + } + ], + "discriminator": { + "propertyName": "type", + "mapping": { + "text": "#/components/schemas/TextDelta", + "image": "#/components/schemas/ImageDelta", + "tool_call": "#/components/schemas/ToolCallDelta" + } + }, "description": "Content generated since last event. This can be one or more tokens, or a tool call." }, "logprobs": { @@ -8607,27 +8636,6 @@ "title": "ChatCompletionResponseStreamChunk", "description": "A chunk of a streamed chat completion response." }, - "ContentDelta": { - "oneOf": [ - { - "$ref": "#/components/schemas/TextDelta" - }, - { - "$ref": "#/components/schemas/ImageDelta" - }, - { - "$ref": "#/components/schemas/ToolCallDelta" - } - ], - "discriminator": { - "propertyName": "type", - "mapping": { - "text": "#/components/schemas/TextDelta", - "image": "#/components/schemas/ImageDelta", - "tool_call": "#/components/schemas/ToolCallDelta" - } - } - }, "ImageDelta": { "type": "object", "properties": { @@ -9556,7 +9564,37 @@ "type": "object", "properties": { "payload": { - "$ref": "#/components/schemas/AgentTurnResponseEventPayload", + "oneOf": [ + { + "$ref": "#/components/schemas/AgentTurnResponseStepStartPayload" + }, + { + "$ref": "#/components/schemas/AgentTurnResponseStepProgressPayload" + }, + { + "$ref": "#/components/schemas/AgentTurnResponseStepCompletePayload" + }, + { + "$ref": "#/components/schemas/AgentTurnResponseTurnStartPayload" + }, + { + "$ref": "#/components/schemas/AgentTurnResponseTurnCompletePayload" + }, + { + "$ref": "#/components/schemas/AgentTurnResponseTurnAwaitingInputPayload" + } + ], + "discriminator": { + "propertyName": "event_type", + "mapping": { + "step_start": "#/components/schemas/AgentTurnResponseStepStartPayload", + "step_progress": "#/components/schemas/AgentTurnResponseStepProgressPayload", + "step_complete": "#/components/schemas/AgentTurnResponseStepCompletePayload", + "turn_start": "#/components/schemas/AgentTurnResponseTurnStartPayload", + "turn_complete": "#/components/schemas/AgentTurnResponseTurnCompletePayload", + "turn_awaiting_input": "#/components/schemas/AgentTurnResponseTurnAwaitingInputPayload" + } + }, "description": "Event-specific payload containing event data" } }, @@ -9567,39 +9605,6 @@ "title": "AgentTurnResponseEvent", "description": "An event in an agent turn response stream." }, - "AgentTurnResponseEventPayload": { - "oneOf": [ - { - "$ref": "#/components/schemas/AgentTurnResponseStepStartPayload" - }, - { - "$ref": "#/components/schemas/AgentTurnResponseStepProgressPayload" - }, - { - "$ref": "#/components/schemas/AgentTurnResponseStepCompletePayload" - }, - { - "$ref": "#/components/schemas/AgentTurnResponseTurnStartPayload" - }, - { - "$ref": "#/components/schemas/AgentTurnResponseTurnCompletePayload" - }, - { - "$ref": "#/components/schemas/AgentTurnResponseTurnAwaitingInputPayload" - } - ], - "discriminator": { - "propertyName": "event_type", - "mapping": { - "step_start": "#/components/schemas/AgentTurnResponseStepStartPayload", - "step_progress": "#/components/schemas/AgentTurnResponseStepProgressPayload", - "step_complete": "#/components/schemas/AgentTurnResponseStepCompletePayload", - "turn_start": "#/components/schemas/AgentTurnResponseTurnStartPayload", - "turn_complete": "#/components/schemas/AgentTurnResponseTurnCompletePayload", - "turn_awaiting_input": "#/components/schemas/AgentTurnResponseTurnAwaitingInputPayload" - } - } - }, "AgentTurnResponseStepCompletePayload": { "type": "object", "properties": { @@ -9700,7 +9705,25 @@ "description": "Unique identifier for the step within a turn" }, "delta": { - "$ref": "#/components/schemas/ContentDelta", + "oneOf": [ + { + "$ref": "#/components/schemas/TextDelta" + }, + { + "$ref": "#/components/schemas/ImageDelta" + }, + { + "$ref": "#/components/schemas/ToolCallDelta" + } + ], + "discriminator": { + "propertyName": "type", + "mapping": { + "text": "#/components/schemas/TextDelta", + "image": "#/components/schemas/ImageDelta", + "tool_call": "#/components/schemas/ToolCallDelta" + } + }, "description": "Incremental content changes during step execution" } }, @@ -11110,23 +11133,6 @@ "title": "OpenAIResponseOutputMessageMCPListTools", "description": "MCP list tools output message containing available tools from an MCP server." }, - "OpenAIResponseContentPart": { - "oneOf": [ - { - "$ref": "#/components/schemas/OpenAIResponseContentPartOutputText" - }, - { - "$ref": "#/components/schemas/OpenAIResponseContentPartRefusal" - } - ], - "discriminator": { - "propertyName": "type", - "mapping": { - "output_text": "#/components/schemas/OpenAIResponseContentPartOutputText", - "refusal": "#/components/schemas/OpenAIResponseContentPartRefusal" - } - } - }, "OpenAIResponseContentPartOutputText": { "type": "object", "properties": { @@ -11292,7 +11298,21 @@ "description": "Unique identifier of the output item containing this content part" }, "part": { - "$ref": "#/components/schemas/OpenAIResponseContentPart", + "oneOf": [ + { + "$ref": "#/components/schemas/OpenAIResponseContentPartOutputText" + }, + { + "$ref": "#/components/schemas/OpenAIResponseContentPartRefusal" + } + ], + "discriminator": { + "propertyName": "type", + "mapping": { + "output_text": "#/components/schemas/OpenAIResponseContentPartOutputText", + "refusal": "#/components/schemas/OpenAIResponseContentPartRefusal" + } + }, "description": "The content part that was added" }, "sequence_number": { @@ -11329,7 +11349,21 @@ "description": "Unique identifier of the output item containing this content part" }, "part": { - "$ref": "#/components/schemas/OpenAIResponseContentPart", + "oneOf": [ + { + "$ref": "#/components/schemas/OpenAIResponseContentPartOutputText" + }, + { + "$ref": "#/components/schemas/OpenAIResponseContentPartRefusal" + } + ], + "discriminator": { + "propertyName": "type", + "mapping": { + "output_text": "#/components/schemas/OpenAIResponseContentPartOutputText", + "refusal": "#/components/schemas/OpenAIResponseContentPartRefusal" + } + }, "description": "The completed content part" }, "sequence_number": { @@ -11653,7 +11687,37 @@ "description": "Unique identifier of the response containing this output" }, "item": { - "$ref": "#/components/schemas/OpenAIResponseOutput", + "oneOf": [ + { + "$ref": "#/components/schemas/OpenAIResponseMessage" + }, + { + "$ref": "#/components/schemas/OpenAIResponseOutputMessageWebSearchToolCall" + }, + { + "$ref": "#/components/schemas/OpenAIResponseOutputMessageFileSearchToolCall" + }, + { + "$ref": "#/components/schemas/OpenAIResponseOutputMessageFunctionToolCall" + }, + { + "$ref": "#/components/schemas/OpenAIResponseOutputMessageMCPCall" + }, + { + "$ref": "#/components/schemas/OpenAIResponseOutputMessageMCPListTools" + } + ], + "discriminator": { + "propertyName": "type", + "mapping": { + "message": "#/components/schemas/OpenAIResponseMessage", + "web_search_call": "#/components/schemas/OpenAIResponseOutputMessageWebSearchToolCall", + "file_search_call": "#/components/schemas/OpenAIResponseOutputMessageFileSearchToolCall", + "function_call": "#/components/schemas/OpenAIResponseOutputMessageFunctionToolCall", + "mcp_call": "#/components/schemas/OpenAIResponseOutputMessageMCPCall", + "mcp_list_tools": "#/components/schemas/OpenAIResponseOutputMessageMCPListTools" + } + }, "description": "The output item that was added (message, tool call, etc.)" }, "output_index": { @@ -11690,7 +11754,37 @@ "description": "Unique identifier of the response containing this output" }, "item": { - "$ref": "#/components/schemas/OpenAIResponseOutput", + "oneOf": [ + { + "$ref": "#/components/schemas/OpenAIResponseMessage" + }, + { + "$ref": "#/components/schemas/OpenAIResponseOutputMessageWebSearchToolCall" + }, + { + "$ref": "#/components/schemas/OpenAIResponseOutputMessageFileSearchToolCall" + }, + { + "$ref": "#/components/schemas/OpenAIResponseOutputMessageFunctionToolCall" + }, + { + "$ref": "#/components/schemas/OpenAIResponseOutputMessageMCPCall" + }, + { + "$ref": "#/components/schemas/OpenAIResponseOutputMessageMCPListTools" + } + ], + "discriminator": { + "propertyName": "type", + "mapping": { + "message": "#/components/schemas/OpenAIResponseMessage", + "web_search_call": "#/components/schemas/OpenAIResponseOutputMessageWebSearchToolCall", + "file_search_call": "#/components/schemas/OpenAIResponseOutputMessageFileSearchToolCall", + "function_call": "#/components/schemas/OpenAIResponseOutputMessageFunctionToolCall", + "mcp_call": "#/components/schemas/OpenAIResponseOutputMessageMCPCall", + "mcp_list_tools": "#/components/schemas/OpenAIResponseOutputMessageMCPListTools" + } + }, "description": "The completed output item (message, tool call, etc.)" }, "output_index": { @@ -12043,7 +12137,21 @@ "type": "object", "properties": { "eval_candidate": { - "$ref": "#/components/schemas/EvalCandidate", + "oneOf": [ + { + "$ref": "#/components/schemas/ModelCandidate" + }, + { + "$ref": "#/components/schemas/AgentCandidate" + } + ], + "discriminator": { + "propertyName": "type", + "mapping": { + "model": "#/components/schemas/ModelCandidate", + "agent": "#/components/schemas/AgentCandidate" + } + }, "description": "The candidate to evaluate." }, "scoring_params": { @@ -12066,23 +12174,6 @@ "title": "BenchmarkConfig", "description": "A benchmark configuration for evaluation." }, - "EvalCandidate": { - "oneOf": [ - { - "$ref": "#/components/schemas/ModelCandidate" - }, - { - "$ref": "#/components/schemas/AgentCandidate" - } - ], - "discriminator": { - "propertyName": "type", - "mapping": { - "model": "#/components/schemas/ModelCandidate", - "agent": "#/components/schemas/AgentCandidate" - } - } - }, "LLMAsJudgeScoringFnParams": { "type": "object", "properties": { @@ -12718,7 +12809,33 @@ "type": "object", "properties": { "message": { - "$ref": "#/components/schemas/OpenAIMessageParam", + "oneOf": [ + { + "$ref": "#/components/schemas/OpenAIUserMessageParam" + }, + { + "$ref": "#/components/schemas/OpenAISystemMessageParam" + }, + { + "$ref": "#/components/schemas/OpenAIAssistantMessageParam" + }, + { + "$ref": "#/components/schemas/OpenAIToolMessageParam" + }, + { + "$ref": "#/components/schemas/OpenAIDeveloperMessageParam" + } + ], + "discriminator": { + "propertyName": "role", + "mapping": { + "user": "#/components/schemas/OpenAIUserMessageParam", + "system": "#/components/schemas/OpenAISystemMessageParam", + "assistant": "#/components/schemas/OpenAIAssistantMessageParam", + "tool": "#/components/schemas/OpenAIToolMessageParam", + "developer": "#/components/schemas/OpenAIDeveloperMessageParam" + } + }, "description": "The message from the model" }, "finish_reason": { @@ -13094,23 +13211,6 @@ ], "title": "OpenAICompletionWithInputMessages" }, - "DataSource": { - "oneOf": [ - { - "$ref": "#/components/schemas/URIDataSource" - }, - { - "$ref": "#/components/schemas/RowsDataSource" - } - ], - "discriminator": { - "propertyName": "type", - "mapping": { - "uri": "#/components/schemas/URIDataSource", - "rows": "#/components/schemas/RowsDataSource" - } - } - }, "Dataset": { "type": "object", "properties": { @@ -13150,7 +13250,21 @@ "description": "Purpose of the dataset indicating its intended use" }, "source": { - "$ref": "#/components/schemas/DataSource", + "oneOf": [ + { + "$ref": "#/components/schemas/URIDataSource" + }, + { + "$ref": "#/components/schemas/RowsDataSource" + } + ], + "discriminator": { + "propertyName": "type", + "mapping": { + "uri": "#/components/schemas/URIDataSource", + "rows": "#/components/schemas/RowsDataSource" + } + }, "description": "Data source configuration for the dataset" }, "metadata": { @@ -13479,55 +13593,6 @@ "title": "ObjectType", "description": "Parameter type for object values." }, - "ParamType": { - "oneOf": [ - { - "$ref": "#/components/schemas/StringType" - }, - { - "$ref": "#/components/schemas/NumberType" - }, - { - "$ref": "#/components/schemas/BooleanType" - }, - { - "$ref": "#/components/schemas/ArrayType" - }, - { - "$ref": "#/components/schemas/ObjectType" - }, - { - "$ref": "#/components/schemas/JsonType" - }, - { - "$ref": "#/components/schemas/UnionType" - }, - { - "$ref": "#/components/schemas/ChatCompletionInputType" - }, - { - "$ref": "#/components/schemas/CompletionInputType" - }, - { - "$ref": "#/components/schemas/AgentTurnInputType" - } - ], - "discriminator": { - "propertyName": "type", - "mapping": { - "string": "#/components/schemas/StringType", - "number": "#/components/schemas/NumberType", - "boolean": "#/components/schemas/BooleanType", - "array": "#/components/schemas/ArrayType", - "object": "#/components/schemas/ObjectType", - "json": "#/components/schemas/JsonType", - "union": "#/components/schemas/UnionType", - "chat_completion_input": "#/components/schemas/ChatCompletionInputType", - "completion_input": "#/components/schemas/CompletionInputType", - "agent_turn_input": "#/components/schemas/AgentTurnInputType" - } - } - }, "ScoringFn": { "type": "object", "properties": { @@ -13586,7 +13651,53 @@ } }, "return_type": { - "$ref": "#/components/schemas/ParamType" + "oneOf": [ + { + "$ref": "#/components/schemas/StringType" + }, + { + "$ref": "#/components/schemas/NumberType" + }, + { + "$ref": "#/components/schemas/BooleanType" + }, + { + "$ref": "#/components/schemas/ArrayType" + }, + { + "$ref": "#/components/schemas/ObjectType" + }, + { + "$ref": "#/components/schemas/JsonType" + }, + { + "$ref": "#/components/schemas/UnionType" + }, + { + "$ref": "#/components/schemas/ChatCompletionInputType" + }, + { + "$ref": "#/components/schemas/CompletionInputType" + }, + { + "$ref": "#/components/schemas/AgentTurnInputType" + } + ], + "discriminator": { + "propertyName": "type", + "mapping": { + "string": "#/components/schemas/StringType", + "number": "#/components/schemas/NumberType", + "boolean": "#/components/schemas/BooleanType", + "array": "#/components/schemas/ArrayType", + "object": "#/components/schemas/ObjectType", + "json": "#/components/schemas/JsonType", + "union": "#/components/schemas/UnionType", + "chat_completion_input": "#/components/schemas/ChatCompletionInputType", + "completion_input": "#/components/schemas/CompletionInputType", + "agent_turn_input": "#/components/schemas/AgentTurnInputType" + } + } }, "params": { "$ref": "#/components/schemas/ScoringFnParams" @@ -15496,7 +15607,21 @@ "description": "Event type identifier set to STRUCTURED_LOG" }, "payload": { - "$ref": "#/components/schemas/StructuredLogPayload", + "oneOf": [ + { + "$ref": "#/components/schemas/SpanStartPayload" + }, + { + "$ref": "#/components/schemas/SpanEndPayload" + } + ], + "discriminator": { + "propertyName": "type", + "mapping": { + "span_start": "#/components/schemas/SpanStartPayload", + "span_end": "#/components/schemas/SpanEndPayload" + } + }, "description": "The structured payload data for the log event" } }, @@ -15511,23 +15636,6 @@ "title": "StructuredLogEvent", "description": "A structured log event containing typed payload data." }, - "StructuredLogPayload": { - "oneOf": [ - { - "$ref": "#/components/schemas/SpanStartPayload" - }, - { - "$ref": "#/components/schemas/SpanEndPayload" - } - ], - "discriminator": { - "propertyName": "type", - "mapping": { - "span_start": "#/components/schemas/SpanStartPayload", - "span_end": "#/components/schemas/SpanEndPayload" - } - } - }, "StructuredLogType": { "type": "string", "enum": [ @@ -15812,7 +15920,21 @@ "description": "Key-value attributes associated with the file" }, "chunking_strategy": { - "$ref": "#/components/schemas/VectorStoreChunkingStrategy", + "oneOf": [ + { + "$ref": "#/components/schemas/VectorStoreChunkingStrategyAuto" + }, + { + "$ref": "#/components/schemas/VectorStoreChunkingStrategyStatic" + } + ], + "discriminator": { + "propertyName": "type", + "mapping": { + "auto": "#/components/schemas/VectorStoreChunkingStrategyAuto", + "static": "#/components/schemas/VectorStoreChunkingStrategyStatic" + } + }, "description": "Strategy used for splitting the file into chunks" }, "created_at": { @@ -17625,6 +17747,25 @@ ], "title": "OpenaiUpdateVectorStoreFileRequest" }, + "ExpiresAfter": { + "type": "object", + "properties": { + "anchor": { + "type": "string", + "const": "created_at" + }, + "seconds": { + "type": "integer" + } + }, + "additionalProperties": false, + "required": [ + "anchor", + "seconds" + ], + "title": "ExpiresAfter", + "description": "Control expiration of uploaded files.\nParams:\n - anchor, must be \"created_at\"\n - seconds, must be int between 3600 and 2592000 (1 hour to 30 days)" + }, "DPOAlignmentConfig": { "type": "object", "properties": { @@ -17976,7 +18117,21 @@ "type": "object", "properties": { "query_generator_config": { - "$ref": "#/components/schemas/RAGQueryGeneratorConfig", + "oneOf": [ + { + "$ref": "#/components/schemas/DefaultRAGQueryGeneratorConfig" + }, + { + "$ref": "#/components/schemas/LLMRAGQueryGeneratorConfig" + } + ], + "discriminator": { + "propertyName": "type", + "mapping": { + "default": "#/components/schemas/DefaultRAGQueryGeneratorConfig", + "llm": "#/components/schemas/LLMRAGQueryGeneratorConfig" + } + }, "description": "Configuration for the query generator." }, "max_tokens_in_context": { @@ -18014,23 +18169,6 @@ "title": "RAGQueryConfig", "description": "Configuration for the RAG query generation." }, - "RAGQueryGeneratorConfig": { - "oneOf": [ - { - "$ref": "#/components/schemas/DefaultRAGQueryGeneratorConfig" - }, - { - "$ref": "#/components/schemas/LLMRAGQueryGeneratorConfig" - } - ], - "discriminator": { - "propertyName": "type", - "mapping": { - "default": "#/components/schemas/DefaultRAGQueryGeneratorConfig", - "llm": "#/components/schemas/LLMRAGQueryGeneratorConfig" - } - } - }, "RAGSearchMode": { "type": "string", "enum": [ @@ -18612,6 +18750,23 @@ ], "title": "RegisterBenchmarkRequest" }, + "DataSource": { + "oneOf": [ + { + "$ref": "#/components/schemas/URIDataSource" + }, + { + "$ref": "#/components/schemas/RowsDataSource" + } + ], + "discriminator": { + "propertyName": "type", + "mapping": { + "uri": "#/components/schemas/URIDataSource", + "rows": "#/components/schemas/RowsDataSource" + } + } + }, "RegisterDatasetRequest": { "type": "object", "properties": { @@ -18718,6 +18873,55 @@ ], "title": "RegisterModelRequest" }, + "ParamType": { + "oneOf": [ + { + "$ref": "#/components/schemas/StringType" + }, + { + "$ref": "#/components/schemas/NumberType" + }, + { + "$ref": "#/components/schemas/BooleanType" + }, + { + "$ref": "#/components/schemas/ArrayType" + }, + { + "$ref": "#/components/schemas/ObjectType" + }, + { + "$ref": "#/components/schemas/JsonType" + }, + { + "$ref": "#/components/schemas/UnionType" + }, + { + "$ref": "#/components/schemas/ChatCompletionInputType" + }, + { + "$ref": "#/components/schemas/CompletionInputType" + }, + { + "$ref": "#/components/schemas/AgentTurnInputType" + } + ], + "discriminator": { + "propertyName": "type", + "mapping": { + "string": "#/components/schemas/StringType", + "number": "#/components/schemas/NumberType", + "boolean": "#/components/schemas/BooleanType", + "array": "#/components/schemas/ArrayType", + "object": "#/components/schemas/ObjectType", + "json": "#/components/schemas/JsonType", + "union": "#/components/schemas/UnionType", + "chat_completion_input": "#/components/schemas/ChatCompletionInputType", + "completion_input": "#/components/schemas/CompletionInputType", + "agent_turn_input": "#/components/schemas/AgentTurnInputType" + } + } + }, "RegisterScoringFunctionRequest": { "type": "object", "properties": { diff --git a/docs/static/llama-stack-spec.yaml b/docs/static/llama-stack-spec.yaml index 5e3a44813..fe86b0ff0 100644 --- a/docs/static/llama-stack-spec.yaml +++ b/docs/static/llama-stack-spec.yaml @@ -4393,8 +4393,13 @@ paths: file: type: string format: binary + purpose: + $ref: '#/components/schemas/OpenAIFilePurpose' + expires_after: + $ref: '#/components/schemas/ExpiresAfter' required: - file + - purpose required: true /v1/openai/v1/files: get: @@ -4499,8 +4504,13 @@ paths: file: type: string format: binary + purpose: + $ref: '#/components/schemas/OpenAIFilePurpose' + expires_after: + $ref: '#/components/schemas/ExpiresAfter' required: - file + - purpose required: true /v1/openai/v1/models: get: @@ -5733,7 +5743,16 @@ components: type: object properties: strategy: - $ref: '#/components/schemas/SamplingStrategy' + oneOf: + - $ref: '#/components/schemas/GreedySamplingStrategy' + - $ref: '#/components/schemas/TopPSamplingStrategy' + - $ref: '#/components/schemas/TopKSamplingStrategy' + discriminator: + propertyName: type + mapping: + greedy: '#/components/schemas/GreedySamplingStrategy' + top_p: '#/components/schemas/TopPSamplingStrategy' + top_k: '#/components/schemas/TopKSamplingStrategy' description: The sampling strategy. max_tokens: type: integer @@ -5761,17 +5780,6 @@ components: - strategy title: SamplingParams description: Sampling parameters. - SamplingStrategy: - oneOf: - - $ref: '#/components/schemas/GreedySamplingStrategy' - - $ref: '#/components/schemas/TopPSamplingStrategy' - - $ref: '#/components/schemas/TopKSamplingStrategy' - discriminator: - propertyName: type - mapping: - greedy: '#/components/schemas/GreedySamplingStrategy' - top_p: '#/components/schemas/TopPSamplingStrategy' - top_k: '#/components/schemas/TopKSamplingStrategy' SystemMessage: type: object properties: @@ -6218,7 +6226,16 @@ components: - progress description: Type of the event delta: - $ref: '#/components/schemas/ContentDelta' + oneOf: + - $ref: '#/components/schemas/TextDelta' + - $ref: '#/components/schemas/ImageDelta' + - $ref: '#/components/schemas/ToolCallDelta' + discriminator: + propertyName: type + mapping: + text: '#/components/schemas/TextDelta' + image: '#/components/schemas/ImageDelta' + tool_call: '#/components/schemas/ToolCallDelta' description: >- Content generated since last event. This can be one or more tokens, or a tool call. @@ -6261,17 +6278,6 @@ components: title: ChatCompletionResponseStreamChunk description: >- A chunk of a streamed chat completion response. - ContentDelta: - oneOf: - - $ref: '#/components/schemas/TextDelta' - - $ref: '#/components/schemas/ImageDelta' - - $ref: '#/components/schemas/ToolCallDelta' - discriminator: - propertyName: type - mapping: - text: '#/components/schemas/TextDelta' - image: '#/components/schemas/ImageDelta' - tool_call: '#/components/schemas/ToolCallDelta' ImageDelta: type: object properties: @@ -6953,7 +6959,22 @@ components: type: object properties: payload: - $ref: '#/components/schemas/AgentTurnResponseEventPayload' + oneOf: + - $ref: '#/components/schemas/AgentTurnResponseStepStartPayload' + - $ref: '#/components/schemas/AgentTurnResponseStepProgressPayload' + - $ref: '#/components/schemas/AgentTurnResponseStepCompletePayload' + - $ref: '#/components/schemas/AgentTurnResponseTurnStartPayload' + - $ref: '#/components/schemas/AgentTurnResponseTurnCompletePayload' + - $ref: '#/components/schemas/AgentTurnResponseTurnAwaitingInputPayload' + discriminator: + propertyName: event_type + mapping: + step_start: '#/components/schemas/AgentTurnResponseStepStartPayload' + step_progress: '#/components/schemas/AgentTurnResponseStepProgressPayload' + step_complete: '#/components/schemas/AgentTurnResponseStepCompletePayload' + turn_start: '#/components/schemas/AgentTurnResponseTurnStartPayload' + turn_complete: '#/components/schemas/AgentTurnResponseTurnCompletePayload' + turn_awaiting_input: '#/components/schemas/AgentTurnResponseTurnAwaitingInputPayload' description: >- Event-specific payload containing event data additionalProperties: false @@ -6962,23 +6983,6 @@ components: title: AgentTurnResponseEvent description: >- An event in an agent turn response stream. - AgentTurnResponseEventPayload: - oneOf: - - $ref: '#/components/schemas/AgentTurnResponseStepStartPayload' - - $ref: '#/components/schemas/AgentTurnResponseStepProgressPayload' - - $ref: '#/components/schemas/AgentTurnResponseStepCompletePayload' - - $ref: '#/components/schemas/AgentTurnResponseTurnStartPayload' - - $ref: '#/components/schemas/AgentTurnResponseTurnCompletePayload' - - $ref: '#/components/schemas/AgentTurnResponseTurnAwaitingInputPayload' - discriminator: - propertyName: event_type - mapping: - step_start: '#/components/schemas/AgentTurnResponseStepStartPayload' - step_progress: '#/components/schemas/AgentTurnResponseStepProgressPayload' - step_complete: '#/components/schemas/AgentTurnResponseStepCompletePayload' - turn_start: '#/components/schemas/AgentTurnResponseTurnStartPayload' - turn_complete: '#/components/schemas/AgentTurnResponseTurnCompletePayload' - turn_awaiting_input: '#/components/schemas/AgentTurnResponseTurnAwaitingInputPayload' AgentTurnResponseStepCompletePayload: type: object properties: @@ -7057,7 +7061,16 @@ components: description: >- Unique identifier for the step within a turn delta: - $ref: '#/components/schemas/ContentDelta' + oneOf: + - $ref: '#/components/schemas/TextDelta' + - $ref: '#/components/schemas/ImageDelta' + - $ref: '#/components/schemas/ToolCallDelta' + discriminator: + propertyName: type + mapping: + text: '#/components/schemas/TextDelta' + image: '#/components/schemas/ImageDelta' + tool_call: '#/components/schemas/ToolCallDelta' description: >- Incremental content changes during step execution additionalProperties: false @@ -8126,15 +8139,6 @@ components: title: OpenAIResponseOutputMessageMCPListTools description: >- MCP list tools output message containing available tools from an MCP server. - OpenAIResponseContentPart: - oneOf: - - $ref: '#/components/schemas/OpenAIResponseContentPartOutputText' - - $ref: '#/components/schemas/OpenAIResponseContentPartRefusal' - discriminator: - propertyName: type - mapping: - output_text: '#/components/schemas/OpenAIResponseContentPartOutputText' - refusal: '#/components/schemas/OpenAIResponseContentPartRefusal' OpenAIResponseContentPartOutputText: type: object properties: @@ -8242,7 +8246,14 @@ components: description: >- Unique identifier of the output item containing this content part part: - $ref: '#/components/schemas/OpenAIResponseContentPart' + oneOf: + - $ref: '#/components/schemas/OpenAIResponseContentPartOutputText' + - $ref: '#/components/schemas/OpenAIResponseContentPartRefusal' + discriminator: + propertyName: type + mapping: + output_text: '#/components/schemas/OpenAIResponseContentPartOutputText' + refusal: '#/components/schemas/OpenAIResponseContentPartRefusal' description: The content part that was added sequence_number: type: integer @@ -8277,7 +8288,14 @@ components: description: >- Unique identifier of the output item containing this content part part: - $ref: '#/components/schemas/OpenAIResponseContentPart' + oneOf: + - $ref: '#/components/schemas/OpenAIResponseContentPartOutputText' + - $ref: '#/components/schemas/OpenAIResponseContentPartRefusal' + discriminator: + propertyName: type + mapping: + output_text: '#/components/schemas/OpenAIResponseContentPartOutputText' + refusal: '#/components/schemas/OpenAIResponseContentPartRefusal' description: The completed content part sequence_number: type: integer @@ -8563,7 +8581,22 @@ components: description: >- Unique identifier of the response containing this output item: - $ref: '#/components/schemas/OpenAIResponseOutput' + oneOf: + - $ref: '#/components/schemas/OpenAIResponseMessage' + - $ref: '#/components/schemas/OpenAIResponseOutputMessageWebSearchToolCall' + - $ref: '#/components/schemas/OpenAIResponseOutputMessageFileSearchToolCall' + - $ref: '#/components/schemas/OpenAIResponseOutputMessageFunctionToolCall' + - $ref: '#/components/schemas/OpenAIResponseOutputMessageMCPCall' + - $ref: '#/components/schemas/OpenAIResponseOutputMessageMCPListTools' + discriminator: + propertyName: type + mapping: + message: '#/components/schemas/OpenAIResponseMessage' + web_search_call: '#/components/schemas/OpenAIResponseOutputMessageWebSearchToolCall' + file_search_call: '#/components/schemas/OpenAIResponseOutputMessageFileSearchToolCall' + function_call: '#/components/schemas/OpenAIResponseOutputMessageFunctionToolCall' + mcp_call: '#/components/schemas/OpenAIResponseOutputMessageMCPCall' + mcp_list_tools: '#/components/schemas/OpenAIResponseOutputMessageMCPListTools' description: >- The output item that was added (message, tool call, etc.) output_index: @@ -8599,7 +8632,22 @@ components: description: >- Unique identifier of the response containing this output item: - $ref: '#/components/schemas/OpenAIResponseOutput' + oneOf: + - $ref: '#/components/schemas/OpenAIResponseMessage' + - $ref: '#/components/schemas/OpenAIResponseOutputMessageWebSearchToolCall' + - $ref: '#/components/schemas/OpenAIResponseOutputMessageFileSearchToolCall' + - $ref: '#/components/schemas/OpenAIResponseOutputMessageFunctionToolCall' + - $ref: '#/components/schemas/OpenAIResponseOutputMessageMCPCall' + - $ref: '#/components/schemas/OpenAIResponseOutputMessageMCPListTools' + discriminator: + propertyName: type + mapping: + message: '#/components/schemas/OpenAIResponseMessage' + web_search_call: '#/components/schemas/OpenAIResponseOutputMessageWebSearchToolCall' + file_search_call: '#/components/schemas/OpenAIResponseOutputMessageFileSearchToolCall' + function_call: '#/components/schemas/OpenAIResponseOutputMessageFunctionToolCall' + mcp_call: '#/components/schemas/OpenAIResponseOutputMessageMCPCall' + mcp_list_tools: '#/components/schemas/OpenAIResponseOutputMessageMCPListTools' description: >- The completed output item (message, tool call, etc.) output_index: @@ -8922,7 +8970,14 @@ components: type: object properties: eval_candidate: - $ref: '#/components/schemas/EvalCandidate' + oneOf: + - $ref: '#/components/schemas/ModelCandidate' + - $ref: '#/components/schemas/AgentCandidate' + discriminator: + propertyName: type + mapping: + model: '#/components/schemas/ModelCandidate' + agent: '#/components/schemas/AgentCandidate' description: The candidate to evaluate. scoring_params: type: object @@ -8943,15 +8998,6 @@ components: title: BenchmarkConfig description: >- A benchmark configuration for evaluation. - EvalCandidate: - oneOf: - - $ref: '#/components/schemas/ModelCandidate' - - $ref: '#/components/schemas/AgentCandidate' - discriminator: - propertyName: type - mapping: - model: '#/components/schemas/ModelCandidate' - agent: '#/components/schemas/AgentCandidate' LLMAsJudgeScoringFnParams: type: object properties: @@ -9415,7 +9461,20 @@ components: type: object properties: message: - $ref: '#/components/schemas/OpenAIMessageParam' + oneOf: + - $ref: '#/components/schemas/OpenAIUserMessageParam' + - $ref: '#/components/schemas/OpenAISystemMessageParam' + - $ref: '#/components/schemas/OpenAIAssistantMessageParam' + - $ref: '#/components/schemas/OpenAIToolMessageParam' + - $ref: '#/components/schemas/OpenAIDeveloperMessageParam' + discriminator: + propertyName: role + mapping: + user: '#/components/schemas/OpenAIUserMessageParam' + system: '#/components/schemas/OpenAISystemMessageParam' + assistant: '#/components/schemas/OpenAIAssistantMessageParam' + tool: '#/components/schemas/OpenAIToolMessageParam' + developer: '#/components/schemas/OpenAIDeveloperMessageParam' description: The message from the model finish_reason: type: string @@ -9708,15 +9767,6 @@ components: - model - input_messages title: OpenAICompletionWithInputMessages - DataSource: - oneOf: - - $ref: '#/components/schemas/URIDataSource' - - $ref: '#/components/schemas/RowsDataSource' - discriminator: - propertyName: type - mapping: - uri: '#/components/schemas/URIDataSource' - rows: '#/components/schemas/RowsDataSource' Dataset: type: object properties: @@ -9751,7 +9801,14 @@ components: description: >- Purpose of the dataset indicating its intended use source: - $ref: '#/components/schemas/DataSource' + oneOf: + - $ref: '#/components/schemas/URIDataSource' + - $ref: '#/components/schemas/RowsDataSource' + discriminator: + propertyName: type + mapping: + uri: '#/components/schemas/URIDataSource' + rows: '#/components/schemas/RowsDataSource' description: >- Data source configuration for the dataset metadata: @@ -9997,31 +10054,6 @@ components: - type title: ObjectType description: Parameter type for object values. - ParamType: - oneOf: - - $ref: '#/components/schemas/StringType' - - $ref: '#/components/schemas/NumberType' - - $ref: '#/components/schemas/BooleanType' - - $ref: '#/components/schemas/ArrayType' - - $ref: '#/components/schemas/ObjectType' - - $ref: '#/components/schemas/JsonType' - - $ref: '#/components/schemas/UnionType' - - $ref: '#/components/schemas/ChatCompletionInputType' - - $ref: '#/components/schemas/CompletionInputType' - - $ref: '#/components/schemas/AgentTurnInputType' - discriminator: - propertyName: type - mapping: - string: '#/components/schemas/StringType' - number: '#/components/schemas/NumberType' - boolean: '#/components/schemas/BooleanType' - array: '#/components/schemas/ArrayType' - object: '#/components/schemas/ObjectType' - json: '#/components/schemas/JsonType' - union: '#/components/schemas/UnionType' - chat_completion_input: '#/components/schemas/ChatCompletionInputType' - completion_input: '#/components/schemas/CompletionInputType' - agent_turn_input: '#/components/schemas/AgentTurnInputType' ScoringFn: type: object properties: @@ -10060,7 +10092,30 @@ components: - type: array - type: object return_type: - $ref: '#/components/schemas/ParamType' + oneOf: + - $ref: '#/components/schemas/StringType' + - $ref: '#/components/schemas/NumberType' + - $ref: '#/components/schemas/BooleanType' + - $ref: '#/components/schemas/ArrayType' + - $ref: '#/components/schemas/ObjectType' + - $ref: '#/components/schemas/JsonType' + - $ref: '#/components/schemas/UnionType' + - $ref: '#/components/schemas/ChatCompletionInputType' + - $ref: '#/components/schemas/CompletionInputType' + - $ref: '#/components/schemas/AgentTurnInputType' + discriminator: + propertyName: type + mapping: + string: '#/components/schemas/StringType' + number: '#/components/schemas/NumberType' + boolean: '#/components/schemas/BooleanType' + array: '#/components/schemas/ArrayType' + object: '#/components/schemas/ObjectType' + json: '#/components/schemas/JsonType' + union: '#/components/schemas/UnionType' + chat_completion_input: '#/components/schemas/ChatCompletionInputType' + completion_input: '#/components/schemas/CompletionInputType' + agent_turn_input: '#/components/schemas/AgentTurnInputType' params: $ref: '#/components/schemas/ScoringFnParams' additionalProperties: false @@ -11512,7 +11567,14 @@ components: description: >- Event type identifier set to STRUCTURED_LOG payload: - $ref: '#/components/schemas/StructuredLogPayload' + oneOf: + - $ref: '#/components/schemas/SpanStartPayload' + - $ref: '#/components/schemas/SpanEndPayload' + discriminator: + propertyName: type + mapping: + span_start: '#/components/schemas/SpanStartPayload' + span_end: '#/components/schemas/SpanEndPayload' description: >- The structured payload data for the log event additionalProperties: false @@ -11525,15 +11587,6 @@ components: title: StructuredLogEvent description: >- A structured log event containing typed payload data. - StructuredLogPayload: - oneOf: - - $ref: '#/components/schemas/SpanStartPayload' - - $ref: '#/components/schemas/SpanEndPayload' - discriminator: - propertyName: type - mapping: - span_start: '#/components/schemas/SpanStartPayload' - span_end: '#/components/schemas/SpanEndPayload' StructuredLogType: type: string enum: @@ -11742,7 +11795,14 @@ components: description: >- Key-value attributes associated with the file chunking_strategy: - $ref: '#/components/schemas/VectorStoreChunkingStrategy' + oneOf: + - $ref: '#/components/schemas/VectorStoreChunkingStrategyAuto' + - $ref: '#/components/schemas/VectorStoreChunkingStrategyStatic' + discriminator: + propertyName: type + mapping: + auto: '#/components/schemas/VectorStoreChunkingStrategyAuto' + static: '#/components/schemas/VectorStoreChunkingStrategyStatic' description: >- Strategy used for splitting the file into chunks created_at: @@ -13054,6 +13114,25 @@ components: required: - attributes title: OpenaiUpdateVectorStoreFileRequest + ExpiresAfter: + type: object + properties: + anchor: + type: string + const: created_at + seconds: + type: integer + additionalProperties: false + required: + - anchor + - seconds + title: ExpiresAfter + description: >- + Control expiration of uploaded files. + + Params: + - anchor, must be "created_at" + - seconds, must be int between 3600 and 2592000 (1 hour to 30 days) DPOAlignmentConfig: type: object properties: @@ -13339,7 +13418,14 @@ components: type: object properties: query_generator_config: - $ref: '#/components/schemas/RAGQueryGeneratorConfig' + oneOf: + - $ref: '#/components/schemas/DefaultRAGQueryGeneratorConfig' + - $ref: '#/components/schemas/LLMRAGQueryGeneratorConfig' + discriminator: + propertyName: type + mapping: + default: '#/components/schemas/DefaultRAGQueryGeneratorConfig' + llm: '#/components/schemas/LLMRAGQueryGeneratorConfig' description: Configuration for the query generator. max_tokens_in_context: type: integer @@ -13382,15 +13468,6 @@ components: title: RAGQueryConfig description: >- Configuration for the RAG query generation. - RAGQueryGeneratorConfig: - oneOf: - - $ref: '#/components/schemas/DefaultRAGQueryGeneratorConfig' - - $ref: '#/components/schemas/LLMRAGQueryGeneratorConfig' - discriminator: - propertyName: type - mapping: - default: '#/components/schemas/DefaultRAGQueryGeneratorConfig' - llm: '#/components/schemas/LLMRAGQueryGeneratorConfig' RAGSearchMode: type: string enum: @@ -13826,6 +13903,15 @@ components: - dataset_id - scoring_functions title: RegisterBenchmarkRequest + DataSource: + oneOf: + - $ref: '#/components/schemas/URIDataSource' + - $ref: '#/components/schemas/RowsDataSource' + discriminator: + propertyName: type + mapping: + uri: '#/components/schemas/URIDataSource' + rows: '#/components/schemas/RowsDataSource' RegisterDatasetRequest: type: object properties: @@ -13910,6 +13996,31 @@ components: required: - model_id title: RegisterModelRequest + ParamType: + oneOf: + - $ref: '#/components/schemas/StringType' + - $ref: '#/components/schemas/NumberType' + - $ref: '#/components/schemas/BooleanType' + - $ref: '#/components/schemas/ArrayType' + - $ref: '#/components/schemas/ObjectType' + - $ref: '#/components/schemas/JsonType' + - $ref: '#/components/schemas/UnionType' + - $ref: '#/components/schemas/ChatCompletionInputType' + - $ref: '#/components/schemas/CompletionInputType' + - $ref: '#/components/schemas/AgentTurnInputType' + discriminator: + propertyName: type + mapping: + string: '#/components/schemas/StringType' + number: '#/components/schemas/NumberType' + boolean: '#/components/schemas/BooleanType' + array: '#/components/schemas/ArrayType' + object: '#/components/schemas/ObjectType' + json: '#/components/schemas/JsonType' + union: '#/components/schemas/UnionType' + chat_completion_input: '#/components/schemas/ChatCompletionInputType' + completion_input: '#/components/schemas/CompletionInputType' + agent_turn_input: '#/components/schemas/AgentTurnInputType' RegisterScoringFunctionRequest: type: object properties: diff --git a/llama_stack/apis/files/files.py b/llama_stack/apis/files/files.py index dd51bf63a..e4cf6283a 100644 --- a/llama_stack/apis/files/files.py +++ b/llama_stack/apis/files/files.py @@ -7,7 +7,7 @@ from enum import StrEnum from typing import Annotated, ClassVar, Literal, Protocol, runtime_checkable -from fastapi import File, Response, UploadFile +from fastapi import File, Form, Response, UploadFile from pydantic import BaseModel, Field from llama_stack.apis.common.responses import Order @@ -110,9 +110,8 @@ class Files(Protocol): async def openai_upload_file( self, file: Annotated[UploadFile, File()], - purpose: OpenAIFilePurpose, - expires_after: ExpiresAfter | None = None, - # TODO: expires_after is producing strange openapi spec, params are showing up as a required w/ oneOf being null + purpose: Annotated[OpenAIFilePurpose, Form()], + expires_after: Annotated[ExpiresAfter | None, Form()] = None, ) -> OpenAIFileObject: """ Upload a file that can be used across various endpoints. diff --git a/llama_stack/providers/inline/files/localfs/files.py b/llama_stack/providers/inline/files/localfs/files.py index d1bc64d38..6e0c72de3 100644 --- a/llama_stack/providers/inline/files/localfs/files.py +++ b/llama_stack/providers/inline/files/localfs/files.py @@ -9,7 +9,7 @@ import uuid from pathlib import Path from typing import Annotated -from fastapi import File, Response, UploadFile +from fastapi import File, Form, Response, UploadFile from llama_stack.apis.common.errors import ResourceNotFoundError from llama_stack.apis.common.responses import Order @@ -86,8 +86,8 @@ class LocalfsFilesImpl(Files): async def openai_upload_file( self, file: Annotated[UploadFile, File()], - purpose: OpenAIFilePurpose, - expires_after: ExpiresAfter | None = None, + purpose: Annotated[OpenAIFilePurpose, Form()], + expires_after: Annotated[ExpiresAfter | None, Form()] = None, ) -> OpenAIFileObject: """Upload a file that can be used across various endpoints.""" if not self.sql_store: diff --git a/llama_stack/providers/remote/files/s3/files.py b/llama_stack/providers/remote/files/s3/files.py index 0e1b72e29..8520f70b6 100644 --- a/llama_stack/providers/remote/files/s3/files.py +++ b/llama_stack/providers/remote/files/s3/files.py @@ -10,7 +10,7 @@ from typing import Annotated, Any import boto3 from botocore.exceptions import BotoCoreError, ClientError, NoCredentialsError -from fastapi import File, Response, UploadFile +from fastapi import File, Form, Response, UploadFile from llama_stack.apis.common.errors import ResourceNotFoundError from llama_stack.apis.common.responses import Order @@ -194,8 +194,8 @@ class S3FilesImpl(Files): async def openai_upload_file( self, file: Annotated[UploadFile, File()], - purpose: OpenAIFilePurpose, - expires_after: ExpiresAfter | None = None, + purpose: Annotated[OpenAIFilePurpose, Form()], + expires_after: Annotated[ExpiresAfter | None, Form()] = None, ) -> OpenAIFileObject: file_id = f"file-{uuid.uuid4().hex}" diff --git a/llama_stack/strong_typing/inspection.py b/llama_stack/strong_typing/inspection.py index a75a170cf..00afa31a6 100644 --- a/llama_stack/strong_typing/inspection.py +++ b/llama_stack/strong_typing/inspection.py @@ -567,6 +567,20 @@ def get_class_properties(typ: type) -> Iterable[Tuple[str, type | str]]: if is_dataclass_type(typ): return ((field.name, field.type) for field in dataclasses.fields(typ)) + elif hasattr(typ, 'model_fields'): + # Pydantic BaseModel - use model_fields to exclude ClassVar and other non-field attributes + # Reconstruct Annotated type if discriminator exists to preserve metadata + from typing import Annotated + from pydantic.fields import FieldInfo + + def get_field_type(name: str, field) -> type | str: + # If field has discriminator, wrap in Annotated to preserve it for schema generation + if field.discriminator: + field_info = FieldInfo(annotation=None, discriminator=field.discriminator) + return Annotated[field.annotation, field_info] + return field.annotation + + return ((name, get_field_type(name, field)) for name, field in typ.model_fields.items()) else: resolved_hints = get_resolved_hints(typ) return resolved_hints.items() diff --git a/llama_stack/strong_typing/schema.py b/llama_stack/strong_typing/schema.py index 82baddc86..1a162c01e 100644 --- a/llama_stack/strong_typing/schema.py +++ b/llama_stack/strong_typing/schema.py @@ -93,6 +93,11 @@ def get_class_property_docstrings( """ result = {} + # Only try to get MRO if data_type is actually a class + # Special types like Literal, Union, etc. don't have MRO + if not inspect.isclass(data_type): + return result + for base in inspect.getmro(data_type): docstr = docstring.parse_type(base) for param in docstr.params.values():