openapi: 3.0.3
info:
  title: Liya AI — 外部 API
  description: |
    将 Liya AI 集成到您自己应用程序中的 REST API。

    使用此 API 构建聊天组件、3D 虚拟形象集成、语音助手，
    或在 Liya AI 平台上打造任何自定义 AI 驱动的体验。

    ## 身份验证

    每个对 `/api/v1/external/` 的请求都必须在 `X-API-Key` 头部中包含您的 API 密钥。
    从 [ai.liyalabs.com](https://ai.liyalabs.com) → **项目 → API 密钥** 获取您的密钥。

    ```
    X-API-Key: your-api-key
    ```

    ## GAR — 全局应用路由器

    Liya AI 使用分布式后端。每个项目都被路由到专用的后端实例。
    您的后端 URL 显示在控制面板的 **设置 → API 配置** 下：

    ```
    https://app-{X}-ai.liyalabs.com
    ```

    始终使用分配给您的实例 URL 作为所有请求的基础 URL。

    ## 速率限制

    - 每个 API 密钥每分钟 120 次请求
    - 剩余配额：`X-RateLimit-Remaining` 响应头
    - 重置时间（Unix）：`X-RateLimit-Reset` 响应头

    ## 账户等级与功能权限

    | 功能 | STANDARD | PREMIUM | PREMIUM PLUS |
    |---------|----------|---------|--------------|
    | 聊天 | ✅ | ✅ | ✅ |
    | 文件上传 | ❌ | ✅ | ✅ |
    | 语音输出 (TTS) | ❌ | ✅ | ✅ |
    | 3D 虚拟形象 | ❌ | ✅ | ✅ |
    | 自定义虚拟形象模型 | ❌ | ❌ | ✅ |
    | 自助服务终端模式 | ❌ | ❌ | ✅ |

    ## 稳定性与版本控制

    本 API 遵循 [语义化版本控制](https://semver.org)。

    - **破坏性变更** 会增加主版本号 (v1 → v2)，
      并会 **提前 60 天** 通知。
    - **新功能** 会增加次版本号 (v0.1 → v0.2)，
      并且向后兼容。
    - **错误修复** 会增加补丁版本号 (v0.1.0 → v0.1.1)。

    在版本为 **0.x** 期间，破坏性变更可能不会提前通知。
    请在 [liyalabs.com/changelog](https://liyalabs.com/changelog) 订阅更新日志
    以获取最新信息。

  version: 0.1.0
  contact:
    name: Liya Labs Support
    url: https://liyalabs.com
    email: support@liyalabs.com
  license:
    name: Proprietary
    url: https://liyalabs.com/terms

servers:
  - url: https://app-1-ai.liyalabs.com
    description: 实例 1
  - url: https://app-2-ai.liyalabs.com
    description: 实例 2
  - url: https://app-3-ai.liyalabs.com
    description: 实例 3

security:
  - ApiKeyAuth: []

tags:
  - name: Assistants
    description: 列出项目中的可用 AI 助手
  - name: Sessions
    description: 创建和管理每个用户的对话会话
  - name: Chat
    description: 发送消息并接收 AI 回复
  - name: Files
    description: 上传文件以附加到聊天消息中
  - name: Avatar & TTS
    description: 文本转语音、语音转文本和 3D 虚拟形象唇形同步（PREMIUM+）
  - name: Widget
    description: 组件配置、场景背景和功能访问权限
  - name: Presentations
    description: 列出演示文稿并提供幻灯片图片
  - name: Tasks
    description: 轮询异步任务状态（图片/视频生成）
  - name: Translation
    description: 在 100+ 种语言之间翻译文本
  - name: Coding Agent
    description: 编码助手对话和会话摘要（VS Code 扩展）
  - name: Plugin
    description: VS Code 扩展配置、分析和余额
  - name: Mobile
    description: 移动应用全局配置

paths:
  # ─── ASSISTANTS ────────────────────────────────────────────────────────────

  /api/v1/external/assistants/:
    get:
      tags: [Assistants]
      summary: 列出助手
      description: 返回属于您项目的所有助手。
      parameters:
        - name: type
          in: query
          description: 按助手类型筛选
          required: false
          schema:
            type: string
            enum: [custom, coding_agent, linux_os]
      responses:
        "200":
          description: 助手列表
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: success
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/Assistant"
              example:
                status: success
                data:
                  - id: "550e8400-e29b-41d4-a716-446655440000"
                    name: "Customer Support"
                    model: "gpt-4o"
                    assistant_type: "custom"
                    is_reasoning_model: false
                    total_messages: 1240
        "401":
          $ref: "#/components/responses/Unauthorized"

  # ─── SESSIONS ──────────────────────────────────────────────────────────────

  /api/v1/external/sessions/:
    get:
      tags: [Sessions]
      summary: 列出会话
      description: |
        返回指定助手的会话列表。使用 `external_session_id` 将会话
        映射到您自己的用户 ID。
      parameters:
        - name: assistant_id
          in: query
          required: true
          schema:
            type: string
            format: uuid
        - name: limit
          in: query
          schema:
            type: integer
            default: 20
            minimum: 1
            maximum: 50
        - name: offset
          in: query
          schema:
            type: integer
            default: 0
      responses:
        "200":
          description: 分页会话列表
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: success
                  data:
                    $ref: "#/components/schemas/SessionListResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"

    post:
      tags: [Sessions]
      summary: 创建会话
      description: |
        创建一个新的对话会话。传入 `external_session_id` 以将会话
        与您系统中的用户关联。
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateSessionRequest"
            example:
              assistant_id: "550e8400-e29b-41d4-a716-446655440000"
              session_name: "Support ticket #1234"
              external_session_id: "user_42"
      responses:
        "201":
          description: 已创建的会话
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: success
                  data:
                    $ref: "#/components/schemas/Session"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /api/v1/external/sessions/{session_id}/:
    parameters:
      - name: session_id
        in: path
        required: true
        schema:
          type: string
          format: uuid

    get:
      tags: [Sessions]
      summary: 获取会话详情
      responses:
        "200":
          description: 会话详情
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: success
                  data:
                    $ref: "#/components/schemas/Session"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"

    delete:
      tags: [Sessions]
      summary: 关闭会话
      description: 软删除会话。历史记录保留，但会话变为非活跃状态。
      responses:
        "200":
          description: 会话已关闭
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: success
                  message:
                    type: string
                    example: Session closed
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"

  /api/v1/external/sessions/{session_id}/history/:
    get:
      tags: [Sessions]
      summary: 获取聊天历史
      description: 返回会话中的所有消息，支持分页。
      parameters:
        - name: session_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: limit
          in: query
          schema:
            type: integer
            default: 50
            minimum: 1
            maximum: 100
        - name: offset
          in: query
          schema:
            type: integer
            default: 0
      responses:
        "200":
          description: 消息历史记录
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: success
                  data:
                    $ref: "#/components/schemas/SessionHistoryResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"

  # ─── CHAT ──────────────────────────────────────────────────────────────────

  /api/v1/external/chat/:
    post:
      tags: [Chat]
      summary: 发送消息
      description: |
        向助手发送消息并返回 AI 回复。

        - 如果省略 `session_id`，将自动创建新会话。
        - 响应中的 `media` 数组包含 MediaExtractor 从 AI 回复中提取的
          图片或视频（例如来自 DALL-E 或 Kling AI）。
        - 如果响应包含 `metadata` 中的 `async_task_id`，则表示生成
          仍在进行中 — 轮询 `/tasks/{task_id}/status/` 获取结果。
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SendMessageRequest"
            example:
              assistant_id: "550e8400-e29b-41d4-a716-446655440000"
              message: "Hello, how can you help me?"
              session_id: "660e8400-e29b-41d4-a716-446655440000"
      responses:
        "200":
          description: 助手回复
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: success
                  data:
                    $ref: "#/components/schemas/ChatResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "402":
          $ref: "#/components/responses/InsufficientBalance"
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/RateLimited"

  /api/v1/external/chat/with-files/:
    post:
      tags: [Chat]
      summary: 发送带文件的消息
      description: |
        发送引用之前上传文件的消息。
        先通过 `POST /files/` 上传文件，然后将返回的 ID 传入此处。
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
                - $ref: "#/components/schemas/SendMessageRequest"
                - type: object
                  properties:
                    file_ids:
                      type: array
                      items:
                        type: string
                        format: uuid
                      description: 上传接口返回的文件 ID
            example:
              assistant_id: "550e8400-e29b-41d4-a716-446655440000"
              message: "Analyze this document"
              session_id: "660e8400-e29b-41d4-a716-446655440000"
              file_ids: ["770e8400-e29b-41d4-a716-446655440000"]
      responses:
        "200":
          description: 助手回复
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: success
                  data:
                    $ref: "#/components/schemas/ChatResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "402":
          $ref: "#/components/responses/InsufficientBalance"

  # ─── FILES ─────────────────────────────────────────────────────────────────

  /api/v1/external/files/:
    post:
      tags: [Files]
      summary: 上传文件
      description: |
        上传文件并将其附加到会话中。最大 50 MB。

        支持格式：`txt`、`csv`、`md`、`pdf`、`doc`、`docx`、`xls`、
        `xlsx`、`json` 以及所有常见图片格式。

        返回的 `id` 可在后续聊天请求中作为 `file_id` 传入。
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [session_id, file]
              properties:
                session_id:
                  type: string
                  format: uuid
                file:
                  type: string
                  format: binary
      responses:
        "201":
          description: 已上传文件信息
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: success
                  data:
                    $ref: "#/components/schemas/FileAttachment"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"

  # ─── AVATAR & TTS ──────────────────────────────────────────────────────────

  /api/v1/external/tts/:
    post:
      tags: [Avatar & TTS]
      summary: 文本转语音
      description: |
        将文本转换为音频。直接返回音频文件为二进制数据
        （非 JSON）。用于无需唇形同步的简单语音输出。

        **要求：** PREMIUM 或更高等级账户。
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TTSRequest"
      responses:
        "200":
          description: 音频文件（Opus 或 MP3）
          content:
            audio/ogg:
              schema:
                type: string
                format: binary
            audio/mpeg:
              schema:
                type: string
                format: binary
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"

  /api/v1/external/avatar/speech/:
    post:
      tags: [Avatar & TTS]
      summary: 虚拟形象语音（口型同步 + 音频）
      description: |
        将文本转换为语音，并返回 ARKit 兼容的口型同步时间数据，
        用于实时 3D 虚拟形象唇形同步。

        音频以 Base64 格式在 JSON 响应体中返回。
        在 iOS/Safari 上，格式自动设置为 MP3；其他所有
        浏览器接收 Opus 格式。

        **要求：** PREMIUM 或更高等级账户。

        ### 口型同步变形目标
        `visemes` 数组驱动您的虚拟形象模型上的这些混合形状目标：
        `viseme_PP`、`viseme_FF`、`viseme_TH`、`viseme_DD`、`viseme_kk`、
        `viseme_CH`、`viseme_SS`、`viseme_nn`、`viseme_RR`、`viseme_aa`、
        `viseme_E`、`viseme_I`、`viseme_O`、`viseme_U`
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AvatarSpeechRequest"
            example:
              text: "Hello! How can I help you today?"
              voice: nova
              speed: 1.0
              include_audio: true
      responses:
        "200":
          description: 口型同步数据和 Base64 音频
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: success
                  data:
                    $ref: "#/components/schemas/AvatarSpeechResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"

  /api/v1/external/avatar/model/:
    get:
      tags: [Avatar & TTS]
      summary: 获取虚拟形象模型 URL
      description: |
        返回分配给助手的 GLB/GLTF 虚拟形象模型的 HTTPS URL。

        - PREMIUM 账户获得默认的 Liya 虚拟形象模型。
        - PREMIUM PLUS 账户可能为每个助手分配了自定义模型。

        **要求：** PREMIUM 或更高等级账户。
      parameters:
        - name: assistant_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: 虚拟形象模型信息
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: success
                  data:
                    $ref: "#/components/schemas/AvatarModelResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"

  /api/v1/external/transcribe/:
    post:
      tags: [Avatar & TTS]
      summary: 语音转文本 (STT)
      description: |
        使用 OpenAI Whisper 将录音转录为文本。

        以 `multipart/form-data` 形式上传音频文件。支持的格式：
        `webm`、`mp3`、`wav`、`m4a`、`ogg` 以及 Whisper 兼容的其他格式。
        最大文件大小：25 MB。
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required: [audio]
              properties:
                audio:
                  type: string
                  format: binary
                  description: 要转录的音频文件
                model:
                  type: string
                  default: gpt-4o-mini-transcribe
                  description: 转录模型
      responses:
        "200":
          description: 转录文本
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: success
                  data:
                    type: object
                    properties:
                      text:
                        type: string
                        description: 转录后的文本
                        example: "你好，请问需要什么帮助？"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /api/v1/external/user/access/:
    get:
      tags: [Avatar & TTS]
      summary: 检查功能访问权限
      description: |
        返回账户等级和可用的高级功能。
        在组件加载时使用，根据账户订阅级别有条件地显示虚拟形象按钮、
        语音按钮或文件上传功能。
      responses:
        "200":
          description: 访问权限信息
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: success
                  data:
                    $ref: "#/components/schemas/UserAccessResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"

  # ─── WIDGET ──────────────────────────────────────────────────────────────────

  /api/v1/external/widget/config/:
    get:
      tags: [Widget]
      summary: 组件配置
      description: |
        返回允许的功能、组件和账户类型信息。
        在组件加载时使用，根据账户订阅级别有条件地显示虚拟形象、
        语音或文件上传按钮。

        当为 Linux OS 助手提供 `assistant_id` 时，响应还会包含守护进程连接状态。
      parameters:
        - name: assistant_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: 可选 — 为 Linux OS 助手返回守护进程状态
      responses:
        "200":
          description: 组件配置
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: success
                  data:
                    $ref: "#/components/schemas/WidgetConfigResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"

  /api/v1/external/scene/background/:
    get:
      tags: [Widget]
      summary: 3D 场景背景
      description: |
        返回分配给助手的 3D 场景背景模型（GLB/GLTF）的 HTTPS URL。

        - PREMIUM 账户获得默认的 Liya 场景。
        - PREMIUM PLUS 账户可能为每个助手分配了自定义场景。

        **要求：** PREMIUM 或更高等级账户。
      parameters:
        - name: assistant_id
          in: query
          required: false
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: 场景背景模型信息
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: success
                  data:
                    $ref: "#/components/schemas/SceneBackgroundResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"

  # ─── PRESENTATIONS ───────────────────────────────────────────────────────────

  /api/v1/external/presentations/:
    get:
      tags: [Presentations]
      summary: 列出演示文稿
      description: |
        返回分配给助手的所有活跃演示文稿。每个演示文稿包含页面，
        页面中包含可在组件中显示的图片 URL。
      parameters:
        - name: assistant_id
          in: query
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: 演示文稿列表
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: success
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/Presentation"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"

  /api/v1/external/presentations/page/{page_id}/image/:
    get:
      tags: [Presentations]
      summary: 演示文稿页面图片
      description: |
        返回特定演示文稿页面的 PNG 图片。
        图片直接作为二进制文件提供（非 JSON）。
      parameters:
        - name: page_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: 页面图片 (PNG)
          content:
            image/png:
              schema:
                type: string
                format: binary
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"

  # ─── TASKS ─────────────────────────────────────────────────────────────────

  /api/v1/external/tasks/{task_id}/status/:
    get:
      tags: [Tasks]
      summary: 获取异步任务状态
      description: |
        轮询异步任务（如图像或视频生成，Kling AI）的状态。

        当聊天响应中包含 `metadata.async_task_id` 时，表示媒体仍在生成中。
        轮询此接口直到 `status` 变为 `completed` 或 `failed`，
        然后使用 `result_urls` 显示输出。

        **建议轮询间隔：** 3–5 秒。
      parameters:
        - name: task_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "200":
          description: 任务状态
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: success
                  data:
                    $ref: "#/components/schemas/TaskStatusResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "404":
          $ref: "#/components/responses/NotFound"

  # ─── TRANSLATION ────────────────────────────────────────────────────────────

  /api/v1/external/translate/:
    post:
      tags: [Translation]
      summary: 文本翻译
      description: |
        使用 OpenAI 翻译能力将文本从一种语言翻译成另一种语言。
        支持 100 多种语言。

        未指定源语言时将自动检测。
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TranslationRequest"
            example:
              text: "Hello, how are you?"
              target_language: "tr"
              source_language: "en"
      responses:
        "200":
          description: 翻译结果
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: success
                  data:
                    $ref: "#/components/schemas/TranslationResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "429":
          $ref: "#/components/responses/RateLimited"

  /api/v1/external/translate/languages/:
    get:
      tags: [Translation]
      summary: 列出支持的语言
      description: |
        返回翻译支持的所有语言列表。
        使用 `code` 字段作为 `target_language` 或 `source_language` 参数。
      responses:
        "200":
          description: 支持的语言列表
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: success
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/LanguageInfo"
              example:
                status: success
                data:
                  - code: "en"
                    name: "英语"
                    native_name: "English"
                  - code: "tr"
                    name: "土耳其语"
                    native_name: "Türkçe"
                  - code: "zh"
                    name: "中文"
                    native_name: "中文"
        "401":
          $ref: "#/components/responses/Unauthorized"

# ─── COMPONENTS ──────────────────────────────────────────────────────────────

components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: 您的 Liya AI API 密钥，从控制面板获取

  schemas:
    # ── Core objects ──────────────────────────────────────────────────────────

    Assistant:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
          example: Customer Support
        description:
          type: string
        model:
          type: string
          example: gpt-4o
        model_display:
          type: string
          example: GPT-4o
        assistant_type:
          type: string
          enum: [custom, coding_agent, linux_os]
          example: custom
        is_reasoning_model:
          type: boolean
        total_messages:
          type: integer

    Session:
      type: object
      properties:
        id:
          type: string
          format: uuid
        assistant_id:
          type: string
          format: uuid
        session_name:
          type: string
          example: New Conversation
        message_count:
          type: integer
        created_at:
          type: string
          format: date-time
        last_message_at:
          type: string
          format: date-time
          nullable: true
        external_session_id:
          type: string
          nullable: true

    SessionListResponse:
      type: object
      properties:
        sessions:
          type: array
          items:
            $ref: "#/components/schemas/Session"
        total:
          type: integer
        limit:
          type: integer
        offset:
          type: integer

    SessionHistoryResponse:
      type: object
      properties:
        session_id:
          type: string
          format: uuid
        messages:
          type: array
          items:
            $ref: "#/components/schemas/Message"
        total:
          type: integer

    Message:
      type: object
      properties:
        id:
          type: string
          format: uuid
        content:
          type: string
          description: 消息文本（助手消息支持 Markdown 格式）
        role:
          type: string
          enum: [user, assistant]
        created_at:
          type: string
          format: date-time
        response_time:
          type: number
          format: float
          description: 助手响应时间（秒）
          nullable: true

    MessageMediaItem:
      type: object
      description: 从助手回复中提取的图片或视频
      properties:
        type:
          type: string
          enum: [image, video]
        url:
          type: string
          format: uri
        alt:
          type: string
          nullable: true
        source:
          type: string
          description: 内容来源标识符（例如 dall-e、klingai）
          nullable: true

    FileAttachment:
      type: object
      properties:
        id:
          type: string
          format: uuid
        file_name:
          type: string
        file_size:
          type: integer
          description: 文件大小（字节）
        file_type:
          type: string
        openai_file_id:
          type: string
          nullable: true
        created_at:
          type: string
          format: date-time

    # ── Requests ──────────────────────────────────────────────────────────────

    CreateSessionRequest:
      type: object
      required: [assistant_id]
      properties:
        assistant_id:
          type: string
          format: uuid
        session_name:
          type: string
          example: Support ticket #1234
        external_session_id:
          type: string
          description: 您自己的用户/会话标识符，用于映射

    SendMessageRequest:
      type: object
      required: [assistant_id, message]
      properties:
        assistant_id:
          type: string
          format: uuid
        message:
          type: string
          example: What are your business hours?
        session_id:
          type: string
          format: uuid
          description: 留空则自动创建新会话
        external_session_id:
          type: string
          description: 您自己的会话标识符

    TTSRequest:
      type: object
      required: [text]
      properties:
        text:
          type: string
          maxLength: 4096
          example: Hello, how can I help you today?
        voice:
          type: string
          default: nova
          description: OpenAI TTS 语音名称
          example: nova
        speed:
          type: number
          format: float
          default: 1.0
          minimum: 0.25
          maximum: 4.0

    AvatarSpeechRequest:
      type: object
      required: [text]
      properties:
        text:
          type: string
          maxLength: 4096
        voice:
          type: string
          default: nova
        speed:
          type: number
          format: float
          default: 1.0
        include_audio:
          type: boolean
          default: true
          description: 设为 false 则仅返回口型同步数据，不含音频

    # ── Responses ─────────────────────────────────────────────────────────────

    ChatResponse:
      type: object
      properties:
        session_id:
          type: string
          format: uuid
        message_id:
          type: string
          format: uuid
        response:
          type: string
          description: 助手回复文本（支持 Markdown 格式）
        response_time:
          type: number
          format: float
          description: 生成回复耗时（秒）
        suggestions:
          type: array
          items:
            type: string
          description: 显示给用户的快速回复建议
        metadata:
          type: object
          nullable: true
          properties:
            async_task_id:
              type: string
              format: uuid
              description: 当异步任务（图片/视频生成）启动时存在
            async_task_type:
              type: string
              example: klingai
        annotations:
          type: array
          description: 网络搜索引用（如果使用了网络搜索）
          items:
            type: object
            properties:
              type:
                type: string
                enum: [url_citation, file_citation]
              url:
                type: string
                format: uri
              title:
                type: string
        media:
          type: array
          description: 从回复中提取的图片/视频（MediaExtractor）
          items:
            $ref: "#/components/schemas/MessageMediaItem"
        usage:
          type: object
          nullable: true
          properties:
            tokens_used:
              type: integer
            cost_usd:
              type: number
              format: float

    VisemeData:
      type: object
      description: 口型同步动画的单个 viseme 时间条目
      properties:
        time:
          type: number
          format: float
          description: 时间戳（秒）
        viseme:
          type: integer
          description: Viseme 索引（0–20，ARKit 兼容）
        duration:
          type: number
          format: float
          description: 持续时间（秒）

    AvatarSpeechResponse:
      type: object
      properties:
        visemes:
          type: array
          items:
            $ref: "#/components/schemas/VisemeData"
        duration:
          type: number
          format: float
          description: 音频总时长（秒）
        text:
          type: string
        audio_base64:
          type: string
          format: byte
          description: Base64 编码的音频数据（include_audio=true 时存在）
        audio_format:
          type: string
          enum: [mp3, opus]
          description: iOS/Safari 上为 mp3，其他浏览器为 opus
        audio_mime:
          type: string
          example: audio/mpeg

    AvatarModelResponse:
      type: object
      properties:
        model_url:
          type: string
          format: uri
          description: GLB/GLTF 虚拟形象模型文件的 HTTPS URL
        model_type:
          type: string
          example: glb
        source:
          type: string
          enum: [default, custom]
          description: default = Liya 默认模型，custom = 助手专属模型

    UserAccessResponse:
      type: object
      properties:
        has_avatar_access:
          type: boolean
          description: PREMIUM 及以上为 true
        account_type:
          type: string
          enum: [standard, premium, premium_plus, enterprise, system_admin]
        can_use_custom_avatar:
          type: boolean
          description: PREMIUM_PLUS 及以上为 true

    TaskStatusResponse:
      type: object
      properties:
        task_id:
          type: string
          format: uuid
        status:
          type: string
          enum: [pending, submitted, processing, completed, failed, cancelled]
        task_type:
          type: string
          example: klingai
        result_urls:
          type: array
          items:
            type: string
            format: uri
          description: 状态为 completed 时的输出 URL
        error_code:
          type: string
          nullable: true
        error_message:
          type: string
          nullable: true
        created_at:
          type: string
          format: date-time
          nullable: true
        completed_at:
          type: string
          format: date-time
          nullable: true

    TranslationRequest:
      type: object
      required: [text, target_language]
      properties:
        text:
          type: string
          maxLength: 10000
          description: 要翻译的文本
          example: "Hello, how are you?"
        target_language:
          type: string
          description: 目标语言代码（例如 tr、en、zh、de、fr）
          example: "tr"
        source_language:
          type: string
          description: 源语言代码（留空则自动检测）
          example: "en"

    TranslationResponse:
      type: object
      properties:
        translated_text:
          type: string
          description: 翻译后的文本
          example: "Merhaba, nasılsın?"
        source_language:
          type: string
          description: 检测到或提供的源语言代码
          example: "en"
        target_language:
          type: string
          description: 目标语言代码
          example: "tr"

    LanguageInfo:
      type: object
      properties:
        code:
          type: string
          description: ISO 639-1 语言代码
          example: "en"
        name:
          type: string
          description: 英文语言名称
          example: "English"
        native_name:
          type: string
          description: 语言本身的名称（原生文字）
          example: "English"

    ErrorResponse:
      type: object
      properties:
        status:
          type: string
          example: error
        code:
          type: string
          example: INVALID_API_KEY
        message:
          type: string
          example: The API key provided is invalid or expired.
        data:
          type: object
          nullable: true

  responses:
    Unauthorized:
      description: 缺少或无效的 API 密钥
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          example:
            status: error
            code: INVALID_API_KEY
            message: The API key provided is invalid or expired.

    Forbidden:
      description: 当前账户等级无法使用此功能
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          example:
            status: error
            code: FEATURE_NOT_AVAILABLE
            message: 3D 虚拟形象功能需要 Premium 或更高级别订阅。

    NotFound:
      description: 资源未找到
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          example:
            status: error
            code: NOT_FOUND
            message: The requested resource was not found.

    BadRequest:
      description: 无效的请求参数
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          example:
            status: error
            code: BAD_REQUEST
            message: assistant_id is required.

    InsufficientBalance:
      description: 账户余额不足
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          example:
            status: error
            code: INSUFFICIENT_BALANCE
            message: Insufficient balance to process this request.
            data:
              current_balance: 0.50
              estimated_cost: 0.003

    RateLimited:
      description: 超出速率限制
      headers:
        X-RateLimit-Remaining:
          schema:
            type: integer
          description: 当前时间窗口内剩余的请求次数
        X-RateLimit-Reset:
          schema:
            type: integer
          description: 速率限制重置的 Unix 时间戳
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          example:
            status: error
            code: RATE_LIMITED
            message: Rate limit exceeded. Please wait before retrying.
