openapi: 3.0.3
info:
  title: XDRO · OpenAI 兼容 API
  version: "2026-06-17"
  description: |
    **OpenAI 100% 兼容**的统一接口,一个 endpoint 同时调用 Claude 与 GPT。把 `base_url` 指向下方地址、
    `api_key` 换成发给你的密钥即可,现有 OpenAI SDK / 代码无需改动。

    - **鉴权**:HTTP 头 `Authorization: Bearer <API_KEY>`(点右上角 **Authorize** 填入)
    - **计费**:按 token 用量,每个 Key 有配额上限
    - **可用模型**:Claude(opus-4-8 / sonnet-4-6 / haiku-4-5)、GPT(gpt-4o / gpt-4o-mini / gpt-5 / gpt-5-mini / gpt-5.5 / gpt-5.4 / gpt-5.4-mini / gpt-5.4-nano)、Gemini(gemini-2.5-pro / gemini-2.5-flash / gemini-2.5-flash-lite)
    - **长上下文**:Opus / Sonnet 支持 1M context;Gemini 2.5 全系 1M context(可塞整个代码库)
    - ⚠️ **GPT-5 系列**(`gpt-5` / `gpt-5-mini` / `gpt-5.4` / `gpt-5.4-mini` / `gpt-5.4-nano` / `gpt-5.5`)为推理模型,限制输出长度请用 `max_completion_tokens`(不是 `max_tokens`,否则 400);且建议给足额度(过小会因推理耗尽预算而返回空)。
    - ⚠️ **Gemini 2.5 Pro** 为思考型模型,用 `max_tokens` 限制输出时要给足额度(建议 ≥4096,否则思考会耗尽预算返回空);Flash / Flash-Lite 无需特别处理。
    - 仅列**已支持**的接口。向量(embeddings)、图像生成、语音等 OpenAI 独有端点**当前不提供**。
  contact:
    name: 技术对接
    email: 470786558zyf@gmail.com
servers:
  - url: https://api.xdro.net/v1
    description: 生产网关(新加坡)
security:
  - bearerAuth: []
tags:
  - name: Chat
    description: 对话补全(主力接口)
  - name: Models
    description: 模型列表
paths:
  /chat/completions:
    post:
      tags: [Chat]
      summary: 发送对话,获取回复
      operationId: createChatCompletion
      description: |
        **你发一段对话(`messages`),模型返回回复** —— 这就是 OpenAI 所说的「对话补全 / chat completion」。
        支持多轮 `messages`、`system` 角色、流式 SSE、工具/函数调用、图像理解。

        **流式**:设 `stream: true`,按 SSE(`data: {...}` 行)逐块返回。
        **工具调用**:传 `tools` + 可选 `tool_choice`,模型返回 `tool_calls`,你执行后把结果作为
        `role: "tool"` 消息回传继续。
        **图像理解(vision)**:把 `content` 写成数组,含 `{type:"image_url", image_url:{url:"..."}}`(建议先小测)。

        ---
        **cURL 示例**(把 `$API_KEY` 换成发给你的密钥):

        ```bash
        # 基础(非流式)
        curl https://api.xdro.net/v1/chat/completions \
          -H "Authorization: Bearer $API_KEY" \
          -H "Content-Type: application/json" \
          -d '{
            "model": "claude-sonnet-4-6",
            "messages": [{"role":"user","content":"你好"}]
          }'

        # 流式(SSE,逐块返回)
        curl -N https://api.xdro.net/v1/chat/completions \
          -H "Authorization: Bearer $API_KEY" \
          -H "Content-Type: application/json" \
          -d '{
            "model": "claude-sonnet-4-6",
            "stream": true,
            "messages": [{"role":"user","content":"写个快速排序"}]
          }'

        # 工具/函数调用
        curl https://api.xdro.net/v1/chat/completions \
          -H "Authorization: Bearer $API_KEY" \
          -H "Content-Type: application/json" \
          -d '{
            "model": "claude-opus-4-8",
            "messages": [{"role":"user","content":"北京现在天气?"}],
            "tools": [{"type":"function","function":{"name":"get_weather","parameters":{"type":"object","properties":{"city":{"type":"string"}},"required":["city"]}}}]
          }'
        ```
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ChatCompletionRequest'
            examples:
              基础对话:
                value:
                  model: claude-sonnet-4-6
                  messages:
                    - role: system
                      content: 你是一个简洁的助手。
                    - role: user
                      content: 用一句话解释什么是中转站。
                  max_tokens: 256
              流式:
                value:
                  model: claude-sonnet-4-6
                  stream: true
                  messages:
                    - role: user
                      content: 写一个 Python 快速排序。
              工具调用:
                value:
                  model: claude-opus-4-8
                  messages:
                    - role: user
                      content: 北京现在天气如何?
                  tools:
                    - type: function
                      function:
                        name: get_weather
                        description: 查询某城市当前天气
                        parameters:
                          type: object
                          properties:
                            city: { type: string }
                          required: [city]
              图像理解:
                value:
                  model: claude-sonnet-4-6
                  messages:
                    - role: user
                      content:
                        - type: text
                          text: 这张截图里报了什么错?
                        - type: image_url
                          image_url:
                            url: https://example.com/screenshot.png
      responses:
        '200':
          description: 成功。`stream:false` 返回完整 JSON;`stream:true` 返回 SSE 流。
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ChatCompletionResponse'
        '401':
          description: 鉴权失败(Key 错误或缺失)
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Error' }
        '429':
          description: 限流或超出配额
        '5XX':
          description: 上游临时故障(建议自动重试;网关也已配置重试)
  /models:
    get:
      tags: [Models]
      summary: 列出可用模型
      operationId: listModels
      description: |
        返回当前网关可调用的模型清单。

        ```bash
        curl https://api.xdro.net/v1/models -H "Authorization: Bearer $API_KEY"
        ```
      responses:
        '200':
          description: 成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ModelList'
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: sk-...
      description: 填入发给你的 API Key(形如 `sk-...`)。
  schemas:
    ChatCompletionRequest:
      type: object
      required: [model, messages]
      properties:
        model:
          type: string
          description: 模型 ID(Claude、GPT、Gemini 同一接口)。
          enum:
            - claude-opus-4-8
            - claude-sonnet-4-6
            - claude-haiku-4-5-20251001
            - gpt-4o
            - gpt-4o-mini
            - gpt-5
            - gpt-5-mini
            - gpt-5.5
            - gpt-5.4
            - gpt-5.4-mini
            - gpt-5.4-nano
            - gemini-2.5-pro
            - gemini-2.5-flash
            - gemini-2.5-flash-lite
          example: claude-sonnet-4-6
        messages:
          type: array
          description: 对话历史,按顺序累加。
          items: { $ref: '#/components/schemas/Message' }
        stream:
          type: boolean
          default: false
          description: 为 true 时按 SSE 流式返回。
        max_tokens:
          type: integer
          description: 限制输出长度(Claude / GPT-4o 系列)。大输出(>16K)请配合 stream。
          example: 1024
        max_completion_tokens:
          type: integer
          description: >
            GPT-5 系列(`gpt-5` / `gpt-5-mini`)的输出长度限制。这些推理模型**只认
            `max_completion_tokens`**,传 `max_tokens` 会报 400。
          example: 1024
        temperature:
          type: number
          description: >
            采样温度 0~1。⚠️ **`claude-opus-4-8` 不支持采样参数,传入会报 400**——调 opus 请省略
            temperature / top_p;sonnet / haiku 与 GPT 系列正常支持。
        top_p:
          type: number
          description: 同 temperature,二选一;`claude-opus-4-8` 不支持。
        stop:
          oneOf:
            - type: string
            - type: array
              items: { type: string }
          description: 停止序列。
        tools:
          type: array
          description: 工具/函数定义(OpenAI tools 格式)。
          items: { $ref: '#/components/schemas/Tool' }
        tool_choice:
          description: 'auto | none | required | {"type":"function","function":{"name":"..."}}'
        response_format:
          type: object
          description: 结构化输出(如 {"type":"json_object"})。建议先小测。
          properties:
            type: { type: string, example: json_object }
    Message:
      type: object
      required: [role]
      properties:
        role:
          type: string
          enum: [system, user, assistant, tool]
        content:
          oneOf:
            - type: string
            - type: array
              items: { $ref: '#/components/schemas/ContentPart' }
          description: 字符串,或多模态内容数组(文本 + 图像)。
        tool_call_id:
          type: string
          description: role=tool 时,对应的工具调用 ID。
    ContentPart:
      type: object
      properties:
        type:
          type: string
          enum: [text, image_url]
        text:
          type: string
        image_url:
          type: object
          properties:
            url: { type: string, description: 'http(s) 链接或 data:image/...;base64,' }
    Tool:
      type: object
      properties:
        type: { type: string, example: function }
        function:
          type: object
          properties:
            name: { type: string }
            description: { type: string }
            parameters: { type: object, description: JSON Schema }
    ChatCompletionResponse:
      type: object
      properties:
        id: { type: string }
        object: { type: string, example: chat.completion }
        created: { type: integer }
        model: { type: string }
        choices:
          type: array
          items: { $ref: '#/components/schemas/Choice' }
        usage: { $ref: '#/components/schemas/Usage' }
    Choice:
      type: object
      properties:
        index: { type: integer }
        finish_reason: { type: string, example: stop }
        message:
          type: object
          properties:
            role: { type: string, example: assistant }
            content: { type: string }
            tool_calls:
              type: array
              items: { type: object }
    Usage:
      type: object
      properties:
        prompt_tokens: { type: integer }
        completion_tokens: { type: integer }
        total_tokens: { type: integer }
    ModelList:
      type: object
      properties:
        object: { type: string, example: list }
        data:
          type: array
          items: { $ref: '#/components/schemas/Model' }
    Model:
      type: object
      properties:
        id: { type: string, example: claude-sonnet-4-6 }
        object: { type: string, example: model }
        owned_by: { type: string }
    Error:
      type: object
      properties:
        error:
          type: object
          properties:
            message: { type: string }
            type: { type: string }
            code: { type: string }
