From 0f9256b024ad55d4cc70845cb6732ec6739c8247 Mon Sep 17 00:00:00 2001 From: saladday <1203511142@qq.com> Date: Wed, 7 Oct 2026 22:01:43 +0800 Subject: [PATCH] refactor(api): trim schema generation and unify text types --- Makefile | 1 - contracts/agents-api/core.openapi.yaml | 28 +-- contracts/agents-api/go-bindings.json | 192 ++++-------------- contracts/agents-api/index.md | 2 +- contracts/agents-api/v1/official.gen.go | 90 ++------ contracts/agents-api/zh/index.md | 4 +- scripts/generate-public-api.py | 13 +- .../core/internal/api/saved_configuration.go | 25 ++- services/core/internal/api/session_agent.go | 6 +- .../core/internal/api/text_configuration.go | 25 --- 10 files changed, 85 insertions(+), 301 deletions(-) delete mode 100644 services/core/internal/api/text_configuration.go diff --git a/Makefile b/Makefile index 4d91c1ca8..f769ed092 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,6 @@ check-openapi: python3 scripts/generate-public-api.test.py openapi: - python3 scripts/generate-public-api.py $(OPENAPI_FLAGS) @set -e; root="$${OAC_DEV_HOME:-$$HOME/.oac}/build"; mkdir -p "$$root"; \ output=$$(mktemp -d "$$root/core-openapi.XXXXXX"); trap 'rm -rf "$$output"' EXIT; \ python3 scripts/generate-public-api.py $(OPENAPI_FLAGS) --swag-roots "$$output/roots.go"; \ diff --git a/contracts/agents-api/core.openapi.yaml b/contracts/agents-api/core.openapi.yaml index fb65920bd..c3412ffd4 100644 --- a/contracts/agents-api/core.openapi.yaml +++ b/contracts/agents-api/core.openapi.yaml @@ -2587,7 +2587,7 @@ definitions: - fast type: string text: - $ref: '#/definitions/v1.SavedAgentText' + $ref: '#/definitions/v1.TextConfig' tools: items: type: object @@ -2649,32 +2649,6 @@ definitions: - last_id - object type: object - v1.SavedAgentText: - properties: - format: - $ref: '#/definitions/v1.SavedAgentTextFormat' - verbosity: - enum: - - low - - medium - - high - type: string - required: - - format - - verbosity - type: object - v1.SavedAgentTextFormat: - properties: - schema: - type: object - type: - enum: - - text - - json_schema - type: string - required: - - type - type: object v1.Session: properties: agent: diff --git a/contracts/agents-api/go-bindings.json b/contracts/agents-api/go-bindings.json index 176a7d1c7..5b5d9253e 100644 --- a/contracts/agents-api/go-bindings.json +++ b/contracts/agents-api/go-bindings.json @@ -1,31 +1,24 @@ { "APIError": { "sources": ["#/components/schemas/Error"], - "fields": { - "code": {"type": "*string"} - }, "order": ["message", "type", "code", "param"] }, "Agent": { "sources": ["#/components/schemas/SessionAgentResource"], "fields": { "x_agents_core": {"type": "*AgentsCore"}, - "reasoning": {"type": "Reasoning"}, - "text": {"type": "TextConfig"} + "reasoning": {"type": "Reasoning"} }, "order": ["x_agents_core", "id", "instructions", "model", "multi_agent", "name", "reasoning", "service_tier", "text", "tools"] }, "AgentContent": { - "sources": ["#/components/schemas/AgentContentResource"], - "order": ["type", "text", "encrypted_content"] + "sources": ["#/components/schemas/AgentContentResource"] }, "AgentDeleted": { - "sources": ["#/components/schemas/DeletedAgentResource"], - "order": ["id", "object", "deleted"] + "sources": ["#/components/schemas/DeletedAgentResource"] }, "AgentMessageItem": { - "sources": ["#/components/schemas/AgentMessageItemResource"], - "order": ["id", "turn_id", "type", "sender_agent_id", "recipient_agent_id", "content"] + "sources": ["#/components/schemas/AgentMessageItemResource"] }, "CreateAgentRequest": { "sources": ["#/components/schemas/CreateAgentParams"], @@ -34,22 +27,10 @@ "model": {"type": "*string"}, "metadata": {"type": "map[string]*string"}, "reasoning": {"type": "*Reasoning"}, - "text": {"type": "*SavedAgentTextInput"} + "text": {"type": "*TextConfigInput"} }, "order": ["x_agents_core", "model", "name", "instructions", "metadata", "multi_agent", "reasoning", "service_tier", "text", "tools"] }, - "CreateCredentialRequest": { - "sources": ["#/components/schemas/CreateVaultCredentialParams"], - "fields": { - "name": {"type": "*string"}, - "auth": {"type": "*CredentialAuthInput"} - }, - "order": ["name", "auth"] - }, - "CreateEventsRequest": { - "sources": ["#/components/schemas/CreateSessionEventsParams"], - "order": ["events"] - }, "CreateSessionRequest": { "sources": ["#/components/schemas/CreateAgentSessionParams"], "fields": { @@ -64,13 +45,6 @@ "sources": ["#/components/schemas/CreateSubagentCallItemResource"], "order": ["id", "turn_id", "type", "status", "agent_id", "content", "model", "reasoning_effort"] }, - "CreateVaultRequest": { - "sources": ["#/components/schemas/CreateVaultParams"], - "fields": { - "metadata": {"type": "map[string]*string"} - }, - "order": ["name", "metadata"] - }, "Credential": { "sources": ["#/components/schemas/VaultCredentialResource"], "order": ["id", "vault_id", "name", "object", "auth", "created_at", "updated_at"] @@ -80,8 +54,7 @@ "fields": { "expires_at": {"omit": false}, "refresh": {"type": "*OAuthCredentialRefresh", "omit": false} - }, - "order": ["type", "mcp_server_url", "expires_at", "refresh"] + } }, "CredentialAuthInput": { "sources": ["#/components/schemas/CreateVaultCredentialAuthParam"], @@ -134,20 +107,17 @@ "order": ["type", "data", "file_id", "path"] }, "EnvironmentFileList": { - "sources": ["#/components/schemas/EnvironmentFileListResource"], - "order": ["object", "data", "next", "has_more"] + "sources": ["#/components/schemas/EnvironmentFileListResource"] }, "EnvironmentInfo": { "sources": ["#/components/schemas/PublicEnvironmentResource"], "order": ["id", "object", "type", "status", "files", "plugins", "skills"] }, "EnvironmentNetwork": { - "sources": ["#/components/schemas/NetworkPolicyResource"], - "order": ["access", "allowed_domains"] + "sources": ["#/components/schemas/NetworkPolicyResource"] }, "EnvironmentNetworkInput": { - "sources": ["#/components/schemas/NetworkPolicyParam"], - "order": ["access", "allowed_domains"] + "sources": ["#/components/schemas/NetworkPolicyParam"] }, "EnvironmentPackages": { "exclude": ["system"], @@ -158,11 +128,6 @@ }, "order": ["npm", "python"] }, - "EnvironmentPackagesInput": { - "exclude": ["system"], - "sources": ["#/components/schemas/EnvironmentPackagesParam"], - "order": ["npm", "python"] - }, "EnvironmentPackagesResponse": { "sources": ["#/components/schemas/EnvironmentPackagesResource"], "order": ["npm", "python", "system"] @@ -172,26 +137,17 @@ "order": ["id", "object", "name", "created_at", "updated_at", "capability_directories", "network", "packages", "files", "plugins", "skills"] }, "EnvironmentTemplateDeleted": { - "sources": ["#/components/schemas/DeletedEnvironmentTemplateResource"], - "order": ["id", "object", "deleted"] + "sources": ["#/components/schemas/DeletedEnvironmentTemplateResource"] }, "EnvironmentTemplateList": { "sources": ["#/components/schemas/EnvironmentTemplateListResource"], "order": ["object", "data", "has_more", "first_id", "last_id"] }, - "EnvironmentTemplateRequest": { - "sources": ["#/components/schemas/CreateEnvironmentTemplateParams"], - "fields": { - "network": {"type": "*EnvironmentNetworkInput"}, - "files": {"type": "[]json.RawMessage"}, - "packages": {"type": "*EnvironmentPackagesInput"} - }, - "order": ["name", "network", "capability_directories", "env", "files", "packages", "plugins", "skills", "setup_commands"] - }, "ErrorResponse": { "sources": ["#/components/schemas/ErrorResponse-2"], - "fields": {"error": {"type": "APIError"}}, - "order": ["error"] + "fields": { + "error": {"type": "APIError"} + } }, "FunctionCallAction": { "sources": ["#/components/schemas/SessionRequiredActionResourceFunctionCall"], @@ -207,32 +163,28 @@ "description": {"type": "*string"}, "parameters": {"type": "json.RawMessage"}, "defer_loading": {"type": "json.RawMessage"} - }, - "order": ["type", "name", "description", "parameters", "defer_loading"] + } }, "InlineAgent": { "sources": ["#/components/schemas/SessionAgentConfigParam"], "fields": { "x_agents_core": {"type": "*AgentsCore"}, "reasoning": {"type": "*Reasoning"}, - "text": {"type": "*SavedAgentTextInput"} + "text": {"type": "*TextConfigInput"} }, "order": ["x_agents_core", "model", "instructions", "multi_agent", "reasoning", "service_tier", "text", "tools"] }, "InputContent": { - "sources": ["#/components/schemas/InputContentParam"], - "order": ["type", "text", "image_url"] + "sources": ["#/components/schemas/InputContentParam"] }, "InputMessage": { "sources": ["#/components/schemas/InputMessageParam"], "fields": { "type": {"type": "string"} - }, - "order": ["type", "role", "content"] + } }, "InputTokenDetails": { - "sources": ["#/components/schemas/InputTokensDetailsResource"], - "order": ["cached_tokens"] + "sources": ["#/components/schemas/InputTokensDetailsResource"] }, "Item": { "sources": ["#/components/schemas/SessionTurnItemResource"], @@ -260,8 +212,7 @@ "sources": ["#/components/schemas/MessageContentResource", "#/components/schemas/EncryptedContentResource"], "fields": { "image_url": {"type": "string"} - }, - "order": ["type", "text", "image_url", "encrypted_content"] + } }, "ItemList": { "sources": ["#/components/schemas/SessionItemListResource"], @@ -271,8 +222,7 @@ "sources": ["#/components/schemas/PersistedMcpTransportConfigParamHttp"], "fields": { "headers": {"type": "*map[string]string"} - }, - "order": ["type", "server_url", "headers"] + } }, "MCPTool": { "sources": ["#/components/schemas/AgentToolResourceMcp"], @@ -298,8 +248,7 @@ "sources": ["#/components/schemas/MultiAgentConfigResource"], "fields": { "max_concurrent_subagents": {"type": "*int"} - }, - "order": ["enabled", "max_concurrent_subagents"] + } }, "OAuthCredentialRefresh": { "sources": ["#/components/schemas/McpOauthRefreshResource"], @@ -320,28 +269,22 @@ "fields": { "scope": {"type": "json.RawMessage"}, "token_endpoint_auth": {"type": "*OAuthEndpointAuthReplacement"} - }, - "order": ["refresh_token", "scope", "token_endpoint_auth"] + } }, "OAuthEndpointAuth": { - "sources": ["#/components/schemas/McpOauthTokenEndpointAuthResource"], - "order": ["type"] + "sources": ["#/components/schemas/McpOauthTokenEndpointAuthResource"] }, "OAuthEndpointAuthInput": { - "sources": ["#/components/schemas/CreateMcpOauthTokenEndpointAuthParam"], - "order": ["type", "client_secret"] + "sources": ["#/components/schemas/CreateMcpOauthTokenEndpointAuthParam"] }, "OAuthEndpointAuthReplacement": { - "sources": ["#/components/schemas/RotateMcpOauthTokenEndpointAuthParam"], - "order": ["type", "client_secret"] + "sources": ["#/components/schemas/RotateMcpOauthTokenEndpointAuthParam"] }, "OutputTokenDetails": { - "sources": ["#/components/schemas/OutputTokensDetailsResource"], - "order": ["reasoning_tokens"] + "sources": ["#/components/schemas/OutputTokensDetailsResource"] }, "Reasoning": { - "sources": ["#/components/schemas/ReasoningParam"], - "order": ["effort", "summary"] + "sources": ["#/components/schemas/ReasoningParam"] }, "ReasoningItem": { "sources": ["#/components/schemas/ReasoningItemResource"], @@ -376,20 +319,8 @@ "sources": ["#/components/schemas/AgentListResource"], "order": ["object", "data", "has_more", "first_id", "last_id"] }, - "SavedAgentText": { - "sources": ["#/components/schemas/TextResource"], - "order": ["format", "verbosity"] - }, - "SavedAgentTextFormat": { - "sources": ["#/components/schemas/TextFormatResource"], - "fields": { - "schema": {"type": "json.RawMessage"} - }, - "order": ["type", "schema"] - }, - "SavedAgentTextInput": { - "sources": ["#/components/schemas/TextParam"], - "order": ["format", "verbosity"] + "TextConfigInput": { + "sources": ["#/components/schemas/TextParam"] }, "SendSubagentInputCallItem": { "sources": ["#/components/schemas/SendSubagentInputCallItemResource"], @@ -408,8 +339,7 @@ "order": ["id", "created_at", "environment_id", "object", "path", "session_id", "size_bytes", "turn_id"] }, "SessionArtifactDeleted": { - "sources": ["#/components/schemas/DeletedSessionArtifactResource"], - "order": ["id", "object", "deleted"] + "sources": ["#/components/schemas/DeletedSessionArtifactResource"] }, "SessionArtifactList": { "sources": ["#/components/schemas/SessionArtifactListResource"], @@ -436,8 +366,7 @@ "sources": ["#/components/schemas/SessionEnvironmentStateResource"], "fields": { "error": {"type": "*StreamError"} - }, - "order": ["id", "type", "status", "error"] + } }, "SessionEvent": { "sources": ["#/components/schemas/SessionEvent"], @@ -479,12 +408,10 @@ "order": ["id", "object", "deleted"] }, "SkillList": { - "sources": ["#/components/schemas/SkillListResource"], - "order": ["object", "data", "first_id", "last_id", "has_more"] + "sources": ["#/components/schemas/SkillListResource"] }, "SkillUpdateRequest": { - "sources": ["#/components/schemas/SetDefaultSkillVersionBody"], - "order": ["default_version"] + "sources": ["#/components/schemas/SetDefaultSkillVersionBody"] }, "SkillVersion": { "sources": ["#/components/schemas/SkillVersionResource"], @@ -495,8 +422,7 @@ "order": ["id", "object", "version", "deleted"] }, "SkillVersionList": { - "sources": ["#/components/schemas/SkillVersionListResource"], - "order": ["object", "data", "first_id", "last_id", "has_more"] + "sources": ["#/components/schemas/SkillVersionListResource"] }, "SourceFile": { "sources": ["#/components/schemas/OpenAIFile"], @@ -507,8 +433,7 @@ "order": ["id", "object", "bytes", "created_at", "filename", "purpose", "status", "expires_at", "status_details"] }, "SourceFileDeleted": { - "sources": ["#/components/schemas/DeleteFileResponse"], - "order": ["id", "object", "deleted"] + "sources": ["#/components/schemas/DeleteFileResponse"] }, "SourceFileList": { "sources": ["#/components/schemas/ListFilesResponse"], @@ -539,33 +464,19 @@ "order": ["object", "first_id", "last_id", "data", "has_more"] }, "SummaryText": { - "sources": ["#/components/schemas/SummaryTextResource"], - "order": ["type", "text"] + "sources": ["#/components/schemas/SummaryTextResource"] }, "TextConfig": { - "sources": ["#/components/schemas/TextResource"], - "order": ["format", "verbosity"], - "fields": { - "format": {"type": "TextFormat"} - } - }, - "TextConfigInput": { - "sources": ["#/components/schemas/TextParam"], - "fields": { - "format": {"type": "*TextFormat"} - }, - "order": ["format", "verbosity"] + "sources": ["#/components/schemas/TextResource"] }, "TextFormat": { "sources": ["#/components/schemas/TextFormatResource"], "fields": { "schema": {"type": "json.RawMessage"} - }, - "order": ["type", "schema"] + } }, "TokenUsage": { - "sources": ["#/components/schemas/TokenUsageResource"], - "order": ["input_tokens", "input_tokens_details", "output_tokens", "output_tokens_details", "total_tokens"] + "sources": ["#/components/schemas/TokenUsageResource"] }, "Turn": { "sources": ["#/components/schemas/TurnResource"], @@ -576,8 +487,7 @@ "order": ["id", "agent_id", "subagent_id", "session_id", "object", "status", "created_at", "started_at", "completed_at", "error", "usage"] }, "TurnError": { - "sources": ["#/components/schemas/SessionTurnErrorResource"], - "order": ["code", "message"] + "sources": ["#/components/schemas/SessionTurnErrorResource"] }, "TurnList": { "sources": ["#/components/schemas/SessionTurnListResource"], @@ -589,24 +499,10 @@ "x_agents_core": {"type": "*SavedAgentCoreInput"}, "metadata": {"type": "map[string]*string"}, "reasoning": {"type": "*Reasoning"}, - "text": {"type": "*SavedAgentTextInput"} + "text": {"type": "*TextConfigInput"} }, "order": ["x_agents_core", "model", "name", "instructions", "metadata", "multi_agent", "reasoning", "service_tier", "text", "tools"] }, - "UpdateCredentialRequest": { - "sources": ["#/components/schemas/RotateVaultCredentialParams"], - "fields": { - "auth": {"type": "*CredentialAuthReplacement"} - }, - "order": ["auth"] - }, - "UpdateSessionRequest": { - "sources": ["#/components/schemas/UpdateAgentSessionParams"], - "fields": { - "metadata": {"omit": false} - }, - "order": ["metadata"] - }, "Vault": { "sources": ["#/components/schemas/VaultResource"], "order": ["id", "object", "created_at", "name", "metadata"] @@ -625,12 +521,10 @@ }, "WebSearchAction": { "marshal_union": true, - "sources": ["#/components/schemas/WebSearchActionResource"], - "order": ["type", "query", "queries", "url", "pattern"] + "sources": ["#/components/schemas/WebSearchActionResource"] }, "reasoningResponse": { - "sources": ["#/components/schemas/ReasoningResource"], - "order": ["effort", "summary"] + "sources": ["#/components/schemas/ReasoningResource"] }, "sessionError": { "sources": ["#/components/schemas/SessionErrorResource"], diff --git a/contracts/agents-api/index.md b/contracts/agents-api/index.md index 87dd67793..5b2f933fc 100644 --- a/contracts/agents-api/index.md +++ b/contracts/agents-api/index.md @@ -16,7 +16,7 @@ Core targets the complete OpenAI Agents API as pinned below ([public API rule](h Run `make openapi` to regenerate the public Go types, route inventory and all three OpenAPI documents. `scripts/generate-public-api.py` reads the checked-in, checksum-verified official source without network access. It selects Agents, Vaults, Files and Skills and follows their schema references, preserving union types, nullability, required fields and constraints. Core's extension types in `v1/` remain authored in Go and are added to the public schema during generation. The internal `/core/v1` and `/api/v1` documents come from handler annotations. `make check-openapi` checks freshness and the generator; it also runs through `make check-go`. -The public contract is the official API plus Core extensions. Standard fields are generated into `v1/official.gen.go`; `go-bindings.json` controls their Go representation where existing storage or custom JSON encoding requires it. Selected discriminated unions also generate JSON serializers to retain required nullable fields for each variant. Other union serializers, request admission and state transitions remain implementation code. Contract tests verify that the public schema preserves the official definitions, extensions remain in `x_agents_core`, and all documents match registered routes. Official-client and raw HTTP tests verify behavior. Schema generation does not qualify an unimplemented feature; the gaps below still apply. Upstream upgrades update the OpenAPI and SDK pins together after comparison and compatibility tests. +The public contract is the official API plus Core extensions. Standard fields are generated into `v1/official.gen.go`; `go-bindings.json` lists types consumed by Core and overrides only the Go representation or field order that existing storage or custom JSON encoding requires. Unspecified fields follow the official schema; shared shapes use one Go type. Selected discriminated unions also generate JSON serializers to retain required nullable fields for each variant. Other union serializers, request admission and state transitions remain implementation code. Contract tests verify that the public schema preserves the official definitions, extensions remain in `x_agents_core`, and all documents match registered routes. Official-client and raw HTTP tests verify behavior. Schema generation does not qualify an unimplemented feature; the gaps below still apply. Upstream upgrades update the OpenAPI and SDK pins together after comparison and compatibility tests. The official source and existing service have these recorded differences: Agents authentication errors can return a null `code`; empty Files pages return null `first_id` and `last_id`; File resources can return null `expires_at` and `status_details`. The source declares those fields non-null. The official-client response validator allows null only for these named fields and otherwise validates OpenAPI 3.1 response schemas. Files and Skills operations omit error responses in the source, so those error bodies use the upstream shared `ErrorResponse` schema. [Wire semantics](./wire-semantics.md) and raw HTTP tests qualify service behavior; the published schema retains the official definitions. diff --git a/contracts/agents-api/v1/official.gen.go b/contracts/agents-api/v1/official.gen.go index 0c6985fe6..1cc7b4653 100644 --- a/contracts/agents-api/v1/official.gen.go +++ b/contracts/agents-api/v1/official.gen.go @@ -62,21 +62,10 @@ type CreateAgentRequest struct { MultiAgent json.RawMessage `json:"multi_agent,omitempty" extensions:"x-nullable" swaggertype:"object"` Reasoning *Reasoning `json:"reasoning,omitempty" extensions:"x-nullable"` ServiceTier *string `json:"service_tier,omitempty" extensions:"x-nullable" enums:"auto,default,flex,priority,fast"` - Text *SavedAgentTextInput `json:"text,omitempty" extensions:"x-nullable"` + Text *TextConfigInput `json:"text,omitempty" extensions:"x-nullable"` Tools []json.RawMessage `json:"tools,omitempty" extensions:"x-nullable" swaggertype:"array,object"` } -// CreateCredentialRequest projects CreateVaultCredentialParams. -type CreateCredentialRequest struct { - Name *string `json:"name" binding:"required"` - Auth *CredentialAuthInput `json:"auth" binding:"required"` -} - -// CreateEventsRequest projects CreateSessionEventsParams. -type CreateEventsRequest struct { - Events []SessionInput `json:"events" binding:"required"` -} - // CreateSessionRequest projects CreateAgentSessionParams. type CreateSessionRequest struct { XAgentsCore *SessionExecutionInput `json:"x_agents_core,omitempty"` @@ -101,12 +90,6 @@ type CreateSubagentCallItem struct { ReasoningEffort *string `json:"reasoning_effort" binding:"required" extensions:"x-nullable"` } -// CreateVaultRequest projects CreateVaultParams. -type CreateVaultRequest struct { - Name *string `json:"name,omitempty"` - Metadata map[string]*string `json:"metadata,omitempty" extensions:"x-nullable"` -} - // Credential projects VaultCredentialResource. type Credential struct { ID string `json:"id" binding:"required"` @@ -235,12 +218,6 @@ type EnvironmentPackages struct { Python []string `json:"python" extensions:"x-nullable"` } -// EnvironmentPackagesInput projects EnvironmentPackagesParam. -type EnvironmentPackagesInput struct { - NPM []string `json:"npm,omitempty" extensions:"x-nullable"` - Python []string `json:"python,omitempty" extensions:"x-nullable"` -} - // EnvironmentPackagesResponse projects EnvironmentPackagesResource. type EnvironmentPackagesResponse struct { NPM []string `json:"npm" binding:"required"` @@ -279,19 +256,6 @@ type EnvironmentTemplateList struct { LastID *string `json:"last_id" binding:"required" extensions:"x-nullable"` } -// EnvironmentTemplateRequest projects CreateEnvironmentTemplateParams. -type EnvironmentTemplateRequest struct { - Name *string `json:"name,omitempty" extensions:"x-nullable"` - Network *EnvironmentNetworkInput `json:"network,omitempty" extensions:"x-nullable"` - CapabilityDirectories []string `json:"capability_directories,omitempty" extensions:"x-nullable"` - Env map[string]string `json:"env,omitempty" extensions:"x-nullable"` - Files []json.RawMessage `json:"files,omitempty" extensions:"x-nullable" swaggertype:"array,object"` - Packages *EnvironmentPackagesInput `json:"packages,omitempty" extensions:"x-nullable"` - Plugins []json.RawMessage `json:"plugins,omitempty" extensions:"x-nullable" swaggertype:"array,object"` - Skills []json.RawMessage `json:"skills,omitempty" extensions:"x-nullable" swaggertype:"array,object"` - SetupCommands []json.RawMessage `json:"setup_commands,omitempty" extensions:"x-nullable" swaggertype:"array,object"` -} - // ErrorResponse projects ErrorResponse-2. type ErrorResponse struct { Error APIError `json:"error" binding:"required"` @@ -317,14 +281,14 @@ type FunctionToolInput struct { // InlineAgent projects SessionAgentConfigParam. type InlineAgent struct { - XAgentsCore *AgentsCore `json:"x_agents_core,omitempty"` - Model *string `json:"model,omitempty"` - Instructions *string `json:"instructions,omitempty" extensions:"x-nullable"` - MultiAgent json.RawMessage `json:"multi_agent,omitempty" extensions:"x-nullable" swaggertype:"object"` - Reasoning *Reasoning `json:"reasoning,omitempty" extensions:"x-nullable"` - ServiceTier *string `json:"service_tier,omitempty" extensions:"x-nullable" enums:"auto,default,flex,priority,fast"` - Text *SavedAgentTextInput `json:"text,omitempty" extensions:"x-nullable"` - Tools []json.RawMessage `json:"tools,omitempty" extensions:"x-nullable" swaggertype:"array,object"` + XAgentsCore *AgentsCore `json:"x_agents_core,omitempty"` + Model *string `json:"model,omitempty"` + Instructions *string `json:"instructions,omitempty" extensions:"x-nullable"` + MultiAgent json.RawMessage `json:"multi_agent,omitempty" extensions:"x-nullable" swaggertype:"object"` + Reasoning *Reasoning `json:"reasoning,omitempty" extensions:"x-nullable"` + ServiceTier *string `json:"service_tier,omitempty" extensions:"x-nullable" enums:"auto,default,flex,priority,fast"` + Text *TextConfigInput `json:"text,omitempty" extensions:"x-nullable"` + Tools []json.RawMessage `json:"tools,omitempty" extensions:"x-nullable" swaggertype:"array,object"` } // InputContent projects InputContentParam. @@ -521,7 +485,7 @@ type SavedAgentConfiguration struct { MultiAgent MultiAgentConfig `json:"multi_agent" binding:"required"` Reasoning Reasoning `json:"reasoning" binding:"required"` ServiceTier string `json:"service_tier" binding:"required" enums:"auto,default,flex,priority,fast"` - Text SavedAgentText `json:"text" binding:"required"` + Text TextConfig `json:"text" binding:"required"` Tools []json.RawMessage `json:"tools" binding:"required" swaggertype:"array,object"` } @@ -534,24 +498,6 @@ type SavedAgentList struct { LastID *string `json:"last_id" binding:"required" extensions:"x-nullable"` } -// SavedAgentText projects TextResource. -type SavedAgentText struct { - Format SavedAgentTextFormat `json:"format" binding:"required"` - Verbosity string `json:"verbosity" binding:"required" enums:"low,medium,high"` -} - -// SavedAgentTextFormat projects TextFormatResource. -type SavedAgentTextFormat struct { - Type string `json:"type" binding:"required" enums:"text,json_schema"` - Schema json.RawMessage `json:"schema,omitempty" swaggertype:"object"` -} - -// SavedAgentTextInput projects TextParam. -type SavedAgentTextInput struct { - Format json.RawMessage `json:"format,omitempty" extensions:"x-nullable" swaggertype:"object"` - Verbosity *string `json:"verbosity,omitempty" extensions:"x-nullable" enums:"low,medium,high"` -} - // SendSubagentInputCallItem projects SendSubagentInputCallItemResource. type SendSubagentInputCallItem struct { ID string `json:"id" binding:"required"` @@ -823,8 +769,8 @@ type TextConfig struct { // TextConfigInput projects TextParam. type TextConfigInput struct { - Format *TextFormat `json:"format,omitempty" extensions:"x-nullable"` - Verbosity *string `json:"verbosity,omitempty" extensions:"x-nullable" enums:"low,medium,high"` + Format json.RawMessage `json:"format,omitempty" extensions:"x-nullable" swaggertype:"object"` + Verbosity *string `json:"verbosity,omitempty" extensions:"x-nullable" enums:"low,medium,high"` } // TextFormat projects TextFormatResource. @@ -882,20 +828,10 @@ type UpdateAgentRequest struct { MultiAgent json.RawMessage `json:"multi_agent,omitempty" extensions:"x-nullable" swaggertype:"object"` Reasoning *Reasoning `json:"reasoning,omitempty" extensions:"x-nullable"` ServiceTier *string `json:"service_tier,omitempty" extensions:"x-nullable" enums:"auto,default,flex,priority,fast"` - Text *SavedAgentTextInput `json:"text,omitempty" extensions:"x-nullable"` + Text *TextConfigInput `json:"text,omitempty" extensions:"x-nullable"` Tools []json.RawMessage `json:"tools,omitempty" extensions:"x-nullable" swaggertype:"array,object"` } -// UpdateCredentialRequest projects RotateVaultCredentialParams. -type UpdateCredentialRequest struct { - Auth *CredentialAuthReplacement `json:"auth" binding:"required"` -} - -// UpdateSessionRequest projects UpdateAgentSessionParams. -type UpdateSessionRequest struct { - Metadata map[string]string `json:"metadata" extensions:"x-nullable"` -} - // Vault projects VaultResource. type Vault struct { ID string `json:"id" binding:"required"` diff --git a/contracts/agents-api/zh/index.md b/contracts/agents-api/zh/index.md index 8fcf71028..f84d692b7 100644 --- a/contracts/agents-api/zh/index.md +++ b/contracts/agents-api/zh/index.md @@ -1,7 +1,7 @@ --- title: "Agents API 覆盖台账" source: contracts/agents-api/index.md -source_hash: 7b56bd48dd80dc6b04909d0ee82fc873ff51bf4daeb8fbdd1570ad66a9349cd0 +source_hash: 61cd594bb0bd475b019c916e099b6e8de935b4e5cb1cde7a39f620818097900b --- Core 旨在以下方固定版本为准支持完整的 OpenAI Agents API([public API rule](https://github.com/MiniMax-AI/OpenAgentCore/blob/main/AGENTS.md#public-api))。本台账记录 Core 对各项资源实现了哪些内容、哪些契约保存其详细信息,并列出相对于 OpenAI 服务的所有已知差异和所有未解决缺口。[API namespaces and credentials](../../../docs/zh/api/index.md) 说明谁调用哪些 API;[Agents API guide](../../../docs/zh/api/public-agent-api.md) 介绍使用方法。 @@ -18,7 +18,7 @@ Core 旨在以下方固定版本为准支持完整的 OpenAI Agents API([publi 运行 `make openapi` 重新生成公共 Go 类型、路由清单和三个 OpenAPI 文档。`scripts/generate-public-api.py` 读取仓库内经过校验和验证的官方源文件,无需网络。它选择 Agents、Vaults、Files 和 Skills,并跟随 schema 引用,保留联合类型、可空性、必填字段和约束。Core 在 `v1/` 中的扩展类型继续由 Go 定义,在生成时加入公共 schema。内部 `/core/v1` 和 `/api/v1` 文档由处理函数注解生成。`make check-openapi` 检查生成结果是否最新并测试生成器;`make check-go` 也会运行此检查。 -公共契约是官方 API 加上 Core 扩展。标准字段生成到 `v1/official.gen.go`;已有存储或自定义 JSON 编码需要特定表示时,由 `go-bindings.json` 控制其 Go 表示。部分带判别字段的联合类型也从 schema 生成 JSON 序列化代码,保留每个分支必需的可空字段。其他联合类型序列化、请求准入和状态转换仍由实现代码负责。契约测试验证公共 schema 保留官方定义、扩展位于 `x_agents_core` 中,且所有文档与注册路由一致。官方客户端和原始 HTTP 测试验证行为。生成 schema 不代表某个尚未实现的功能已经得到验证;下方缺口仍然适用。升级上游时,在比对和兼容性测试后一起更新 OpenAPI 和 SDK 固定版本。 +公共契约是官方 API 加上 Core 扩展。标准字段生成到 `v1/official.gen.go`;`go-bindings.json` 只列出 Core 使用的类型,仅在已有存储或自定义 JSON 编码需要时覆盖 Go 表示或字段顺序。未覆盖的字段遵循官方 schema,相同结构复用同一个 Go 类型。部分带判别字段的联合类型也从 schema 生成 JSON 序列化代码,保留每个分支必需的可空字段。其他联合类型序列化、请求准入和状态转换仍由实现代码负责。契约测试验证公共 schema 保留官方定义、扩展位于 `x_agents_core` 中,且所有文档与注册路由一致。官方客户端和原始 HTTP 测试验证行为。生成 schema 不代表某个尚未实现的功能已经得到验证;下方缺口仍然适用。升级上游时,在比对和兼容性测试后一起更新 OpenAPI 和 SDK 固定版本。 官方源文件与已有服务存在以下已记录的差异:Agents 鉴权错误的 `code` 可以为 null;Files 空页的 `first_id` 和 `last_id` 为 null;File 资源的 `expires_at` 和 `status_details` 可以为 null。源文件将这些字段声明为非空。官方客户端响应验证器只对这些指定字段允许 null,其余部分按 OpenAPI 3.1 响应 schema 验证。源文件中的 Files 和 Skills 操作未声明错误响应,因此这些错误体使用上游共享的 `ErrorResponse` schema。[传输语义](wire-semantics.md)和原始 HTTP 测试验证服务行为;发布的 schema 保留官方定义。 diff --git a/scripts/generate-public-api.py b/scripts/generate-public-api.py index 9df4b2fce..69081f3e8 100644 --- a/scripts/generate-public-api.py +++ b/scripts/generate-public-api.py @@ -109,7 +109,7 @@ def enum_values(document, schema): def union_marshaler(document, name, binding, field_types): schema = resolve(document, binding['sources'][0]) discriminator = schema['discriminator']['propertyName'] - selector = binding.get('fields', {}).get(discriminator, {}).get('name', go_name(discriminator)) + selector = go_name(discriminator) lines = [f'func (value {name}) MarshalJSON() ([]byte, error) {{', f'switch value.{selector} {{'] for variant in variants(document, schema): tag, = variant['properties'][discriminator]['enum'] @@ -168,7 +168,7 @@ def go_types(document, bindings): # Swag still projects the internal APIs that reference these types. if 'json.RawMessage' in typ: tags.append('swaggertype:"' + ('array,object' if typ.lstrip('*').startswith('[]') else 'object') + '"') - field_types[field] = (override.get('name', go_name(field)), typ) + field_types[field] = (go_name(field), typ) lines.append('\t' + field_types[field][0] + ' ' + typ + ' `' + ' '.join(tags) + '`') lines.append('}\n') if binding.get('marshal_union'): @@ -254,17 +254,18 @@ def write(path, data, check): def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('--check', action='store_true') - parser.add_argument('--swag-roots', type=Path, help='Temporary entry points for Go-owned extension types') - parser.add_argument('--extensions', type=Path, help='JSON definitions projected from Core extension types') + stage = parser.add_mutually_exclusive_group(required=True) + stage.add_argument('--swag-roots', type=Path, help='Temporary entry points for Go-owned extension types') + stage.add_argument('--extensions', type=Path, help='JSON definitions projected from Core extension types') args = parser.parse_args() source, pin = read_source() bindings = json.loads((CONTRACT / 'go-bindings.json').read_text()) - write(CONTRACT / 'v1/official.gen.go', go_types(source, bindings), args.check) owners = extension_owners(bindings) if args.swag_roots: + write(CONTRACT / 'v1/official.gen.go', go_types(source, bindings), args.check) roots = 'package extensions\n\n' + '\n'.join(f'// @Success 200 {{object}} v1.{name}' for name in sorted(set(owners.values()))) + '\nfunc extensions() {}\n' args.swag_roots.write_text(roots) - if args.extensions: + else: doc = public_document(source, json.loads(args.extensions.read_text()), owners, pin["beta_header"]) # JSON is a YAML subset and keeps generation independent of PyYAML. write(CONTRACT / 'openapi.yaml', (json.dumps(doc, indent=2, ensure_ascii=False) + '\n').encode(), args.check) diff --git a/services/core/internal/api/saved_configuration.go b/services/core/internal/api/saved_configuration.go index a73036000..37463d1bc 100644 --- a/services/core/internal/api/saved_configuration.go +++ b/services/core/internal/api/saved_configuration.go @@ -63,7 +63,7 @@ func resolveSavedFields(input v1.CreateAgentRequest) (agents.CreateCommand, erro } // Model-derived effort resolution is a recorded gap. Do not manufacture a // default from the operator's execution engine or another model's catalog. - cfg.Text, err = resolveSavedText(input.Text) + cfg.Text, err = resolveText(input.Text) if err != nil { return agents.CreateCommand{}, err } @@ -103,21 +103,21 @@ func resolveSavedMultiAgent(raw json.RawMessage) (v1.MultiAgentConfig, error) { return result, nil } -func resolveSavedText(input *v1.SavedAgentTextInput) (v1.SavedAgentText, error) { - result := v1.SavedAgentText{Format: v1.SavedAgentTextFormat{Type: "text"}, Verbosity: "medium"} +func resolveText(input *v1.TextConfigInput) (v1.TextConfig, error) { + result := v1.TextConfig{Format: v1.TextFormat{Type: "text"}, Verbosity: "medium"} if input == nil { return result, nil } - // Reuse the Session verbosity policy without its narrower format admission. - text, err := resolveText(&v1.TextConfigInput{Verbosity: input.Verbosity}) - if err != nil { - return result, err + if input.Verbosity != nil { + if err := validateTextVerbosity(*input.Verbosity); err != nil { + return result, err + } + result.Verbosity = *input.Verbosity } - result.Verbosity = text.Verbosity if len(input.Format) == 0 || bytes.Equal(bytes.TrimSpace(input.Format), []byte("null")) { return result, nil } - result.Format = v1.SavedAgentTextFormat{} + result.Format = v1.TextFormat{} if decodeInputObject(input.Format, &result.Format, "type", "schema") != nil { return result, errors.New("text.format must be a supported format object.") } @@ -136,3 +136,10 @@ func resolveSavedText(input *v1.SavedAgentTextInput) (v1.SavedAgentText, error) } return result, nil } + +func validateTextVerbosity(value string) error { + if !slices.Contains([]string{"low", "medium", "high"}, value) { + return errors.New("text.verbosity must be low, medium or high.") + } + return nil +} diff --git a/services/core/internal/api/session_agent.go b/services/core/internal/api/session_agent.go index eb7b2ce24..bd95dfa65 100644 --- a/services/core/internal/api/session_agent.go +++ b/services/core/internal/api/session_agent.go @@ -84,11 +84,9 @@ func admitSessionAgent(cfg v1.SavedAgentConfiguration) (v1.Agent, error) { if cfg.ServiceTier != "auto" { return v1.Agent{}, errors.New("Execution currently supports service_tier=auto only.") } - text, err := resolveText(&v1.TextConfigInput{Verbosity: &cfg.Text.Verbosity}) - if err != nil { + if err := validateTextVerbosity(cfg.Text.Verbosity); err != nil { return v1.Agent{}, err } - text.Format = v1.TextFormat{Type: cfg.Text.Format.Type, Schema: cfg.Text.Format.Schema} tools, err := resolveSessionTools(cfg.Tools) if err != nil { return v1.Agent{}, err @@ -99,5 +97,5 @@ func admitSessionAgent(cfg v1.SavedAgentConfiguration) (v1.Agent, error) { } return v1.Agent{XAgentsCore: extension, Model: cfg.Model, Name: cfg.Name, Instructions: cfg.Instructions, MultiAgent: cfg.MultiAgent, Reasoning: cfg.Reasoning, ServiceTier: cfg.ServiceTier, - Text: text, Tools: tools}, nil + Text: cfg.Text, Tools: tools}, nil } diff --git a/services/core/internal/api/text_configuration.go b/services/core/internal/api/text_configuration.go deleted file mode 100644 index dca8f5358..000000000 --- a/services/core/internal/api/text_configuration.go +++ /dev/null @@ -1,25 +0,0 @@ -package api - -import ( - "errors" - v1 "github.com/MiniMax-AI/OpenAgentCore/contracts/agents-api/v1" -) - -func resolveText(input *v1.TextConfigInput) (v1.TextConfig, error) { - text := v1.TextConfig{Format: v1.TextFormat{Type: "text"}, Verbosity: "medium"} - if input == nil { - return text, nil - } - if input.Format != nil && input.Format.Type != "text" { - return text, errors.New("This service currently supports text.format.type=text only.") - } - if input.Verbosity != nil { - switch *input.Verbosity { - case "low", "medium", "high": - text.Verbosity = *input.Verbosity - default: - return text, errors.New("text.verbosity must be low, medium or high.") - } - } - return text, nil -}