Apply a legacy order so we can easily see diff against what was generated

This commit is contained in:
Ashwin Bharambe 2025-11-14 10:39:00 -08:00
parent 9381673405
commit 69e1176ff8
8 changed files with 29509 additions and 29034 deletions

View file

@ -0,0 +1,410 @@
#!/usr/bin/env python3
# Copyright (c) Meta Platforms, Inc. and affiliates.
# All rights reserved.
#
# This source code is licensed under the terms described in the LICENSE file in
# the root directory of this source tree.
"""
Temporary ordering helpers extracted from origin/main client-sdks/stainless/openapi.yml.
These lists help the new generator match the previous ordering so that diffs
remain readable while we debug schema content regressions. Remove once stable.
"""
# TODO: remove once generator output stabilizes
LEGACY_PATH_ORDER = ['/v1/batches',
'/v1/batches/{batch_id}',
'/v1/batches/{batch_id}/cancel',
'/v1/chat/completions',
'/v1/chat/completions/{completion_id}',
'/v1/completions',
'/v1/conversations',
'/v1/conversations/{conversation_id}',
'/v1/conversations/{conversation_id}/items',
'/v1/conversations/{conversation_id}/items/{item_id}',
'/v1/embeddings',
'/v1/files',
'/v1/files/{file_id}',
'/v1/files/{file_id}/content',
'/v1/health',
'/v1/inspect/routes',
'/v1/models',
'/v1/models/{model_id}',
'/v1/moderations',
'/v1/prompts',
'/v1/prompts/{prompt_id}',
'/v1/prompts/{prompt_id}/set-default-version',
'/v1/prompts/{prompt_id}/versions',
'/v1/providers',
'/v1/providers/{provider_id}',
'/v1/responses',
'/v1/responses/{response_id}',
'/v1/responses/{response_id}/input_items',
'/v1/safety/run-shield',
'/v1/scoring-functions',
'/v1/scoring-functions/{scoring_fn_id}',
'/v1/scoring/score',
'/v1/scoring/score-batch',
'/v1/shields',
'/v1/shields/{identifier}',
'/v1/tool-runtime/invoke',
'/v1/tool-runtime/list-tools',
'/v1/toolgroups',
'/v1/toolgroups/{toolgroup_id}',
'/v1/tools',
'/v1/tools/{tool_name}',
'/v1/vector-io/insert',
'/v1/vector-io/query',
'/v1/vector_stores',
'/v1/vector_stores/{vector_store_id}',
'/v1/vector_stores/{vector_store_id}/file_batches',
'/v1/vector_stores/{vector_store_id}/file_batches/{batch_id}',
'/v1/vector_stores/{vector_store_id}/file_batches/{batch_id}/cancel',
'/v1/vector_stores/{vector_store_id}/file_batches/{batch_id}/files',
'/v1/vector_stores/{vector_store_id}/files',
'/v1/vector_stores/{vector_store_id}/files/{file_id}',
'/v1/vector_stores/{vector_store_id}/files/{file_id}/content',
'/v1/vector_stores/{vector_store_id}/search',
'/v1/version',
'/v1beta/datasetio/append-rows/{dataset_id}',
'/v1beta/datasetio/iterrows/{dataset_id}',
'/v1beta/datasets',
'/v1beta/datasets/{dataset_id}',
'/v1alpha/eval/benchmarks',
'/v1alpha/eval/benchmarks/{benchmark_id}',
'/v1alpha/eval/benchmarks/{benchmark_id}/evaluations',
'/v1alpha/eval/benchmarks/{benchmark_id}/jobs',
'/v1alpha/eval/benchmarks/{benchmark_id}/jobs/{job_id}',
'/v1alpha/eval/benchmarks/{benchmark_id}/jobs/{job_id}/result',
'/v1alpha/inference/rerank',
'/v1alpha/post-training/job/artifacts',
'/v1alpha/post-training/job/cancel',
'/v1alpha/post-training/job/status',
'/v1alpha/post-training/jobs',
'/v1alpha/post-training/preference-optimize',
'/v1alpha/post-training/supervised-fine-tune']
LEGACY_SCHEMA_ORDER = ['Error',
'ListBatchesResponse',
'CreateBatchRequest',
'Batch',
'Order',
'ListOpenAIChatCompletionResponse',
'OpenAIAssistantMessageParam',
'OpenAIChatCompletionContentPartImageParam',
'OpenAIChatCompletionContentPartParam',
'OpenAIChatCompletionContentPartTextParam',
'OpenAIChatCompletionToolCall',
'OpenAIChatCompletionToolCallFunction',
'OpenAIChatCompletionUsage',
'OpenAIChoice',
'OpenAIChoiceLogprobs',
'OpenAIDeveloperMessageParam',
'OpenAIFile',
'OpenAIFileFile',
'OpenAIImageURL',
'OpenAIMessageParam',
'OpenAISystemMessageParam',
'OpenAITokenLogProb',
'OpenAIToolMessageParam',
'OpenAITopLogProb',
'OpenAIUserMessageParam',
'OpenAIJSONSchema',
'OpenAIResponseFormatJSONObject',
'OpenAIResponseFormatJSONSchema',
'OpenAIResponseFormatParam',
'OpenAIResponseFormatText',
'OpenAIChatCompletionRequestWithExtraBody',
'OpenAIChatCompletion',
'OpenAIChatCompletionChunk',
'OpenAIChoiceDelta',
'OpenAIChunkChoice',
'OpenAICompletionWithInputMessages',
'OpenAICompletionRequestWithExtraBody',
'OpenAICompletion',
'OpenAICompletionChoice',
'ConversationItem',
'OpenAIResponseAnnotationCitation',
'OpenAIResponseAnnotationContainerFileCitation',
'OpenAIResponseAnnotationFileCitation',
'OpenAIResponseAnnotationFilePath',
'OpenAIResponseAnnotations',
'OpenAIResponseContentPartRefusal',
'OpenAIResponseInputFunctionToolCallOutput',
'OpenAIResponseInputMessageContent',
'OpenAIResponseInputMessageContentFile',
'OpenAIResponseInputMessageContentImage',
'OpenAIResponseInputMessageContentText',
'OpenAIResponseMCPApprovalRequest',
'OpenAIResponseMCPApprovalResponse',
'OpenAIResponseMessage',
'OpenAIResponseOutputMessageContent',
'OpenAIResponseOutputMessageContentOutputText',
'OpenAIResponseOutputMessageFileSearchToolCall',
'OpenAIResponseOutputMessageFunctionToolCall',
'OpenAIResponseOutputMessageMCPCall',
'OpenAIResponseOutputMessageMCPListTools',
'OpenAIResponseOutputMessageWebSearchToolCall',
'CreateConversationRequest',
'Conversation',
'UpdateConversationRequest',
'ConversationDeletedResource',
'ConversationItemList',
'AddItemsRequest',
'ConversationItemDeletedResource',
'OpenAIEmbeddingsRequestWithExtraBody',
'OpenAIEmbeddingData',
'OpenAIEmbeddingUsage',
'OpenAIEmbeddingsResponse',
'OpenAIFilePurpose',
'ListOpenAIFileResponse',
'OpenAIFileObject',
'ExpiresAfter',
'OpenAIFileDeleteResponse',
'Response',
'HealthInfo',
'RouteInfo',
'ListRoutesResponse',
'OpenAIModel',
'OpenAIListModelsResponse',
'Model',
'ModelType',
'RunModerationRequest',
'ModerationObject',
'ModerationObjectResults',
'Prompt',
'ListPromptsResponse',
'CreatePromptRequest',
'UpdatePromptRequest',
'SetDefaultVersionRequest',
'ProviderInfo',
'ListProvidersResponse',
'ListOpenAIResponseObject',
'OpenAIResponseError',
'OpenAIResponseInput',
'OpenAIResponseInputToolFileSearch',
'OpenAIResponseInputToolFunction',
'OpenAIResponseInputToolWebSearch',
'OpenAIResponseObjectWithInput',
'OpenAIResponseOutput',
'OpenAIResponsePrompt',
'OpenAIResponseText',
'OpenAIResponseTool',
'OpenAIResponseToolMCP',
'OpenAIResponseUsage',
'ResponseGuardrailSpec',
'OpenAIResponseInputTool',
'OpenAIResponseInputToolMCP',
'CreateOpenaiResponseRequest',
'OpenAIResponseObject',
'OpenAIResponseContentPartOutputText',
'OpenAIResponseContentPartReasoningSummary',
'OpenAIResponseContentPartReasoningText',
'OpenAIResponseObjectStream',
'OpenAIResponseObjectStreamResponseCompleted',
'OpenAIResponseObjectStreamResponseContentPartAdded',
'OpenAIResponseObjectStreamResponseContentPartDone',
'OpenAIResponseObjectStreamResponseCreated',
'OpenAIResponseObjectStreamResponseFailed',
'OpenAIResponseObjectStreamResponseFileSearchCallCompleted',
'OpenAIResponseObjectStreamResponseFileSearchCallInProgress',
'OpenAIResponseObjectStreamResponseFileSearchCallSearching',
'OpenAIResponseObjectStreamResponseFunctionCallArgumentsDelta',
'OpenAIResponseObjectStreamResponseFunctionCallArgumentsDone',
'OpenAIResponseObjectStreamResponseInProgress',
'OpenAIResponseObjectStreamResponseIncomplete',
'OpenAIResponseObjectStreamResponseMcpCallArgumentsDelta',
'OpenAIResponseObjectStreamResponseMcpCallArgumentsDone',
'OpenAIResponseObjectStreamResponseMcpCallCompleted',
'OpenAIResponseObjectStreamResponseMcpCallFailed',
'OpenAIResponseObjectStreamResponseMcpCallInProgress',
'OpenAIResponseObjectStreamResponseMcpListToolsCompleted',
'OpenAIResponseObjectStreamResponseMcpListToolsFailed',
'OpenAIResponseObjectStreamResponseMcpListToolsInProgress',
'OpenAIResponseObjectStreamResponseOutputItemAdded',
'OpenAIResponseObjectStreamResponseOutputItemDone',
'OpenAIResponseObjectStreamResponseOutputTextAnnotationAdded',
'OpenAIResponseObjectStreamResponseOutputTextDelta',
'OpenAIResponseObjectStreamResponseOutputTextDone',
'OpenAIResponseObjectStreamResponseReasoningSummaryPartAdded',
'OpenAIResponseObjectStreamResponseReasoningSummaryPartDone',
'OpenAIResponseObjectStreamResponseReasoningSummaryTextDelta',
'OpenAIResponseObjectStreamResponseReasoningSummaryTextDone',
'OpenAIResponseObjectStreamResponseReasoningTextDelta',
'OpenAIResponseObjectStreamResponseReasoningTextDone',
'OpenAIResponseObjectStreamResponseRefusalDelta',
'OpenAIResponseObjectStreamResponseRefusalDone',
'OpenAIResponseObjectStreamResponseWebSearchCallCompleted',
'OpenAIResponseObjectStreamResponseWebSearchCallInProgress',
'OpenAIResponseObjectStreamResponseWebSearchCallSearching',
'OpenAIDeleteResponseObject',
'ListOpenAIResponseInputItem',
'RunShieldRequest',
'RunShieldResponse',
'SafetyViolation',
'ViolationLevel',
'AggregationFunctionType',
'ArrayType',
'BasicScoringFnParams',
'BooleanType',
'ChatCompletionInputType',
'CompletionInputType',
'JsonType',
'LLMAsJudgeScoringFnParams',
'NumberType',
'ObjectType',
'RegexParserScoringFnParams',
'ScoringFn',
'ScoringFnParams',
'ScoringFnParamsType',
'StringType',
'UnionType',
'ListScoringFunctionsResponse',
'ScoreRequest',
'ScoreResponse',
'ScoringResult',
'ScoreBatchRequest',
'ScoreBatchResponse',
'Shield',
'ListShieldsResponse',
'InvokeToolRequest',
'ImageContentItem',
'InterleavedContent',
'InterleavedContentItem',
'TextContentItem',
'ToolInvocationResult',
'URL',
'ToolDef',
'ListToolDefsResponse',
'ToolGroup',
'ListToolGroupsResponse',
'Chunk',
'ChunkMetadata',
'InsertChunksRequest',
'QueryChunksRequest',
'QueryChunksResponse',
'VectorStoreFileCounts',
'VectorStoreListResponse',
'VectorStoreObject',
'VectorStoreChunkingStrategy',
'VectorStoreChunkingStrategyAuto',
'VectorStoreChunkingStrategyStatic',
'VectorStoreChunkingStrategyStaticConfig',
'OpenAICreateVectorStoreRequestWithExtraBody',
'OpenaiUpdateVectorStoreRequest',
'VectorStoreDeleteResponse',
'OpenAICreateVectorStoreFileBatchRequestWithExtraBody',
'VectorStoreFileBatchObject',
'VectorStoreFileStatus',
'VectorStoreFileLastError',
'VectorStoreFileObject',
'VectorStoreFilesListInBatchResponse',
'VectorStoreListFilesResponse',
'OpenaiAttachFileToVectorStoreRequest',
'OpenaiUpdateVectorStoreFileRequest',
'VectorStoreFileDeleteResponse',
'bool',
'VectorStoreContent',
'VectorStoreFileContentResponse',
'OpenaiSearchVectorStoreRequest',
'VectorStoreSearchResponse',
'VectorStoreSearchResponsePage',
'VersionInfo',
'AppendRowsRequest',
'PaginatedResponse',
'Dataset',
'RowsDataSource',
'URIDataSource',
'ListDatasetsResponse',
'Benchmark',
'ListBenchmarksResponse',
'BenchmarkConfig',
'GreedySamplingStrategy',
'ModelCandidate',
'SamplingParams',
'SystemMessage',
'TopKSamplingStrategy',
'TopPSamplingStrategy',
'EvaluateRowsRequest',
'EvaluateResponse',
'RunEvalRequest',
'Job',
'RerankRequest',
'RerankData',
'RerankResponse',
'Checkpoint',
'PostTrainingJobArtifactsResponse',
'PostTrainingMetric',
'CancelTrainingJobRequest',
'PostTrainingJobStatusResponse',
'ListPostTrainingJobsResponse',
'DPOAlignmentConfig',
'DPOLossType',
'DataConfig',
'DatasetFormat',
'EfficiencyConfig',
'OptimizerConfig',
'OptimizerType',
'TrainingConfig',
'PreferenceOptimizeRequest',
'PostTrainingJob',
'AlgorithmConfig',
'LoraFinetuningConfig',
'QATFinetuningConfig',
'SupervisedFineTuneRequest',
'RegisterModelRequest',
'ParamType',
'RegisterScoringFunctionRequest',
'RegisterShieldRequest',
'RegisterToolGroupRequest',
'DataSource',
'RegisterDatasetRequest',
'RegisterBenchmarkRequest']
LEGACY_RESPONSE_ORDER = ['BadRequest400', 'TooManyRequests429', 'InternalServerError500', 'DefaultError']
LEGACY_TAG_ORDER = ['Agents',
'Batches',
'Benchmarks',
'Conversations',
'DatasetIO',
'Datasets',
'Eval',
'Files',
'Inference',
'Inspect',
'Models',
'PostTraining (Coming Soon)',
'Prompts',
'Providers',
'Safety',
'Scoring',
'ScoringFunctions',
'Shields',
'ToolGroups',
'ToolRuntime',
'VectorIO']
LEGACY_TAG_GROUPS = [{'name': 'Operations',
'tags': ['Agents',
'Batches',
'Benchmarks',
'Conversations',
'DatasetIO',
'Datasets',
'Eval',
'Files',
'Inference',
'Inspect',
'Models',
'PostTraining (Coming Soon)',
'Prompts',
'Providers',
'Safety',
'Scoring',
'ScoringFunctions',
'Shields',
'ToolGroups',
'ToolRuntime',
'VectorIO']}]

View file

@ -141,6 +141,7 @@ def generate_openapi_spec(output_dir: str) -> dict[str, Any]:
for schema, _ in schemas_to_validate:
schema_transforms._fix_schema_issues(schema)
schema_transforms._apply_legacy_sorting(schema)
print("\n🔍 Validating generated schemas...")
failed_schemas = [

View file

@ -9,6 +9,7 @@ Schema transformations and fixes for OpenAPI generation.
"""
import copy
from collections import OrderedDict
from pathlib import Path
from typing import Any
@ -17,6 +18,13 @@ from openapi_spec_validator import validate_spec
from openapi_spec_validator.exceptions import OpenAPISpecValidatorError
from . import endpoints, schema_collection
from ._legacy_order import (
LEGACY_PATH_ORDER,
LEGACY_RESPONSE_ORDER,
LEGACY_SCHEMA_ORDER,
LEGACY_TAG_GROUPS,
LEGACY_TAG_ORDER,
)
from .state import _extra_body_fields
@ -821,6 +829,62 @@ def _write_yaml_file(file_path: Path, schema: dict[str, Any]) -> None:
f.writelines(cleaned_lines)
def _apply_legacy_sorting(openapi_schema: dict[str, Any]) -> dict[str, Any]:
"""
Temporarily match the legacy ordering from origin/main so diffs are easier to read.
Remove this once the generator output stabilizes and we no longer need legacy diffs.
"""
def order_mapping(data: dict[str, Any], priority: list[str]) -> OrderedDict[str, Any]:
ordered: OrderedDict[str, Any] = OrderedDict()
for key in priority:
if key in data:
ordered[key] = data[key]
for key, value in data.items():
if key not in ordered:
ordered[key] = value
return ordered
paths = openapi_schema.get("paths")
if isinstance(paths, dict):
openapi_schema["paths"] = order_mapping(paths, LEGACY_PATH_ORDER)
components = openapi_schema.setdefault("components", {})
schemas = components.get("schemas")
if isinstance(schemas, dict):
components["schemas"] = order_mapping(schemas, LEGACY_SCHEMA_ORDER)
responses = components.get("responses")
if isinstance(responses, dict):
components["responses"] = order_mapping(responses, LEGACY_RESPONSE_ORDER)
tags = openapi_schema.get("tags")
if isinstance(tags, list):
tag_priority = {name: idx for idx, name in enumerate(LEGACY_TAG_ORDER)}
def tag_sort(tag_obj: dict[str, Any]) -> tuple[int, int | str]:
name = tag_obj.get("name", "")
if name in tag_priority:
return (0, tag_priority[name])
return (1, name)
openapi_schema["tags"] = sorted(tags, key=tag_sort)
tag_groups = openapi_schema.get("x-tagGroups")
if isinstance(tag_groups, list) and LEGACY_TAG_GROUPS:
legacy_tags = LEGACY_TAG_GROUPS[0].get("tags", [])
tag_priority = {name: idx for idx, name in enumerate(legacy_tags)}
for group in tag_groups:
group_tags = group.get("tags")
if isinstance(group_tags, list):
group["tags"] = sorted(
group_tags,
key=lambda name: (0, tag_priority[name]) if name in tag_priority else (1, name),
)
openapi_schema["x-tagGroups"] = tag_groups
return openapi_schema
def _fix_schema_issues(openapi_schema: dict[str, Any]) -> dict[str, Any]:
"""Fix common schema issues: exclusiveMinimum, null defaults, and add titles to unions."""
# Convert anyOf with const values to enums across the entire schema