diff --git a/client-sdks/stainless/openapi.yml b/client-sdks/stainless/openapi.yml index ff86e30e1..3a6735cbc 100644 --- a/client-sdks/stainless/openapi.yml +++ b/client-sdks/stainless/openapi.yml @@ -1810,7 +1810,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/RegisterScoringFunctionRequestLoose' + $ref: '#/components/schemas/RegisterScoringFunctionRequest' required: true deprecated: true /v1/scoring-functions/{scoring_fn_id}: @@ -3300,7 +3300,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/RegisterDatasetRequestLoose' + $ref: '#/components/schemas/RegisterDatasetRequest' required: true deprecated: true /v1beta/datasets/{dataset_id}: @@ -3557,7 +3557,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/BenchmarkConfig' + $ref: '#/components/schemas/RunEvalRequest' required: true /v1alpha/eval/benchmarks/{benchmark_id}/jobs/{job_id}: get: @@ -10586,6 +10586,14 @@ components: - scores title: EvaluateResponse description: The response from an evaluation. + RunEvalRequest: + properties: + benchmark_config: + $ref: '#/components/schemas/BenchmarkConfig' + type: object + required: + - benchmark_config + title: RunEvalRequest Job: properties: job_id: @@ -11169,6 +11177,67 @@ components: - $ref: '#/components/schemas/CompletionInputType' title: CompletionInputType title: StringType | ... (9 variants) + RegisterScoringFunctionRequest: + properties: + scoring_fn_id: + type: string + title: Scoring Fn Id + description: + type: string + title: Description + return_type: + anyOf: + - $ref: '#/components/schemas/StringType' + title: StringType + - $ref: '#/components/schemas/NumberType' + title: NumberType + - $ref: '#/components/schemas/BooleanType' + title: BooleanType + - $ref: '#/components/schemas/ArrayType' + title: ArrayType + - $ref: '#/components/schemas/ObjectType' + title: ObjectType + - $ref: '#/components/schemas/JsonType' + title: JsonType + - $ref: '#/components/schemas/UnionType' + title: UnionType + - $ref: '#/components/schemas/ChatCompletionInputType' + title: ChatCompletionInputType + - $ref: '#/components/schemas/CompletionInputType' + title: CompletionInputType + title: StringType | ... (9 variants) + provider_scoring_fn_id: + anyOf: + - type: string + - type: 'null' + provider_id: + anyOf: + - type: string + - type: 'null' + params: + anyOf: + - oneOf: + - $ref: '#/components/schemas/LLMAsJudgeScoringFnParams' + title: LLMAsJudgeScoringFnParams + - $ref: '#/components/schemas/RegexParserScoringFnParams' + title: RegexParserScoringFnParams + - $ref: '#/components/schemas/BasicScoringFnParams' + title: BasicScoringFnParams + discriminator: + propertyName: type + mapping: + basic: '#/components/schemas/BasicScoringFnParams' + llm_as_judge: '#/components/schemas/LLMAsJudgeScoringFnParams' + regex_parser: '#/components/schemas/RegexParserScoringFnParams' + title: LLMAsJudgeScoringFnParams | RegexParserScoringFnParams | BasicScoringFnParams + - type: 'null' + title: Params + type: object + required: + - scoring_fn_id + - description + - return_type + title: RegisterScoringFunctionRequest RegisterShieldRequest: properties: shield_id: @@ -11227,6 +11296,31 @@ components: - $ref: '#/components/schemas/RowsDataSource' title: RowsDataSource title: URIDataSource | RowsDataSource + RegisterDatasetRequest: + properties: + purpose: + $ref: '#/components/schemas/DatasetPurpose' + source: + anyOf: + - $ref: '#/components/schemas/URIDataSource' + title: URIDataSource + - $ref: '#/components/schemas/RowsDataSource' + title: RowsDataSource + title: URIDataSource | RowsDataSource + metadata: + anyOf: + - additionalProperties: true + type: object + - type: 'null' + dataset_id: + anyOf: + - type: string + - type: 'null' + type: object + required: + - purpose + - source + title: RegisterDatasetRequest RegisterBenchmarkRequest: properties: benchmark_id: @@ -11963,41 +12057,6 @@ components: required: - reasoning_tokens title: OutputTokensDetails - RegisterDatasetRequestLoose: - properties: - purpose: - title: Purpose - source: - title: Source - metadata: - title: Metadata - dataset_id: - title: Dataset Id - type: object - required: - - purpose - - source - title: RegisterDatasetRequestLoose - RegisterScoringFunctionRequestLoose: - properties: - scoring_fn_id: - title: Scoring Fn Id - description: - title: Description - return_type: - title: Return Type - provider_scoring_fn_id: - title: Provider Scoring Fn Id - provider_id: - title: Provider Id - params: - title: Params - type: object - required: - - scoring_fn_id - - description - - return_type - title: RegisterScoringFunctionRequestLoose SearchRankingOptions: properties: ranker: diff --git a/docs/static/deprecated-llama-stack-spec.yaml b/docs/static/deprecated-llama-stack-spec.yaml index 3bc06d7d7..0bade1866 100644 --- a/docs/static/deprecated-llama-stack-spec.yaml +++ b/docs/static/deprecated-llama-stack-spec.yaml @@ -193,7 +193,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/RegisterScoringFunctionRequestLoose' + $ref: '#/components/schemas/RegisterScoringFunctionRequest' required: true deprecated: true /v1/scoring-functions/{scoring_fn_id}: @@ -549,7 +549,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/RegisterDatasetRequestLoose' + $ref: '#/components/schemas/RegisterDatasetRequest' required: true deprecated: true /v1beta/datasets/{dataset_id}: @@ -7429,6 +7429,14 @@ components: - scores title: EvaluateResponse description: The response from an evaluation. + RunEvalRequest: + properties: + benchmark_config: + $ref: '#/components/schemas/BenchmarkConfig' + type: object + required: + - benchmark_config + title: RunEvalRequest Job: properties: job_id: @@ -8012,6 +8020,67 @@ components: - $ref: '#/components/schemas/CompletionInputType' title: CompletionInputType title: StringType | ... (9 variants) + RegisterScoringFunctionRequest: + properties: + scoring_fn_id: + type: string + title: Scoring Fn Id + description: + type: string + title: Description + return_type: + anyOf: + - $ref: '#/components/schemas/StringType' + title: StringType + - $ref: '#/components/schemas/NumberType' + title: NumberType + - $ref: '#/components/schemas/BooleanType' + title: BooleanType + - $ref: '#/components/schemas/ArrayType' + title: ArrayType + - $ref: '#/components/schemas/ObjectType' + title: ObjectType + - $ref: '#/components/schemas/JsonType' + title: JsonType + - $ref: '#/components/schemas/UnionType' + title: UnionType + - $ref: '#/components/schemas/ChatCompletionInputType' + title: ChatCompletionInputType + - $ref: '#/components/schemas/CompletionInputType' + title: CompletionInputType + title: StringType | ... (9 variants) + provider_scoring_fn_id: + anyOf: + - type: string + - type: 'null' + provider_id: + anyOf: + - type: string + - type: 'null' + params: + anyOf: + - oneOf: + - $ref: '#/components/schemas/LLMAsJudgeScoringFnParams' + title: LLMAsJudgeScoringFnParams + - $ref: '#/components/schemas/RegexParserScoringFnParams' + title: RegexParserScoringFnParams + - $ref: '#/components/schemas/BasicScoringFnParams' + title: BasicScoringFnParams + discriminator: + propertyName: type + mapping: + basic: '#/components/schemas/BasicScoringFnParams' + llm_as_judge: '#/components/schemas/LLMAsJudgeScoringFnParams' + regex_parser: '#/components/schemas/RegexParserScoringFnParams' + title: LLMAsJudgeScoringFnParams | RegexParserScoringFnParams | BasicScoringFnParams + - type: 'null' + title: Params + type: object + required: + - scoring_fn_id + - description + - return_type + title: RegisterScoringFunctionRequest RegisterShieldRequest: properties: shield_id: @@ -8070,6 +8139,31 @@ components: - $ref: '#/components/schemas/RowsDataSource' title: RowsDataSource title: URIDataSource | RowsDataSource + RegisterDatasetRequest: + properties: + purpose: + $ref: '#/components/schemas/DatasetPurpose' + source: + anyOf: + - $ref: '#/components/schemas/URIDataSource' + title: URIDataSource + - $ref: '#/components/schemas/RowsDataSource' + title: RowsDataSource + title: URIDataSource | RowsDataSource + metadata: + anyOf: + - additionalProperties: true + type: object + - type: 'null' + dataset_id: + anyOf: + - type: string + - type: 'null' + type: object + required: + - purpose + - source + title: RegisterDatasetRequest RegisterBenchmarkRequest: properties: benchmark_id: @@ -8806,41 +8900,6 @@ components: required: - reasoning_tokens title: OutputTokensDetails - RegisterDatasetRequestLoose: - properties: - purpose: - title: Purpose - source: - title: Source - metadata: - title: Metadata - dataset_id: - title: Dataset Id - type: object - required: - - purpose - - source - title: RegisterDatasetRequestLoose - RegisterScoringFunctionRequestLoose: - properties: - scoring_fn_id: - title: Scoring Fn Id - description: - title: Description - return_type: - title: Return Type - provider_scoring_fn_id: - title: Provider Scoring Fn Id - provider_id: - title: Provider Id - params: - title: Params - type: object - required: - - scoring_fn_id - - description - - return_type - title: RegisterScoringFunctionRequestLoose SearchRankingOptions: properties: ranker: diff --git a/docs/static/experimental-llama-stack-spec.yaml b/docs/static/experimental-llama-stack-spec.yaml index 2b36ebf47..4271989d6 100644 --- a/docs/static/experimental-llama-stack-spec.yaml +++ b/docs/static/experimental-llama-stack-spec.yaml @@ -300,7 +300,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/BenchmarkConfig' + $ref: '#/components/schemas/RunEvalRequest' required: true /v1alpha/eval/benchmarks/{benchmark_id}/jobs/{job_id}: get: @@ -6711,6 +6711,14 @@ components: - scores title: EvaluateResponse description: The response from an evaluation. + RunEvalRequest: + properties: + benchmark_config: + $ref: '#/components/schemas/BenchmarkConfig' + type: object + required: + - benchmark_config + title: RunEvalRequest Job: properties: job_id: diff --git a/docs/static/stainless-llama-stack-spec.yaml b/docs/static/stainless-llama-stack-spec.yaml index ff86e30e1..3a6735cbc 100644 --- a/docs/static/stainless-llama-stack-spec.yaml +++ b/docs/static/stainless-llama-stack-spec.yaml @@ -1810,7 +1810,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/RegisterScoringFunctionRequestLoose' + $ref: '#/components/schemas/RegisterScoringFunctionRequest' required: true deprecated: true /v1/scoring-functions/{scoring_fn_id}: @@ -3300,7 +3300,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/RegisterDatasetRequestLoose' + $ref: '#/components/schemas/RegisterDatasetRequest' required: true deprecated: true /v1beta/datasets/{dataset_id}: @@ -3557,7 +3557,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/BenchmarkConfig' + $ref: '#/components/schemas/RunEvalRequest' required: true /v1alpha/eval/benchmarks/{benchmark_id}/jobs/{job_id}: get: @@ -10586,6 +10586,14 @@ components: - scores title: EvaluateResponse description: The response from an evaluation. + RunEvalRequest: + properties: + benchmark_config: + $ref: '#/components/schemas/BenchmarkConfig' + type: object + required: + - benchmark_config + title: RunEvalRequest Job: properties: job_id: @@ -11169,6 +11177,67 @@ components: - $ref: '#/components/schemas/CompletionInputType' title: CompletionInputType title: StringType | ... (9 variants) + RegisterScoringFunctionRequest: + properties: + scoring_fn_id: + type: string + title: Scoring Fn Id + description: + type: string + title: Description + return_type: + anyOf: + - $ref: '#/components/schemas/StringType' + title: StringType + - $ref: '#/components/schemas/NumberType' + title: NumberType + - $ref: '#/components/schemas/BooleanType' + title: BooleanType + - $ref: '#/components/schemas/ArrayType' + title: ArrayType + - $ref: '#/components/schemas/ObjectType' + title: ObjectType + - $ref: '#/components/schemas/JsonType' + title: JsonType + - $ref: '#/components/schemas/UnionType' + title: UnionType + - $ref: '#/components/schemas/ChatCompletionInputType' + title: ChatCompletionInputType + - $ref: '#/components/schemas/CompletionInputType' + title: CompletionInputType + title: StringType | ... (9 variants) + provider_scoring_fn_id: + anyOf: + - type: string + - type: 'null' + provider_id: + anyOf: + - type: string + - type: 'null' + params: + anyOf: + - oneOf: + - $ref: '#/components/schemas/LLMAsJudgeScoringFnParams' + title: LLMAsJudgeScoringFnParams + - $ref: '#/components/schemas/RegexParserScoringFnParams' + title: RegexParserScoringFnParams + - $ref: '#/components/schemas/BasicScoringFnParams' + title: BasicScoringFnParams + discriminator: + propertyName: type + mapping: + basic: '#/components/schemas/BasicScoringFnParams' + llm_as_judge: '#/components/schemas/LLMAsJudgeScoringFnParams' + regex_parser: '#/components/schemas/RegexParserScoringFnParams' + title: LLMAsJudgeScoringFnParams | RegexParserScoringFnParams | BasicScoringFnParams + - type: 'null' + title: Params + type: object + required: + - scoring_fn_id + - description + - return_type + title: RegisterScoringFunctionRequest RegisterShieldRequest: properties: shield_id: @@ -11227,6 +11296,31 @@ components: - $ref: '#/components/schemas/RowsDataSource' title: RowsDataSource title: URIDataSource | RowsDataSource + RegisterDatasetRequest: + properties: + purpose: + $ref: '#/components/schemas/DatasetPurpose' + source: + anyOf: + - $ref: '#/components/schemas/URIDataSource' + title: URIDataSource + - $ref: '#/components/schemas/RowsDataSource' + title: RowsDataSource + title: URIDataSource | RowsDataSource + metadata: + anyOf: + - additionalProperties: true + type: object + - type: 'null' + dataset_id: + anyOf: + - type: string + - type: 'null' + type: object + required: + - purpose + - source + title: RegisterDatasetRequest RegisterBenchmarkRequest: properties: benchmark_id: @@ -11963,41 +12057,6 @@ components: required: - reasoning_tokens title: OutputTokensDetails - RegisterDatasetRequestLoose: - properties: - purpose: - title: Purpose - source: - title: Source - metadata: - title: Metadata - dataset_id: - title: Dataset Id - type: object - required: - - purpose - - source - title: RegisterDatasetRequestLoose - RegisterScoringFunctionRequestLoose: - properties: - scoring_fn_id: - title: Scoring Fn Id - description: - title: Description - return_type: - title: Return Type - provider_scoring_fn_id: - title: Provider Scoring Fn Id - provider_id: - title: Provider Id - params: - title: Params - type: object - required: - - scoring_fn_id - - description - - return_type - title: RegisterScoringFunctionRequestLoose SearchRankingOptions: properties: ranker: diff --git a/scripts/openapi_generator/endpoints.py b/scripts/openapi_generator/endpoints.py index 39086f47f..85203cb71 100644 --- a/scripts/openapi_generator/endpoints.py +++ b/scripts/openapi_generator/endpoints.py @@ -15,6 +15,7 @@ import typing from typing import Annotated, Any, get_args, get_origin from fastapi import FastAPI +from fastapi.params import Body as FastAPIBody from pydantic import Field, create_model from llama_stack.log import get_logger @@ -26,6 +27,8 @@ from .state import _extra_body_fields, register_dynamic_model logger = get_logger(name=__name__, category="core") +type QueryParameter = tuple[str, type, Any, bool] + def _to_pascal_case(segment: str) -> str: tokens = re.findall(r"[A-Za-z]+|\d+", segment) @@ -75,12 +78,12 @@ def _create_endpoint_with_request_model( return endpoint -def _build_field_definitions(query_parameters: list[tuple[str, type, Any]], use_any: bool = False) -> dict[str, tuple]: +def _build_field_definitions(query_parameters: list[QueryParameter], use_any: bool = False) -> dict[str, tuple]: """Build field definitions for a Pydantic model from query parameters.""" from typing import Any field_definitions = {} - for param_name, param_type, default_value in query_parameters: + for param_name, param_type, default_value, _ in query_parameters: if use_any: field_definitions[param_name] = (Any, ... if default_value is inspect.Parameter.empty else default_value) continue @@ -108,10 +111,10 @@ def _build_field_definitions(query_parameters: list[tuple[str, type, Any]], use_ field_definitions[param_name] = (Any, ... if default_value is inspect.Parameter.empty else default_value) # Ensure all parameters are included - expected_params = {name for name, _, _ in query_parameters} + expected_params = {name for name, _, _, _ in query_parameters} missing = expected_params - set(field_definitions.keys()) if missing: - for param_name, _, default_value in query_parameters: + for param_name, _, default_value, _ in query_parameters: if param_name in missing: field_definitions[param_name] = ( Any, @@ -126,7 +129,7 @@ def _create_dynamic_request_model( webmethod, method_name: str, http_method: str, - query_parameters: list[tuple[str, type, Any]], + query_parameters: list[QueryParameter], use_any: bool = False, variant_suffix: str | None = None, ) -> type | None: @@ -143,12 +146,12 @@ def _create_dynamic_request_model( def _build_signature_params( - query_parameters: list[tuple[str, type, Any]], + query_parameters: list[QueryParameter], ) -> tuple[list[inspect.Parameter], dict[str, type]]: """Build signature parameters and annotations from query parameters.""" signature_params = [] param_annotations = {} - for param_name, param_type, default_value in query_parameters: + for param_name, param_type, default_value, _ in query_parameters: param_annotations[param_name] = param_type signature_params.append( inspect.Parameter( @@ -219,6 +222,19 @@ def _is_extra_body_field(metadata_item: Any) -> bool: return isinstance(metadata_item, ExtraBodyField) +def _should_embed_parameter(param_type: Any) -> bool: + """Determine whether a parameter should be embedded (wrapped) in the request body.""" + if get_origin(param_type) is Annotated: + args = get_args(param_type) + metadata = args[1:] if len(args) > 1 else [] + for metadata_item in metadata: + if isinstance(metadata_item, FastAPIBody): + # FastAPI treats embed=None as False, so default to False when unset. + return bool(metadata_item.embed) + # Unannotated parameters default to embed=True through create_dynamic_typed_route. + return True + + def _is_async_iterator_type(type_obj: Any) -> bool: """Check if a type is AsyncIterator or AsyncIterable.""" from collections.abc import AsyncIterable, AsyncIterator @@ -282,7 +298,7 @@ def _find_models_for_endpoint( Returns: tuple: (request_model, response_model, query_parameters, file_form_params, streaming_response_model, response_schema_name) - where query_parameters is a list of (name, type, default_value) tuples + where query_parameters is a list of (name, type, default_value, should_embed) tuples and file_form_params is a list of inspect.Parameter objects for File()/Form() params and streaming_response_model is the model for streaming responses (AsyncIterator content) """ @@ -299,7 +315,7 @@ def _find_models_for_endpoint( # Find request model and collect all body parameters request_model = None - query_parameters = [] + query_parameters: list[QueryParameter] = [] file_form_params = [] path_params = set() extra_body_params = [] @@ -325,6 +341,7 @@ def _find_models_for_endpoint( # Check if it's a File() or Form() parameter - these need special handling param_type = param.annotation + param_should_embed = _should_embed_parameter(param_type) if _is_file_or_form_param(param_type): # File() and Form() parameters must be in the function signature directly # They cannot be part of a Pydantic model @@ -350,30 +367,14 @@ def _find_models_for_endpoint( # Store as extra body parameter - exclude from request model extra_body_params.append((param_name, base_type, extra_body_description)) continue + param_type = base_type # Check if it's a Pydantic model (for POST/PUT requests) if hasattr(param_type, "model_json_schema"): - # Collect all body parameters including Pydantic models - # We'll decide later whether to use a single model or create a combined one - query_parameters.append((param_name, param_type, param.default)) - elif get_origin(param_type) is Annotated: - # Handle Annotated types - get the base type - args = get_args(param_type) - if args and hasattr(args[0], "model_json_schema"): - # Collect Pydantic models from Annotated types - query_parameters.append((param_name, args[0], param.default)) - else: - # Regular annotated parameter (but not File/Form, already handled above) - query_parameters.append((param_name, param_type, param.default)) + query_parameters.append((param_name, param_type, param.default, param_should_embed)) else: - # This is likely a body parameter for POST/PUT or query parameter for GET - # Store the parameter info for later use - # Preserve inspect.Parameter.empty to distinguish "no default" from "default=None" - default_value = param.default - - # Extract the base type from union types (e.g., str | None -> str) - # Also make it safe for FastAPI to avoid forward reference issues - query_parameters.append((param_name, param_type, default_value)) + # Regular annotated parameter (but not File/Form, already handled above) + query_parameters.append((param_name, param_type, param.default, param_should_embed)) # Store extra body fields for later use in post-processing # We'll store them when the endpoint is created, as we need the full path @@ -385,8 +386,8 @@ def _find_models_for_endpoint( # Otherwise, we'll create a combined request model from all parameters # BUT: For GET requests, never create a request body - all parameters should be query parameters if is_post_put and len(query_parameters) == 1: - param_name, param_type, default_value = query_parameters[0] - if hasattr(param_type, "model_json_schema"): + param_name, param_type, default_value, should_embed = query_parameters[0] + if hasattr(param_type, "model_json_schema") and not should_embed: request_model = param_type query_parameters = [] # Clear query_parameters so we use the single model @@ -495,7 +496,7 @@ def _create_fastapi_endpoint(app: FastAPI, route, webmethod, api: Api): if file_form_params and is_post_put: signature_params = list(file_form_params) param_annotations = {param.name: param.annotation for param in file_form_params} - for param_name, param_type, default_value in query_parameters: + for param_name, param_type, default_value, _ in query_parameters: signature_params.append( inspect.Parameter( param_name,