# YunQi AI Agent API 文档 版本:v1.1.0。更新日期:2026-10-23。 本文档与网页内容一致,API 地址为 https://www.yunqiai.chat。调用使用用户指定的完整模型 ID、分组和协议,失败时报告原因,未经确认不更换。Key 从安全运行环境读取,不写入前端或日志。 # 快速开始 网页路径:`/docs/cn/quickstart`。 ## 接入地址 API 地址为 `https://www.yunqiai.chat`,请按客户端要求填写下表中的地址。 | 使用方式 | 地址 | | --- | --- | | OpenAI SDK 的 base_url / baseURL | `https://www.yunqiai.chat/v1` | | Anthropic SDK / Claude Code 的 base URL | `https://www.yunqiai.chat` | | Gemini REST | `https://www.yunqiai.chat/v1beta/models/{model}:generateContent` | | Realtime WebSocket | `wss://www.yunqiai.chat/v1/realtime?model={model}` | OpenAI SDK 会在 base URL 后追加接口路径,填写时只保留一个 `/v1`。Claude Code 使用根地址,不填写 `/chat/completions`。 ## 配置密钥 登录 [控制台](https://www.yunqiai.chat),创建有目标模型权限的 API Key,并设置环境变量。接口使用 API Key 鉴权,不能使用账户密码。请在服务端保存密钥,避免将其写入前端、公开仓库、示例文件或错误日志。 ```powershell $env:YUNQIAI_API_KEY = "你的 API Key" $env:YUNQIAI_MODEL = "你选择的完整模型 ID" ``` ```bash export YUNQIAI_API_KEY='你的 API Key' export YUNQIAI_MODEL='你选择的完整模型 ID' ``` 将 `YUNQIAI_MODEL` 设为[模型目录](/docs/cn/api-reference/models)中的完整 ID。大小写、标点和命名空间均需保留,例如 `DeepSeek-V4.1-Flash`。 ## 读取密钥可见模型 ```bash curl --fail-with-body 'https://www.yunqiai.chat/v1/models' \ --header "Authorization: Bearer $YUNQIAI_API_KEY" ``` 返回的 `data[].id` 是当前 Key 可见的模型 ID。调用权限取决于 Key 所属分组;模型是否能完成请求,还需通过对应接口确认。 ## 发送请求 以下示例适用于使用 Responses 协议的模型。Claude、Gemini、Images、MJ 和 Realtime 的请求格式见各自接口页。 ```bash curl --fail-with-body 'https://www.yunqiai.chat/v1/responses' \ --header "Authorization: Bearer $YUNQIAI_API_KEY" \ --header 'Content-Type: application/json' \ --data "{\"model\":\"$YUNQIAI_MODEL\",\"input\":\"仅回复 READY\",\"max_output_tokens\":2048}" ``` 文本位于 `output[]` 中 `type=message` 的内容项,读取其中 `content[].type=output_text` 的文字。首项可能是推理内容,因此需要遍历数组。响应完成后检查正文;`incomplete`、空文本和拒绝响应按异常结果处理。 ## Agent 文档入口 - [完整文档](/llms-full.txt),无需登录读取。 - [Markdown 文件](/yunqi-agent-guide.md),与网页同源生成。 - [机器可读模型目录](/models.json),包含精确模型 ID、协议及路径。 - [HTTP OpenAPI](/openapi.json),WebSocket 事件另见 [Realtime](/docs/cn/api-reference/realtime)。 交给 Agent 接入时,同时提供要使用的模型 ID,并在运行环境配置 Key。调用失败后应保留所选模型和分组、报告原因。生图请求超时后,先确认任务状态再决定是否重发,避免重复生成。 # 密钥与调用权限 网页路径:`/docs/cn/permissions`。 ## 账户、密钥与分组 账户用于登录控制台,API Key 用于调用接口。Key 的分组、模型权限、有效期、IP 限制和可用额度都会影响调用。 分组在控制台为 Key 配置,不属于模型 ID,也不能通过请求体中的 `group`、渠道 ID 或上游账户字段切换。修改分组后,请重新查询模型目录并发送一次目标请求。 ## 权限确认 1. 在控制台确认 Key 已启用、未过期且额度可用。 2. 用这个 Key 调用 `GET /v1/models`,核对完整 `data[].id`。 3. 使用该型号对应协议发送最小请求,并检查返回的内容和结束状态。 4. 按业务需要分别检查工具、文件、图像编辑或实时音频。文本请求只验证文本调用。 文档中的模型目录列出型号及其协议。实际可用范围由 Key 权限和当前服务状态决定;请以 Key 设置页的分组为准,而非账户可见的全部分组。 ## 权限错误 | 情况 | 处理 | | --- | --- | | 无效密钥、密钥禁用或过期 | 核对 Key,不使用账户密码或旧环境变量 | | 模型不在 Key 权限内 | 核对精确 ID、分组及模型白名单 | | 当前分组无可用渠道 | 联系平台确认该型号的可用资源,保留原模型 ID | | 预扣额度不足 | 分别检查账户额度和 Key 额度;查询模型目录不消除这类限制 | | 浏览器能登录但 SDK 失败 | 浏览器 Cookie 与 API Key 是两种认证;检查 SDK 实际发送的头 | 联系平台排查时,请提供发生时间、完整模型 ID、请求路径、HTTP 状态和脱敏后的 request ID。完整 Key、Cookie、私有文件和完整提示词无需发送。 # 模型目录 网页路径:`/docs/cn/api-reference/models`。 ## 读取 Key 可见型号 调用 `GET https://www.yunqiai.chat/v1/models`,请求头为 `Authorization: Bearer `。返回的 `data[].id` 是此 Key 可见的模型。调用前还需确认对应协议及当前资源是否可用。 ## 型号与协议 | 完整模型 ID | 协议 | 详情 | | --- | --- | --- | | `gpt-5.6-sol` | responses | [接口与参数](/docs/cn/models/gpt/gpt-5.6-sol) | | `gpt-5.6-terra` | responses | [接口与参数](/docs/cn/models/gpt/gpt-5.6-terra) | | `gpt-5.6-luna` | responses | [接口与参数](/docs/cn/models/gpt/gpt-5.6-luna) | | `gpt-6-astra` | responses | [接口与参数](/docs/cn/models/gpt/gpt-6-astra) | | `gpt-6-sol` | responses | [接口与参数](/docs/cn/models/gpt/gpt-6-sol) | | `gpt-6-luna` | responses | [接口与参数](/docs/cn/models/gpt/gpt-6-luna) | | `claude-sonnet-4-6` | anthropic-messages | [接口与参数](/docs/cn/models/claude/claude-sonnet-4-6) | | `claude-opus-4-6` | anthropic-messages | [接口与参数](/docs/cn/models/claude/claude-opus-4-6) | | `claude-opus-4-7` | anthropic-messages | [接口与参数](/docs/cn/models/claude/claude-opus-4-7) | | `claude-opus-4-8` | anthropic-messages | [接口与参数](/docs/cn/models/claude/claude-opus-4-8) | | `claude-sonnet-5` | anthropic-messages | [接口与参数](/docs/cn/models/claude/claude-sonnet-5) | | `claude-fable-5` | anthropic-messages | [接口与参数](/docs/cn/models/claude/claude-fable-5) | | `claude-opus-5` | anthropic-messages | [接口与参数](/docs/cn/models/claude/claude-opus-5) | | `claude-opus-5-5` | anthropic-messages | [接口与参数](/docs/cn/models/claude/claude-opus-5-5) | | `gemini-3.5-flash` | gemini-generate-content | [接口与参数](/docs/cn/models/gemini/gemini-3.5-flash) | | `gemini-3.1-pro-preview` | gemini-generate-content | [接口与参数](/docs/cn/models/gemini/gemini-3.1-pro-preview) | | `gemini-3.7-flash` | gemini-generate-content | [接口与参数](/docs/cn/models/gemini/gemini-3.7-flash) | | `gemini-3.8-flash` | gemini-generate-content | [接口与参数](/docs/cn/models/gemini/gemini-3.8-flash) | | `gemini-3.1-flash-image` | gemini-generate-content | [接口与参数](/docs/cn/models/gemini/gemini-3.1-flash-image) | | `gemini-3-pro-image-preview` | gemini-generate-content | [接口与参数](/docs/cn/models/gemini/gemini-3-pro-image-preview) | | `gemini-nano-banana-2.1` | gemini-generate-content | [接口与参数](/docs/cn/models/gemini/gemini-nano-banana-2.1) | | `gpt-image-2` | images | [接口与参数](/docs/cn/models/image/gpt-image-2) | | `gpt-image-2.5-flare` | images | [接口与参数](/docs/cn/models/image/gpt-image-2.5-flare) | | `gpt-image-2.5-sunburst` | images | [接口与参数](/docs/cn/models/image/gpt-image-2.5-sunburst) | | `mj_imagine` | youchuan | [接口与参数](/docs/cn/models/image/mj) | | `youchuan-image` | youchuan | [接口与参数](/docs/cn/models/image/youchuan-image) | | `gpt-realtime-2.1` | realtime-websocket | [接口与参数](/docs/cn/models/realtime/gpt-realtime-2.1) | | `gpt-realtime-2.1-mini` | realtime-websocket | [接口与参数](/docs/cn/models/realtime/gpt-realtime-2.1-mini) | | `kimi-k3` | chat-completions | [接口与参数](/docs/cn/models/other/kimi-k3) | | `DeepSeek-V4.1-Flash` | chat-completions | [接口与参数](/docs/cn/models/other/DeepSeek-V4.1-Flash) | ## 下线型号 `gpt-image-2-each` 已从调用目录移除。`glm-5.2` 已灰度下线,如有需要请联系商务。更换型号时请重新指定完整 ID。 # 模型参数 网页路径:`/docs/cn/api-reference/model-parameters`。 ## 文本型号字段 下表列出本版支持的推理取值、工具调用和结构化输出。请求还需符合所选型号、协议及 Key 权限;相同参数不会保证相同的耗时、质量或推理消耗。 | 模型 ID | 推理字段 | 可用值 | 函数调用与结果回传 | 强制 JSON Schema | | --- | --- | --- | --- | --- | | [gpt-5.6-sol](/docs/cn/models/gpt/gpt-5.6-sol) | `reasoning.effort` | none、low、medium、high、xhigh、max | 支持,保留原始历史和调用 ID | 支持,应用需校验并处理拒绝响应 | | [gpt-5.6-terra](/docs/cn/models/gpt/gpt-5.6-terra) | `reasoning.effort` | none、low、medium、high、xhigh、max | 支持,保留原始历史和调用 ID | 支持,应用需校验并处理拒绝响应 | | [gpt-5.6-luna](/docs/cn/models/gpt/gpt-5.6-luna) | `reasoning.effort` | none、low、medium、high、xhigh、max | 支持,保留原始历史和调用 ID | 支持,应用需校验并处理拒绝响应 | | [gpt-6-astra](/docs/cn/models/gpt/gpt-6-astra) | `reasoning.effort` | low、medium、high、xhigh、max | 支持,保留原始历史和调用 ID | 支持,应用需校验并处理拒绝响应 | | [gpt-6-sol](/docs/cn/models/gpt/gpt-6-sol) | `reasoning.effort` | none、low、medium、high、xhigh、max | 支持,保留原始历史和调用 ID | 支持,应用需校验并处理拒绝响应 | | [gpt-6-luna](/docs/cn/models/gpt/gpt-6-luna) | `reasoning.effort` | none、low、medium、high、xhigh、max | 支持,保留原始历史和调用 ID | 支持,应用需校验并处理拒绝响应 | | [claude-sonnet-4-6](/docs/cn/models/claude/claude-sonnet-4-6) | `output_config.effort` | low、medium、high、max | 支持,保留原始历史和调用 ID | 支持,应用需校验并处理拒绝响应 | | [claude-opus-4-6](/docs/cn/models/claude/claude-opus-4-6) | `output_config.effort` | low、medium、high、max | 支持,保留原始历史和调用 ID | 支持,应用需校验并处理拒绝响应 | | [claude-opus-4-7](/docs/cn/models/claude/claude-opus-4-7) | `output_config.effort` | low、medium、high、xhigh、max | 支持,保留原始历史和调用 ID | 支持,应用需校验并处理拒绝响应 | | [claude-opus-4-8](/docs/cn/models/claude/claude-opus-4-8) | `output_config.effort` | low、medium、high、xhigh、max | 支持,保留原始历史和调用 ID | 支持,应用需校验并处理拒绝响应 | | [claude-sonnet-5](/docs/cn/models/claude/claude-sonnet-5) | `output_config.effort` | low、medium、high、xhigh、max | 支持,保留原始历史和调用 ID | 支持,应用需校验并处理拒绝响应 | | [claude-fable-5](/docs/cn/models/claude/claude-fable-5) | `output_config.effort` | low、medium、high、xhigh、max | 支持,保留原始历史和调用 ID | 支持,应用需校验并处理拒绝响应 | | [claude-opus-5](/docs/cn/models/claude/claude-opus-5) | `output_config.effort` | low、medium、high、xhigh、max | 支持,保留原始历史和调用 ID | 支持,应用需校验并处理拒绝响应 | | [claude-opus-5-5](/docs/cn/models/claude/claude-opus-5-5) | `output_config.effort` | low、medium、high、xhigh、max | 支持,保留原始历史和调用 ID | 暂不支持 | | [gemini-3.5-flash](/docs/cn/models/gemini/gemini-3.5-flash) | `generationConfig.thinkingConfig.thinkingLevel` | low、medium、high | 暂不支持 | 暂不支持 | | [gemini-3.1-pro-preview](/docs/cn/models/gemini/gemini-3.1-pro-preview) | `generationConfig.thinkingConfig.thinkingLevel` | low、medium、high | 暂不支持 | 暂不支持 | | [gemini-3.7-flash](/docs/cn/models/gemini/gemini-3.7-flash) | `generationConfig.thinkingConfig.thinkingLevel` | low、medium、high | 暂不支持 | 暂不支持 | | [gemini-3.8-flash](/docs/cn/models/gemini/gemini-3.8-flash) | `generationConfig.thinkingConfig.thinkingLevel` | low、medium、high | 暂不支持 | 暂不支持 | | [kimi-k3](/docs/cn/models/other/kimi-k3) | 本版未提供 | 省略 | 支持,保留原始历史和调用 ID | 支持,应用需校验并处理拒绝响应 | | [DeepSeek-V4.1-Flash](/docs/cn/models/other/DeepSeek-V4.1-Flash) | 本版未提供 | 省略 | 支持,保留原始历史和调用 ID | 支持,应用需校验并处理拒绝响应 | ## 推理和预算 推理字段分别为 Responses 的 reasoning.effort、Claude 的 output_config.effort 和 Gemini 的 thinkingLevel。各协议的字段及取值不同,Codex 客户端的 ultra 等执行模式也不属于原生 API 枚举。max_tokens / max_output_tokens / maxOutputTokens 同时包含推理和正文预算,不能直接按字数换算。 GPT 处于推理模式时不发送 temperature、top_p、top_logprobs。gpt-6-astra 不接受 none / minimal;工具使用 Responses。Gemini 3.7 / 3.8 Flash 不使用 minimal。Claude Opus 5.5 始终自适应推理,不传旧的 disabled / enabled + budget_tokens,不强制 any / 命名工具。 ## 其他能力 内置搜索、代码解释器、持久文件检索、Files API、Batch、缓存管理和 Responses 服务端历史存储需单独确认支持情况,未列明的功能不在本版支持范围内。图片参数见 [Images](/docs/cn/api-reference/images)、[Gemini 生图](/docs/cn/api-reference/gemini-generate-content) 和 [MJ](/docs/cn/models/image/mj);Realtime 使用独立会话事件。 官方字段参考:[OpenAI](https://developers.openai.com/api/docs/guides/latest-model)、[Claude Effort](https://platform.claude.com/docs/en/build-with-claude/effort)、[Gemini Thinking](https://ai.google.dev/gemini-api/docs/thinking)。YunQi 的请求路径、型号和限制以本文档为准。 # Responses API 网页路径:`/docs/cn/api-reference/responses`。 ## 端点与鉴权 `POST https://www.yunqiai.chat/v1/responses` 请求头为 `Authorization: Bearer ` 和 `Content-Type: application/json`。`model` 填写要使用的完整模型 ID。 ## 请求字段 | 字段 | 类型 | 说明 | | --- | --- | --- | | model | string,必填 | 完整模型 ID | | input | string 或 array,必填 | 单条文本,或完整的消息与工具结果序列 | | instructions | string | 本次请求的顶层指令;需要沿用时在后续请求中再次传入 | | max_output_tokens | integer | 推理及可见输出的总预算;小预算可能只有推理没有正文 | | reasoning.effort | string | 推理强度,可用值见对应型号页 | | tools | array | 函数工具定义,`type=function`,名称及 JSON Schema | | tool_choice | string 或 object | `auto`、`none`、`required` 或命名函数;需匹配型号支持范围 | | text.format | object | 结构化输出定义,如 `type=json_schema` | | stream | boolean | true 时返回 SSE,不是单个 JSON | `temperature`、`top_p`、`top_logprobs` 是否可用取决于模型和推理模式。Responses 的输出预算和格式分别使用 `max_output_tokens`、`text.format`,不使用 Chat 的 `max_tokens`、`response_format`。 ```json { "model": "gpt-6-astra", "input": [{"role":"user","content":[{"type":"input_text","text":"仅回复 READY"}]}], "reasoning": {"effort":"low"}, "max_output_tokens": 2048 } ``` 示例中的模型 ID 可替换为要使用的 Responses 型号。 ## 读取响应 先检查 `status`、`error` 和 `incomplete_details`,再遍历 `output[]`。正文位于 `type=message` 的 `content[]`,读取其中 `type=output_text` 的 `text`。推理项、拒绝内容和 `function_call` 分别处理,不并入普通正文。 `status=incomplete` 表示生成未完成。保存已有内容,并按 `incomplete_details.reason` 处理。增加预算后重新调用属于新请求,可能产生额外消耗,应限制重试次数。HTTP 200 但没有所需文本或工具调用时,也需按异常结果处理。 ## 工具与多轮 定义函数时使用 `{"type":"function","name":"lookup","description":"...","parameters":{"type":"object","properties":{},"additionalProperties":false},"strict":true}`。严格模式的对象字段需要满足对应 JSON Schema 约束。 收到 `function_call` 后: 1. 保存整个 `output` 序列,包括推理项。 2. 校验函数名和 JSON 参数,仅执行已允许的本地工具。 3. 将历史输入、原 `output`、`{"type":"function_call_output","call_id":"原 call_id","output":"工具结果字符串"}` 一起作为后续 `input`。 4. 同轮多个调用逐一回传,结果中的 `call_id` 对应原调用的 `call_id`,而非 `id`。 对话历史由应用保存并随请求传入,此写法不依赖 `previous_response_id` 或 `store`。自定义函数调用与内置网页检索、文件检索、代码解释器是不同功能,后者需单独确认支持情况。 ## 图片与文件 图片消息内容块为 `{"type":"input_image","image_url":"data:image/png;base64,"}`。可在同一 `content` 数组放入多张图片,每张单独一个内容块。 PDF 内容块为 `{"type":"input_file","filename":"brief.pdf","file_data":"data:application/pdf;base64,"}`。在同一消息中保留任务文本,返回后可用文件内的标识、页码或数据核对回答。此字段用于文件理解,不用于生图或图片编辑。 DOCX、XLSX、TXT、CSV、JSON 同样使用 `input_file`,filename 和 data URI 的 MIME 与文件类型保持一致。DOCX MIME 为 `application/vnd.openxmlformats-officedocument.wordprocessingml.document`,XLSX 为 `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`。非 PDF 文件的内嵌图片、图表和版式不保证可读取;需要理解版式时,先转换成 PDF。处理电子表格时,请说明工作表、列名、行范围和计算口径。 `file_data` 与 `image_url` 是不同字段,均不能填写本地磁盘路径。此端点暂不提供稳定的音频、视频内联输入,见[媒体输入](/docs/cn/media)。 ## 流式输出 SSE 按空行分帧,同一帧的多行 `data:` 合并后再解析 JSON。需要处理 `response.output_text.delta`、函数参数增量、`response.completed`、`response.incomplete`、`response.failed` 和 `error`。网络断开时若未收到结束事件,请按中断处理。 完整示例见 [Python 请求程序](/examples/http-client.py),包含 HTTP 错误正文读取、结束状态检查,且不自动重发 POST。 # Chat Completions 网页路径:`/docs/cn/api-reference/chat-completions`。 ## 端点 `POST https://www.yunqiai.chat/v1/chat/completions` 使用 `Authorization: Bearer ` 和 JSON 请求体。不同模型的可用参数见型号页,客户端自动识别结果仅供参考。 ```json { "model": "kimi-k3", "messages": [{"role":"user","content":"仅回复 READY"}], "max_tokens": 4096, "stream": false } ``` ## 字段与响应 | 字段 | 说明 | | --- | --- | | messages | 按顺序传递 system、user、assistant、tool 消息 | | max_tokens / max_completion_tokens | 使用目标模型接受的字段,不同时发送两个预算字段 | | tools | `type=function`,定义放在 `function` 内,与 Responses 的扁平函数格式不同 | | tool_choice | 自动、禁用或指定工具,受型号约束 | | response_format | 支持时设置 JSON object / JSON Schema,不是 Responses 的 text.format | | stream | true 返回 SSE,需逐帧解析 | 响应需读取 `choices[].message.content`、`tool_calls`、`finish_reason` 和 `usage`。`finish_reason=length` 表示截断,`tool_calls` 表示需要执行工具。若返回 `reasoning_content`,请将其作为推理内容单独处理,不并入最终正文或普通 `content`。 ## 工具历史 工具定义示例为 `{"type":"function","function":{"name":"lookup","description":"...","parameters":{"type":"object","properties":{}}}}`。 收到工具调用后,将完整 assistant 消息追加到 `messages`,保留调用 ID,再为每个调用追加 `{"role":"tool","tool_call_id":"原调用 id","content":"结果字符串"}`。后续请求沿用同一型号。 ## 图片与流式 支持视觉的型号使用 `content` 数组,文本块为 `{"type":"text","text":"问题"}`,图片块为 `{"type":"image_url","image_url":{"url":"data:image/png;base64,"}}`。图片理解需同时得到模型支持,接口接受该字段并不能验证图片是否被读取。 流式数据的正文通常在 `choices[].delta.content`,工具参数需要按调用下标累积,最后解析成 JSON。`data: [DONE]` 为流结束标记;收到标记前的断开不当作完整成功。 GPT 的工具与文件调用使用[Responses](/docs/cn/api-reference/responses)。`gpt-6-astra` 在 Chat 接口中的文本兼容不包含工具调用。 # Anthropic Messages 网页路径:`/docs/cn/api-reference/anthropic-messages`。 ## 端点与请求头 `POST https://www.yunqiai.chat/v1/messages` ```http x-api-key: anthropic-version: 2023-06-01 Content-Type: application/json ``` Anthropic SDK 的 base URL 使用 `https://www.yunqiai.chat`,由 SDK 拼接 `/v1/messages`。 ## 请求体 ```json { "model": "claude-opus-5-5", "max_tokens": 8192, "messages": [{"role":"user","content":[{"type":"text","text":"仅回复 READY"}]}] } ``` `model`、`max_tokens`、`messages` 为必填项。系统提示词放在顶层 `system`;`messages` 使用 user / assistant 消息,不接收 `role=system`。 ## 推理与预算 `max_tokens` 包含推理与正文。预算耗尽时,响应可能以 `stop_reason=max_tokens` 结束,且没有可见文本。正文需遍历 `content[]`,拼接所有 `type=text` 内容块;首块可能是推理内容。 `claude-opus-5-5` 始终使用自适应推理,不支持关闭,也不接受旧的 `thinking.type=enabled` 与 `budget_tokens` 配置。推理强度使用 `output_config.effort`,可用值见型号页。该型号不支持 `tool_choice.type=any` 或强制指定工具名。 `claude-fable-5` 同样不支持旧式关闭推理配置。新型号可能省略可见的 thinking 文本,但仍会进行推理。 ## 工具与历史 工具格式为 `{"name":"lookup","description":"...","input_schema":{"type":"object","properties":{}}}`。返回 `type=tool_use` 内容块时,校验 `name`、`input`,执行工具后按以下顺序续接: 1. 将原响应的完整 content 放入 assistant 消息,保留 thinking、signature 等内容块。 2. user 消息包含 `{"type":"tool_result","tool_use_id":"原 tool_use.id","content":"工具结果"}`。 3. 多个工具调用全部匹配返回;执行错误可设置 `is_error:true`。 thinking 内容块及其签名需随原对话保存,不能修改、伪造或跨型号使用。结合 `stop_reason` 和内容块判断是否还需执行工具。不支持强制工具选择的推理配置使用默认自动选择。 ## 图片与 PDF 图片块: ```json {"type":"image","source":{"type":"base64","media_type":"image/png","data":""}} ``` PDF 块: ```json {"type":"document","source":{"type":"base64","media_type":"application/pdf","data":""}} ``` `data` 仅放 Base64 正文,不加 `data:...;base64,` 前缀。多张图片放多个独立 image 块。文字、图片和 PDF 可以共同组成 user 的 content 数组。 本页采用内联上传,不依赖 `/v1/files`。Messages 使用自己的内容块格式,不接收 OpenAI 的 `input_file`、`image_url`。纯文本文件解码后放入 text;document 在此用于 PDF,不直接接收 Word、Excel、视频或音频。 ## 流式事件 使用 `stream:true`。处理 `message_start`、`content_block_start`、`content_block_delta`、`content_block_stop`、`message_delta`、`message_stop`、`error`。工具 `input_json_delta` 需按内容块索引拼接;推理签名增量也要保留。HTTP 200 后仍可能出现 error 事件。 # Gemini Generate Content 网页路径:`/docs/cn/api-reference/gemini-generate-content`。 ## 端点与鉴权 `POST https://www.yunqiai.chat/v1beta/models/{model}:generateContent` 请求头使用 `x-goog-api-key: ` 和 `Content-Type: application/json`。路径中的 `model` 使用完整模型 ID,拼接 URL 时按路径参数编码。 流式请求使用 `POST /v1beta/models/{model}:streamGenerateContent?alt=sse`。普通 generateContent 不通过 `stream:true` 切换为流式。 ```json { "contents": [{"role":"user","parts":[{"text":"仅回复 READY"}]}], "generationConfig": {"maxOutputTokens":4096} } ``` ## 配置字段 | 字段 | 用途 | | --- | --- | | contents | 历史消息,role 为 user 或 model,内容为 parts 数组 | | systemInstruction.parts | 系统指令,与 contents 分开 | | generationConfig.maxOutputTokens | 输出预算,需同时为推理及最终结果留出空间 | | generationConfig.thinkingConfig | thinkingLevel / thinkingBudget 等型号专属设置,不混用不支持的字段 | `gemini-3.7-flash` 和 `gemini-3.8-flash` 的 thinkingLevel 不包含 minimal,可用值与 `gemini-3.5-flash` 有区别。 ## 内联媒体 图片、音频、视频、PDF 均使用独立 part: ```json { "contents": [{"role":"user","parts":[ {"text":"描述视频中先后出现的画面,并给出时间顺序"}, {"inlineData":{"mimeType":"video/mp4","data":""}} ]}] } ``` Base64 不带 data URI 前缀,MIME 需与文件编码一致。文件大小、上下文预算、视频帧采样和输出限制都会影响读取范围,见[媒体输入](/docs/cn/media)。本页使用内联文件,不依赖 `/upload/v1beta/files`;本地路径或未上传文件的 `fileUri` 均不能代替文件内容。 ## 工具与多轮 多轮对话按顺序传入 user / model contents。响应 part 含 `thoughtSignature` 时,连同其他字段原样保存,并在下一轮回传完整内容,而非只保留 text。 当前接入暂不支持原生工具结果回传和强制 JSON Schema。tools / functionResponse、responseMimeType、responseJsonSchema 不在本版支持范围内。通过提示词要求返回 JSON 时,应用仍需严格解析和校验,代码围栏也应按格式异常处理。 ## 图像生成与参考图 图片模型在 `generationConfig` 设置 `responseModalities:["TEXT","IMAGE"]`,尺寸相关设置位于 `imageConfig`,不是 OpenAI Images 的顶层 `size`。 ```json { "contents": [{"role":"user","parts":[ {"text":"保留第一张产品外形与第二张包装配色,生成正面产品图"}, {"inlineData":{"mimeType":"image/png","data":""}}, {"inlineData":{"mimeType":"image/jpeg","data":""}} ]}], "generationConfig": { "responseModalities":["TEXT","IMAGE"], "imageConfig":{"aspectRatio":"1:1","imageSize":"1K"} } } ``` 每张参考图分别放入 `inlineData`,提示词说明各图的用途。多轮编辑时保留上一轮完整 model parts 和签名,再追加新的 user 指令;图片的 Base64 无需写入提示词。 `imageConfig.imageSize` 可填 `1K`、`2K` 或 `4K`,`aspectRatio` 可填 `1:1`、`16:9`、`9:16` 等比例。实际宽高由两者共同决定,长边未必固定为 1024。输出可能同时包含中间预览和最终图片。 遍历 `candidates[].content.parts[]`,分别读取 text 与 `inlineData` / `inline_data`。带 `thought:true` 的文字或图片属于中间推理内容,其中的预览图可能低于请求分辨率。保存图片时跳过这些 part,读取剩余图片、解码并按文件格式保存。响应若只有文字或中间预览,则没有返回最终图片。 安装 `requests pillow` 后可运行 [gemini-images.py](/examples/gemini-images.py)。多张参考图重复传入 `--image`,使用 `--size 2K --aspect 16:9` 设置规格。`--history private-history.json` 保存完整消息与签名,后续编辑继续使用同一文件。历史文件含原始图片和对话内容,请限制访问权限并排除在版本控制之外。 ```bash python gemini-images.py --prompt '保留两张参考图的独有形状、颜色和标识,将图1放左上、图2放右下' \ --image first.png --image second.png --size 2K --aspect 1:1 \ --history private-history.json --out image-round-1 python gemini-images.py --prompt '保持两个主体不变,仅将背景改成白色' \ --size 2K --aspect 1:1 --history private-history.json --out image-round-2 ``` ## 结束状态 响应需检查 `promptFeedback`、`candidates[].finishReason`、`safetyRatings` 和 `usageMetadata`。`MAX_TOKENS` 表示截断,`SAFETY` 等表示受限结束,即使 HTTP 为 200 也需单独处理。流式请求以候选的最终结束状态为准,SSE 未必返回 OpenAI 风格的 `[DONE]` 标记。 # Images API 网页路径:`/docs/cn/api-reference/images`。 ## 接口与请求体 | 操作 | 路径 | 编码 | | --- | --- | --- | | 文生图 | `POST /v1/images/generations` | application/json | | gpt-image-2 参考图编辑 | `POST /v1/images/edits` | multipart/form-data | | gpt-image-2.5-flare / gpt-image-2.5-sunburst 参考图编辑 | `POST /v1/images/edits?api-version=2025-04-01-preview` | multipart/form-data | 使用 `Authorization: Bearer ` 鉴权。编辑接口需上传图片文件内容;multipart 的 Content-Type 和 boundary 由 HTTP 库生成,无需手动设置。 ## 生成字段 | 字段 | gpt-image-2 | gpt-image-2.5-flare / gpt-image-2.5-sunburst | | --- | --- | --- | | model | 完整 ID,含命名空间 | 完整 ID,含型号后缀 | | prompt | 文本描述或编辑指令 | 文本描述或编辑指令 | | size | auto 或 WIDTHxHEIGHT | auto 或 WIDTHxHEIGHT | | quality | auto、low、medium、high | auto、low、medium、high、xhigh、max | | output_format | png、jpeg | png、jpeg | | output_compression | 0–100,仅 jpeg | 0–100,仅 jpeg | | background | auto、opaque | auto、opaque、transparent;透明输出用 png | | moderation | auto、low | auto、low | | n | 1–10,结果逐项读取 data 数组 | 1–10,结果逐项读取 data 数组 | | response_format | 不发送,解析 b64_json | 不发送,解析 b64_json | | input_fidelity | 不发送 | 不发送 | `quality` 设置生成质量,`size` 设置像素尺寸。质量档位不能仅凭 HTTP 状态、字段回显、文件体积或观感判断。请保存请求参数和返回元数据,图片大小也不能用来计算费用。 ```json { "model": "gpt-image-2", "prompt": "白色桌面上一个蓝色陶瓷杯,正面产品摄影,无文字", "size": "1024x1024", "quality": "medium", "output_format": "png", "n": 1 } ``` ## 尺寸约束 `size` 使用小写 `x`,例如 `1024x1024`、`1536x1024`、`1024x1536`。自定义尺寸需符合型号要求。gpt-image-2 的宽高须为 16 的倍数,长短边比例不超过 3,最长边不超过 3840,总像素为 655360–8294400。 `auto` 的输出宽高由服务决定,可能为非正方形。排版、存储和制作蒙版时,请读取解码后的图片尺寸。本接口不使用 `resolution`、`aspectRatio` 或 Chat 的 `messages`。 ## 单图、多图和蒙版 单张参考图使用 `image`,多张则重复使用 `image[]` 文件字段,并在提示词中按上传顺序说明图 1、图 2 的用途。支持 PNG、JPEG、WebP;gpt-image-2 可传 1–16 张,每张小于 50 MB。总请求大小还需计入 multipart 开销,并符合网关限制。 `mask` 为带 Alpha 通道的 PNG,与第一张参考图宽高一致,透明区域表示主要编辑区域。蒙版作用于第一张参考图,其余图片是辅助参考。生成式编辑不保证蒙版外逐像素不变。 ```bash curl --fail-with-body --max-time 300 \ 'https://www.yunqiai.chat/v1/images/edits?api-version=2025-04-01-preview' \ -H "Authorization: Bearer $YUNQIAI_API_KEY" \ -F 'model=gpt-image-2.5-flare' \ -F 'image[]=@scene.png;type=image/png' \ -F 'image[]=@product.png;type=image/png' \ -F 'prompt=保留图1的场景,将图2的商品放在桌面中央,保持商品轮廓和颜色' \ -F 'size=1024x1024' -F 'quality=medium' -F 'n=1' ``` 参考图通过独立文件字段提交,prompt 只填写编辑指令。其他协议可能用 Base64 编码图片,但都需放在相应的图片字段中;将 Base64 或 URL 当作普通提示词文字发送,无法代替此处的文件上传。 ## 保存图片 完整程序见 [images.py](/examples/images.py),支持 generate / edit、多参考图和 mask。程序逐张解码图片,检查格式及尺寸后按对应后缀保存;转换格式需实际转码,不能只改后缀。 先检查 HTTP 状态和错误正文,再确认 `data[]` 非空、每张图片可解码且实际尺寸符合请求。指定 size 与返回宽高不符时,保留原图并报告校验错误,不直接拉伸。返回 URL 时请及时下载,下载请求无需向第三方图片域名发送 YunQi Key;签名链接不宜用作永久存储。 `images.py --timeout 600` 将客户端读取超时设为 600 秒,不改变服务端执行时限。程序发送前校验数量、尺寸格式和质量取值,返回后校验图片格式、尺寸和张数。客户端超时后,服务端可能仍在生成,请先确认状态再重发。 Images 用于生成和编辑图片。图片理解使用 Chat / Responses / Messages / Gemini 的内容块,不使用这里的 prompt、mask 字段。 参数定义可参考 [OpenAI Images](https://developers.openai.com/api/reference/resources/images/methods/generate) 和 [Microsoft Foundry 图像接口](https://learn.microsoft.com/en-us/azure/foundry/openai/how-to/dall-e?view=foundry-classic)。调用 YunQi 时,地址、Key 和模型 ID 使用本站配置,无需 Azure 资源域名、deployment 路径或上游密钥。 # Realtime WebSocket 网页路径:`/docs/cn/api-reference/realtime`。 ## WebSocket 连接 `wss://www.yunqiai.chat/v1/realtime?model={model}` 模型 ID 为 `gpt-realtime-2.1` 或 `gpt-realtime-2.1-mini`。服务端 WebSocket 客户端在握手时发送 `Authorization: Bearer `。Realtime 使用持续会话,与 Chat、TTS 和录音转写 HTTP 接口不同。 浏览器原生 WebSocket 无法任意设置 Authorization 请求头,因此浏览器应用需通过自己的后端中继连接。长期 API Key 保存在后端,不放入前端 URL、子协议或构建产物。本页方案不依赖临时令牌端点。 ## 会话配置 连接后等待 `session.created`,再发送 GA 格式的 `session.update`。音频采用下例的嵌套字段,不使用旧版顶层 `modalities`、`input_audio_format`。 ```json { "type":"session.update", "session":{ "type":"realtime", "output_modalities":["audio"], "instructions":"用中文简短回答。", "audio":{ "input":{"format":{"type":"audio/pcm","rate":24000},"turn_detection":null}, "output":{"format":{"type":"audio/pcm","rate":24000},"voice":"marin"} } } } ``` 收到 `session.updated` 后即可提交输入。声音和格式需在型号支持范围内,部分渠道可能不支持所有官方语音名。 ## 文本输入 ```json {"type":"conversation.item.create","item":{"type":"message","role":"user","content":[{"type":"input_text","text":"仅回复 READY"}]}} ``` 随后发送 `{"type":"response.create","response":{"output_modalities":["text"]}}` 请求纯文本回复。读取 `response.output_text.delta` 增量,并在 `response.done` 中检查 response.status。 ## 音频输入与输出 手动轮次模式使用 `turn_detection:null`: 1. 读取 24 kHz、单声道、16 位有符号 little-endian PCM。发送原始 PCM,不把 WAV 文件头一起当成采样。 2. 分块 Base64 编码,发送 `{"type":"input_audio_buffer.append","audio":""}`。 3. 发送 `input_audio_buffer.commit`,等到提交确认。 4. 发送 `response.create`。自动 VAD 模式与手动提交使用不同的轮次逻辑,不要重复触发回复。 5. 将 `response.output_audio.delta` 的 delta 解码、按顺序拼接,最终按已协商音频格式封装 WAV。 转录文字来自 `response.output_audio_transcript.delta`,音频需另行读取和保存。语音请求在 `response.done` 成功后还应检查音频数据;只有转录文字时,音频结果仍不完整。 ## 图片与工具 图片通过 user 消息中的独立 `input_image` 内容块传入,不写入 input_text。工具执行前校验名称、参数和权限,执行后用 `function_call_output` 回传结果并保留 call_id,再创建后续 response。 ## 生命周期与错误 记录 event_id、response_id 和结束状态,并处理 `error`、握手 HTTP 错误、close code / reason、超时及中断。`response.done` 返回 `failed`、`cancelled`、`incomplete` 时按相应异常处理。关闭前保存已完成输出,重连不会自动恢复旧会话。 重放音频或工具调用前需确认不会重复执行。用户打断时取消当前 response,并按实际播放进度更新服务端对话项,排除尚未播放的音频。 Node.js 程序见 [realtime.mjs](/examples/realtime.mjs),依赖 `ws` 并从环境变量读取 Key,无需 Bun。WebSocket 事件见本页,不包含在 [HTTP OpenAPI](/openapi.json) 中。 # 文件与媒体输入 网页路径:`/docs/cn/media`。 ## 编码规则 | 协议 | 图片 | PDF / 其他媒体 | | --- | --- | --- | | Responses | input_image.image_url = data URI | input_file.file_data = 文件实际 MIME 的 data URI,含 filename | | Chat Completions | image_url.url = data URI | 不把 PDF、音频或视频套进 image_url | | Claude Messages | image.source.data = 纯 Base64 | document.source.data = PDF 纯 Base64 | | Gemini | inlineData.data = 纯 Base64,单独 mimeType | PDF / 音频 / 视频使用同结构,每个文件独立 part | | Images edits | multipart 二进制 image / image[] | mask 为独立 PNG 文件,不是文件理解 | | MJ | text 中的公开图片 URL / 专用 URL 字段 | 不接收任意文件 Base64 | | Realtime | input_image 内容块 | 音频缓冲区传原始采样的 Base64,不是任意文件上传 | data URI 的格式为 `data:image/png;base64,...`,纯 Base64 则不带前缀。读取文件二进制后编码一次即可;磁盘路径不能用作远端文件地址。 ## MIME 与文件类型 | 输入 | 格式与 MIME | 接入方式 | | --- | --- | --- | | 图片理解 | PNG image/png、JPEG image/jpeg、WebP image/webp | 支持视觉的 Responses / Claude / Gemini;Chat 按具体型号 | | PDF | application/pdf | Responses input_file;Claude document;Gemini inlineData | | Gemini 音频 | WAV audio/wav、MP3 audio/mp3 或 audio/mpeg | 文本多模态 Gemini 的 inlineData;容器和编码需匹配 | | Gemini 视频 | MP4 video/mp4、MPEG video/mpeg、MOV video/mov、AVI video/avi、MPG video/mpg、WMV video/wmv | 文本多模态 Gemini 的 inlineData;不将 MOV 默认标成 video/quicktime | | TXT / CSV / JSON | text/plain、text/csv、application/json | Responses input_file;其他协议可先解码成文本 | | DOCX / XLSX | 对应的 Office Open XML MIME | Responses input_file;非 PDF 文件的内嵌图片与图表不保证可读取 | | 压缩包 / 可执行文件 | 不作为原生文件输入 | 应用按安全规则提取内容,不能直接作为模型附件执行 | 上传前检查文件头、页数、像素数、声道、采样率、时长和容器。改后缀不会转换编码;加密 PDF、损坏文件和空文件应在发送前拦截。 本版暂不提供 FLAC、AAC、OGG、WebM 和 3GP 等其他音视频格式的稳定支持。请求失败时先查看错误正文,区分格式问题、上游不可用和网络中断。逐型号格式及下载表见[模型输入规格表](/docs/cn/input-matrix)。 ## 大小与上下文 文件输入同时受单文件大小、总请求体、模型上下文和网关/上游限制影响。Base64 体积约为原文件的 4/3,总请求还包含 JSON、提示词和历史消息。下表中的官方上限仅供参考,本站请求还受网关及渠道限制。 | 协议 | 大小限制 | | --- | --- | | Claude Messages | 官方请求体上限 32 MB;图片与 PDF 内联会增加编码体积,PDF 还受页数及上下文限制 | | Responses 文件 | 官方单文件小于 50 MB、单请求文件总计不超过 50 MB;YunQi 实际请求体仍需留余量 | | Gemini inline | 按编码后的完整 JSON 体计算;原厂视频 inline 小于 100 MB,YunQi 网关上限另受渠道限制 | | Images gpt-image-2 edits | 每张参考图小于 50 MB,1–16 张;mask 小于 4 MB,PNG 且尺寸匹配 | | Realtime PCM | 按会话格式、缓冲区与轮次分块;不直接发送超大文件单帧 | 大视频、大音频或大 PDF 可先压缩、裁剪、抽帧、分段或拆页。保留片段时间和页码,方便合并回答。本接入不提供 Gemini `/upload/v1beta/files`、Claude `/v1/files` 或通用持久文件存储。远端 URL 需能被上游访问;私有链接或需要 Cookie 的文件,请先由自己的后端读取。 Claude PDF 的官方上限为 600 页,低于 1M 上下文时为 100 页;Gemini PDF 的官方上限为 1000 页。YunQi 尚未公布统一的单文件大小、视频时长或 PDF 页数上限,请结合所选型号和业务文件确认。请求遇到 413、上下文超限或读取超时时,按相应限制缩减或拆分文件。 ## 结果核验 文件理解可用文件内独有的标识、页码、数字或画面顺序核对。若回复仍要求上传文件、只复述提示词或没有使用附件信息,请检查输入内容。图生图则需核对主体、颜色、位置及多图对应关系,图片生成成功后仍可能与参考图有差异。 官方格式说明见 [OpenAI 文件输入](https://developers.openai.com/api/docs/guides/file-inputs)、[Claude PDF](https://platform.claude.com/docs/en/build-with-claude/pdf-support)、[Gemini 视频](https://ai.google.dev/gemini-api/docs/video-understanding)。调用 YunQi 时,请同时遵守本页的格式和接口限制。 # 模型输入规格表 网页路径:`/docs/cn/input-matrix`。 ## 逐型号输入规格 下表列出各型号的输入格式、编码和限制。上传时 MIME 需与文件一致;大小同时受官方限制、YunQi 请求体和模型上下文影响。未公布的最大值在表中注明。MIME、Base64 结构和分段方法见[文件与媒体输入](/docs/cn/media)。 可下载 [CSV 规格表](/model-input-specs.csv) 或读取 [JSON 模型目录](/models.json) 的 inputSpec、functionTools、jsonSchema。 | 模型 ID | 格式 | 编码 / 字段 | 官方限制 | 上传要求与其他限制 | | --- | --- | --- | --- | --- | | [gpt-5.6-sol](/docs/cn/models/gpt/gpt-5.6-sol) | PNG/JPEG/WebP; PDF/DOCX/XLSX/TXT/CSV/JSON | input_image data URI / input_file data URI + filename | 文件 <50 MB/个;文件总计 <=50 MB/请求 | 未公布统一文件大小上限,请同时检查 Base64 编码后的请求体、上下文和超时限制;不支持原生音视频、ZIP 或持久 Files 存储 | | [gpt-5.6-terra](/docs/cn/models/gpt/gpt-5.6-terra) | PNG/JPEG/WebP; PDF/DOCX/XLSX/TXT/CSV/JSON | input_image data URI / input_file data URI + filename | 文件 <50 MB/个;文件总计 <=50 MB/请求 | 未公布统一文件大小上限,请同时检查 Base64 编码后的请求体、上下文和超时限制;不支持原生音视频、ZIP 或持久 Files 存储 | | [gpt-5.6-luna](/docs/cn/models/gpt/gpt-5.6-luna) | PNG/JPEG/WebP; PDF/DOCX/XLSX/TXT/CSV/JSON | input_image data URI / input_file data URI + filename | 文件 <50 MB/个;文件总计 <=50 MB/请求 | 未公布统一文件大小上限,请同时检查 Base64 编码后的请求体、上下文和超时限制;不支持原生音视频、ZIP 或持久 Files 存储 | | [gpt-6-astra](/docs/cn/models/gpt/gpt-6-astra) | PNG/JPEG/WebP; PDF/DOCX/XLSX/TXT/CSV/JSON | input_image data URI / input_file data URI + filename | 文件 <50 MB/个;文件总计 <=50 MB/请求 | 未公布统一文件大小上限,请同时检查 Base64 编码后的请求体、上下文和超时限制;不支持原生音视频、ZIP 或持久 Files 存储 | | [gpt-6-sol](/docs/cn/models/gpt/gpt-6-sol) | PNG/JPEG/WebP; PDF/DOCX/XLSX/TXT/CSV/JSON | input_image data URI / input_file data URI + filename | 文件 <50 MB/个;文件总计 <=50 MB/请求 | 未公布统一文件大小上限,请同时检查 Base64 编码后的请求体、上下文和超时限制;不支持原生音视频、ZIP 或持久 Files 存储 | | [gpt-6-luna](/docs/cn/models/gpt/gpt-6-luna) | PNG/JPEG/WebP; PDF/DOCX/XLSX/TXT/CSV/JSON | input_image data URI / input_file data URI + filename | 文件 <50 MB/个;文件总计 <=50 MB/请求 | 未公布统一文件大小上限,请同时检查 Base64 编码后的请求体、上下文和超时限制;不支持原生音视频、ZIP 或持久 Files 存储 | | [claude-sonnet-4-6](/docs/cn/models/claude/claude-sonnet-4-6) | PNG/JPEG/WebP; PDF | image/document source: base64 + media_type | 完整请求体 <=32 MB;PDF <=600 页,低于 1M 上下文时 <=100 页 | 按编码后的完整 JSON 体计算,同时受上下文限制;网关可用上限可能低于官方值;不支持 Office、音视频或 /v1/files 上传 | | [claude-opus-4-6](/docs/cn/models/claude/claude-opus-4-6) | PNG/JPEG/WebP; PDF | image/document source: base64 + media_type | 完整请求体 <=32 MB;PDF <=600 页,低于 1M 上下文时 <=100 页 | 按编码后的完整 JSON 体计算,同时受上下文限制;网关可用上限可能低于官方值;不支持 Office、音视频或 /v1/files 上传 | | [claude-opus-4-7](/docs/cn/models/claude/claude-opus-4-7) | PNG/JPEG/WebP; PDF | image/document source: base64 + media_type | 完整请求体 <=32 MB;PDF <=600 页,低于 1M 上下文时 <=100 页 | 按编码后的完整 JSON 体计算,同时受上下文限制;网关可用上限可能低于官方值;不支持 Office、音视频或 /v1/files 上传 | | [claude-opus-4-8](/docs/cn/models/claude/claude-opus-4-8) | PNG/JPEG/WebP; PDF | image/document source: base64 + media_type | 完整请求体 <=32 MB;PDF <=600 页,低于 1M 上下文时 <=100 页 | 按编码后的完整 JSON 体计算,同时受上下文限制;网关可用上限可能低于官方值;不支持 Office、音视频或 /v1/files 上传 | | [claude-sonnet-5](/docs/cn/models/claude/claude-sonnet-5) | PNG/JPEG/WebP; PDF | image/document source: base64 + media_type | 完整请求体 <=32 MB;PDF <=600 页,低于 1M 上下文时 <=100 页 | 按编码后的完整 JSON 体计算,同时受上下文限制;网关可用上限可能低于官方值;不支持 Office、音视频或 /v1/files 上传 | | [claude-fable-5](/docs/cn/models/claude/claude-fable-5) | PNG/JPEG/WebP; PDF | image/document source: base64 + media_type | 完整请求体 <=32 MB;PDF <=600 页,低于 1M 上下文时 <=100 页 | 按编码后的完整 JSON 体计算,同时受上下文限制;网关可用上限可能低于官方值;不支持 Office、音视频或 /v1/files 上传 | | [claude-opus-5](/docs/cn/models/claude/claude-opus-5) | PNG/JPEG/WebP; PDF | image/document source: base64 + media_type | 完整请求体 <=32 MB;PDF <=600 页,低于 1M 上下文时 <=100 页 | 按编码后的完整 JSON 体计算,同时受上下文限制;网关可用上限可能低于官方值;不支持 Office、音视频或 /v1/files 上传 | | [claude-opus-5-5](/docs/cn/models/claude/claude-opus-5-5) | PNG/JPEG/WebP; PDF | image/document source: base64 + media_type | 完整请求体 <=32 MB;PDF <=600 页,低于 1M 上下文时 <=100 页 | 按编码后的完整 JSON 体计算,同时受上下文限制;网关可用上限可能低于官方值;不支持 Office、音视频或 /v1/files 上传 | | [gemini-3.5-flash](/docs/cn/models/gemini/gemini-3.5-flash) | PNG/JPEG/WebP; PDF; WAV/MP3; MP4/MPEG/MOV/AVI/MPG/WMV | contents[].parts[].inlineData: mimeType + Base64 | 视频 inline 原厂 <100 MB;PDF 原厂 <=1000 页 | 未公布统一文件大小、时长或页数上限,大小按编码后的完整 JSON 体计算;不支持 Files API、其他音视频格式、原生工具结果回传或强制 JSON Schema | | [gemini-3.1-pro-preview](/docs/cn/models/gemini/gemini-3.1-pro-preview) | PNG/JPEG/WebP; PDF; WAV/MP3; MP4/MPEG/MOV/AVI/MPG/WMV | contents[].parts[].inlineData: mimeType + Base64 | 视频 inline 原厂 <100 MB;PDF 原厂 <=1000 页 | 未公布统一文件大小、时长或页数上限,大小按编码后的完整 JSON 体计算;不支持 Files API、其他音视频格式、原生工具结果回传或强制 JSON Schema | | [gemini-3.7-flash](/docs/cn/models/gemini/gemini-3.7-flash) | PNG/JPEG/WebP; PDF; WAV/MP3; MP4/MPEG/MOV/AVI/MPG/WMV | contents[].parts[].inlineData: mimeType + Base64 | 视频 inline 原厂 <100 MB;PDF 原厂 <=1000 页 | 未公布统一文件大小、时长或页数上限,大小按编码后的完整 JSON 体计算;不支持 Files API、其他音视频格式、原生工具结果回传或强制 JSON Schema | | [gemini-3.8-flash](/docs/cn/models/gemini/gemini-3.8-flash) | PNG/JPEG/WebP; PDF; WAV/MP3; MP4/MPEG/MOV/AVI/MPG/WMV | contents[].parts[].inlineData: mimeType + Base64 | 视频 inline 原厂 <100 MB;PDF 原厂 <=1000 页 | 未公布统一文件大小、时长或页数上限,大小按编码后的完整 JSON 体计算;不支持 Files API、其他音视频格式、原生工具结果回传或强制 JSON Schema | | [gemini-3.1-flash-image](/docs/cn/models/gemini/gemini-3.1-flash-image) | PNG/JPEG/WebP 参考图 | text + 独立 inlineData 图片 parts | 参考图数量、尺寸与像素预算依型号;不可套用 GPT Images 上限 | 参考图分别上传;总张数和总大小按型号确认,实际可用范围还受网关限制;不支持函数工具、独立 mask 或通用文件上传 | | [gemini-3-pro-image-preview](/docs/cn/models/gemini/gemini-3-pro-image-preview) | PNG/JPEG/WebP 参考图 | text + 独立 inlineData 图片 parts | 参考图数量、尺寸与像素预算依型号;不可套用 GPT Images 上限 | 参考图分别上传;总张数和总大小按型号确认,实际可用范围还受网关限制;不支持函数工具、独立 mask 或通用文件上传 | | [gemini-nano-banana-2.1](/docs/cn/models/gemini/gemini-nano-banana-2.1) | PNG/JPEG/WebP 参考图 | text + 独立 inlineData 图片 parts | 参考图数量、尺寸与像素预算依型号;不可套用 GPT Images 上限 | 参考图分别上传;总张数和总大小按型号确认,实际可用范围还受网关限制;不支持函数工具、独立 mask 或通用文件上传 | | [gpt-image-2](/docs/cn/models/image/gpt-image-2) | 参考图 PNG/JPEG/WebP;mask PNG Alpha;输出 PNG/JPEG | 生成 JSON;编辑 multipart image / image[] / mask | gpt-image-2: 参考图 1–16 张且 <50 MB/张;mask <4 MB | 总请求需符合网关限制;mask 与首图同宽高,返回图片需解码并核对尺寸;不支持文件理解、视频或 Chat/Responses 生图字段 | | [gpt-image-2.5-flare](/docs/cn/models/image/gpt-image-2.5-flare) | 参考图 PNG/JPEG/WebP;mask PNG Alpha;输出 PNG/JPEG | 生成 JSON;编辑 multipart image / image[] / mask | gpt-image-2: 参考图 1–16 张且 <50 MB/张;mask <4 MB | 总请求需符合网关限制;mask 与首图同宽高,返回图片需解码并核对尺寸;不支持文件理解、视频或 Chat/Responses 生图字段 | | [gpt-image-2.5-sunburst](/docs/cn/models/image/gpt-image-2.5-sunburst) | 参考图 PNG/JPEG/WebP;mask PNG Alpha;输出 PNG/JPEG | 生成 JSON;编辑 multipart image / image[] / mask | gpt-image-2: 参考图 1–16 张且 <50 MB/张;mask <4 MB | 总请求需符合网关限制;mask 与首图同宽高,返回图片需解码并核对尺寸;不支持文件理解、视频或 Chat/Responses 生图字段 | | [mj_imagine](/docs/cn/models/image/mj) | 公开可访问的图片直链,不是 Base64 或本地路径 | JSON text 中的公开图片 URL;专用操作使用 imgUrl | 受版本、URL 可达性和任务操作限制 | 未公布统一附件大小上限;查询到 status=2 后下载图片;不支持 MJ 视频或 OpenAI size/n/quality JSON 字段 | | [youchuan-image](/docs/cn/models/image/youchuan-image) | 公开可访问的图片直链,不是 Base64 或本地路径 | JSON text 中的公开图片 URL;专用操作使用 imgUrl | 受版本、URL 可达性和任务操作限制 | 未公布统一附件大小上限;查询到 status=2 后下载图片;不支持 MJ 视频或 OpenAI size/n/quality JSON 字段 | | [gpt-realtime-2.1](/docs/cn/models/realtime/gpt-realtime-2.1) | 文本;PNG/JPEG 图片;PCM16 LE 单声道 24 kHz | WebSocket input_text / input_image;PCM Base64 分块 | 会话、音频缓冲及响应预算独立限制 | 输入受会话和单帧限制;音频按协商格式分块发送,不直接传入超大文件;不直接接收 WAV/MP3 容器、PDF 或视频文件 | | [gpt-realtime-2.1-mini](/docs/cn/models/realtime/gpt-realtime-2.1-mini) | 文本;PNG/JPEG 图片;PCM16 LE 单声道 24 kHz | WebSocket input_text / input_image;PCM Base64 分块 | 会话、音频缓冲及响应预算独立限制 | 输入受会话和单帧限制;音频按协商格式分块发送,不直接传入超大文件;不直接接收 WAV/MP3 容器、PDF 或视频文件 | | [kimi-k3](/docs/cn/models/other/kimi-k3) | 文本;PNG/JPEG/WebP 图片 data URI | Chat text / image_url 内容块 | 依型号上下文及图片预算 | 未公布统一附件大小上限,仅接收本行所列内容类型;不支持原生 PDF、Office、音频或视频附件 | | [DeepSeek-V4.1-Flash](/docs/cn/models/other/DeepSeek-V4.1-Flash) | 文本;PNG/JPEG/WebP 图片 data URI | Chat text / image_url 内容块 | 依型号上下文及图片预算 | 未公布统一附件大小上限,仅接收本行所列内容类型;不支持原生 PDF、Office、音频或视频附件 | ## 文件处理 文件任务返回后,可用文件内独有的信息、引用位置、画面顺序和业务数据核对读取情况;一次回复未必覆盖全部页面、音轨或每一帧。超大文件可裁剪、压缩或拆分,并保留页码和时间范围。图像编辑会重新生成像素,即使主体得到保留,蒙版外的像素也可能变化。 上下文和输出预算以具体型号及当前服务能力为准,不能套用同系列型号的最大值。表中未列明的最大文件数、媒体时长或文件大小范围,尚未作为统一限额发布。 # Agent 调用约定 网页路径:`/docs/cn/agent-workflows`。 ## 型号与协议 调用前确认模型 ID、Key 和协议,并发送一次目标请求。Agent 应沿用用户指定的模型;失败时报告原因,未经确认不更换型号。 ## 多轮与工具 应用负责保存对话历史,包括原始 assistant / model 消息、工具调用 ID、推理项和签名。工具结果与原调用逐一对应。Chat 使用 tool_call_id,Responses 使用 call_id,Claude 使用 tool_use_id,Gemini 使用 functionResponse,字段格式各不相同。 模型返回工具调用后,由应用校验 JSON Schema、函数名、路径和权限,再执行操作。有副作用的操作还需检查授权。执行结果返回模型后,继续读取最终答复;模型本身不会替应用执行外部动作。 完整程序见 [tools.py](/examples/tools.py)。设置对应型号的 `YUNQIAI_MODEL` 后,运行 `python tools.py responses`、`python tools.py messages` 或 `python tools.py chat`。示例只读取内置订单,不访问外部系统;tool_choice 保持与推理模式兼容。当前 Gemini 接入暂不支持原生工具结果回传,示例不提交 functionResponse,也不改用其他协议。 ## 长文本与结构化输出 上下文长度包含系统指令、消息、文件、图片、推理和工具结果。输出另有预算限制;可输入的文本长度与单次可生成的文本长度不同。 长篇写作可按章节组织,并保存人物设定、已写段落和后续情节。响应以 length / max_tokens / MAX_TOKENS / incomplete 结束时,先保存正文,再按用户需求续写;此时内容尚未完成。 [http-client.py](/examples/http-client.py) 支持 `--prompt-file script.txt --stream --max-output-tokens 16384`,从 UTF-8 文件读取输入并设置输出预算。预算需在所选型号的范围内。程序会检查流式结束状态,遇到截断时报错。生成长度仍需由应用核对,长篇内容还应检查章节是否完整、人物设定是否前后一致。 结构化输出使用对应协议的 JSON Schema 字段。收到响应后,先检查结束状态,再解析 JSON 并在本地校验 Schema。代码围栏、拒绝响应和工具请求都需另行处理,不能直接作为业务 JSON。 | 协议 | JSON Schema 字段 | | --- | --- | | Responses | text.format,type=json_schema,含 name、schema、strict | | Claude Messages | output_config.format,type=json_schema,含 schema;不包含 claude-opus-5-5 | | Gemini | 暂不支持强制 JSON Schema;通过提示词要求 JSON 时,应用仍需解析和校验 | | Chat Completions | response_format.type=json_schema,json_schema 内含 name、schema、strict | 将 [structured.py](/examples/structured.py) 与 [http-client.py](/examples/http-client.py) 放在同一目录,安装 `requests jsonschema`。使用 [result.schema.json](/examples/result.schema.json) 可运行 `python structured.py responses --schema result.schema.json --prompt "按 Schema 返回 code、items 和 ok"`。请按所选型号设置协议。输出无法解析或不符合 Schema 时,程序会报错,保留原响应和模型 ID。 `claude-opus-5-5` 也暂不支持强制 JSON Schema。普通文本任务可以要求返回 JSON,但代码围栏、额外文字和 Schema 不匹配仍需按校验失败处理。本站支持范围以本页为准。 ## 超时、流式和重试 超时分为连接超时、首字节等待、读取超时和任务总时限。解析 SSE 时按空行分帧,同一事件的 UTF-8 和 JSON 可能跨多个 TCP 数据块。除文字增量外,还需处理错误事件和结束事件。 GET 查询可以有限次数退避重试。生成类 POST 超时或断开后,服务端可能已受理,重发前应先确认状态。MJ 收到 id 后继续查询原任务;Realtime 重连前先检查旧工具和音频状态,避免重复执行或重放。 ## 接入检查 1. 请求中没有硬编码 Key,日志不包含 Authorization、Cookie 和 Base64 客户文件。 2. 请求使用指定型号和协议,响应正常结束且包含所需内容。 3. 工具轮次完成调用、校验、执行、回传和最终答复。 4. 文件任务能读取文件独有内容,图像结果符合参考图的对应关系。 5. 流式能区分完成、截断、错误与网络中断。 6. 401、权限、额度、限流、无渠道及格式错误有明确提示,出错后保留原模型 ID。 7. 临时文件、图片 URL、任务 ID、会话连接和密钥均有对应的存储或清理策略。 # OpenAI SDK 网页路径:`/docs/cn/integrations/openai-sdk`。 ## Base URL 与环境变量 Python 的 `base_url` 和 Node.js 的 `baseURL` 均填写 `https://www.yunqiai.chat/v1`。SDK 自动追加 `/responses` 或 `/chat/completions`,不再重复添加 `/v1`。 设置 `YUNQIAI_API_KEY` 和 `YUNQIAI_MODEL` 后即可调用。以下示例使用 Responses,请填写支持该协议的模型 ID。 ```python import os from openai import OpenAI client = OpenAI( api_key=os.environ["YUNQIAI_API_KEY"], base_url="https://www.yunqiai.chat/v1", max_retries=0, timeout=300.0, ) response = client.responses.create( model=os.environ["YUNQIAI_MODEL"], input="仅回复 READY", max_output_tokens=2048, ) if response.status != "completed" or not response.output_text: raise RuntimeError("Response did not contain a completed text answer") print(response.output_text) ``` ```javascript import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.YUNQIAI_API_KEY, baseURL: "https://www.yunqiai.chat/v1", maxRetries: 0, timeout: 300000, }); const response = await client.responses.create({ model: process.env.YUNQIAI_MODEL, input: "仅回复 READY", max_output_tokens: 2048, }); if (response.status !== "completed" || !response.output_text) { throw new Error("Response did not contain a completed text answer"); } console.log(response.output_text); ``` Python 使用 `python -m pip install openai` 安装,Node.js 使用 `npm install openai`。项目应锁定 SDK 版本,升级后检查请求字段和响应解析是否仍兼容。 ## 协议差异 Chat 使用 `client.chat.completions.create`,输入为 `messages`,输出为 choices。Responses 使用 `client.responses.create`,输入为 input,输出为 output。切换协议时,请同时调整请求和响应处理。 示例已关闭 SDK 自动重试,重试逻辑由应用按[错误处理](/docs/cn/errors)控制,以免重复提交生成请求。遇到“连接失败”时,先检查请求 URL、代理、证书和 DNS。 # Claude Code 网页路径:`/docs/cn/integrations/claude-code`。 ## 接入范围 Claude Code 通过 Anthropic Messages 接入第三方 API。模型填写 Key 有权限的完整 Claude ID,此配置不用于 GPT、Gemini 或图片模型。 ## settings.json 配置文件为运行用户的 `~/.claude/settings.json`,Windows 通常位于 `%USERPROFILE%\.claude\settings.json`。将以下 env 字段合并到现有文件,保留其他设置。 ```json { "env": { "ANTHROPIC_BASE_URL": "https://www.yunqiai.chat", "ANTHROPIC_API_KEY": "你的 YunQi API Key", "ANTHROPIC_MODEL": "客户选择的完整 Claude 模型 ID" } } ``` 此配置会以明文保存 Key,请限制文件权限并排除在版本控制之外。也可以在运行环境中设置变量。检查是否残留其他服务的 `ANTHROPIC_AUTH_TOKEN` 或 `ANTHROPIC_API_KEY`,避免客户端读取旧认证。 `ANTHROPIC_BASE_URL` 不带 `/v1` 或 `/messages`。保存后完全退出并重新启动 Claude Code,确认使用的模型。出现登录选项时选择 API Key 方式;YunQi 账户密码仅用于本站控制台。 ## VS Code、WSL 与远程环境 在扩展实际运行的环境中配置用户目录和变量。Windows、WSL、SSH、Dev Container 各有自己的 HOME、代理、证书和 settings.json;Windows 终端的设置不会自动同步到远程环境。 修改后重新加载 VS Code 窗口或重启扩展进程。如配置未生效,检查项目设置、用户设置、启动终端变量及 CC Switch 的覆盖关系。Key 应保存在私有配置或环境变量中,不写入仓库共享的 `.vscode/settings.json`。 ## 调用与诊断 先用目标 Key 和模型发送 `/v1/messages` 请求,再在 Claude Code 中检查对话和一次只读工具调用。这样可以分别确认接口连接与客户端工具处理。 Opus 5.5 等型号的推理和工具限制见[Messages](/docs/cn/api-reference/anthropic-messages)。若客户端发送了不兼容的 thinking 或 tool_choice,需升级或调整客户端配置,保留原模型 ID。 Claude 官方网页不能配置第三方 API base URL。本页适用于 Claude Code / Anthropic SDK,YunQi Key 不用于 Claude 网页登录。 # Codex 网页路径:`/docs/cn/integrations/codex`。 ## 配置位置 Codex CLI 与 Codex 的 VS Code 扩展使用运行环境中的 Codex 配置。默认目录为 `~/.codex`,设置了 `CODEX_HOME` 时以它为准。WSL、远程主机和容器需要在各自环境配置。 ## config.toml 将以下服务商配置合并到现有文件,保留项目与安全设置。`model` 填写要调用的完整型号,该型号需支持 Responses 及业务所需的工具能力。 ```toml model_provider = "yunqi" model = "客户选择的完整 GPT 模型 ID" [model_providers.yunqi] name = "YunQi AI" base_url = "https://www.yunqiai.chat/v1" wire_api = "responses" env_key = "YUNQIAI_API_KEY" ``` 在启动 Codex 的进程环境中设置 `YUNQIAI_API_KEY`,无需将 Key 写入 TOML。此自定义 provider 使用 API Key,不启用仅适用于官方登录的认证选项。修改服务商配置时保留原有 auth.json。 `model_reasoning_effort` 的可用值见型号页。`gpt-6-astra` 不支持 none / minimal;省略该设置时,服务和客户端使用默认推理配置。 ## VS Code 在扩展实际运行的环境中设置用户目录和环境变量,重启后确认 provider、model 和请求 URL。已打开的 VS Code 通常不会继承外部终端后来设置的变量。 若同时使用 CC Switch,请检查其写入的文件是否覆盖了手工设置。认证失败时,确认扩展进程能读取 env_key 指定的变量;排查日志和截图中需隐藏密钥。 ## 检查接入 先发送一次 Responses 文本请求,再检查一次文件或目录的只读工具调用。若文本正常、工具失败,请保留脱敏后的请求结构和错误,核对客户端的工具字段。此配置的 wire_api 保持 responses。 字段定义见 [Codex 官方配置参考](https://developers.openai.com/codex/config-reference/),YunQi 地址和模型 ID 按本页填写。 # CC Switch 网页路径:`/docs/cn/integrations/cc-switch`。 ## Claude Code 服务商 | 字段 | 值 | | --- | --- | | 应用 | Claude Code | | API 格式 | Anthropic Messages(原生) | | Base URL | `https://www.yunqiai.chat` | | 认证字段 | `ANTHROPIC_API_KEY` | | 密钥 | 有目标 Claude 型号权限的 YunQi API Key | | 模型 | 要调用的完整 Claude ID | 保存并启用服务商,然后重启 Claude Code 或 VS Code 扩展。此处的 Base URL 使用根地址,不带 OpenAI 地址中的 `/v1`,也不填写完整的 `/v1/messages` 路径。密钥填写 API Key,而非账户密码。 ## Codex 服务商 | 字段 | 值 | | --- | --- | | 应用 | Codex | | Base URL | `https://www.yunqiai.chat/v1` | | Wire API | responses | | 密钥 | 有目标 GPT 型号权限的 YunQi API Key | | 模型 | 要调用的完整 GPT ID | 不同版本的字段名称可能有变化,保存后可检查 CC Switch 写入的 config.toml 和认证配置,具体见[Codex 配置](/docs/cn/integrations/codex)。 ## 测速失败 测速可能仅访问根路径或发送客户端预设请求,无法覆盖模型生成过程。排查时分别确认地址可连接、Key 鉴权通过,以及目标模型能完成请求。 出现“error sending request / DNS / TLS / timeout” 时,先检查连接配置。核对完整 URL,域名应为 `www.yunqiai.chat`,结尾有 `t`。浏览器能打开但客户端失败时,继续检查客户端代理、系统代理、DNS、IPv6、证书链和证书吊销查询。 IP 地址也可以提供 HTTP / HTTPS 服务。HTTPS 通常需要证书和 SNI 与域名匹配,因此 IP 能访问时仍需检查域名证书。正式配置应保留 HTTPS、证书验证和吊销检查。 ## 配置覆盖 CC Switch、手工编辑、项目设置和进程环境变量可能互相覆盖。请检查当前启用的服务商及配置文件,保存后重启客户端。若 HTTP 直接请求成功而应用失败,对比两者的请求路径、认证头、模型名和代理环境。 # 通用客户端 网页路径:`/docs/cn/integrations/chatbox`。 ## 自定义服务商 Chatbox、Cherry Studio、WorkBuddy 等客户端可用的功能取决于应用版本和协议。添加自定义服务商时,填写协议、基础地址、Key 和完整模型 ID,并核对客户端是否改写了模型名。 | 协议 | 通常填写的基础地址 | 结果接口 | | --- | --- | --- | | OpenAI Compatible | `https://www.yunqiai.chat/v1` | 客户端需支持目标 Chat 或 Responses 接口 | | Anthropic 原生 | `https://www.yunqiai.chat` | /v1/messages | | Gemini 原生 | `https://www.yunqiai.chat`,依客户端拼接规则 | /v1beta/models/{model}:generateContent | 各应用对“API 地址”的定义不同,可能要求完整端点,也可能只要求根地址。保存后检查最终 URL,常见错误包括 `/v1/v1`、`/responses/chat/completions` 和缺少版本路径。 ## 文件与图片 上传附件前,确认模型支持相应格式,且客户端会按协议发送图片、PDF 或媒体内容块。仅将路径或 Base64 作为普通文字发送,无法完成文件上传。 部分聊天客户端不支持 Images multipart 编辑、MJ 任务轮询或 Realtime 音频事件。此时需使用文档中的接口程序,或在应用中实现相应协议;在模型列表中添加名称并不会补齐这些功能。 ## 保存后的检查 确认服务商已启用,模型 ID、Key、代理和证书设置正确。然后分别检查文本请求、流式输出,以及业务需要的文件或工具调用。测速仅供连接排查使用。 # 错误与连接排查 网页路径:`/docs/cn/errors`。 ## HTTP 错误 | 状态 | 处理 | | --- | --- | | 400 / 422 | 检查字段、枚举、协议、内容格式和型号约束;修改后再提交,不重复发送原错误请求 | | 401 | 核对 Key、认证头、失效状态和客户端覆盖配置 | | 403 | 查看错误正文,确认权限、策略、额度或预扣原因 | | 404 | 核对接口路径和任务 ID,路径错误也会返回此状态 | | 413 | 请求体过大;压缩、拆分并核对 Base64 膨胀后的总大小 | | 429 | 区分速率、并发和额度;遵守 Retry-After,限制重试次数 | | 500 / 502 / 503 / 504 | 保留 request ID 与正文;查询可退避重试,生成 POST 避免重复提交 | 排查时保留 HTTP 状态、Content-Type、request ID 和脱敏后的错误正文。正文可能是 JSON、网关 HTML 或纯文本;`.json()` 解析失败时仍应保留原始状态和正文。 ## 协议内错误 HTTP 200 表示接口返回了响应,业务结果还需读取协议字段:Responses 的 failed / incomplete、Chat 的 finish_reason、Claude 的 stop_reason、Gemini 的 finishReason、MJ 的 status,以及 Realtime 的 response.done。SSE / WebSocket 的 error 事件按错误处理。 诊断信息包括 `error.message`、`error.type`、`error.code`、嵌套错误和平台 message 字段。写日志前删除其中的密钥、签名 URL、文件正文和个人信息。 ## 网络与证书 DNS 解析、TCP 连接、TLS 握手、HTTP 响应和模型生成是不同阶段。浏览器、终端、CC Switch、VS Code、WSL 可能各用不同代理与证书库。 Windows 上的 Python requests 可能读取系统代理。遇到 ProxyError 时检查代理地址、证书路径和 NO_PROXY。若网络策略允许,可为目标域名配置直连,TLS 验证保持开启。 有效期内的证书也可能出现证书吊销、证书链缺失、域名不匹配、本机时间错误或吊销服务不可达。请按具体错误排查,保留 HTTPS 校验,不向未确认的 IP 或 HTTP 地址发送 Key。 Gemini 返回 thought signature 错误时,检查 model parts、签名和消息顺序是否完整,并保留 request ID。签名应原样回传;完整历史仍失败时,请联系平台排查会话兼容性。也可以由应用另行发起新的参考图编辑,上传上一轮成图,但这会创建新请求,并非恢复原对话。 ## 重试原则 无副作用的 GET 可有限次数退避重试,明确的参数错误需修改后再提交。生成类 POST 超时后可能已经受理,直接重发可能重复生成。MJ 已收到 id 时继续查询原任务;Realtime 重连前先处理旧工具和音频状态。 向平台反馈时,请附发生时间、模型 ID、路径、脱敏 request ID、HTTP 状态和最小复现请求,并移除完整 Authorization、API Key、Cookie 及私有文件。 # 常见问题 网页路径:`/docs/cn/faqs`。 ## 可以直接把文档地址交给 Agent 吗 可以。`/llms-full.txt` 和 `/yunqi-agent-guide.md` 包含完整文档,`/models.json` 提供模型 ID 和协议。另行指定要使用的模型,并将 Key 配置在 Agent 的安全运行环境中即可。 ## 模型列表里有,为什么请求仍然失败 模型列表只列目录和权限。调用时还会检查渠道、预算、并发、字段和输入格式,具体原因见错误正文。出错后保留原模型 ID 排查。 ## Base URL 要不要带 /v1 OpenAI SDK 和 Codex 带 `/v1`,Anthropic SDK 和 Claude Code 不带。原生 Gemini 与 Realtime 使用各自路径,具体见[快速开始](/docs/cn/quickstart)的地址表。 ## 参考图是原生图生图吗 是。Images edits 通过 image / image[] 上传文件,Gemini 通过 inlineData 传入图片,MJ 使用图片 URL 语法。Base64 只是图片的编码方式,应放入协议规定的图片字段,而非普通提示词。 ## 多张参考图能否单独引用 可以,分别上传后在提示词中说明顺序或各图用途。Images 的 mask 作用于第一张图,Gemini 每张图使用独立 part,MJ 的普通垫图、sref、cref、oref 则按版本使用。各协议的参数格式不同。 ## 能否上传大视频或任意文件 Gemini 文本多模态型号支持本版列出的内联媒体格式。大文件受编码体积和上下文限制,通用 Files API 不在支持范围内。Word、Excel 和压缩包的处理方式见[文件与媒体](/docs/cn/media)。 ## 下线型号怎么办 `gpt-image-2-each` 已从可调用目录和示例中移除。`glm-5.2` 已灰度下线,如有需要请联系商务。需要更换型号时,请重新指定完整模型 ID。 # gpt-5.6-sol 网页路径:`/docs/cn/models/gpt/gpt-5.6-sol`。 ## 型号与端点 模型 ID:`gpt-5.6-sol`,请求中使用完整名称。 协议:`responses`。端点:`POST /v1/responses`。 输入:`text`、`image`、`pdf`、`docx`、`xlsx`、`txt`、`csv`、`json`。输出:`text`、`function_call`。 认证、完整字段和响应格式见 [responses](/docs/cn/api-reference/responses)。调用前请确认 Key 已开通此型号。 ## 请求示例 ```json { "model": "gpt-5.6-sol", "input": "仅回复 READY", "reasoning": { "effort": "low" }, "max_output_tokens": 4096 } ``` 以上为请求体示例,地址和请求头见协议页。其他可选字段请查阅对应参数说明。 ## 参数与输入 工具和文件调用使用 Responses。多轮请求需保留完整 output,包括推理项和函数调用;文本从消息内容中读取。 reasoning.effort 按当前型号及推理模式设置,上例使用 low。采样、推理和工具参数的可用值以本型号说明为准。 推理字段 `reasoning.effort` 支持:`none`、`low`、`medium`、`high`、`xhigh`、`max`。省略时使用默认设置,输出预算另行配置。[原厂字段说明](https://developers.openai.com/api/docs/guides/latest-model)与[型号参数](/docs/cn/api-reference/model-parameters)。 附件格式:PNG/JPEG/WebP; PDF/DOCX/XLSX/TXT/CSV/JSON。编码:input_image data URI / input_file data URI + filename。 官方限制:文件 <50 MB/个;文件总计 <=50 MB/请求。 上传说明:未公布统一文件大小上限,请同时检查 Base64 编码后的请求体、上下文和超时限制。其他限制:不支持原生音视频、ZIP 或持久 Files 存储。完整规格见[模型输入规格表](/docs/cn/input-matrix)。 ## 响应处理 检查 status 和 output。completed 表示响应完成;function_call 需交给应用执行,incomplete、拒绝或空正文需单独处理。 文件大小及格式要求见[文件与媒体输入](/docs/cn/media)。 # gpt-5.6-terra 网页路径:`/docs/cn/models/gpt/gpt-5.6-terra`。 ## 型号与端点 模型 ID:`gpt-5.6-terra`,请求中使用完整名称。 协议:`responses`。端点:`POST /v1/responses`。 输入:`text`、`image`、`pdf`、`docx`、`xlsx`、`txt`、`csv`、`json`。输出:`text`、`function_call`。 认证、完整字段和响应格式见 [responses](/docs/cn/api-reference/responses)。调用前请确认 Key 已开通此型号。 ## 请求示例 ```json { "model": "gpt-5.6-terra", "input": "仅回复 READY", "reasoning": { "effort": "low" }, "max_output_tokens": 4096 } ``` 以上为请求体示例,地址和请求头见协议页。其他可选字段请查阅对应参数说明。 ## 参数与输入 工具和文件调用使用 Responses。多轮请求需保留完整 output,包括推理项和函数调用;文本从消息内容中读取。 reasoning.effort 按当前型号及推理模式设置,上例使用 low。采样、推理和工具参数的可用值以本型号说明为准。 推理字段 `reasoning.effort` 支持:`none`、`low`、`medium`、`high`、`xhigh`、`max`。省略时使用默认设置,输出预算另行配置。[原厂字段说明](https://developers.openai.com/api/docs/guides/latest-model)与[型号参数](/docs/cn/api-reference/model-parameters)。 附件格式:PNG/JPEG/WebP; PDF/DOCX/XLSX/TXT/CSV/JSON。编码:input_image data URI / input_file data URI + filename。 官方限制:文件 <50 MB/个;文件总计 <=50 MB/请求。 上传说明:未公布统一文件大小上限,请同时检查 Base64 编码后的请求体、上下文和超时限制。其他限制:不支持原生音视频、ZIP 或持久 Files 存储。完整规格见[模型输入规格表](/docs/cn/input-matrix)。 ## 响应处理 检查 status 和 output。completed 表示响应完成;function_call 需交给应用执行,incomplete、拒绝或空正文需单独处理。 文件大小及格式要求见[文件与媒体输入](/docs/cn/media)。 # gpt-5.6-luna 网页路径:`/docs/cn/models/gpt/gpt-5.6-luna`。 ## 型号与端点 模型 ID:`gpt-5.6-luna`,请求中使用完整名称。 协议:`responses`。端点:`POST /v1/responses`。 输入:`text`、`image`、`pdf`、`docx`、`xlsx`、`txt`、`csv`、`json`。输出:`text`、`function_call`。 认证、完整字段和响应格式见 [responses](/docs/cn/api-reference/responses)。调用前请确认 Key 已开通此型号。 ## 请求示例 ```json { "model": "gpt-5.6-luna", "input": "仅回复 READY", "reasoning": { "effort": "low" }, "max_output_tokens": 4096 } ``` 以上为请求体示例,地址和请求头见协议页。其他可选字段请查阅对应参数说明。 ## 参数与输入 工具和文件调用使用 Responses。多轮请求需保留完整 output,包括推理项和函数调用;文本从消息内容中读取。 reasoning.effort 按当前型号及推理模式设置,上例使用 low。采样、推理和工具参数的可用值以本型号说明为准。 推理字段 `reasoning.effort` 支持:`none`、`low`、`medium`、`high`、`xhigh`、`max`。省略时使用默认设置,输出预算另行配置。[原厂字段说明](https://developers.openai.com/api/docs/guides/latest-model)与[型号参数](/docs/cn/api-reference/model-parameters)。 附件格式:PNG/JPEG/WebP; PDF/DOCX/XLSX/TXT/CSV/JSON。编码:input_image data URI / input_file data URI + filename。 官方限制:文件 <50 MB/个;文件总计 <=50 MB/请求。 上传说明:未公布统一文件大小上限,请同时检查 Base64 编码后的请求体、上下文和超时限制。其他限制:不支持原生音视频、ZIP 或持久 Files 存储。完整规格见[模型输入规格表](/docs/cn/input-matrix)。 ## 响应处理 检查 status 和 output。completed 表示响应完成;function_call 需交给应用执行,incomplete、拒绝或空正文需单独处理。 文件大小及格式要求见[文件与媒体输入](/docs/cn/media)。 # gpt-6-astra 网页路径:`/docs/cn/models/gpt/gpt-6-astra`。 ## 型号与端点 模型 ID:`gpt-6-astra`,请求中使用完整名称。 协议:`responses`。端点:`POST /v1/responses`。 输入:`text`、`image`、`pdf`、`docx`、`xlsx`、`txt`、`csv`、`json`。输出:`text`、`function_call`。 认证、完整字段和响应格式见 [responses](/docs/cn/api-reference/responses)。调用前请确认 Key 已开通此型号。 ## 请求示例 ```json { "model": "gpt-6-astra", "input": "仅回复 READY", "reasoning": { "effort": "low" }, "max_output_tokens": 4096 } ``` 以上为请求体示例,地址和请求头见协议页。其他可选字段请查阅对应参数说明。 ## 参数与输入 工具和文件调用使用 Responses。多轮请求需保留完整 output,包括推理项和函数调用;文本从消息内容中读取。 reasoning.effort 支持 low、medium、high、xhigh、max,不支持 none / minimal。Chat 接口仅兼容文本,工具调用使用 Responses。请求不发送 temperature、top_p 或 top_logprobs。 推理字段 `reasoning.effort` 支持:`low`、`medium`、`high`、`xhigh`、`max`。省略时使用默认设置,输出预算另行配置。[原厂字段说明](https://developers.openai.com/api/docs/guides/latest-model)与[型号参数](/docs/cn/api-reference/model-parameters)。 附件格式:PNG/JPEG/WebP; PDF/DOCX/XLSX/TXT/CSV/JSON。编码:input_image data URI / input_file data URI + filename。 官方限制:文件 <50 MB/个;文件总计 <=50 MB/请求。 上传说明:未公布统一文件大小上限,请同时检查 Base64 编码后的请求体、上下文和超时限制。其他限制:不支持原生音视频、ZIP 或持久 Files 存储。完整规格见[模型输入规格表](/docs/cn/input-matrix)。 ## 响应处理 检查 status 和 output。completed 表示响应完成;function_call 需交给应用执行,incomplete、拒绝或空正文需单独处理。 文件大小及格式要求见[文件与媒体输入](/docs/cn/media)。 # gpt-6-sol 网页路径:`/docs/cn/models/gpt/gpt-6-sol`。 ## 型号与端点 模型 ID:`gpt-6-sol`,请求中使用完整名称。 协议:`responses`。端点:`POST /v1/responses`。 输入:`text`、`image`、`pdf`、`docx`、`xlsx`、`txt`、`csv`、`json`。输出:`text`、`function_call`。 认证、完整字段和响应格式见 [responses](/docs/cn/api-reference/responses)。调用前请确认 Key 已开通此型号。 ## 请求示例 ```json { "model": "gpt-6-sol", "input": "仅回复 READY", "reasoning": { "effort": "low" }, "max_output_tokens": 4096 } ``` 以上为请求体示例,地址和请求头见协议页。其他可选字段请查阅对应参数说明。 ## 参数与输入 工具和文件调用使用 Responses。多轮请求需保留完整 output,包括推理项和函数调用;文本从消息内容中读取。 reasoning.effort 按当前型号及推理模式设置,上例使用 low。采样、推理和工具参数的可用值以本型号说明为准。 推理字段 `reasoning.effort` 支持:`none`、`low`、`medium`、`high`、`xhigh`、`max`。省略时使用默认设置,输出预算另行配置。[原厂字段说明](https://developers.openai.com/api/docs/guides/latest-model)与[型号参数](/docs/cn/api-reference/model-parameters)。 附件格式:PNG/JPEG/WebP; PDF/DOCX/XLSX/TXT/CSV/JSON。编码:input_image data URI / input_file data URI + filename。 官方限制:文件 <50 MB/个;文件总计 <=50 MB/请求。 上传说明:未公布统一文件大小上限,请同时检查 Base64 编码后的请求体、上下文和超时限制。其他限制:不支持原生音视频、ZIP 或持久 Files 存储。完整规格见[模型输入规格表](/docs/cn/input-matrix)。 ## 响应处理 检查 status 和 output。completed 表示响应完成;function_call 需交给应用执行,incomplete、拒绝或空正文需单独处理。 文件大小及格式要求见[文件与媒体输入](/docs/cn/media)。 # gpt-6-luna 网页路径:`/docs/cn/models/gpt/gpt-6-luna`。 ## 型号与端点 模型 ID:`gpt-6-luna`,请求中使用完整名称。 协议:`responses`。端点:`POST /v1/responses`。 输入:`text`、`image`、`pdf`、`docx`、`xlsx`、`txt`、`csv`、`json`。输出:`text`、`function_call`。 认证、完整字段和响应格式见 [responses](/docs/cn/api-reference/responses)。调用前请确认 Key 已开通此型号。 ## 请求示例 ```json { "model": "gpt-6-luna", "input": "仅回复 READY", "reasoning": { "effort": "low" }, "max_output_tokens": 4096 } ``` 以上为请求体示例,地址和请求头见协议页。其他可选字段请查阅对应参数说明。 ## 参数与输入 工具和文件调用使用 Responses。多轮请求需保留完整 output,包括推理项和函数调用;文本从消息内容中读取。 reasoning.effort 按当前型号及推理模式设置,上例使用 low。采样、推理和工具参数的可用值以本型号说明为准。 推理字段 `reasoning.effort` 支持:`none`、`low`、`medium`、`high`、`xhigh`、`max`。省略时使用默认设置,输出预算另行配置。[原厂字段说明](https://developers.openai.com/api/docs/guides/latest-model)与[型号参数](/docs/cn/api-reference/model-parameters)。 附件格式:PNG/JPEG/WebP; PDF/DOCX/XLSX/TXT/CSV/JSON。编码:input_image data URI / input_file data URI + filename。 官方限制:文件 <50 MB/个;文件总计 <=50 MB/请求。 上传说明:未公布统一文件大小上限,请同时检查 Base64 编码后的请求体、上下文和超时限制。其他限制:不支持原生音视频、ZIP 或持久 Files 存储。完整规格见[模型输入规格表](/docs/cn/input-matrix)。 ## 响应处理 检查 status 和 output。completed 表示响应完成;function_call 需交给应用执行,incomplete、拒绝或空正文需单独处理。 文件大小及格式要求见[文件与媒体输入](/docs/cn/media)。 # claude-sonnet-4-6 网页路径:`/docs/cn/models/claude/claude-sonnet-4-6`。 ## 型号与端点 模型 ID:`claude-sonnet-4-6`,请求中使用完整名称。 协议:`anthropic-messages`。端点:`POST /v1/messages`。 输入:`text`、`image`、`pdf`。输出:`text`、`tool_use`。 认证、完整字段和响应格式见 [anthropic-messages](/docs/cn/api-reference/anthropic-messages)。调用前请确认 Key 已开通此型号。 ## 请求示例 ```json { "model": "claude-sonnet-4-6", "max_tokens": 8192, "messages": [ { "role": "user", "content": "仅回复 READY" } ] } ``` 以上为请求体示例,地址和请求头见协议页。其他可选字段请查阅对应参数说明。 ## 参数与输入 图片使用 image.source,PDF 使用 document.source。工具调用后回传完整 content,保留 thinking 和签名。max_tokens 同时包含推理和正文,预算耗尽时可能截断或没有正文。 推理字段 `output_config.effort` 支持:`low`、`medium`、`high`、`max`。省略时使用默认设置,输出预算另行配置。[原厂字段说明](https://platform.claude.com/docs/en/build-with-claude/effort)与[型号参数](/docs/cn/api-reference/model-parameters)。 附件格式:PNG/JPEG/WebP; PDF。编码:image/document source: base64 + media_type。 官方限制:完整请求体 <=32 MB;PDF <=600 页,低于 1M 上下文时 <=100 页。 上传说明:按编码后的完整 JSON 体计算,同时受上下文限制;网关可用上限可能低于官方值。其他限制:不支持 Office、音视频或 /v1/files 上传。完整规格见[模型输入规格表](/docs/cn/input-matrix)。 ## 响应处理 结合 stop_reason 与 content 处理结果。tool_use 需执行工具后继续请求,max_tokens、refusal 或空正文不能作为完整答复。 文件大小及格式要求见[文件与媒体输入](/docs/cn/media)。 # claude-opus-4-6 网页路径:`/docs/cn/models/claude/claude-opus-4-6`。 ## 型号与端点 模型 ID:`claude-opus-4-6`,请求中使用完整名称。 协议:`anthropic-messages`。端点:`POST /v1/messages`。 输入:`text`、`image`、`pdf`。输出:`text`、`tool_use`。 认证、完整字段和响应格式见 [anthropic-messages](/docs/cn/api-reference/anthropic-messages)。调用前请确认 Key 已开通此型号。 ## 请求示例 ```json { "model": "claude-opus-4-6", "max_tokens": 8192, "messages": [ { "role": "user", "content": "仅回复 READY" } ] } ``` 以上为请求体示例,地址和请求头见协议页。其他可选字段请查阅对应参数说明。 ## 参数与输入 图片使用 image.source,PDF 使用 document.source。工具调用后回传完整 content,保留 thinking 和签名。max_tokens 同时包含推理和正文,预算耗尽时可能截断或没有正文。 推理字段 `output_config.effort` 支持:`low`、`medium`、`high`、`max`。省略时使用默认设置,输出预算另行配置。[原厂字段说明](https://platform.claude.com/docs/en/build-with-claude/effort)与[型号参数](/docs/cn/api-reference/model-parameters)。 附件格式:PNG/JPEG/WebP; PDF。编码:image/document source: base64 + media_type。 官方限制:完整请求体 <=32 MB;PDF <=600 页,低于 1M 上下文时 <=100 页。 上传说明:按编码后的完整 JSON 体计算,同时受上下文限制;网关可用上限可能低于官方值。其他限制:不支持 Office、音视频或 /v1/files 上传。完整规格见[模型输入规格表](/docs/cn/input-matrix)。 ## 响应处理 结合 stop_reason 与 content 处理结果。tool_use 需执行工具后继续请求,max_tokens、refusal 或空正文不能作为完整答复。 文件大小及格式要求见[文件与媒体输入](/docs/cn/media)。 # claude-opus-4-7 网页路径:`/docs/cn/models/claude/claude-opus-4-7`。 ## 型号与端点 模型 ID:`claude-opus-4-7`,请求中使用完整名称。 协议:`anthropic-messages`。端点:`POST /v1/messages`。 输入:`text`、`image`、`pdf`。输出:`text`、`tool_use`。 认证、完整字段和响应格式见 [anthropic-messages](/docs/cn/api-reference/anthropic-messages)。调用前请确认 Key 已开通此型号。 ## 请求示例 ```json { "model": "claude-opus-4-7", "max_tokens": 8192, "messages": [ { "role": "user", "content": "仅回复 READY" } ] } ``` 以上为请求体示例,地址和请求头见协议页。其他可选字段请查阅对应参数说明。 ## 参数与输入 图片使用 image.source,PDF 使用 document.source。工具调用后回传完整 content,保留 thinking 和签名。max_tokens 同时包含推理和正文,预算耗尽时可能截断或没有正文。 推理字段 `output_config.effort` 支持:`low`、`medium`、`high`、`xhigh`、`max`。省略时使用默认设置,输出预算另行配置。[原厂字段说明](https://platform.claude.com/docs/en/build-with-claude/effort)与[型号参数](/docs/cn/api-reference/model-parameters)。 附件格式:PNG/JPEG/WebP; PDF。编码:image/document source: base64 + media_type。 官方限制:完整请求体 <=32 MB;PDF <=600 页,低于 1M 上下文时 <=100 页。 上传说明:按编码后的完整 JSON 体计算,同时受上下文限制;网关可用上限可能低于官方值。其他限制:不支持 Office、音视频或 /v1/files 上传。完整规格见[模型输入规格表](/docs/cn/input-matrix)。 ## 响应处理 结合 stop_reason 与 content 处理结果。tool_use 需执行工具后继续请求,max_tokens、refusal 或空正文不能作为完整答复。 文件大小及格式要求见[文件与媒体输入](/docs/cn/media)。 # claude-opus-4-8 网页路径:`/docs/cn/models/claude/claude-opus-4-8`。 ## 型号与端点 模型 ID:`claude-opus-4-8`,请求中使用完整名称。 协议:`anthropic-messages`。端点:`POST /v1/messages`。 输入:`text`、`image`、`pdf`。输出:`text`、`tool_use`。 认证、完整字段和响应格式见 [anthropic-messages](/docs/cn/api-reference/anthropic-messages)。调用前请确认 Key 已开通此型号。 ## 请求示例 ```json { "model": "claude-opus-4-8", "max_tokens": 8192, "messages": [ { "role": "user", "content": "仅回复 READY" } ] } ``` 以上为请求体示例,地址和请求头见协议页。其他可选字段请查阅对应参数说明。 ## 参数与输入 图片使用 image.source,PDF 使用 document.source。工具调用后回传完整 content,保留 thinking 和签名。max_tokens 同时包含推理和正文,预算耗尽时可能截断或没有正文。 推理字段 `output_config.effort` 支持:`low`、`medium`、`high`、`xhigh`、`max`。省略时使用默认设置,输出预算另行配置。[原厂字段说明](https://platform.claude.com/docs/en/build-with-claude/effort)与[型号参数](/docs/cn/api-reference/model-parameters)。 附件格式:PNG/JPEG/WebP; PDF。编码:image/document source: base64 + media_type。 官方限制:完整请求体 <=32 MB;PDF <=600 页,低于 1M 上下文时 <=100 页。 上传说明:按编码后的完整 JSON 体计算,同时受上下文限制;网关可用上限可能低于官方值。其他限制:不支持 Office、音视频或 /v1/files 上传。完整规格见[模型输入规格表](/docs/cn/input-matrix)。 ## 响应处理 结合 stop_reason 与 content 处理结果。tool_use 需执行工具后继续请求,max_tokens、refusal 或空正文不能作为完整答复。 文件大小及格式要求见[文件与媒体输入](/docs/cn/media)。 # claude-sonnet-5 网页路径:`/docs/cn/models/claude/claude-sonnet-5`。 ## 型号与端点 模型 ID:`claude-sonnet-5`,请求中使用完整名称。 协议:`anthropic-messages`。端点:`POST /v1/messages`。 输入:`text`、`image`、`pdf`。输出:`text`、`tool_use`。 认证、完整字段和响应格式见 [anthropic-messages](/docs/cn/api-reference/anthropic-messages)。调用前请确认 Key 已开通此型号。 ## 请求示例 ```json { "model": "claude-sonnet-5", "max_tokens": 8192, "messages": [ { "role": "user", "content": "仅回复 READY" } ] } ``` 以上为请求体示例,地址和请求头见协议页。其他可选字段请查阅对应参数说明。 ## 参数与输入 图片使用 image.source,PDF 使用 document.source。工具调用后回传完整 content,保留 thinking 和签名。max_tokens 同时包含推理和正文,预算耗尽时可能截断或没有正文。 推理字段 `output_config.effort` 支持:`low`、`medium`、`high`、`xhigh`、`max`。省略时使用默认设置,输出预算另行配置。[原厂字段说明](https://platform.claude.com/docs/en/build-with-claude/effort)与[型号参数](/docs/cn/api-reference/model-parameters)。 附件格式:PNG/JPEG/WebP; PDF。编码:image/document source: base64 + media_type。 官方限制:完整请求体 <=32 MB;PDF <=600 页,低于 1M 上下文时 <=100 页。 上传说明:按编码后的完整 JSON 体计算,同时受上下文限制;网关可用上限可能低于官方值。其他限制:不支持 Office、音视频或 /v1/files 上传。完整规格见[模型输入规格表](/docs/cn/input-matrix)。 ## 响应处理 结合 stop_reason 与 content 处理结果。tool_use 需执行工具后继续请求,max_tokens、refusal 或空正文不能作为完整答复。 文件大小及格式要求见[文件与媒体输入](/docs/cn/media)。 # claude-fable-5 网页路径:`/docs/cn/models/claude/claude-fable-5`。 ## 型号与端点 模型 ID:`claude-fable-5`,请求中使用完整名称。 协议:`anthropic-messages`。端点:`POST /v1/messages`。 输入:`text`、`image`、`pdf`。输出:`text`、`tool_use`。 认证、完整字段和响应格式见 [anthropic-messages](/docs/cn/api-reference/anthropic-messages)。调用前请确认 Key 已开通此型号。 ## 请求示例 ```json { "model": "claude-fable-5", "max_tokens": 8192, "messages": [ { "role": "user", "content": "仅回复 READY" } ] } ``` 以上为请求体示例,地址和请求头见协议页。其他可选字段请查阅对应参数说明。 ## 参数与输入 图片使用 image.source,PDF 使用 document.source。工具调用后回传完整 content,保留 thinking 和签名。max_tokens 同时包含推理和正文,预算耗尽时可能截断或没有正文。 不支持旧式关闭推理配置。thinking 文本可能为空,是否返回可见思考文本与是否进行推理不同。 推理字段 `output_config.effort` 支持:`low`、`medium`、`high`、`xhigh`、`max`。省略时使用默认设置,输出预算另行配置。[原厂字段说明](https://platform.claude.com/docs/en/build-with-claude/effort)与[型号参数](/docs/cn/api-reference/model-parameters)。 附件格式:PNG/JPEG/WebP; PDF。编码:image/document source: base64 + media_type。 官方限制:完整请求体 <=32 MB;PDF <=600 页,低于 1M 上下文时 <=100 页。 上传说明:按编码后的完整 JSON 体计算,同时受上下文限制;网关可用上限可能低于官方值。其他限制:不支持 Office、音视频或 /v1/files 上传。完整规格见[模型输入规格表](/docs/cn/input-matrix)。 ## 响应处理 结合 stop_reason 与 content 处理结果。tool_use 需执行工具后继续请求,max_tokens、refusal 或空正文不能作为完整答复。 文件大小及格式要求见[文件与媒体输入](/docs/cn/media)。 # claude-opus-5 网页路径:`/docs/cn/models/claude/claude-opus-5`。 ## 型号与端点 模型 ID:`claude-opus-5`,请求中使用完整名称。 协议:`anthropic-messages`。端点:`POST /v1/messages`。 输入:`text`、`image`、`pdf`。输出:`text`、`tool_use`。 认证、完整字段和响应格式见 [anthropic-messages](/docs/cn/api-reference/anthropic-messages)。调用前请确认 Key 已开通此型号。 ## 请求示例 ```json { "model": "claude-opus-5", "max_tokens": 8192, "messages": [ { "role": "user", "content": "仅回复 READY" } ] } ``` 以上为请求体示例,地址和请求头见协议页。其他可选字段请查阅对应参数说明。 ## 参数与输入 图片使用 image.source,PDF 使用 document.source。工具调用后回传完整 content,保留 thinking 和签名。max_tokens 同时包含推理和正文,预算耗尽时可能截断或没有正文。 推理字段 `output_config.effort` 支持:`low`、`medium`、`high`、`xhigh`、`max`。省略时使用默认设置,输出预算另行配置。[原厂字段说明](https://platform.claude.com/docs/en/build-with-claude/effort)与[型号参数](/docs/cn/api-reference/model-parameters)。 附件格式:PNG/JPEG/WebP; PDF。编码:image/document source: base64 + media_type。 官方限制:完整请求体 <=32 MB;PDF <=600 页,低于 1M 上下文时 <=100 页。 上传说明:按编码后的完整 JSON 体计算,同时受上下文限制;网关可用上限可能低于官方值。其他限制:不支持 Office、音视频或 /v1/files 上传。完整规格见[模型输入规格表](/docs/cn/input-matrix)。 ## 响应处理 结合 stop_reason 与 content 处理结果。tool_use 需执行工具后继续请求,max_tokens、refusal 或空正文不能作为完整答复。 文件大小及格式要求见[文件与媒体输入](/docs/cn/media)。 # claude-opus-5-5 网页路径:`/docs/cn/models/claude/claude-opus-5-5`。 ## 型号与端点 模型 ID:`claude-opus-5-5`,请求中使用完整名称。 协议:`anthropic-messages`。端点:`POST /v1/messages`。 输入:`text`、`image`、`pdf`。输出:`text`、`tool_use`。 认证、完整字段和响应格式见 [anthropic-messages](/docs/cn/api-reference/anthropic-messages)。调用前请确认 Key 已开通此型号。 ## 请求示例 ```json { "model": "claude-opus-5-5", "max_tokens": 8192, "messages": [ { "role": "user", "content": "仅回复 READY" } ] } ``` 以上为请求体示例,地址和请求头见协议页。其他可选字段请查阅对应参数说明。 ## 参数与输入 图片使用 image.source,PDF 使用 document.source。工具调用后回传完整 content,保留 thinking 和签名。max_tokens 同时包含推理和正文,预算耗尽时可能截断或没有正文。 始终使用自适应推理,不支持 thinking.disabled、旧式 enabled + budget_tokens 或强制 any / 命名 tool_choice。工具采用自动选择,推理强度在 output_config.effort 设置。 暂不支持强制 JSON Schema。通过提示词要求 JSON 时,应用仍需解析回复并校验 Schema。 推理字段 `output_config.effort` 支持:`low`、`medium`、`high`、`xhigh`、`max`。省略时使用默认设置,输出预算另行配置。[原厂字段说明](https://platform.claude.com/docs/en/build-with-claude/effort)与[型号参数](/docs/cn/api-reference/model-parameters)。 附件格式:PNG/JPEG/WebP; PDF。编码:image/document source: base64 + media_type。 官方限制:完整请求体 <=32 MB;PDF <=600 页,低于 1M 上下文时 <=100 页。 上传说明:按编码后的完整 JSON 体计算,同时受上下文限制;网关可用上限可能低于官方值。其他限制:不支持 Office、音视频或 /v1/files 上传。完整规格见[模型输入规格表](/docs/cn/input-matrix)。 ## 响应处理 结合 stop_reason 与 content 处理结果。tool_use 需执行工具后继续请求,max_tokens、refusal 或空正文不能作为完整答复。 文件大小及格式要求见[文件与媒体输入](/docs/cn/media)。 # gemini-3.5-flash 网页路径:`/docs/cn/models/gemini/gemini-3.5-flash`。 ## 型号与端点 模型 ID:`gemini-3.5-flash`,请求中使用完整名称。 协议:`gemini-generate-content`。端点:`POST /v1beta/models/gemini-3.5-flash:generateContent`。 输入:`text`、`image`、`audio`、`video`、`pdf`。输出:`text`。 认证、完整字段和响应格式见 [gemini-generate-content](/docs/cn/api-reference/gemini-generate-content)。调用前请确认 Key 已开通此型号。 ## 请求示例 ```json { "contents": [ { "role": "user", "parts": [ { "text": "仅回复 READY" } ] } ], "generationConfig": { "maxOutputTokens": 8192 } } ``` 以上为请求体示例,地址和请求头见协议页。其他可选字段请查阅对应参数说明。 ## 参数与输入 图片、PDF、音频和视频各使用独立 inlineData part。thinkingConfig 的可用值按型号设置,3.7 / 3.8 Flash 不支持 minimal。多轮消息需保留 thoughtSignature。本版支持范围不包含 Files API、原生工具结果回传和强制 JSON Schema。 推理字段 `generationConfig.thinkingConfig.thinkingLevel` 支持:`low`、`medium`、`high`。省略时使用默认设置,输出预算另行配置。[原厂字段说明](https://ai.google.dev/gemini-api/docs/thinking)与[型号参数](/docs/cn/api-reference/model-parameters)。 附件格式:PNG/JPEG/WebP; PDF; WAV/MP3; MP4/MPEG/MOV/AVI/MPG/WMV。编码:contents[].parts[].inlineData: mimeType + Base64。 官方限制:视频 inline 原厂 <100 MB;PDF 原厂 <=1000 页。 上传说明:未公布统一文件大小、时长或页数上限,大小按编码后的完整 JSON 体计算。其他限制:不支持 Files API、其他音视频格式、原生工具结果回传或强制 JSON Schema。完整规格见[模型输入规格表](/docs/cn/input-matrix)。 ## 响应处理 检查 candidates 的 finishReason 和内容。MAX_TOKENS 表示截断,受限结束或空候选需另行处理。 文件大小及格式要求见[文件与媒体输入](/docs/cn/media)。 # gemini-3.1-pro-preview 网页路径:`/docs/cn/models/gemini/gemini-3.1-pro-preview`。 ## 型号与端点 模型 ID:`gemini-3.1-pro-preview`,请求中使用完整名称。 协议:`gemini-generate-content`。端点:`POST /v1beta/models/gemini-3.1-pro-preview:generateContent`。 输入:`text`、`image`、`audio`、`video`、`pdf`。输出:`text`。 认证、完整字段和响应格式见 [gemini-generate-content](/docs/cn/api-reference/gemini-generate-content)。调用前请确认 Key 已开通此型号。 ## 请求示例 ```json { "contents": [ { "role": "user", "parts": [ { "text": "仅回复 READY" } ] } ], "generationConfig": { "maxOutputTokens": 8192 } } ``` 以上为请求体示例,地址和请求头见协议页。其他可选字段请查阅对应参数说明。 ## 参数与输入 图片、PDF、音频和视频各使用独立 inlineData part。thinkingConfig 的可用值按型号设置,3.7 / 3.8 Flash 不支持 minimal。多轮消息需保留 thoughtSignature。本版支持范围不包含 Files API、原生工具结果回传和强制 JSON Schema。 推理字段 `generationConfig.thinkingConfig.thinkingLevel` 支持:`low`、`medium`、`high`。省略时使用默认设置,输出预算另行配置。[原厂字段说明](https://ai.google.dev/gemini-api/docs/thinking)与[型号参数](/docs/cn/api-reference/model-parameters)。 附件格式:PNG/JPEG/WebP; PDF; WAV/MP3; MP4/MPEG/MOV/AVI/MPG/WMV。编码:contents[].parts[].inlineData: mimeType + Base64。 官方限制:视频 inline 原厂 <100 MB;PDF 原厂 <=1000 页。 上传说明:未公布统一文件大小、时长或页数上限,大小按编码后的完整 JSON 体计算。其他限制:不支持 Files API、其他音视频格式、原生工具结果回传或强制 JSON Schema。完整规格见[模型输入规格表](/docs/cn/input-matrix)。 ## 响应处理 检查 candidates 的 finishReason 和内容。MAX_TOKENS 表示截断,受限结束或空候选需另行处理。 文件大小及格式要求见[文件与媒体输入](/docs/cn/media)。 # gemini-3.7-flash 网页路径:`/docs/cn/models/gemini/gemini-3.7-flash`。 ## 型号与端点 模型 ID:`gemini-3.7-flash`,请求中使用完整名称。 协议:`gemini-generate-content`。端点:`POST /v1beta/models/gemini-3.7-flash:generateContent`。 输入:`text`、`image`、`audio`、`video`、`pdf`。输出:`text`。 认证、完整字段和响应格式见 [gemini-generate-content](/docs/cn/api-reference/gemini-generate-content)。调用前请确认 Key 已开通此型号。 ## 请求示例 ```json { "contents": [ { "role": "user", "parts": [ { "text": "仅回复 READY" } ] } ], "generationConfig": { "maxOutputTokens": 8192 } } ``` 以上为请求体示例,地址和请求头见协议页。其他可选字段请查阅对应参数说明。 ## 参数与输入 图片、PDF、音频和视频各使用独立 inlineData part。thinkingConfig 的可用值按型号设置,3.7 / 3.8 Flash 不支持 minimal。多轮消息需保留 thoughtSignature。本版支持范围不包含 Files API、原生工具结果回传和强制 JSON Schema。 推理字段 `generationConfig.thinkingConfig.thinkingLevel` 支持:`low`、`medium`、`high`。省略时使用默认设置,输出预算另行配置。[原厂字段说明](https://ai.google.dev/gemini-api/docs/thinking)与[型号参数](/docs/cn/api-reference/model-parameters)。 附件格式:PNG/JPEG/WebP; PDF; WAV/MP3; MP4/MPEG/MOV/AVI/MPG/WMV。编码:contents[].parts[].inlineData: mimeType + Base64。 官方限制:视频 inline 原厂 <100 MB;PDF 原厂 <=1000 页。 上传说明:未公布统一文件大小、时长或页数上限,大小按编码后的完整 JSON 体计算。其他限制:不支持 Files API、其他音视频格式、原生工具结果回传或强制 JSON Schema。完整规格见[模型输入规格表](/docs/cn/input-matrix)。 ## 响应处理 检查 candidates 的 finishReason 和内容。MAX_TOKENS 表示截断,受限结束或空候选需另行处理。 文件大小及格式要求见[文件与媒体输入](/docs/cn/media)。 # gemini-3.8-flash 网页路径:`/docs/cn/models/gemini/gemini-3.8-flash`。 ## 型号与端点 模型 ID:`gemini-3.8-flash`,请求中使用完整名称。 协议:`gemini-generate-content`。端点:`POST /v1beta/models/gemini-3.8-flash:generateContent`。 输入:`text`、`image`、`audio`、`video`、`pdf`。输出:`text`。 认证、完整字段和响应格式见 [gemini-generate-content](/docs/cn/api-reference/gemini-generate-content)。调用前请确认 Key 已开通此型号。 ## 请求示例 ```json { "contents": [ { "role": "user", "parts": [ { "text": "仅回复 READY" } ] } ], "generationConfig": { "maxOutputTokens": 8192 } } ``` 以上为请求体示例,地址和请求头见协议页。其他可选字段请查阅对应参数说明。 ## 参数与输入 图片、PDF、音频和视频各使用独立 inlineData part。thinkingConfig 的可用值按型号设置,3.7 / 3.8 Flash 不支持 minimal。多轮消息需保留 thoughtSignature。本版支持范围不包含 Files API、原生工具结果回传和强制 JSON Schema。 推理字段 `generationConfig.thinkingConfig.thinkingLevel` 支持:`low`、`medium`、`high`。省略时使用默认设置,输出预算另行配置。[原厂字段说明](https://ai.google.dev/gemini-api/docs/thinking)与[型号参数](/docs/cn/api-reference/model-parameters)。 附件格式:PNG/JPEG/WebP; PDF; WAV/MP3; MP4/MPEG/MOV/AVI/MPG/WMV。编码:contents[].parts[].inlineData: mimeType + Base64。 官方限制:视频 inline 原厂 <100 MB;PDF 原厂 <=1000 页。 上传说明:未公布统一文件大小、时长或页数上限,大小按编码后的完整 JSON 体计算。其他限制:不支持 Files API、其他音视频格式、原生工具结果回传或强制 JSON Schema。完整规格见[模型输入规格表](/docs/cn/input-matrix)。 ## 响应处理 检查 candidates 的 finishReason 和内容。MAX_TOKENS 表示截断,受限结束或空候选需另行处理。 文件大小及格式要求见[文件与媒体输入](/docs/cn/media)。 # gemini-3.1-flash-image 网页路径:`/docs/cn/models/gemini/gemini-3.1-flash-image`。 ## 型号与端点 模型 ID:`gemini-3.1-flash-image`,请求中使用完整名称。 协议:`gemini-generate-content`。端点:`POST /v1beta/models/gemini-3.1-flash-image:generateContent`。 输入:`text`、`image`。输出:`image`、`text`。 认证、完整字段和响应格式见 [gemini-generate-content](/docs/cn/api-reference/gemini-generate-content)。调用前请确认 Key 已开通此型号。 ## 请求示例 ```json { "contents": [ { "role": "user", "parts": [ { "text": "白色桌面上的蓝色陶瓷杯,无文字" } ] } ], "generationConfig": { "maxOutputTokens": 8192, "responseModalities": [ "TEXT", "IMAGE" ], "imageConfig": { "aspectRatio": "1:1", "imageSize": "1K" } } } ``` 以上为请求体示例,地址和请求头见协议页。其他可选字段请查阅对应参数说明。 ## 参数与输入 文生图和参考图编辑均使用 generateContent,每张参考图放入独立 inlineData 图片 part。响应需遍历全部 parts,分别读取文字和图片。此协议不使用 OpenAI Images 的 size、quality、mask 或 multipart 字段。 附件格式:PNG/JPEG/WebP 参考图。编码:text + 独立 inlineData 图片 parts。 官方限制:参考图数量、尺寸与像素预算依型号;不可套用 GPT Images 上限。 上传说明:参考图分别上传;总张数和总大小按型号确认,实际可用范围还受网关限制。其他限制:不支持函数工具、独立 mask 或通用文件上传。完整规格见[模型输入规格表](/docs/cn/input-matrix)。 ## 响应处理 finishReason 为 STOP 后,跳过 thought:true 的中间内容,读取最终图片 part。只有文字或预览图时,尚未获得最终图片。 文件大小及格式要求见[文件与媒体输入](/docs/cn/media)。 # gemini-3-pro-image-preview 网页路径:`/docs/cn/models/gemini/gemini-3-pro-image-preview`。 ## 型号与端点 模型 ID:`gemini-3-pro-image-preview`,请求中使用完整名称。 协议:`gemini-generate-content`。端点:`POST /v1beta/models/gemini-3-pro-image-preview:generateContent`。 输入:`text`、`image`。输出:`image`、`text`。 认证、完整字段和响应格式见 [gemini-generate-content](/docs/cn/api-reference/gemini-generate-content)。调用前请确认 Key 已开通此型号。 ## 请求示例 ```json { "contents": [ { "role": "user", "parts": [ { "text": "白色桌面上的蓝色陶瓷杯,无文字" } ] } ], "generationConfig": { "maxOutputTokens": 8192, "responseModalities": [ "TEXT", "IMAGE" ], "imageConfig": { "aspectRatio": "1:1", "imageSize": "1K" } } } ``` 以上为请求体示例,地址和请求头见协议页。其他可选字段请查阅对应参数说明。 ## 参数与输入 文生图和参考图编辑均使用 generateContent,每张参考图放入独立 inlineData 图片 part。响应需遍历全部 parts,分别读取文字和图片。此协议不使用 OpenAI Images 的 size、quality、mask 或 multipart 字段。 附件格式:PNG/JPEG/WebP 参考图。编码:text + 独立 inlineData 图片 parts。 官方限制:参考图数量、尺寸与像素预算依型号;不可套用 GPT Images 上限。 上传说明:参考图分别上传;总张数和总大小按型号确认,实际可用范围还受网关限制。其他限制:不支持函数工具、独立 mask 或通用文件上传。完整规格见[模型输入规格表](/docs/cn/input-matrix)。 ## 响应处理 finishReason 为 STOP 后,跳过 thought:true 的中间内容,读取最终图片 part。只有文字或预览图时,尚未获得最终图片。 文件大小及格式要求见[文件与媒体输入](/docs/cn/media)。 # gemini-nano-banana-2.1 网页路径:`/docs/cn/models/gemini/gemini-nano-banana-2.1`。 ## 型号与端点 模型 ID:`gemini-nano-banana-2.1`,请求中使用完整名称。 协议:`gemini-generate-content`。端点:`POST /v1beta/models/gemini-nano-banana-2.1:generateContent`。 输入:`text`、`image`。输出:`image`、`text`。 认证、完整字段和响应格式见 [gemini-generate-content](/docs/cn/api-reference/gemini-generate-content)。调用前请确认 Key 已开通此型号。 ## 请求示例 ```json { "contents": [ { "role": "user", "parts": [ { "text": "白色桌面上的蓝色陶瓷杯,无文字" } ] } ], "generationConfig": { "maxOutputTokens": 8192, "responseModalities": [ "TEXT", "IMAGE" ], "imageConfig": { "aspectRatio": "1:1", "imageSize": "1K" } } } ``` 以上为请求体示例,地址和请求头见协议页。其他可选字段请查阅对应参数说明。 ## 参数与输入 文生图和参考图编辑均使用 generateContent,每张参考图放入独立 inlineData 图片 part。响应需遍历全部 parts,分别读取文字和图片。此协议不使用 OpenAI Images 的 size、quality、mask 或 multipart 字段。 附件格式:PNG/JPEG/WebP 参考图。编码:text + 独立 inlineData 图片 parts。 官方限制:参考图数量、尺寸与像素预算依型号;不可套用 GPT Images 上限。 上传说明:参考图分别上传;总张数和总大小按型号确认,实际可用范围还受网关限制。其他限制:不支持函数工具、独立 mask 或通用文件上传。完整规格见[模型输入规格表](/docs/cn/input-matrix)。 ## 响应处理 finishReason 为 STOP 后,跳过 thought:true 的中间内容,读取最终图片 part。只有文字或预览图时,尚未获得最终图片。 文件大小及格式要求见[文件与媒体输入](/docs/cn/media)。 # gpt-image-2 网页路径:`/docs/cn/models/image/gpt-image-2`。 ## 型号与端点 模型 ID:`gpt-image-2`,请求中使用完整名称。 协议:`images`。端点:`POST /v1/images/generations`。 输入:`text`、`reference_image`。输出:`image`。 认证、完整字段和响应格式见 [images](/docs/cn/api-reference/images)。调用前请确认 Key 已开通此型号。 ## 请求示例 ```json { "model": "gpt-image-2", "prompt": "白色桌面上的蓝色陶瓷杯,无文字", "size": "1024x1024", "quality": "medium", "output_format": "png", "n": 1 } ``` 以上为请求体示例,地址和请求头见协议页。其他可选字段请查阅对应参数说明。 ## 参数与输入 文生图发送 JSON,参考图编辑上传 multipart 文件。编辑路径为 `/v1/images/edits`。质量、格式、尺寸及蒙版参数按 Images API 中的对应型号设置。 附件格式:参考图 PNG/JPEG/WebP;mask PNG Alpha;输出 PNG/JPEG。编码:生成 JSON;编辑 multipart image / image[] / mask。 官方限制:gpt-image-2: 参考图 1–16 张且 <50 MB/张;mask <4 MB。 上传说明:总请求需符合网关限制;mask 与首图同宽高,返回图片需解码并核对尺寸。其他限制:不支持文件理解、视频或 Chat/Responses 生图字段。完整规格见[模型输入规格表](/docs/cn/input-matrix)。 ## 响应处理 检查错误正文与 data 数组,逐张解码图片,核对格式、尺寸和参考图对应关系。HTTP 200 后仍需完成这些检查。 文件大小及格式要求见[文件与媒体输入](/docs/cn/media)。 # gpt-image-2.5-flare 网页路径:`/docs/cn/models/image/gpt-image-2.5-flare`。 ## 型号与端点 模型 ID:`gpt-image-2.5-flare`,请求中使用完整名称。 协议:`images`。端点:`POST /v1/images/generations`。 输入:`text`、`reference_image`。输出:`image`。 认证、完整字段和响应格式见 [images](/docs/cn/api-reference/images)。调用前请确认 Key 已开通此型号。 ## 请求示例 ```json { "model": "gpt-image-2.5-flare", "prompt": "白色桌面上的蓝色陶瓷杯,无文字", "size": "1024x1024", "quality": "medium", "output_format": "png", "n": 1 } ``` 以上为请求体示例,地址和请求头见协议页。其他可选字段请查阅对应参数说明。 ## 参数与输入 文生图发送 JSON,参考图编辑上传 multipart 文件。编辑路径为 `/v1/images/edits?api-version=2025-04-01-preview`。质量、格式、尺寸及蒙版参数按 Images API 中的对应型号设置。 附件格式:参考图 PNG/JPEG/WebP;mask PNG Alpha;输出 PNG/JPEG。编码:生成 JSON;编辑 multipart image / image[] / mask。 官方限制:gpt-image-2: 参考图 1–16 张且 <50 MB/张;mask <4 MB。 上传说明:总请求需符合网关限制;mask 与首图同宽高,返回图片需解码并核对尺寸。其他限制:不支持文件理解、视频或 Chat/Responses 生图字段。完整规格见[模型输入规格表](/docs/cn/input-matrix)。 ## 响应处理 检查错误正文与 data 数组,逐张解码图片,核对格式、尺寸和参考图对应关系。HTTP 200 后仍需完成这些检查。 文件大小及格式要求见[文件与媒体输入](/docs/cn/media)。 # gpt-image-2.5-sunburst 网页路径:`/docs/cn/models/image/gpt-image-2.5-sunburst`。 ## 型号与端点 模型 ID:`gpt-image-2.5-sunburst`,请求中使用完整名称。 协议:`images`。端点:`POST /v1/images/generations`。 输入:`text`、`reference_image`。输出:`image`。 认证、完整字段和响应格式见 [images](/docs/cn/api-reference/images)。调用前请确认 Key 已开通此型号。 ## 请求示例 ```json { "model": "gpt-image-2.5-sunburst", "prompt": "白色桌面上的蓝色陶瓷杯,无文字", "size": "1024x1024", "quality": "medium", "output_format": "png", "n": 1 } ``` 以上为请求体示例,地址和请求头见协议页。其他可选字段请查阅对应参数说明。 ## 参数与输入 文生图发送 JSON,参考图编辑上传 multipart 文件。编辑路径为 `/v1/images/edits?api-version=2025-04-01-preview`。质量、格式、尺寸及蒙版参数按 Images API 中的对应型号设置。 附件格式:参考图 PNG/JPEG/WebP;mask PNG Alpha;输出 PNG/JPEG。编码:生成 JSON;编辑 multipart image / image[] / mask。 官方限制:gpt-image-2: 参考图 1–16 张且 <50 MB/张;mask <4 MB。 上传说明:总请求需符合网关限制;mask 与首图同宽高,返回图片需解码并核对尺寸。其他限制:不支持文件理解、视频或 Chat/Responses 生图字段。完整规格见[模型输入规格表](/docs/cn/input-matrix)。 ## 响应处理 检查错误正文与 data 数组,逐张解码图片,核对格式、尺寸和参考图对应关系。HTTP 200 后仍需完成这些检查。 文件大小及格式要求见[文件与媒体输入](/docs/cn/media)。 # mj_imagine 网页路径:`/docs/cn/models/image/mj`。 ## 生成端点 `POST https://www.yunqiai.chat/v1/tob/diffusion` `model` 使用 `mj_imagine` 或 `youchuan-image`,提示词放在 `text`。请求头为 `Authorization: Bearer `、`Content-Type: application/json`、`Accept: application/json`;本接口不使用 x-api-key、x-goog-api-key 或 x-youchuan-secret。 ```json { "model":"mj_imagine", "text":"现代东方建筑,清晨薄雾,建筑摄影,无文字 --v 8.1 --fast --hd --q 4 --raw --ar 16:9" } ``` 版本、比例、质量等参数写在 text 末尾,参数前留空格,参数后不加逗号或句号。JSON 顶层不使用 prompt、size、n、quality、aspectRatio 或 response_format。 `/v1/tob/subscribe` 用于账户查询,图片生成使用上面的 diffusion 接口。本页不包含 MJ 视频接口。 ## 版本与质量 | 版本参数 | 质量参数 | | --- | --- | | --v 5.1 / --v 5.2 / --v 6 | --q 0.25 / 0.5 / 1 | | --v 6.1 | --q 0.5 / 1 / 2 | | --v 7 | --q 1 / 2 / 4 | | --v 8 / --v 8.1 | --q 1 / 4 | | --v 8.2 | --q 1 / 2 / 3 / 4 | | --niji 5 / --niji 6 | --q 0.25 / 0.5 / 1 | | --niji 7 | 不使用 --q | 省略版本时使用服务默认值。需要固定配置时,请在 text 中写明版本和参数,默认版本可能调整。 ## 参数 | 参数 | 含义与范围 | | --- | --- | | --ar | 整数宽高比,如 1:1、16:9、9:16、4:3、2:3 | | --raw | 减少默认美化,受版本支持范围限制 | | --chaos | 0–100,结果差异 | | --seed | 0–4294967295,随机种子;不保证跨版本像素复现 | | --stylize / --s | 0–1000,风格化强度 | | --weird / --w | 0–3000,超现实程度,受版本支持限制 | | --no | 排除内容 | | --tile | 平铺图案,受版本及模式限制 | | --fast | 快速模式 | | --turbo | 极速模式,不与 fast 同时使用 | | --draft | 草图模式,受版本及后续操作限制 | | --hd | 原生 2K,仅 v8.1 / v8.2 | | --exp | 0–100,实验性动态美学参数,受版本支持限制 | 任务消耗可能随操作、质量、速度、版本及 HD / Draft 设置变化,无法仅按图片文件体积估算。 参数组合限制:fast 与 turbo 不共用;niji 7 不带 q;v8.1 / v8.2 不用 turbo;v7 draft 不与 tile、oref、q 4 共用。部分参数仅适用于特定版本,请同时检查版本要求。 ## 参考图 | 用途 | text 写法 | 适用版本 | | --- | --- | --- | | 普通垫图 | `<图1URL> <图2URL> 描述 --iw 1 --v 7` | 图片 URL 在开头,按顺序引用 | | 风格参考 | `描述 --sref <图URL> --sw 100 --sv 6` | sref / sv 依版本兼容 | | 角色参考 | `描述 --cref <人物URL> --cw 100 --v 6.1` | v6、v6.1、niji 6 | | 万物引用 | `描述 --oref <物体URL> --ow 200 --v 7` | v7 | URL 使用服务端可直接读取的公开图片直链,不支持网页地址、本地路径、需要 Cookie 的链接或裸 Base64。带签名、查询参数或敏感路径的链接,还需检查是否可访问及是否适合对外发送。 ## 任务流程 提交响应中的 `id` 是 Job ID,收到后立即保存。任务执行期间 `urls` 可能为空,继续查询即可,无需重新提交。 使用同一 Key 调用 `GET https://www.yunqiai.chat/v1/tob/job/{jobId}`,每 5 秒查询一次,本地等待期限设为 6 分钟。 | 结果 | 动作 | | --- | --- | | status=1 | 继续查原任务 | | status=2 | 遍历 urls,下载所有可用图片 | | status=3 | 失败,保存 comment、cost 和脱敏完整响应供诊断 | | HTTP 404 | 任务不存在,核对 id 和权限 | | HTTP 400 | 请求参数错误,停止轮询 | | HTTP 429 / 500 / 502 / 503 / 504 | 有限退避重试查询,不重新提交生成 | | 本地截止时间到达 | 保存 id,稍后恢复查询,不把任务判为已取消 | 查询遇到临时连接错误或读取超时时,可以在等待期限内退避重试同一 GET。证书错误需单独排查,TLS 校验保持开启。 生成 POST 发生网络错误时,任务可能已经受理。[mj.py](/examples/mj.py) 会在提交前写入 `submission-attempt.json`。若未收到 id,请先从平台调用记录确认状态,保留提交记录以防重复生成。已知 id 使用 `--job` 恢复查询;`--wait` 仅设置本地等待秒数,不取消远端任务。 ## 二次操作 以下接口均使用 POST JSON,在源任务完成后调用。`imageNo` 从 0 开始,对应原 urls 数组下标;即使过滤了空项,也需保留原编号。 | 路径 | 字段 | | --- | --- | | /v1/tob/variation | jobId、imageNo、type:0 轻微 / 1 强烈;remixPrompt 可选 | | /v1/tob/upscale | jobId、imageNo、type:0 标准 / 1 创意 / 2 v5_2x / 3 v5_4x;须匹配源版本 | | /v1/tob/reroll | jobId | | /v1/tob/pan | jobId、imageNo、direction:0 下 / 1 右 / 2 上 / 3 左;scale 1.1–3;remixPrompt 可选 | | /v1/tob/outpaint | jobId、imageNo、scale 1.1–2;remixPrompt 可选 | | /v1/tob/inpaint | jobId、imageNo、mask;remixPrompt 可选 | | /v1/tob/remix | jobId、imageNo、remixPrompt;mode:0 强 / 1 弱 | | /v1/tob/edit | jobId、imageNo、canvas、imgPos、remixPrompt;mask 可选 | | /v1/tob/upload-paint | imgUrl、mask、canvas、imgPos、remixPrompt | | /v1/tob/retexture | imgUrl、remixPrompt;版本不低于 6.1 | | /v1/tob/remove-background | imgUrl,PNG / JPEG 输入 | | /v1/tob/enhance | jobId、imageNo,仅 draft 任务 | 每次操作都会产生新任务,请保存新的 id 并查询结果。高级编辑 / 转绘任务可继续执行的操作有所限制。 延展、局部重绘和高清使用相应原生成任务的 id 及图片编号,不能使用高清结果任务的 id。源任务的版本和类型也需支持目标操作。若返回 HTTP 403 并提示任务类型不兼容,请停止提交并检查源任务。 ## 蒙版与画布 `canvas:{width,height}` 设置目标画布像素,`imgPos:{width,height,x,y}` 设置原图在画布上的位置和大小,坐标原点为左上角。输出可能按模型尺寸规则缩放,因此实际像素未必与 canvas 一致。 mask 支持两种写法,选择其一:`{"areas":[{"width":1024,"height":1024,"points":[100,100,500,100,500,500,100,500]}]}` 或 `{"url":"公开黑白蒙版URL"}`。points 按 x、y 成对排列,宽高和坐标对应原图。黑白蒙版中白色部分为重绘区域,与 GPT Images 的透明 Alpha 蒙版规则不同。 ## 图片下载 任务成功后,及时下载 urls 中的全部图片,并保留任务 ID 和原始下标。使用 `mj-imagine-{JobID}-{下标}.png` 命名时,需将内容实际转码为 PNG。下载失败可恢复下载,无需重新生成;结果链接不宜作为永久存储。 程序见 [mj.py](/examples/mj.py),支持提交、恢复查询和读取二次操作 JSON 文件,始终使用所选模型。 # youchuan-image 网页路径:`/docs/cn/models/image/youchuan-image`。 ## 型号与端点 模型 ID:`youchuan-image`。接口为 `POST /v1/tob/diffusion`,请求体如下。 ```json { "model": "youchuan-image", "text": "A blue ceramic mug on a white table --v 7 --fast --ar 1:1" } ``` ## 接口说明 此型号与 `mj_imagine` 均采用悠船图片任务协议。鉴权、版本参数、参考图、轮询、二次操作、蒙版和下载方法见 [MJ 接口](/docs/cn/models/image/mj)。请求保留所选 ID。 # gpt-realtime-2.1 网页路径:`/docs/cn/models/realtime/gpt-realtime-2.1`。 ## 型号与端点 模型 ID:`gpt-realtime-2.1`,请求中使用完整名称。 协议:`realtime-websocket`。端点:`GET /v1/realtime?model=gpt-realtime-2.1`。 输入:`text`、`audio`、`image`。输出:`text`、`audio`、`function_call`。 认证、完整字段和响应格式见 [realtime-websocket](/docs/cn/api-reference/realtime)。调用前请确认 Key 已开通此型号。 ## 请求示例 ```json { "type": "conversation.item.create", "item": { "type": "message", "role": "user", "content": [ { "type": "input_text", "text": "仅回复 READY" } ] } } ``` 此事件在连接建立后发送,还需配置会话并发送 response.create。 ## 参数与输入 通过 WebSocket 会话调用,不使用 Chat Completions。等待 session.created / session.updated 后提交输入,并检查 response.done 的状态和内容。音频采用会话配置中的采样格式。 附件格式:文本;PNG/JPEG 图片;PCM16 LE 单声道 24 kHz。编码:WebSocket input_text / input_image;PCM Base64 分块。 官方限制:会话、音频缓冲及响应预算独立限制。 上传说明:输入受会话和单帧限制;音频按协商格式分块发送,不直接传入超大文件。其他限制:不直接接收 WAV/MP3 容器、PDF 或视频文件。完整规格见[模型输入规格表](/docs/cn/input-matrix)。 ## 响应处理 检查 response.done 中的状态及输出。音频请求还需确认收到可保存的音频数据;连接建立本身不包含生成结果。 文件大小及格式要求见[文件与媒体输入](/docs/cn/media)。 # gpt-realtime-2.1-mini 网页路径:`/docs/cn/models/realtime/gpt-realtime-2.1-mini`。 ## 型号与端点 模型 ID:`gpt-realtime-2.1-mini`,请求中使用完整名称。 协议:`realtime-websocket`。端点:`GET /v1/realtime?model=gpt-realtime-2.1-mini`。 输入:`text`、`audio`、`image`。输出:`text`、`audio`、`function_call`。 认证、完整字段和响应格式见 [realtime-websocket](/docs/cn/api-reference/realtime)。调用前请确认 Key 已开通此型号。 ## 请求示例 ```json { "type": "conversation.item.create", "item": { "type": "message", "role": "user", "content": [ { "type": "input_text", "text": "仅回复 READY" } ] } } ``` 此事件在连接建立后发送,还需配置会话并发送 response.create。 ## 参数与输入 通过 WebSocket 会话调用,不使用 Chat Completions。等待 session.created / session.updated 后提交输入,并检查 response.done 的状态和内容。音频采用会话配置中的采样格式。 附件格式:文本;PNG/JPEG 图片;PCM16 LE 单声道 24 kHz。编码:WebSocket input_text / input_image;PCM Base64 分块。 官方限制:会话、音频缓冲及响应预算独立限制。 上传说明:输入受会话和单帧限制;音频按协商格式分块发送,不直接传入超大文件。其他限制:不直接接收 WAV/MP3 容器、PDF 或视频文件。完整规格见[模型输入规格表](/docs/cn/input-matrix)。 ## 响应处理 检查 response.done 中的状态及输出。音频请求还需确认收到可保存的音频数据;连接建立本身不包含生成结果。 文件大小及格式要求见[文件与媒体输入](/docs/cn/media)。 # kimi-k3 网页路径:`/docs/cn/models/other/kimi-k3`。 ## 型号与端点 模型 ID:`kimi-k3`,请求中使用完整名称。 协议:`chat-completions`。端点:`POST /v1/chat/completions`。 输入:`text`、`image`。输出:`text`、`tool_calls`。 认证、完整字段和响应格式见 [chat-completions](/docs/cn/api-reference/chat-completions)。调用前请确认 Key 已开通此型号。 ## 请求示例 ```json { "model": "kimi-k3", "messages": [ { "role": "user", "content": "仅回复 READY" } ], "max_tokens": 4096 } ``` 以上为请求体示例,地址和请求头见协议页。其他可选字段请查阅对应参数说明。 ## 参数与输入 模型 ID 的大小写和标点需保持一致。图片使用 Chat 的 image_url 内容块,PDF、音频和视频不在本型号的文件输入支持范围内。推理开关和 OpenAI 扩展字段需按本型号设置,工具及结构化输出见对应协议说明。 附件格式:文本;PNG/JPEG/WebP 图片 data URI。编码:Chat text / image_url 内容块。 官方限制:依型号上下文及图片预算。 上传说明:未公布统一附件大小上限,仅接收本行所列内容类型。其他限制:不支持原生 PDF、Office、音频或视频附件。完整规格见[模型输入规格表](/docs/cn/input-matrix)。 ## 响应处理 读取 finish_reason 和 message。length 表示截断,tool_calls 需由应用执行;空正文或异常结束需分别处理。 文件大小及格式要求见[文件与媒体输入](/docs/cn/media)。 # DeepSeek-V4.1-Flash 网页路径:`/docs/cn/models/other/DeepSeek-V4.1-Flash`。 ## 型号与端点 模型 ID:`DeepSeek-V4.1-Flash`,请求中使用完整名称。 协议:`chat-completions`。端点:`POST /v1/chat/completions`。 输入:`text`、`image`。输出:`text`、`tool_calls`。 认证、完整字段和响应格式见 [chat-completions](/docs/cn/api-reference/chat-completions)。调用前请确认 Key 已开通此型号。 ## 请求示例 ```json { "model": "DeepSeek-V4.1-Flash", "messages": [ { "role": "user", "content": "仅回复 READY" } ], "max_tokens": 4096 } ``` 以上为请求体示例,地址和请求头见协议页。其他可选字段请查阅对应参数说明。 ## 参数与输入 模型 ID 的大小写和标点需保持一致。图片使用 Chat 的 image_url 内容块,PDF、音频和视频不在本型号的文件输入支持范围内。推理开关和 OpenAI 扩展字段需按本型号设置,工具及结构化输出见对应协议说明。 附件格式:文本;PNG/JPEG/WebP 图片 data URI。编码:Chat text / image_url 内容块。 官方限制:依型号上下文及图片预算。 上传说明:未公布统一附件大小上限,仅接收本行所列内容类型。其他限制:不支持原生 PDF、Office、音频或视频附件。完整规格见[模型输入规格表](/docs/cn/input-matrix)。 ## 响应处理 读取 finish_reason 和 message。length 表示截断,tool_calls 需由应用执行;空正文或异常结束需分别处理。 文件大小及格式要求见[文件与媒体输入](/docs/cn/media)。 # glm-5.2 网页路径:`/docs/cn/models/other/glm-5.2`。 ## 接入状态 `glm-5.2` 已灰度下线,如有需要请联系商务。此型号不再提供调用示例,更换型号需重新指定完整 ID。 # 可运行示例 ## gemini-images.py ```python """Python 3.8+: pip install requests pillow. Native Gemini image generation/editing.""" import argparse import base64 import io import json import os from pathlib import Path from urllib.parse import quote import requests from PIL import Image def main(): parser = argparse.ArgumentParser() parser.add_argument('--prompt', required=True) parser.add_argument('--image', action='append', default=[]) parser.add_argument('--size', choices=['1K', '2K', '4K'], default='1K') parser.add_argument('--aspect', default='1:1') parser.add_argument('--history', help='Private JSON file storing full native contents for subsequent edits') parser.add_argument('--out', default='gemini-images-output') args = parser.parse_args() key, model = os.environ['YUNQIAI_API_KEY'], os.environ['YUNQIAI_MODEL'] if model not in ['gemini-3.1-flash-image', 'gemini-3-pro-image-preview', 'gemini-nano-banana-2.1']: raise ValueError('Choose an exact Gemini image model ID') history = [] history_path = Path(args.history) if args.history else None if history_path and history_path.exists(): saved = json.loads(history_path.read_text(encoding='utf-8')) if saved.get('model') != model: raise ValueError('History model differs from selected model') history = saved['contents'] parts = [{'text': args.prompt}] for name in args.image: path = Path(name) with Image.open(path) as image: image.verify() mime = {'PNG': 'image/png', 'JPEG': 'image/jpeg', 'WEBP': 'image/webp'}.get(image.format) if not mime: raise ValueError('Reference must be PNG, JPEG or WebP') parts.append({'inlineData': {'mimeType': mime, 'data': base64.b64encode(path.read_bytes()).decode('ascii')}}) history.append({'role': 'user', 'parts': parts}) body = {'contents': history, 'generationConfig': {'maxOutputTokens': 8192, 'responseModalities': ['TEXT', 'IMAGE'], 'imageConfig': {'aspectRatio': args.aspect, 'imageSize': args.size}}} response = requests.post('https://www.yunqiai.chat/v1beta/models/' + quote(model, safe='') + ':generateContent', headers={'x-goog-api-key': key}, json=body, allow_redirects=False, timeout=(15, 300)) if not 200 <= response.status_code < 300: raise RuntimeError('HTTP ' + str(response.status_code) + ': ' + response.text[:4000]) data = response.json() if data.get('error'): raise RuntimeError(json.dumps(data['error'], ensure_ascii=False)) candidates = data.get('candidates', []) if not candidates or candidates[0].get('finishReason') != 'STOP': raise RuntimeError('No completed image candidate: ' + str([c.get('finishReason') for c in candidates])) content = candidates[0]['content'] if history_path: history.append(content) history_path.parent.mkdir(parents=True, exist_ok=True) history_path.write_text(json.dumps({'model': model, 'contents': history}, ensure_ascii=False), encoding='utf-8') output = Path(args.out) output.mkdir(parents=True, exist_ok=True) saved, errors = [], [] for index, part in enumerate(content.get('parts', [])): if part.get('thought'): continue inline = part.get('inlineData') or part.get('inline_data') if not inline: if part.get('text'): print(part['text']) continue try: raw = base64.b64decode(inline['data'], validate=True) with Image.open(io.BytesIO(raw)) as image: image.load() extension = {'PNG': 'png', 'JPEG': 'jpg', 'WEBP': 'webp'}[image.format] target = output / ('image-' + str(index) + '.' + extension) target.write_bytes(raw) saved.append({'file': str(target), 'size': list(image.size)}) if max(image.size) < {'1K': 1000, '2K': 2000, '4K': 4000}[args.size]: errors.append('Final image smaller than requested size tier: ' + str(target)) except Exception as error: errors.append(str(error)) print(json.dumps({'images': saved, 'errors': errors}, ensure_ascii=False)) if not saved or errors: raise RuntimeError('Missing or invalid final images; do not automatically submit again') if __name__ == '__main__': main() ``` ## http-client.py ```python """Native YunQi HTTP requests. Python 3.8+, pip install requests.""" import argparse import base64 import json import mimetypes import os from pathlib import Path import requests def iter_sse(response): data = [] for line in response.iter_lines(decode_unicode=True): if line == "": if data: value = "\n".join(data) data = [] yield value continue if line.startswith(":"): continue field, _, value = line.partition(":") if field == "data": data.append(value[1:] if value.startswith(" ") else value) if data: raise RuntimeError("Incomplete SSE frame at connection close") def build_body(protocol, model, prompt, attachment=None, stream=False, mime_override=None): mime, encoded = None, None if attachment: path = Path(attachment) native_mimes = {".png": "image/png", ".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".webp": "image/webp", ".gif": "image/gif", ".pdf": "application/pdf", ".mp4": "video/mp4", ".mpeg": "video/mpeg", ".mov": "video/mov", ".mp3": "audio/mp3", ".wav": "audio/wav", ".avi": "video/avi", ".wmv": "video/wmv", ".mpg": "video/mpg", ".csv": "text/csv", ".txt": "text/plain", ".json": "application/json", ".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", ".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"} mime = mime_override or native_mimes.get(path.suffix.lower()) or mimetypes.guess_type(str(path))[0] if not mime: raise ValueError("Cannot determine file MIME") encoded = base64.b64encode(path.read_bytes()).decode("ascii") if protocol == "responses": content = [{"type": "input_text", "text": prompt}] if encoded: if mime.startswith("image/"): content.append({"type": "input_image", "image_url": "data:" + mime + ";base64," + encoded}) elif mime in ["application/pdf", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "text/plain", "text/csv", "application/json"]: content.append({"type": "input_file", "filename": Path(attachment).name, "file_data": "data:" + mime + ";base64," + encoded}) else: raise ValueError("Responses example accepts images, PDF, DOCX, XLSX, TXT, CSV or JSON") return "/v1/responses", {"model": model, "input": [{"role": "user", "content": content}], "max_output_tokens": 4096, "stream": stream} if protocol == "messages": content = [{"type": "text", "text": prompt}] if encoded: if not (mime.startswith("image/") or mime == "application/pdf"): raise ValueError("Messages example accepts images or PDF") content.append({"type": "document" if mime == "application/pdf" else "image", "source": {"type": "base64", "media_type": mime, "data": encoded}}) return "/v1/messages", {"model": model, "messages": [{"role": "user", "content": content}], "max_tokens": 8192, "stream": stream} if protocol == "gemini": from urllib.parse import quote parts = [{"text": prompt}] if encoded: parts.append({"inlineData": {"mimeType": mime, "data": encoded}}) action = ":streamGenerateContent?alt=sse" if stream else ":generateContent" return "/v1beta/models/" + quote(model, safe="") + action, {"contents": [{"role": "user", "parts": parts}], "generationConfig": {"maxOutputTokens": 8192}} content = prompt if encoded: if not mime.startswith("image/"): raise ValueError("Chat attachment must be an image") content = [{"type": "text", "text": prompt}, {"type": "image_url", "image_url": {"url": "data:" + mime + ";base64," + encoded}}] return "/v1/chat/completions", {"model": model, "messages": [{"role": "user", "content": content}], "max_tokens": 4096, "stream": stream} def inspect_result(protocol, data): if data.get("error"): raise RuntimeError("API error: " + json.dumps(data["error"], ensure_ascii=False)) if protocol == "responses": if data.get("status") != "completed": raise RuntimeError("Response not completed: " + str(data.get("incomplete_details") or data.get("status"))) text = "".join(p.get("text", "") for item in data.get("output", []) if item.get("type") == "message" for p in item.get("content", []) if p.get("type") == "output_text") elif protocol == "messages": if data.get("stop_reason") != "end_turn": raise RuntimeError("Message not a completed text turn: " + str(data.get("stop_reason"))) text = "".join(x.get("text", "") for x in data.get("content", []) if x.get("type") == "text") elif protocol == "gemini": candidates = data.get("candidates", []) if not candidates or candidates[0].get("finishReason") != "STOP": raise RuntimeError("Candidate not completed: " + str([c.get("finishReason") for c in candidates])) text = "".join(p.get("text", "") for c in candidates for p in c.get("content", {}).get("parts", []) if not p.get("thought")) else: choices = data.get("choices", []) if not choices or any(c.get("finish_reason") != "stop" for c in choices): raise RuntimeError("Chat is truncated or requires a tool round") text = "".join(c.get("message", {}).get("content") or "" for c in choices) if not text.strip(): raise RuntimeError("No final text returned") return text def stream_text(protocol, response): terminal = False text_turn_ended = False text = [] for raw in iter_sse(response): if raw == "[DONE]": if protocol == "chat": terminal = text_turn_ended continue event = json.loads(raw) kind = event.get("type") if kind == "error" or event.get("error") or kind in ["response.failed", "response.incomplete"]: raise RuntimeError("Stream error: " + json.dumps(event, ensure_ascii=False)) delta = "" if protocol == "responses": if kind == "response.output_text.delta": delta = event.get("delta", "") if kind == "response.completed": terminal = event.get("response", {}).get("status") == "completed" elif protocol == "messages": if kind == "content_block_delta" and event.get("delta", {}).get("type") == "text_delta": delta = event["delta"].get("text", "") if kind == "message_delta": reason = event.get("delta", {}).get("stop_reason") if reason not in [None, "end_turn"]: raise RuntimeError("Message stream ended with stop_reason=" + str(reason)) if reason == "end_turn": text_turn_ended = True if kind == "message_stop": terminal = text_turn_ended elif protocol == "gemini": for candidate in event.get("candidates", []): delta += "".join(p.get("text", "") for p in candidate.get("content", {}).get("parts", []) if not p.get("thought")) if candidate.get("finishReason"): if candidate["finishReason"] != "STOP": raise RuntimeError("Gemini stream ended: " + candidate["finishReason"]) terminal = True else: for choice in event.get("choices", []): delta += choice.get("delta", {}).get("content") or "" if choice.get("finish_reason") not in [None, "stop"]: raise RuntimeError("Chat stream truncated or requires tools") if choice.get("finish_reason") == "stop": text_turn_ended = True text.append(delta) print(delta, end="", flush=True) print() if not terminal or not "".join(text).strip(): raise RuntimeError("Stream ended without completed text") return "".join(text) def main(): parser = argparse.ArgumentParser() parser.add_argument("protocol", choices=["responses", "messages", "gemini", "chat"]) prompts = parser.add_mutually_exclusive_group() prompts.add_argument("--prompt") prompts.add_argument("--prompt-file", help="UTF-8 text input; avoids shell command-length limits") parser.add_argument("--file") parser.add_argument("--mime-type", help="Explicit MIME for the real file encoding") parser.add_argument("--stream", action="store_true") parser.add_argument("--max-output-tokens", type=int, help="Positive model-supported output budget, including reasoning") args = parser.parse_args() key, model = os.environ["YUNQIAI_API_KEY"], os.environ["YUNQIAI_MODEL"] prompt = Path(args.prompt_file).read_text(encoding="utf-8") if args.prompt_file else args.prompt or "仅回复 READY" if args.mime_type and not args.file: parser.error("--mime-type requires --file") route, body = build_body(args.protocol, model, prompt, args.file, args.stream, args.mime_type) if args.max_output_tokens is not None: if args.max_output_tokens < 1: parser.error("--max-output-tokens must be positive") if args.protocol == "gemini": body['generationConfig']['maxOutputTokens'] = args.max_output_tokens else: body['max_output_tokens' if args.protocol == 'responses' else 'max_tokens'] = args.max_output_tokens headers = {"Authorization": "Bearer " + key} if args.protocol == "messages": headers = {"x-api-key": key, "anthropic-version": "2023-06-01"} elif args.protocol == "gemini": headers = {"x-goog-api-key": key} # No automatic retries: a disconnected POST may already be accepted. with requests.post("https://www.yunqiai.chat" + route, headers=headers, json=body, stream=args.stream, allow_redirects=False, timeout=(15, 300)) as response: response.encoding = "utf-8" if not 200 <= response.status_code < 300: raise RuntimeError("HTTP " + str(response.status_code) + ": " + response.text[:4000]) if args.stream: stream_text(args.protocol, response) else: print(inspect_result(args.protocol, response.json())) if __name__ == "__main__": main() ``` ## images.py ```python """Python 3.8+: pip install requests pillow. API Key and model come from env.""" import argparse import base64 import io import os import re from contextlib import ExitStack from pathlib import Path import requests from PIL import Image def validate_options(model, size, quality, count, timeout): if isinstance(count, bool) or not isinstance(count, int) or not 1 <= count <= 10: raise ValueError("n must be an integer between 1 and 10") if isinstance(timeout, bool) or not isinstance(timeout, int) or timeout <= 0: raise ValueError("--timeout must be a positive number of seconds") qualities = ["auto", "low", "medium", "high"] if model.startswith("gpt-image-2.5-"): qualities += ["xhigh", "max"] if quality not in qualities: raise ValueError("Unsupported quality for the selected model") if size == "auto": return None if not isinstance(size, str) or not re.fullmatch(r"[1-9][0-9]*x[1-9][0-9]*", size): raise ValueError("size must be auto or positive WIDTHxHEIGHT using lowercase x") dimensions = tuple(int(value) for value in size.split("x")) if model == "gpt-image-2": width, height = dimensions if width % 16 or height % 16 or max(dimensions) > 3840 or max(dimensions) > 3 * min(dimensions) or not 655360 <= width * height <= 8294400: raise ValueError("size violates the selected model's pixel, alignment or aspect-ratio constraints") return dimensions def main(): parser = argparse.ArgumentParser() parser.add_argument("mode", choices=["generate", "edit"]) parser.add_argument("--prompt", required=True) parser.add_argument("--image", action="append", default=[]) parser.add_argument("--mask") parser.add_argument("--size", default="1024x1024") parser.add_argument("--quality", default="medium") parser.add_argument("--format", choices=["png", "jpeg"], default="png") parser.add_argument("--n", type=int, default=1) parser.add_argument("--out", default="image-results") parser.add_argument("--timeout", type=int, default=300, help="Read timeout in seconds; a timeout does not mean generation was cancelled") args = parser.parse_args() key, model = os.environ["YUNQIAI_API_KEY"], os.environ["YUNQIAI_MODEL"] if model not in ["gpt-image-2", "gpt-image-2.5-flare", "gpt-image-2.5-sunburst"]: raise ValueError("Choose an exact Images API model ID") expected_size = validate_options(model, args.size, args.quality, args.n, args.timeout) body = {"model": model, "prompt": args.prompt, "size": args.size, "quality": args.quality, "output_format": args.format, "n": args.n} route = "/v1/images/generations" with ExitStack() as stack: request = {"json": body} if args.mode == "edit": if not args.image: raise ValueError("At least one --image is required") route = "/v1/images/edits" if model.startswith("gpt-image-2.5-"): route += "?api-version=2025-04-01-preview" files = [] for name in args.image: path = Path(name) with Image.open(path) as image: image.verify() mime = {"PNG": "image/png", "JPEG": "image/jpeg", "WEBP": "image/webp"}.get(image.format) if not mime: raise ValueError("Reference must be PNG, JPEG or WebP") files.append(("image[]" if len(args.image) > 1 else "image", (path.name, stack.enter_context(path.open("rb")), mime))) if args.mask: mask_path = Path(args.mask) with Image.open(mask_path) as mask, Image.open(args.image[0]) as first: if mask.format != "PNG" or "A" not in mask.getbands() or mask.size != first.size: raise ValueError("Mask must be PNG with alpha and match the first image dimensions") files.append(("mask", (mask_path.name, stack.enter_context(mask_path.open("rb")), "image/png"))) request = {"data": {k: str(v) for k, v in body.items()}, "files": files} elif args.image or args.mask: raise ValueError("Use edit mode for reference images or masks") try: response = requests.post("https://www.yunqiai.chat" + route, headers={"Authorization": "Bearer " + key}, allow_redirects=False, timeout=(15, args.timeout), **request) except requests.exceptions.RequestException as error: raise RuntimeError("Generation state is unknown after a network failure. Do not automatically repeat the POST; check platform request records first") from error if not 200 <= response.status_code < 300: raise RuntimeError("HTTP " + str(response.status_code) + ": " + response.text[:4000]) data = response.json() if data.get("error") or not data.get("data"): raise RuntimeError("No image data returned") output = Path(args.out) output.mkdir(parents=True, exist_ok=True) failures = [] for index, item in enumerate(data["data"]): try: if item.get("b64_json"): raw = base64.b64decode(item["b64_json"], validate=True) elif item.get("url"): download = requests.get(item["url"], timeout=(15, 300)) download.raise_for_status() raw = download.content else: raise ValueError("No b64_json or URL") with Image.open(io.BytesIO(raw)) as image: image.load() extension = {"JPEG": "jpg", "PNG": "png", "WEBP": "webp"}[image.format] path = output / ("image-" + str(index) + "." + extension) path.write_bytes(raw) print(str(path), image.format, image.size) if expected_size and image.size != expected_size: failures.append({"index": index, "error": "Returned dimensions differ from size", "requested": expected_size, "actual": image.size}) if image.format != {"png": "PNG", "jpeg": "JPEG"}[args.format]: failures.append({"index": index, "error": "Returned image format differs from output_format", "requested": args.format, "actual": image.format}) except Exception as error: failures.append({"index": index, "error": str(error)}) if failures: raise RuntimeError("Image decode/download failures: " + str(failures)) if len(data["data"]) != args.n: raise RuntimeError("Returned image count differs from requested n") if __name__ == "__main__": main() ``` ## mj.py ```python """MJ image jobs. pip install requests pillow. Never resubmit timed-out jobs.""" import argparse import hashlib import io import json import math import os import time from pathlib import Path from urllib.parse import quote import requests from PIL import Image def api(method, path, key, body=None, read_timeout=90): response = requests.request(method, "https://www.yunqiai.chat" + path, headers={"Authorization": "Bearer " + key, "Accept": "application/json"}, json=body, allow_redirects=False, timeout=(min(15, read_timeout), read_timeout)) if not 200 <= response.status_code < 300: error = RuntimeError("HTTP " + str(response.status_code) + ": " + response.text[:4000]) error.status_code = response.status_code error.retry_after = response.headers.get("Retry-After") raise error data = response.json() if data.get("error"): raise RuntimeError("API error: " + json.dumps(data["error"], ensure_ascii=False)) return data def wait_job(job_id, key, output, seconds=360): if not isinstance(seconds, (int, float)) or not math.isfinite(seconds) or seconds <= 0: raise ValueError("Wait duration must be positive and finite") deadline = time.monotonic() + seconds temporary_errors = 0 while time.monotonic() < deadline: try: data = api("GET", "/v1/tob/job/" + quote(job_id, safe=""), key, read_timeout=min(90, max(0.01, deadline - time.monotonic()))) temporary_errors = 0 except (RuntimeError, requests.exceptions.Timeout, requests.exceptions.ConnectionError) as error: if isinstance(error, requests.exceptions.SSLError): raise network_error = isinstance(error, (requests.exceptions.Timeout, requests.exceptions.ConnectionError)) if not network_error and getattr(error, "status_code", 0) not in [429, 500, 502, 503, 504]: raise temporary_errors += 1 if temporary_errors > 6: raise delay = getattr(error, "retry_after", None) pause = min(30, max(1, int(delay))) if delay and delay.isdigit() else min(30, 5 * temporary_errors) time.sleep(min(pause, max(0, deadline - time.monotonic()))) continue (output / "job.json").write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8") if data.get("status") == 2: return data if data.get("status") == 3: raise RuntimeError("Job failed; see job.json for comment and response") if data.get("status") != 1: raise RuntimeError("Unknown job status; see job.json") time.sleep(min(5, max(0, deadline - time.monotonic()))) raise TimeoutError("Local wait expired. Resume using --job " + job_id + "; do not submit again") def download_all(data, job_id, output): results = [] for index, url in enumerate(data.get("urls", [])): try: if not isinstance(url, str) or not url: raise ValueError("Empty image URL") response = requests.get(url, timeout=(15, 180)) response.raise_for_status() with Image.open(io.BytesIO(response.content)) as image: image.load() target = output / ("mj-imagine-" + job_id + "-" + str(index) + ".png") image.save(target, "PNG") results.append({"imageNo": index, "file": target.name, "size": list(image.size)}) except Exception as error: results.append({"imageNo": index, "error": str(error)}) (output / "downloads.json").write_text(json.dumps(results, ensure_ascii=False, indent=2), encoding="utf-8") if not results or any("error" in row for row in results): raise RuntimeError("Some images were not downloaded; resume the original job, see downloads.json") return results def main(): parser = argparse.ArgumentParser() parser.add_argument("--text") parser.add_argument("--job", help="Resume an existing job without resubmission") parser.add_argument("--action", default="diffusion", choices=["diffusion", "variation", "upscale", "reroll", "pan", "outpaint", "inpaint", "remix", "edit", "upload-paint", "retexture", "remove-background", "enhance"]) parser.add_argument("--body", help="JSON request body for a secondary action") parser.add_argument("--out", default="mj-results") parser.add_argument("--wait", type=int, default=360, help="Local polling duration in seconds; does not cancel the remote job") args = parser.parse_args() if args.wait <= 0: raise ValueError("--wait must be positive") key = os.environ["YUNQIAI_API_KEY"] output = Path(args.out) output.mkdir(parents=True, exist_ok=True) if args.job: job_id = args.job else: if (output / "submitted.json").exists() or (output / "submission-attempt.json").exists(): raise RuntimeError("This directory already contains a submission attempt. Resume its known job; if no id was received, confirm its state before any new submission") if args.action == "diffusion": model = os.environ["YUNQIAI_MODEL"] if model not in ["mj_imagine", "youchuan-image"] or not args.text: raise ValueError("Choose an exact MJ model and provide --text") body = {"model": model, "text": args.text} else: if not args.body: raise ValueError("Provide --body for this secondary action") body = json.loads(Path(args.body).read_text(encoding="utf-8")) route = "/v1/tob/" + args.action # An interrupted POST may already have created a billable remote task. with (output / "submission-attempt.json").open("x", encoding="utf-8") as journal: json.dump({"path": route, "request_sha256": hashlib.sha256(json.dumps(body, sort_keys=True, ensure_ascii=False).encode("utf-8")).hexdigest(), "started_at": time.time()}, journal) try: submitted = api("POST", route, key, body) except requests.exceptions.RequestException as error: raise RuntimeError("Submission state is unknown. Do not resubmit; use platform request records to recover the job id") from error (output / "submitted.json").write_text(json.dumps(submitted, ensure_ascii=False, indent=2), encoding="utf-8") job_id = submitted.get("id") if not isinstance(job_id, str) or not job_id: raise RuntimeError("Submission returned no job id; do not automatically resubmit") print("Job ID:", job_id, flush=True) if any(x not in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-" for x in job_id): raise ValueError("Unsafe job ID for local filenames") data = wait_job(job_id, key, output, seconds=args.wait) print(json.dumps(download_all(data, job_id, output), ensure_ascii=False)) if __name__ == "__main__": main() ``` ## realtime.mjs ```javascript // Node.js 22+: npm install ws@8.22.0 import WebSocket from "ws"; import { readFile, writeFile } from "node:fs/promises"; import { pathToFileURL } from "node:url"; function wav(pcm) { const header = Buffer.alloc(44); header.write("RIFF", 0); header.writeUInt32LE(36 + pcm.length, 4); header.write("WAVEfmt ", 8); header.writeUInt32LE(16, 16); header.writeUInt16LE(1, 20); header.writeUInt16LE(1, 22); header.writeUInt32LE(24000, 24); header.writeUInt32LE(48000, 28); header.writeUInt16LE(2, 32); header.writeUInt16LE(16, 34); header.write("data", 36); header.writeUInt32LE(pcm.length, 40); return Buffer.concat([header, pcm]); } export async function runRealtime({ key, model, prompt = "仅回复 READY", pcmFile, audio = false, output = "realtime.wav", timeoutMs = 120000, onEvent = () => {} }) { if (!key || !["gpt-realtime-2.1", "gpt-realtime-2.1-mini"].includes(model)) { throw new Error("Set YUNQIAI_API_KEY and the exact Realtime YUNQIAI_MODEL"); } const pcm = pcmFile ? await readFile(pcmFile) : null; if (pcm && (!pcm.length || pcm.length % 2 || pcm.subarray(0, 4).toString() === "RIFF")) { throw new Error("Input must be raw PCM16 little-endian, mono 24 kHz, without WAV header"); } const socket = new WebSocket("wss://www.yunqiai.chat/v1/realtime?model=" + encodeURIComponent(model), { headers: { Authorization: "Bearer " + key }, handshakeTimeout: 15000, }); return new Promise((resolve, reject) => { let finished = false, configured = false, started = false; const chunks = [], texts = []; const timer = setTimeout(() => finish(new Error("Realtime deadline exceeded; session is not automatically replayed")), timeoutMs); const send = (event) => socket.send(JSON.stringify(event)); function finish(error, value) { if (finished) return; finished = true; clearTimeout(timer); socket.close(); if (error) reject(error); else resolve(value); } function respond() { send({ type: "response.create", response: { output_modalities: [audio ? "audio" : "text"] } }); } socket.on("error", (error) => finish(error)); socket.on("unexpected-response", (_request, response) => { response.resume(); finish(new Error("WebSocket handshake HTTP " + response.statusCode)); }); socket.on("close", (code, reason) => { if (!finished) finish(new Error("Realtime closed before completion: " + code + " " + reason.toString())); }); socket.on("message", async (raw) => { try { const event = JSON.parse(raw.toString()); onEvent(event); if (event.type === "error") throw new Error("Realtime error: " + JSON.stringify(event.error)); if (event.type === "session.created" && !configured) { configured = true; send({ type: "session.update", session: { type: "realtime", output_modalities: [audio ? "audio" : "text"], instructions: "Follow the user's request and answer briefly.", audio: { input: { format: { type: "audio/pcm", rate: 24000 }, turn_detection: null }, output: { format: { type: "audio/pcm", rate: 24000 }, voice: "marin" }, }, } }); } if (event.type === "session.updated" && !started) { started = true; if (pcm) { for (let offset = 0; offset < pcm.length; offset += 24000) { send({ type: "input_audio_buffer.append", audio: pcm.subarray(offset, offset + 24000).toString("base64") }); } send({ type: "input_audio_buffer.commit" }); } else { send({ type: "conversation.item.create", item: { type: "message", role: "user", content: [{ type: "input_text", text: prompt }] } }); respond(); } } if (event.type === "input_audio_buffer.committed" && pcm) respond(); if (["response.output_text.delta", "response.output_audio_transcript.delta"].includes(event.type)) texts.push(event.delta); if (event.type === "response.output_audio.delta") chunks.push(Buffer.from(event.delta, "base64")); if (event.type === "response.done") { if (event.response?.status !== "completed") throw new Error("Response not completed: " + JSON.stringify(event.response?.status_details ?? event.response?.status)); const joined = Buffer.concat(chunks); if (audio) { if (!joined.length) throw new Error("Completed response contained no audio"); await writeFile(output, wav(joined)); } else if (!texts.join("").trim()) throw new Error("Completed response contained no text"); finish(null, { text: texts.join(""), audioBytes: joined.length, output: audio ? output : null, responseId: event.response.id }); } } catch (error) { finish(error); } }); }); } if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { try { const result = await runRealtime({ key: process.env.YUNQIAI_API_KEY, model: process.env.YUNQIAI_MODEL, prompt: process.env.YUNQIAI_PROMPT, pcmFile: process.env.YUNQIAI_PCM_FILE, audio: process.env.YUNQIAI_AUDIO === "1", output: process.env.YUNQIAI_AUDIO_OUT || "realtime.wav" }); console.log(JSON.stringify(result)); } catch (error) { console.error(error.message); process.exitCode = 1; } } ``` ## result.schema.json ```json { "type": "object", "properties": { "code": {"type": "string"}, "items": {"type": "array", "items": {"type": "integer"}}, "ok": {"type": "boolean"} }, "required": ["code", "items", "ok"], "additionalProperties": false } ``` ## structured.py ```python """Keep http-client.py beside this file. pip install requests jsonschema.""" import argparse import importlib.util import json import os from pathlib import Path import requests from jsonschema import Draft202012Validator SPEC = importlib.util.spec_from_file_location("yunqi_http", Path(__file__).with_name("http-client.py")) HTTP = importlib.util.module_from_spec(SPEC) SPEC.loader.exec_module(HTTP) def validate_schema(schema): def check(node): if isinstance(node, dict): if "$ref" in node and not node["$ref"].startswith("#"): raise ValueError("Example accepts only local JSON Schema references") for value in node.values(): check(value) elif isinstance(node, list): for value in node: check(value) check(schema) Draft202012Validator.check_schema(schema) def build_request(protocol, model, prompt, schema): if protocol == "gemini" or model == "claude-opus-5-5": raise ValueError("Native JSON Schema enforcement is not in the current contract for this model/protocol") validate_schema(schema) route, body = HTTP.build_body(protocol, model, prompt) if protocol == "responses": body["text"] = {"format": {"type": "json_schema", "name": "customer_result", "strict": True, "schema": schema}} elif protocol == "messages": body["output_config"] = {"format": {"type": "json_schema", "schema": schema}} else: body["response_format"] = {"type": "json_schema", "json_schema": {"name": "customer_result", "strict": True, "schema": schema}} return route, body def parse_result(protocol, data, schema): text = HTTP.inspect_result(protocol, data) result = json.loads(text) Draft202012Validator(schema).validate(result) return result def main(): parser = argparse.ArgumentParser() parser.add_argument("protocol", choices=["responses", "messages", "chat"]) parser.add_argument("--schema", required=True) parser.add_argument("--prompt", required=True) args = parser.parse_args() schema = json.loads(Path(args.schema).read_text(encoding="utf-8")) key, model = os.environ["YUNQIAI_API_KEY"], os.environ["YUNQIAI_MODEL"] route, body = build_request(args.protocol, model, args.prompt, schema) headers = {"Authorization": "Bearer " + key} if args.protocol == "messages": headers = {"x-api-key": key, "anthropic-version": "2023-06-01"} response = requests.post("https://www.yunqiai.chat" + route, headers=headers, json=body, allow_redirects=False, timeout=(15, 300)) if not 200 <= response.status_code < 300: raise RuntimeError("HTTP " + str(response.status_code) + ": " + response.text[:4000]) print(json.dumps(parse_result(args.protocol, response.json(), schema), ensure_ascii=False)) if __name__ == "__main__": main() ``` ## tools.py ```python """Complete native tool rounds. Python 3.8+, pip install requests.""" import argparse import copy import json import os import requests PARAMETERS = {"type": "object", "properties": {"order_id": {"type": "string"}}, "required": ["order_id"], "additionalProperties": False} DESCRIPTION = "Find an order's shipping information using its order_id." def lookup_order(arguments): if not isinstance(arguments, dict) or set(arguments) != {"order_id"} or arguments["order_id"] != "A17": raise ValueError("Only the example order A17 is allowed") return {"order_id": "A17", "status": "shipped", "tracking": "YQ-TOOL-9241"} def run_tools(protocol, model, key, prompt="Use lookup_order to find the tracking code for order A17. Return only its tracking code.", transport=None): if protocol == "gemini": raise ValueError("Gemini native function-result continuation is not in the current YunQi integration contract") history = [{"role": "user", "content": prompt}] used = [] for turn in range(4): if protocol == "responses": route = "/v1/responses" body = {"model": model, "input": history, "max_output_tokens": 8192, "tools": [{"type": "function", "name": "lookup_order", "description": DESCRIPTION, "parameters": PARAMETERS, "strict": True}]} headers = {"Authorization": "Bearer " + key} elif protocol == "messages": route = "/v1/messages" body = {"model": model, "messages": history, "max_tokens": 8192, "tools": [{"name": "lookup_order", "description": DESCRIPTION, "input_schema": PARAMETERS}]} headers = {"x-api-key": key, "anthropic-version": "2023-06-01"} else: route = "/v1/chat/completions" body = {"model": model, "messages": history, "max_tokens": 8192, "tools": [{"type": "function", "function": {"name": "lookup_order", "description": DESCRIPTION, "parameters": PARAMETERS}}]} headers = {"Authorization": "Bearer " + key} if transport: data = transport(turn, route, copy.deepcopy(body)) else: response = requests.post("https://www.yunqiai.chat" + route, headers=headers, json=body, allow_redirects=False, timeout=(15, 300)) if not 200 <= response.status_code < 300: raise RuntimeError("HTTP " + str(response.status_code) + ": " + response.text[:4000]) data = response.json() if data.get("error"): raise RuntimeError("Tool round API error: " + json.dumps(data["error"], ensure_ascii=False)) if protocol == "responses": if data.get("status") != "completed": raise RuntimeError("Responses tool round did not complete") output = data.get("output", []) calls = [item for item in output if item.get("type") == "function_call"] history.extend(output) text = "".join(p.get("text", "") for item in output if item.get("type") == "message" for p in item.get("content", []) if p.get("type") == "output_text") for call in calls: if call.get("name") != "lookup_order": raise ValueError("Unapproved function") result = lookup_order(json.loads(call["arguments"])) used.append(call["name"]) history.append({"type": "function_call_output", "call_id": call["call_id"], "output": json.dumps(result)}) elif protocol == "messages": if data.get("stop_reason") not in ["end_turn", "tool_use"]: raise RuntimeError("Messages tool round truncated or interrupted") blocks = data.get("content", []) calls = [block for block in blocks if block.get("type") == "tool_use"] history.append({"role": "assistant", "content": blocks}) text = "".join(block.get("text", "") for block in blocks if block.get("type") == "text") results = [] for call in calls: if call.get("name") != "lookup_order": raise ValueError("Unapproved function") result = lookup_order(call["input"]) used.append(call["name"]) results.append({"type": "tool_result", "tool_use_id": call["id"], "content": json.dumps(result)}) if results: history.append({"role": "user", "content": results}) else: choices = data.get("choices", []) if not choices or choices[0].get("finish_reason") not in ["stop", "tool_calls"]: raise RuntimeError("Chat tool round did not complete") message = choices[0]["message"] calls = message.get("tool_calls") or [] history.append(message) text = message.get("content") or "" for call in calls: if call.get("function", {}).get("name") != "lookup_order": raise ValueError("Unapproved function") result = lookup_order(json.loads(call["function"]["arguments"])) used.append(call["function"]["name"]) history.append({"role": "tool", "tool_call_id": call["id"], "content": json.dumps(result)}) if not calls: if not used or not text.strip(): raise RuntimeError("No complete tool-execution-and-answer round") return {"text": text, "tools_used": used, "turns": turn + 1} raise RuntimeError("Tool round limit reached") if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("protocol", choices=["responses", "messages", "chat"]) args = parser.parse_args() print(json.dumps(run_tools(args.protocol, os.environ["YUNQIAI_MODEL"], os.environ["YUNQIAI_API_KEY"]), ensure_ascii=False)) ```