feat: harden public OpenAI API contracts
Quality gate / backend (push) Canceled after 0s
Quality gate / frontend (push) Canceled after 0s

This commit is contained in:
Xin Wang
2026-09-07 14:25:48 +08:00
parent 8025ed8aa0
commit 2649d64b55
36 changed files with 16643 additions and 236 deletions
+11
View File
@@ -0,0 +1,11 @@
# Public API contract snapshots
These generated files define **OpenAI-compatible subset v1**:
- `openapi.json` describes public and admin HTTP routes.
- `openai-realtime-client-events.schema.json` validates accepted Realtime events.
- `openai-realtime-server-events.schema.json` validates the common server-event envelope, supported event names, and extension version.
From the repository root, run `make contract-snapshots` after an intentional contract change. The contract
test fails when generated output differs, so snapshot changes must be reviewed
alongside implementation and documentation changes.
@@ -0,0 +1,452 @@
{
"$defs": {
"CapabilitiesUpdateEvent": {
"additionalProperties": true,
"properties": {
"capabilities": {
"items": {
"type": "string"
},
"title": "Capabilities",
"type": "array"
},
"event_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Event Id"
},
"type": {
"const": "x.interactive_media.capabilities.update",
"title": "Type",
"type": "string"
},
"version": {
"const": "1",
"title": "Version",
"type": "string"
}
},
"required": [
"type",
"version",
"capabilities"
],
"title": "CapabilitiesUpdateEvent",
"type": "object"
},
"ConversationItemCreateEvent": {
"additionalProperties": true,
"properties": {
"event_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Event Id"
},
"item": {
"additionalProperties": true,
"title": "Item",
"type": "object"
},
"previous_item_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Previous Item Id"
},
"type": {
"const": "conversation.item.create",
"title": "Type",
"type": "string"
}
},
"required": [
"type",
"item"
],
"title": "ConversationItemCreateEvent",
"type": "object"
},
"ConversationItemTruncateEvent": {
"additionalProperties": true,
"properties": {
"audio_end_ms": {
"minimum": 0,
"title": "Audio End Ms",
"type": "integer"
},
"content_index": {
"default": 0,
"minimum": 0,
"title": "Content Index",
"type": "integer"
},
"event_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Event Id"
},
"item_id": {
"title": "Item Id",
"type": "string"
},
"type": {
"const": "conversation.item.truncate",
"title": "Type",
"type": "string"
}
},
"required": [
"type",
"item_id",
"audio_end_ms"
],
"title": "ConversationItemTruncateEvent",
"type": "object"
},
"InputAudioBufferAppendEvent": {
"additionalProperties": true,
"properties": {
"audio": {
"title": "Audio",
"type": "string"
},
"event_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Event Id"
},
"type": {
"const": "input_audio_buffer.append",
"title": "Type",
"type": "string"
}
},
"required": [
"type",
"audio"
],
"title": "InputAudioBufferAppendEvent",
"type": "object"
},
"InputAudioBufferClearEvent": {
"additionalProperties": true,
"properties": {
"event_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Event Id"
},
"type": {
"const": "input_audio_buffer.clear",
"title": "Type",
"type": "string"
}
},
"required": [
"type"
],
"title": "InputAudioBufferClearEvent",
"type": "object"
},
"InputAudioBufferCommitEvent": {
"additionalProperties": true,
"properties": {
"event_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Event Id"
},
"type": {
"const": "input_audio_buffer.commit",
"title": "Type",
"type": "string"
}
},
"required": [
"type"
],
"title": "InputAudioBufferCommitEvent",
"type": "object"
},
"OutputAudioBufferClearEvent": {
"additionalProperties": true,
"properties": {
"event_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Event Id"
},
"type": {
"const": "output_audio_buffer.clear",
"title": "Type",
"type": "string"
}
},
"required": [
"type"
],
"title": "OutputAudioBufferClearEvent",
"type": "object"
},
"ResponseCancelEvent": {
"additionalProperties": true,
"properties": {
"event_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Event Id"
},
"response_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Response Id"
},
"type": {
"const": "response.cancel",
"title": "Type",
"type": "string"
}
},
"required": [
"type"
],
"title": "ResponseCancelEvent",
"type": "object"
},
"ResponseCreateEvent": {
"additionalProperties": true,
"properties": {
"event_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Event Id"
},
"response": {
"anyOf": [
{
"additionalProperties": true,
"type": "object"
},
{
"type": "null"
}
],
"default": null,
"title": "Response"
},
"type": {
"const": "response.create",
"title": "Type",
"type": "string"
}
},
"required": [
"type"
],
"title": "ResponseCreateEvent",
"type": "object"
},
"SessionUpdateEvent": {
"additionalProperties": true,
"properties": {
"event_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Event Id"
},
"session": {
"additionalProperties": true,
"title": "Session",
"type": "object"
},
"type": {
"const": "session.update",
"title": "Type",
"type": "string"
}
},
"required": [
"type",
"session"
],
"title": "SessionUpdateEvent",
"type": "object"
},
"SessionVariablesUpdateEvent": {
"additionalProperties": true,
"properties": {
"event_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Event Id"
},
"type": {
"const": "x.interactive_media.session.variables.update",
"title": "Type",
"type": "string"
},
"variables": {
"additionalProperties": true,
"title": "Variables",
"type": "object"
},
"version": {
"const": "1",
"title": "Version",
"type": "string"
}
},
"required": [
"type",
"version",
"variables"
],
"title": "SessionVariablesUpdateEvent",
"type": "object"
}
},
"$id": "https://interactive-media.local/schemas/realtime-client-events-v1.json",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"discriminator": {
"mapping": {
"conversation.item.create": "#/$defs/ConversationItemCreateEvent",
"conversation.item.truncate": "#/$defs/ConversationItemTruncateEvent",
"input_audio_buffer.append": "#/$defs/InputAudioBufferAppendEvent",
"input_audio_buffer.clear": "#/$defs/InputAudioBufferClearEvent",
"input_audio_buffer.commit": "#/$defs/InputAudioBufferCommitEvent",
"output_audio_buffer.clear": "#/$defs/OutputAudioBufferClearEvent",
"response.cancel": "#/$defs/ResponseCancelEvent",
"response.create": "#/$defs/ResponseCreateEvent",
"session.update": "#/$defs/SessionUpdateEvent",
"x.interactive_media.capabilities.update": "#/$defs/CapabilitiesUpdateEvent",
"x.interactive_media.session.variables.update": "#/$defs/SessionVariablesUpdateEvent"
},
"propertyName": "type"
},
"oneOf": [
{
"$ref": "#/$defs/SessionUpdateEvent"
},
{
"$ref": "#/$defs/ConversationItemCreateEvent"
},
{
"$ref": "#/$defs/ConversationItemTruncateEvent"
},
{
"$ref": "#/$defs/InputAudioBufferAppendEvent"
},
{
"$ref": "#/$defs/InputAudioBufferCommitEvent"
},
{
"$ref": "#/$defs/InputAudioBufferClearEvent"
},
{
"$ref": "#/$defs/OutputAudioBufferClearEvent"
},
{
"$ref": "#/$defs/ResponseCreateEvent"
},
{
"$ref": "#/$defs/ResponseCancelEvent"
},
{
"$ref": "#/$defs/CapabilitiesUpdateEvent"
},
{
"$ref": "#/$defs/SessionVariablesUpdateEvent"
}
],
"title": "Interactive Media OpenAI Realtime client events v1"
}
@@ -0,0 +1,91 @@
{
"$id": "https://interactive-media.local/schemas/realtime-server-events-v1.json",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": true,
"allOf": [
{
"if": {
"properties": {
"type": {
"enum": [
"x.interactive_media.capabilities.updated",
"x.interactive_media.session.variables.updated",
"x.interactive_media.workflow.node_active",
"x.interactive_media.workflow.event",
"x.interactive_media.workflow.variables.updated",
"x.interactive_media.workflow.error",
"x.interactive_media.call.handoff_requested",
"x.interactive_media.call.ended"
]
}
},
"required": [
"type"
]
},
"then": {
"properties": {
"version": {
"const": "1",
"type": "string"
}
},
"required": [
"version"
]
}
}
],
"properties": {
"event_id": {
"title": "Event Id",
"type": "string"
},
"type": {
"enum": [
"session.created",
"session.updated",
"conversation.item.added",
"conversation.item.done",
"conversation.item.truncated",
"conversation.item.input_audio_transcription.completed",
"input_audio_buffer.committed",
"input_audio_buffer.cleared",
"input_audio_buffer.speech_started",
"input_audio_buffer.speech_stopped",
"output_audio_buffer.cleared",
"response.created",
"response.output_item.added",
"response.output_item.done",
"response.content_part.added",
"response.content_part.done",
"response.output_text.delta",
"response.output_text.done",
"response.output_audio.delta",
"response.output_audio.done",
"response.output_audio_transcript.delta",
"response.output_audio_transcript.done",
"response.function_call_arguments.delta",
"response.function_call_arguments.done",
"response.done",
"error",
"x.interactive_media.capabilities.updated",
"x.interactive_media.session.variables.updated",
"x.interactive_media.workflow.node_active",
"x.interactive_media.workflow.event",
"x.interactive_media.workflow.variables.updated",
"x.interactive_media.workflow.error",
"x.interactive_media.call.handoff_requested",
"x.interactive_media.call.ended"
],
"title": "Type",
"type": "string"
}
},
"required": [
"type",
"event_id"
],
"title": "Interactive Media OpenAI Realtime server events v1",
"type": "object"
}
File diff suppressed because it is too large Load Diff