Skip to content

Update openapi spec components#423

Open
nezhyborets wants to merge 20 commits into
mainfrom
update-openapi-spec-components
Open

Update openapi spec components#423
nezhyborets wants to merge 20 commits into
mainfrom
update-openapi-spec-components

Conversation

@nezhyborets
Copy link
Copy Markdown
Collaborator

What

Re-generate Components with the latest OpenAPI spec. Update manually maintained code to work with the new Components

Why

It hasn't been updated for a very long time. It could fix some issues and enable people to use newer APIs.

Details

  • Replaces the hand-maintained Components.swift with a version generated from the latest OpenAI OpenAPI spec (openapi.with-code-samples.yml) using openapi-generator + a custom extraction script (Scripts/extract_components.py).
  • Adds a Makefile target to automate future spec updates (make generate-components).
  • Updates all public facades and manual types (OutputItem, ResponseStreamEvent, Tool, CreateModelResponseQuery, ResponseObject, etc.) to align with the new schema.
  • Fixes streaming event handling: corrects the output_text.annotation.added event type mapping and removes the now-unnecessary fixMappingError workaround.
  • Makes WebSearchActionSearch.query and ResponseFunctionCallArgumentsDoneEvent.name optional to match what actually received from API

nezhyborets and others added 20 commits May 15, 2026 20:27
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove 32 invalid redeclarations: duplicate primitive typealiases
(Bool/String/Int/Double = Swift.X) within the same struct scope, and
six `typealias String = [Swift.String]` entries that shadowed Swift.String
in CodingKeys raw type position, breaking compilation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Rename reasoning event types: ResponseReasoningDelta/DoneEvent →
  ResponseReasoningTextDelta/DoneEvent; ResponseReasoningSummaryDelta/DoneEvent →
  ResponseReasoningSummaryTextDelta/DoneEvent
- Reorder decoder chain to match new value indices (value29–50): new
  ReasoningText events inserted at 29/30 pushed Refusal, OutputText,
  WebSearch, ImageGen, MCP events down by 2; MCP call-arguments events
  moved from value40/41 to value42/43, freeing 40/41 for the two
  previously-missing ImageGen events
- Add decoder entries for value40/41 (ImageGenCallInProgress/PartialImage)
  and value49 (MCPListToolsInProgress), which were absent in the old mapping
- Update guard indices to match: value40/41 → value42/43, value49 → value51
- Update case comments to match spec wording

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Generated OutputItem changed from a OneOf struct (value1…value12) to a
discriminated enum with named cases. Rewrite init(from:) to decode the
generated enum and switch on its cases.

Also adds the 13 new cases introduced in the spec:
functionToolCallOutputResource, computerToolCallOutputResource,
toolSearchCall, toolSearchOutput, compactionBody, localShellToolCallOutput,
functionShellCall, functionShellCallOutput, applyPatchToolCall,
applyPatchToolCallOutput, mcpApprovalResponseResource, customToolCall,
customToolCallOutputResource.

Updates existing comments to use absolute URLs and aligns wording with
the spec descriptions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The generated type now has readable case names, making the facade unnecessary.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Rename Includable → IncludeEnum, TextPayload → ResponseTextParam, ToolChoicePayload → ToolChoiceParam
- Replace LocalShellTool → LocalShellToolParam in Tool enum
- Add new Tool cases: computer, computerUsePreview, webSearch, webSearchPreview, functionShell, custom, namespace, toolSearch, applyPatch
- Drop Tool suffix from all enum case names

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Restore Tool suffix on all enum cases; rename codeInterpreter → codeInterpreterTool
- Add required responseId param to ResponseAudioDoneEvent in test
- Add logprobs:[] to mock SSE JSON for ResponseTextDeltaEvent (now required by spec)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Re-adds the OutputItem facade removed in the previous commit so that
public call sites don't need to switch on Components.Schemas.OutputItem
directly. The facade mirrors all 25 cases of the generated type and
preserves the user-friendly names (reasoning, imageGenerationCall,
localShellCall). Also removes the now-redundant ResponseStreamEvent.OutputItem
typealias that pointed at the generated type.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
All discriminator blocks in the spec used propertyName values (e.g.
'type') whose enum strings don't match schema names, making explicit
mapping required. Without it, strict OpenAPI tooling cannot resolve
the correct schema from a discriminator value.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds `make generate` which runs swift-openapi-generator and then
extracts the Components enum from the output into Components.swift,
stripping typealias lines that shadow Swift built-in names and would
cause invalid redeclaration build errors.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
query is deprecated and no longer sent by the API, causing keyNotFound
decoding errors. Removed it from the required list in the spec.
See openai/openai-openapi#544.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The stream event type was incorrectly mapped to the underscore variant
in ModelResponseStreamEventType; corrected to match the spec value
response.output_text.annotation.added.

fixMappingError() was doing the opposite transformation (dot→underscore)
before decoding, which broke decoding after the generated enum was
updated to use the spec's dot-separated value. Removed the function
along with its call site; getPayloadType() is retained as it serves
an unrelated fallback purpose.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The API does not send name in response.function_call_arguments.done
events, despite the spec marking it required. Removed it from required.
See openai/openai-openapi#545.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 21 out of 23 changed files in this pull request and generated 10 comments.

Comments suppressed due to low confidence (1)

Sources/OpenAI/Public/Schemas/Edited/Tool.swift:57

  • Tool.init(from:) uses a trial-and-error decode strategy (many do/try/catch attempts). With the newly added tool cases, this makes decoding increasingly expensive because most cases will fail before the correct one succeeds. Prefer decoding a lightweight discriminator first (e.g., _type / type) and then decoding exactly one concrete schema based on that value; this improves performance and keeps the implementation scalable as more tool variants are added.
    public init(from decoder: any Decoder) throws {
        var errors: [any Error] = []
        do {
            self = .fileSearchTool(try .init(from: decoder))
            return
        } catch {
            errors.append(error)
        }


let rawEvent = try Components.Schemas.ResponseStreamEvent(from: decoder)
if rawEvent.value10 != nil || rawEvent.value13 != nil || rawEvent.value20 != nil || rawEvent.value21 != nil, rawEvent.value22 != nil, rawEvent.value40 != nil, rawEvent.value41 != nil, rawEvent.value49 != nil {
if rawEvent.value10 != nil || rawEvent.value13 != nil || rawEvent.value20 != nil || rawEvent.value21 != nil, rawEvent.value22 != nil, rawEvent.value42 != nil, rawEvent.value43 != nil, rawEvent.value51 != nil {
Comment thread Makefile
Comment on lines +9 to +13
.PHONY: generate
generate:
cd "$(GENERATOR_DIR)" && swift run swift-openapi-generator generate \
--config "$(PROJECT_DIR)/openapi-generator-config.yaml" \
"$(PROJECT_DIR)/openapi.with-code-samples.yml"
Comment on lines +70 to +75
do {
self = .computerUsePreviewTool(try .init(from: decoder))
return
} catch {
errors.append(error)
}
import Foundation

public protocol JSONSchemaConvertible: Codable {
public protocol JSONSchemaConvertible: Codable, Sendable {
private func processEvent(_ event: ServerSentEventsStreamParser.Event) throws {
let finalEvent = event.fixMappingError()
var eventType = finalEvent.eventType
var eventType = event.eventType
/// In this case we check the `data.type` property for a valid model response type.
if eventType == "message" || eventType.isEmpty,
let payloadEventType = finalEvent.getPayloadType() {
let payloadEventType = event.getPayloadType() {
}

let responseStreamEvent = try responseStreamEvent(modelResponseEventType: modelResponseEventType, data: finalEvent.data)
let responseStreamEvent = try responseStreamEvent(modelResponseEventType: modelResponseEventType, data: event.data)
Comment on lines +18 to +25
start_index = None
for i, line in enumerate(lines):
if re.match(r"^public enum Components \{", line):
start_index = i
break

if start_index is None:
print("ERROR: could not find 'public enum Components {' in " + TYPES_SWIFT, file=sys.stderr)

start_index = None
for i, line in enumerate(lines):
if re.match(r"^public enum Components \{", line):
Comment on lines 776 to 801
) throws {
try updateMessageBeingStreamed(messageId: messageId) { message in
switch outputContent {
case .OutputTextContent(let outputText):
case .outputTextContent(let outputText):
message.text = outputText.text
message.annotations = outputText.annotations
case .RefusalContent(let refusal):
case .refusalContent(let refusal):
message.refusalText = refusal.refusal
case .reasoningTextContent:
break
}
}
}

private func updateMessageBeingStreamed(
messageId: String,
outputContent: Components.Schemas.OutputMessageContent
) throws {
try updateMessageBeingStreamed(messageId: messageId) { message in
switch outputContent {
case .outputTextContent(let outputText):
message.text = outputText.text
message.annotations = outputText.annotations
case .refusalContent(let refusal):
message.refusalText = refusal.refusal
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants