{
  "openapi": "3.1.0",
  "info": {
    "title": "笑妹文字与实时语音对话服务",
    "version": "0.1.0",
    "description": "文字走 HTTP/SSE；实时音频走火山 RTC SDK，HTTP 管理通话生命周期。"
  },
  "servers": [
    {
      "url": "http://127.0.0.1:8787"
    }
  ],
  "security": [
    {
      "ServiceKey": []
    },
    {
      "SessionToken": []
    }
  ],
  "paths": {
    "/healthz": {
      "get": {
        "summary": "存活检查",
        "operationId": "get__healthz",
        "description": "",
        "responses": {
          "200": {
            "description": "成功",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Health"
                }
              }
            }
          },
          "default": {
            "description": "请求错误",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": []
      }
    },
    "/v1/capabilities": {
      "get": {
        "summary": "检查配置完整性",
        "operationId": "get__v1_capabilities",
        "description": "会话 Token 鉴权时同时发送 X-Session-Id。ready 只表示字段齐全，不验证云端权限或余额。",
        "responses": {
          "200": {
            "description": "成功",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Capabilities"
                }
              }
            }
          },
          "default": {
            "description": "请求错误",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "X-Session-Id",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ]
      }
    },
    "/v1/sessions": {
      "post": {
        "summary": "创建会话",
        "operationId": "post__v1_sessions",
        "description": "",
        "responses": {
          "201": {
            "description": "成功",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Session"
                }
              }
            }
          },
          "default": {
            "description": "请求错误",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "ServiceKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Empty"
              }
            }
          }
        }
      }
    },
    "/v1/sessions/{session_id}": {
      "get": {
        "summary": "获取会话和历史",
        "operationId": "get__v1_sessions_session_id",
        "description": "",
        "responses": {
          "200": {
            "description": "成功",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Session"
                }
              }
            }
          },
          "default": {
            "description": "请求错误",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "session_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ]
      },
      "delete": {
        "summary": "停止关联通话并删除会话",
        "operationId": "delete__v1_sessions_session_id",
        "description": "",
        "responses": {
          "200": {
            "description": "成功",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Deleted"
                }
              }
            }
          },
          "default": {
            "description": "请求错误",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "session_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ]
      }
    },
    "/v1/sessions/{session_id}/messages": {
      "post": {
        "summary": "发送文字并获取文字回答",
        "operationId": "post__v1_sessions_session_id_messages",
        "description": "stream=true 返回 SSE。delta 事件为增量文本；done 为完整 TextResult；error 为 Error。最近 100 个成功请求支持 client_request_id 去重。",
        "responses": {
          "200": {
            "description": "成功",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TextResult"
                }
              },
              "text/event-stream": {
                "schema": {
                  "type": "string"
                },
                "example": "event: delta\ndata: {\"content\":\"你好\"}\n\nevent: done\ndata: {\"message\":{\"content\":\"你好\"}}\n\n"
              }
            }
          },
          "default": {
            "description": "请求错误",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "session_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TextInput"
              }
            }
          }
        }
      }
    },
    "/v1/sessions/{session_id}/calls": {
      "post": {
        "summary": "准备通话并签发进房信息",
        "operationId": "post__v1_sessions_session_id_calls",
        "description": "",
        "responses": {
          "201": {
            "description": "成功",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Call"
                }
              }
            }
          },
          "default": {
            "description": "请求错误",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "session_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Empty"
              }
            }
          }
        }
      }
    },
    "/v1/calls/{call_id}": {
      "get": {
        "summary": "获取通话状态",
        "operationId": "get__v1_calls_call_id",
        "description": "",
        "responses": {
          "200": {
            "description": "成功",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Call"
                }
              }
            }
          },
          "default": {
            "description": "请求错误",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "call_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ]
      },
      "delete": {
        "summary": "停止云端通话",
        "operationId": "delete__v1_calls_call_id",
        "description": "可重复调用。停止失败保留 stop_pending，由后台重试。客户端仍应关闭麦克风并离房。",
        "responses": {
          "200": {
            "description": "成功",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Call"
                }
              }
            }
          },
          "default": {
            "description": "请求错误",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "call_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ]
      }
    },
    "/v1/calls/{call_id}/start": {
      "post": {
        "summary": "SDK 进房后启动 AI",
        "operationId": "post__v1_calls_call_id_start",
        "description": "",
        "responses": {
          "200": {
            "description": "成功",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Call"
                }
              }
            }
          },
          "default": {
            "description": "请求错误",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "call_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Empty"
              }
            }
          }
        }
      }
    },
    "/v1/calls/{call_id}/heartbeat": {
      "post": {
        "summary": "续租通话",
        "operationId": "post__v1_calls_call_id_heartbeat",
        "description": "",
        "responses": {
          "200": {
            "description": "成功",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Heartbeat"
                }
              }
            }
          },
          "default": {
            "description": "请求错误",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "call_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Empty"
              }
            }
          }
        }
      }
    },
    "/v1/calls/{call_id}/token": {
      "post": {
        "summary": "续签同房间同用户 RTC Token",
        "operationId": "post__v1_calls_call_id_token",
        "description": "",
        "responses": {
          "200": {
            "description": "成功",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Token"
                }
              }
            }
          },
          "default": {
            "description": "请求错误",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "call_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Empty"
              }
            }
          }
        }
      }
    },
    "/v1/calls/{call_id}/interrupt": {
      "post": {
        "summary": "打断 AI 当前回答",
        "operationId": "post__v1_calls_call_id_interrupt",
        "description": "",
        "responses": {
          "200": {
            "description": "成功",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Accepted"
                }
              }
            }
          },
          "default": {
            "description": "请求错误",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "call_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Empty"
              }
            }
          }
        }
      }
    },
    "/v1/calls/{call_id}/messages": {
      "post": {
        "summary": "通话中发送文本给 LLM",
        "operationId": "post__v1_calls_call_id_messages",
        "description": "回答通过 RTC 音频和字幕返回，HTTP 只表示命令已被接受。",
        "responses": {
          "200": {
            "description": "成功",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Accepted"
                }
              }
            }
          },
          "default": {
            "description": "请求错误",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "call_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VoiceTextInput"
              }
            }
          }
        }
      }
    },
    "/v1/calls/{call_id}/speak": {
      "post": {
        "summary": "通话中直接朗读文本",
        "operationId": "post__v1_calls_call_id_speak",
        "description": "",
        "responses": {
          "200": {
            "description": "成功",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Accepted"
                }
              }
            }
          },
          "default": {
            "description": "请求错误",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "call_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VoiceTextInput"
              }
            }
          }
        }
      }
    },
    "/v1/calls/{call_id}/transcripts": {
      "post": {
        "summary": "批量保存客户端字幕",
        "operationId": "post__v1_calls_call_id_transcripts",
        "description": "同一 call_id 内按字幕 id 去重，不能用于权威审计。",
        "responses": {
          "200": {
            "description": "成功",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TranscriptResult"
                }
              }
            }
          },
          "default": {
            "description": "请求错误",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "call_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TranscriptInput"
              }
            }
          }
        }
      }
    },
    "/v1/local/sessions": {
      "post": {
        "summary": "本机同源页面自动创建会话",
        "operationId": "post__v1_local_sessions",
        "description": "仅在启用 LOCAL_UI_ENABLED、只监听回环地址、请求来自回环地址、Host 为 localhost/127.0.0.1/[::1] 加实际端口、Origin 为完全匹配的 http 来源、无转发头且 Sec-Fetch-Site 不为跨站时可用。不能用于远程公开部署。返回当前会话 Token，不返回主密钥或云端密钥。",
        "responses": {
          "201": {
            "description": "成功",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Session"
                }
              }
            }
          },
          "default": {
            "description": "请求错误",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Empty"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "Origin",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "http://127.0.0.1:8787"
          }
        ]
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ServiceKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "服务端 SERVICE_API_KEY"
      },
      "SessionToken": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Session-Token",
        "description": "创建会话时返回的 session_token，仅访问对应会话及通话"
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "details": {
                "type": "object",
                "additionalProperties": true
              }
            },
            "required": [
              "code",
              "message"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "error"
        ],
        "additionalProperties": false
      },
      "Empty": {
        "type": "object",
        "properties": {},
        "required": [],
        "additionalProperties": false
      },
      "TextInput": {
        "type": "object",
        "properties": {
          "content": {
            "type": "string",
            "minLength": 1,
            "maxLength": 8000
          },
          "stream": {
            "type": "boolean",
            "default": false
          },
          "client_request_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          }
        },
        "required": [
          "content"
        ],
        "additionalProperties": false
      },
      "VoiceTextInput": {
        "type": "object",
        "properties": {
          "content": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          }
        },
        "required": [
          "content"
        ],
        "additionalProperties": false
      },
      "Message": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "role": {
            "type": "string",
            "enum": [
              "user",
              "assistant"
            ]
          },
          "content": {
            "type": "string"
          },
          "mode": {
            "type": "string",
            "enum": [
              "text",
              "voice"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "call_id": {
            "type": "string"
          },
          "complete": {
            "type": "boolean"
          },
          "source": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "role",
          "content",
          "mode",
          "created_at"
        ],
        "additionalProperties": false
      },
      "Capabilities": {
        "type": "object",
        "properties": {
          "mode": {
            "type": "string",
            "enum": [
              "live",
              "mock"
            ]
          },
          "text": {
            "type": "object",
            "properties": {
              "ready": {
                "type": "boolean"
              },
              "missing": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "auth": {
                "type": "string"
              }
            }
          },
          "voice": {
            "type": "object",
            "properties": {
              "ready": {
                "type": "boolean"
              },
              "missing": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "transport": {
                "type": "string"
              },
              "api_version": {
                "type": "string"
              },
              "sdk_version": {
                "type": "string"
              }
            }
          }
        },
        "required": [
          "mode",
          "text",
          "voice"
        ],
        "additionalProperties": false
      },
      "Call": {
        "type": "object",
        "properties": {
          "call_id": {
            "type": "string",
            "format": "uuid"
          },
          "session_id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "prepared",
              "starting",
              "active",
              "stop_pending",
              "stopped"
            ]
          },
          "app_id": {
            "type": "string"
          },
          "room_id": {
            "type": "string"
          },
          "user_id": {
            "type": "string"
          },
          "bot_id": {
            "type": "string"
          },
          "task_id": {
            "type": "string"
          },
          "token": {
            "type": "string"
          },
          "expires_at": {
            "type": "integer",
            "description": "Unix 秒，仅签发 Token 时返回"
          },
          "heartbeat_interval_seconds": {
            "type": "integer"
          },
          "lease_seconds": {
            "type": "integer"
          },
          "max_seconds": {
            "type": "integer"
          }
        },
        "required": [
          "call_id",
          "session_id",
          "status",
          "app_id",
          "room_id",
          "user_id",
          "bot_id",
          "task_id"
        ],
        "additionalProperties": false
      },
      "Session": {
        "type": "object",
        "properties": {
          "session_id": {
            "type": "string",
            "format": "uuid"
          },
          "session_token": {
            "type": "string",
            "description": "仅创建时返回"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "messages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Message"
            }
          },
          "active_call": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Call"
              },
              {
                "type": "null"
              }
            ]
          },
          "capabilities": {
            "$ref": "#/components/schemas/Capabilities"
          }
        },
        "required": [
          "session_id",
          "created_at",
          "messages",
          "active_call",
          "capabilities"
        ],
        "additionalProperties": false
      },
      "TextResult": {
        "type": "object",
        "properties": {
          "session_id": {
            "type": "string"
          },
          "client_request_id": {
            "type": "string"
          },
          "message": {
            "$ref": "#/components/schemas/Message"
          },
          "usage": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true
          },
          "model": {
            "type": "string"
          },
          "replayed": {
            "type": "boolean"
          }
        },
        "required": [
          "session_id",
          "client_request_id",
          "message",
          "model"
        ],
        "additionalProperties": false
      },
      "Token": {
        "type": "object",
        "properties": {
          "token": {
            "type": "string"
          },
          "expires_at": {
            "type": "integer"
          }
        },
        "required": [
          "token",
          "expires_at"
        ],
        "additionalProperties": false
      },
      "TranscriptInput": {
        "type": "object",
        "properties": {
          "messages": {
            "type": "array",
            "maxItems": 50,
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 150
                },
                "role": {
                  "type": "string",
                  "enum": [
                    "user",
                    "assistant"
                  ]
                },
                "content": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 8000
                },
                "complete": {
                  "type": "boolean",
                  "default": true
                }
              },
              "required": [
                "id",
                "role",
                "content"
              ],
              "additionalProperties": false
            }
          }
        },
        "required": [
          "messages"
        ],
        "additionalProperties": false
      },
      "Accepted": {
        "type": "object",
        "properties": {
          "accepted": {
            "type": "boolean"
          },
          "request_id": {
            "type": "string"
          }
        },
        "required": [
          "accepted"
        ],
        "additionalProperties": false
      },
      "TranscriptResult": {
        "type": "object",
        "properties": {
          "accepted": {
            "type": "integer",
            "minimum": 0
          }
        },
        "required": [
          "accepted"
        ],
        "additionalProperties": false
      },
      "Heartbeat": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string"
          }
        },
        "required": [
          "status"
        ],
        "additionalProperties": false
      },
      "Deleted": {
        "type": "object",
        "properties": {
          "deleted": {
            "type": "boolean"
          }
        },
        "required": [
          "deleted"
        ],
        "additionalProperties": false
      },
      "Health": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string"
          },
          "version": {
            "type": "string"
          },
          "mode": {
            "type": "string"
          }
        },
        "required": [
          "status",
          "version",
          "mode"
        ],
        "additionalProperties": false
      }
    }
  }
}
