Download API.md from a3216/ds2api: direct link, hf CLI and curl.
- Browser
- Download file 73.6 kB
-
https://huggingface.co/spaces/a3216/ds2api/resolve/fcdd4aa00c4b7afbcde9155c5ec0a9c5bceeeeb4/API.md
- Command line
-
hf download hf://spaces/a3216/ds2api@fcdd4aa00c4b7afbcde9155c5ec0a9c5bceeeeb4/API.md
-
curl -L -o API.md https://huggingface.co/spaces/a3216/ds2api/resolve/fcdd4aa00c4b7afbcde9155c5ec0a9c5bceeeeb4/API.md
DS2API 接口文档1
本文档描述当前 Go 代码库的实际 API 行为。
文档导航:总览 / 架构说明 / 部署指南 / 测试指南 / 上下文兼容流程
目录
基础信息
| 项目 | 说明 |
|---|---|
| Base URL | http://localhost:5001 或你的部署域名 |
| 默认 Content-Type | application/json |
| 健康检查 | GET /healthz、GET /readyz |
| CORS | 已启用(统一覆盖 /v1/*、/anthropic/*、/v1beta/models/*、/api/*、/admin/*;浏览器有 Origin 时回显该 Origin,否则为 *;默认允许 Content-Type, Authorization, X-API-Key, X-Ds2-Target-Account, X-Ds2-Source, X-Vercel-Protection-Bypass, X-Goog-Api-Key, Anthropic-Version, Anthropic-Beta,并会放行预检里声明的第三方请求头,如 x-stainless-*;Vercel 上 /v1/chat/completions 的 Node Runtime 也对齐相同行为;内部专用头 X-Ds2-Internal-Token 仍被拦截) |
- 所有 JSON 请求体都必须是合法 UTF-8;非法字节序列会在入站阶段被拒绝为
400 invalid json。
3.0 接口适配层说明
- OpenAI / Claude / Gemini 三套协议已统一挂在同一
chi路由树上,由internal/server/router.go负责装配。 - 适配器层职责收敛为:请求归一化 → DeepSeek 调用 → 协议形态渲染,减少历史版本中“同能力多处实现”的分叉。
- 从协议请求到纯文本 web-chat prompt 的完整链路(统一标准请求、思考格式注入、历史拆分、完成载荷组装、响应归档)见 docs/prompt-compatibility.md,该文是这条流程的源文档;本文只描述各接口的可见字段与响应形态。
- Tool Calling 的解析策略在 Go 与 Node Runtime 间保持一致:推荐模型输出半角管道符 DSML 外壳
<|DSML|tool_calls>→<|DSML|invoke name="...">→<|DSML|parameter name="...">;兼容层也接受 DSML wrapper 别名<dsml|tool_calls>、<|tool_calls>、常见 DSML 分隔符漏写形态(如<|DSML tool_calls>)、DSML与工具标签名黏连的常见 typo(如<DSMLtool_calls>)、控制分隔符漂移(如<DSML␂tool_calls>/ 原始 STX\x02)、CJK 尖括号、全角感叹号、顿号、PascalCase 本地名、弯引号属性值与属性尾部分隔符漂移(如<DSM|parameter name="command"|>...〈/DSM|parameter〉/<!DSML!invoke name=“Bash”>/<、DSML、tool_calls>/<DSmartToolCalls>/<DSMLtool_calls※>)、任意协议前缀壳(如<proto💥tool_calls>),以及旧式 canonical XML<tool_calls>→<invoke name="...">→<parameter name="...">。实现上采用结构扫描:只要固定本地标签名是tool_calls/invoke/parameter,标签名前或标签名后的非结构性分隔符会在解析入口归一化;CDATA 开头也会容错<![CDATA[/<、[CDATA[这类分隔符漂移;只有tool_callswrapper 或可修复的缺失 opening wrapper 会进入工具路径,裸<invoke>不计为已支持语法;流式场景继续执行防泄漏筛分。若参数体本身是合法 JSON 字面量(如123、true、null、数组或对象),会按结构化值输出,不再一律当作字符串;显式空字符串和纯空白参数会结构化保留为空字符串,是否拒绝缺参由工具执行侧决定;完整但 malformed 的 wrapper 会作为普通文本释放,不会吞掉或伪造成工具调用;若 CDATA 偶发漏闭合,则会在最终 parse / flush 恢复阶段做窄修复,尽量保住已完整包裹的外层工具调用。 Admin API将配置与运行时策略分开:/admin/config*管静态配置,/admin/settings*管运行时行为。- 当上游返回 thinking-only 响应(模型输出了推理链但无可见文本)时,Go 主路径与 Vercel Node 流式路径都会先自动重试一次:以多轮对话 follow-up 方式追加 prompt 后缀
"Please provide a non-empty final answer or tool call."并设置parent_message_id在同一 DeepSeek session 内让模型重新输出;同账号重试最大 1 次。若同账号重试后仍即将返回429 upstream_empty_output,托管账号模式会在返回 429 前自动切换到下一个可用账号,新建 session,用原始 payload 再 fresh retry 一次。 - 引用标记处理边界:流式输出默认隐藏
[citation:N]/[reference:N]这类上游内部占位符;非流式输出默认把 DeepSeek 搜索引用标记转换为 Markdown 引用链接。
配置最佳实践
推荐把 config.json 作为唯一配置源:
cp config.example.json config.json
# 编辑 config.json(keys/accounts)
按部署方式使用:
- 本地运行:直接读取
config.json - Docker / Vercel:从
config.json生成 Base64,填入DS2API_CONFIG_JSON,也可以直接填原始 JSON
DS2API_CONFIG_JSON="$(base64 < config.json | tr -d '\n')"
Vercel 一键部署可先只填 DS2API_ADMIN_KEY,部署后在 /admin 导入配置,再通过 “Vercel 同步” 写回环境变量。
鉴权规则
业务接口(/v1/*、/anthropic/*、/v1beta/models/*)
支持两种传参方式:
| 方式 | 示例 |
|---|---|
| Bearer Token | Authorization: Bearer <token> |
| API Key Header | x-api-key: <token>(无 Bearer 前缀) |
| Gemini 兼容 | x-goog-api-key: <token> 或 ?key=<token> / ?api_key=<token> |
鉴权行为:
- token 在
config.keys中 → 托管账号模式,自动轮询选择账号 - token 不在
config.keys中 → 直通 token 模式,直接作为 DeepSeek token 使用
可选请求头:X-Ds2-Target-Account: <email_or_mobile> — 指定使用某个托管账号;如果目标账号不存在,或管理账号队列已耗尽,相关业务请求会返回 429,当前不会附带 Retry-After 头。若账号存在但登录/刷新失败,则返回对应的 401 或上游错误。未指定目标账号时,托管账号模式的 completion 空输出 429 会先尝试切到另一个可用账号 fresh retry 一次;指定目标账号或无其他可用账号时不会切号。
Gemini 兼容客户端还可以使用 x-goog-api-key、?key= 或 ?api_key= 作为凭据来源。
Admin 接口(/admin/*)
| 端点 | 鉴权 |
|---|---|
POST /admin/login |
无需鉴权 |
GET /admin/verify |
Authorization: Bearer <jwt>(仅 JWT) |
其他 /admin/* |
Authorization: Bearer <jwt> 或 Authorization: Bearer <admin_key>(直传管理密钥) |
路由总览
| 方法 | 路径 | 鉴权 | 说明 |
|---|---|---|---|
| GET | /healthz |
无 | 存活探针 |
| HEAD | /healthz |
无 | 存活探针(无响应体) |
| GET | /readyz |
无 | 就绪探针 |
| HEAD | /readyz |
无 | 就绪探针(无响应体) |
| GET | /v1/models |
无 | OpenAI 模型列表 |
| GET | /v1/models/{id} |
无 | OpenAI 单模型查询(支持 alias 入参) |
| POST | /v1/chat/completions |
业务 | OpenAI 对话补全 |
| POST | /v1/responses |
业务 | OpenAI Responses 接口(流式/非流式) |
| GET | /v1/responses/{response_id} |
业务 | 查询已生成 response(内存 TTL) |
| POST | /v1/embeddings |
业务 | OpenAI Embeddings 接口 |
| POST | /v1/files |
业务 | OpenAI Files 上传(multipart/form-data) |
| GET | /v1/files/{file_id} |
业务 | 查询已上传文件状态 |
| GET | /anthropic/v1/models |
无 | Claude 模型列表 |
| POST | /anthropic/v1/messages |
业务 | Claude 消息接口 |
| POST | /anthropic/v1/messages/count_tokens |
业务 | Claude token 计数 |
| POST | /v1/messages |
业务 | Claude 消息快捷路径 |
| POST | /messages |
业务 | Claude 消息快捷路径 |
| POST | /v1/messages/count_tokens |
业务 | Claude token 计数快捷路径 |
| POST | /messages/count_tokens |
业务 | Claude token 计数快捷路径 |
| POST | /v1beta/models/{model}:generateContent |
业务 | Gemini 非流式 |
| POST | /v1beta/models/{model}:streamGenerateContent |
业务 | Gemini 流式 |
| POST | /v1/models/{model}:generateContent |
业务 | Gemini 非流式兼容路径 |
| POST | /v1/models/{model}:streamGenerateContent |
业务 | Gemini 流式兼容路径 |
| GET | /api/version |
无 | Ollama 版本接口 |
| GET | /api/tags |
无 | Ollama 模型列表 |
| POST | /api/show |
无 | Ollama 单模型能力查询(返回 id 与 capabilities) |
| POST | /admin/login |
无 | 管理登录 |
| GET | /admin/verify |
JWT | 校验管理 JWT |
| GET | /admin/vercel/config |
Admin | 读取 Vercel 预配置 |
| GET | /admin/config |
Admin | 读取配置(脱敏) |
| POST | /admin/config |
Admin | 更新配置 |
| GET | /admin/settings |
Admin | 读取运行时设置 |
| PUT | /admin/settings |
Admin | 更新运行时设置(热更新) |
| POST | /admin/settings/password |
Admin | 更新 Admin 密码并使旧 JWT 失效 |
| GET | /admin/skills |
Admin | 读取技能库配置(全局开关、截断长度、条目列表) |
| PUT | /admin/skills |
Admin | 整体替换技能库配置(校验失败返回 400) |
| POST | /admin/config/import |
Admin | 导入配置(merge/replace) |
| GET | /admin/config/export |
Admin | 导出完整配置(含 config/json/base64) |
| POST | /admin/keys |
Admin | 添加 API key(可附 name/remark) |
| PUT | /admin/keys/{key} |
Admin | 更新 API key 备注信息 |
| DELETE | /admin/keys/{key} |
Admin | 删除 API key |
| GET | /admin/proxies |
Admin | 代理列表 |
| POST | /admin/proxies |
Admin | 添加代理 |
| PUT | /admin/proxies/{proxyID} |
Admin | 更新代理(留空 password 表示保留原密码) |
| DELETE | /admin/proxies/{proxyID} |
Admin | 删除代理(自动解绑引用该代理的账号) |
| POST | /admin/proxies/test |
Admin | 测试代理连通性 |
| GET | /admin/accounts |
Admin | 分页账号列表 |
| POST | /admin/accounts |
Admin | 添加账号 |
| PUT | /admin/accounts/{identifier} |
Admin | 更新账号 name/remark |
| DELETE | /admin/accounts/{identifier} |
Admin | 删除账号 |
| PUT | /admin/accounts/{identifier}/proxy |
Admin | 为账号绑定/解绑代理 |
| GET | /admin/queue/status |
Admin | 账号队列状态 |
| POST | /admin/accounts/test |
Admin | 测试单个账号 |
| POST | /admin/accounts/test-all |
Admin | 测试全部账号 |
| POST | /admin/accounts/sessions/delete-all |
Admin | 删除某账号的全部会话 |
| POST | /admin/import |
Admin | 批量导入 keys/accounts |
| POST | /admin/test |
Admin | 测试当前 API 可用性 |
| POST | /admin/dev/raw-samples/capture |
Admin | 直接发起一次请求并保存为 raw sample |
| GET | /admin/dev/raw-samples/query |
Admin | 按问题关键词查询当前内存抓包链 |
| POST | /admin/dev/raw-samples/save |
Admin | 把命中的内存抓包链保存为 raw sample |
| POST | /admin/vercel/sync |
Admin | 同步配置到 Vercel |
| GET | /admin/vercel/status |
Admin | Vercel 同步状态 |
| POST | /admin/vercel/status |
Admin | Vercel 同步状态 / 草稿对比 |
| GET | /admin/export |
Admin | 导出配置 JSON/Base64 |
| GET | /admin/dev/captures |
Admin | 查看本地抓包记录 |
| DELETE | /admin/dev/captures |
Admin | 清空本地抓包记录 |
| GET | /admin/chat-history |
Admin | 查看服务器端对话记录(支持按 caller/status/surface/关键词筛选与分页) |
| DELETE | /admin/chat-history |
Admin | 清空服务器端对话记录 |
| GET | /admin/chat-history/{id} |
Admin | 查看单条服务器端对话记录 |
| DELETE | /admin/chat-history/{id} |
Admin | 删除单条服务器端对话记录 |
| PUT | /admin/chat-history/settings |
Admin | 更新对话记录保留条数(0..500) |
| GET | /admin/chat-history/export |
Admin | 按相同筛选条件导出对话记录(json / markdown) |
| GET | /admin/version |
Admin | 查询当前版本与最新 Release |
OpenAI /v1/* 仍是规范路径。对于只配置 DS2API 根地址的客户端,同一套 OpenAI handler 也通过根路径快捷路由暴露:/models、/models/{id}、/chat/completions、/responses、/responses/{response_id}、/embeddings、/files、/files/{file_id}。
服务器端记录本质上是 DeepSeek 上游响应归档:OpenAI Chat、OpenAI Responses、Claude Messages、Gemini GenerateContent 等直连 DeepSeek 的生成接口,在收到上游响应后会于各协议回译/裁剪前写入记录;列表按请求创建时间倒序展示,流式请求会在生成过程中持续刷新状态与详情。WebUI「API 测试」发出的请求也会进入该记录。每条记录带 caller_id(API Key 调用方)与 caller_label(Key 的名称/备注/脱敏值),以及 surface(来源接口)、status、status_code 等可筛选字段;列表、详情、导出共用同一份数据,详见 对话记录。
健康检查
GET /healthz
{"status": "ok"}
GET /readyz
{"status": "ready"}
OpenAI 兼容接口
GET /v1/models
无需鉴权。返回当前支持的 DeepSeek 原生模型列表。
响应示例:
{
"object": "list",
"data": [
{"id": "deepseek-v4-flash", "object": "model", "created": 1677610602, "owned_by": "deepseek", "permission": []},
{"id": "deepseek-v4-flash-nothinking", "object": "model", "created": 1677610602, "owned_by": "deepseek", "permission": []},
{"id": "deepseek-v4-flash-search", "object": "model", "created": 1677610602, "owned_by": "deepseek", "permission": []},
{"id": "deepseek-v4-flash-search-nothinking", "object": "model", "created": 1677610602, "owned_by": "deepseek", "permission": []}
]
}
说明:
/v1/models返回的是规范化后的 DeepSeek 原生模型 ID;常见 alias 仅用于请求入参解析,不会在该接口中单独展开返回。带-nothinking后缀的模型表示无论请求里是否显式开启 thinking / reasoning,都会强制关闭思考输出。模型系列已收敛到上表 4 个 flash 变体:
deepseek-v4-pro/deepseek-v4-pro-search/deepseek-v4-vision及其-nothinking形式已下线,不再出现在本接口、Ollama/api/tags与/anthropic/v1/models中,但旧名字仍可被请求,解析时静默映射到 flash 对应项(见下一节)。
模型 alias 解析策略
对 chat / responses / embeddings 的 model 字段采用“宽进严出”:
- 先匹配 DeepSeek 原生模型。
- 再匹配
model_aliases精确映射。 - 如果请求名以
-nothinking结尾,则在最终解析出的规范模型上追加对应的无思考变体。 - 仍未命中则返回
invalid_request_error。当前不会按未知模型家族做启发式兜底;需要新增兼容名时请通过model_aliases明确配置。
当前内置默认 alias 来自 internal/config/models.go,config.model_aliases 会在运行时覆盖或补充同名映射。节选(全部指向 flash 系列):
- OpenAI / Codex:
gpt-4o、gpt-4.1、gpt-5、gpt-5.5、gpt-5-codex、gpt-5.3-codex、codex-mini-latest→deepseek-v4-flash - OpenAI reasoning:
o1、o3、o4-mini→deepseek-v4-flash;o3-deep-research、o4-mini-deep-research→deepseek-v4-flash-search - Claude:
claude-opus-4-6、claude-sonnet-4-6、claude-haiku-4-5、claude-3-5-sonnet-latest→deepseek-v4-flash - Gemini:
gemini-2.5-pro、gemini-2.5-flash、gemini-3.1-pro、gemini-3-pro、gemini-3-flash、gemini-3.1-flash-lite、gemini-pro-vision→deepseek-v4-flash - 其他内置精确 alias:
llama-3.1-70b-instruct、qwen-max→deepseek-v4-flash
上述 alias 若在请求名后追加 -nothinking 后缀,也会映射到对应的强制关闭 thinking 版本。
已下线的 DeepSeek 模型名不会让老客户端失效,解析时静默归一:
| 请求模型名 | 实际解析结果 |
|---|---|
deepseek-v4-pro / deepseek-v4-vision |
deepseek-v4-flash |
deepseek-v4-pro-search |
deepseek-v4-flash-search |
deepseek-v4-pro-nothinking / deepseek-v4-vision-nothinking |
deepseek-v4-flash-nothinking |
deepseek-v4-pro-search-nothinking |
deepseek-v4-flash-search-nothinking |
上游 model_type 现在只剩 default(历史配置里出现的 expert / vision 按 default 处理),因此不存在独立的视觉或 expert 通道。
退役历史模型(如 claude-1.*、claude-2.*、claude-instant-*、gpt-3.5*)会被显式拒绝。
思考与联网搜索解析策略
同一套规则适用于 /v1/chat/completions、/v1/responses、/anthropic/v1/messages 与 Gemini generateContent,实现在 internal/util/thinking.go、internal/util/search.go,不在各协议适配器里。
思考(thinking),优先级从低到高:
| 顺序 | 来源 | 说明 |
|---|---|---|
| 1 | 模型默认值 | -nothinking 变体默认关;其余 flash 模型默认开 |
| 2 | 客户端显式覆盖 | 命中任一字段即生效 |
| 3 | -nothinking 模型 |
强制关闭,优先级最高,客户端无法打开 |
客户端覆盖字段(extra_body 里的同名键同样会被读取):
| 字段 | 接受写法 |
|---|---|
thinking |
bool;"enabled" / "disabled"(含 on / off / true / false / none);{"type": ...} |
reasoning |
{"effort": ...}、{"type": ...}、{"enabled": ...};字符串按 effort 解析 |
reasoning_effort |
minimal / low / medium / high / xhigh / max = 开启;none / disabled / disable / off / false = 关闭 |
Kelivo 这类客户端的「思维强度」界面就是靠 reasoning_effort 传开关:不发该字段表示跟随默认(开启思考),传 none 表示关闭。本项目只区分开/关,不区分强度档位。
联网搜索(search),优先级为「模型默认 → 客户端显式信号优先」:
| 信号 | 说明 |
|---|---|
enable_search |
bool,或 "true" / "false" / "on" / "off" / "enabled" / "disabled" 等字符串 |
web_search_options |
字段出现即视为开启 |
search_options |
字段出现即视为开启 |
tools 原生搜索类型 |
type 以 web_search 开头、type=google_search,或工具对象含 googleSearch / google_search / web_search 字段 |
-search 变体默认开启联网;显式 enable_search=false 可在该模型上临时关掉。普通 function 工具(例如 Kelivo 客户端自己执行的 search_web)不算联网信号,所以默认仍不开启上游联网——要联网请选 -search 模型;一旦客户端开始发送上述原生信号,服务端会自动跟随,无需改配置。
POST /v1/chat/completions
路径说明:除规范路径
/v1/chat/completions外,也支持根路径快捷别名/chat/completions。在 Vercel Runtime 上,vercel.json仅把规范路径/v1/chat/completions重写到 Node 流式桥接;根路径快捷别名仍走 Go 主链路。因此 Vercel 上需要实时流式时请使用/v1/chat/completions。
请求头:
Authorization: Bearer your-api-key
Content-Type: application/json
请求体:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
model |
string | ✅ | 支持 DeepSeek 原生模型 + 常见 alias(如 gpt-5.5、gpt-5.4-mini、gpt-5.3-codex、o3、claude-opus-4-6、claude-sonnet-4-6、gemini-2.5-pro、gemini-3.1-pro、gemini-3-flash 等);若模型名带 -nothinking 后缀,则强制关闭 thinking / reasoning |
messages |
array | ✅ | OpenAI 风格消息数组 |
stream |
boolean | ❌ | 默认 false |
tools |
array | ❌ | Function Calling 定义 |
thinking |
bool / string / object | ❌ | 显式思考开关,覆盖模型默认;-nothinking 模型会忽略 |
reasoning / reasoning_effort |
object / string | ❌ | 与 thinking 等价的另一种写法;reasoning_effort=none 关闭思考,minimal~`max` 开启 |
enable_search |
bool / string | ❌ | 显式联网开关,覆盖模型默认(用于在 -search 模型上临时关闭) |
temperature 等 |
any | ❌ | 兼容透传字段(最终效果由上游决定) |
思考与联网的完整解析优先级见 思考与联网搜索解析策略。
非流式响应
{
"id": "<chat_session_id>",
"object": "chat.completion",
"created": 1738400000,
"model": "deepseek-v4-flash",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "最终回复",
"reasoning_content": "思考内容(开启 thinking 时)"
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 10,
"completion_tokens": 20,
"total_tokens": 30,
"completion_tokens_details": {
"reasoning_tokens": 5
}
}
}
流式响应(stream=true)
SSE 格式:每段为 data: <json>\n\n,结束为 data: [DONE]。
data: {"id":"...","object":"chat.completion.chunk","choices":[{"delta":{"role":"assistant"},"index":0}]}
data: {"id":"...","object":"chat.completion.chunk","choices":[{"delta":{"reasoning_content":"..."},"index":0}]}
data: {"id":"...","object":"chat.completion.chunk","choices":[{"delta":{"content":"..."},"index":0}]}
data: {"id":"...","object":"chat.completion.chunk","choices":[{"delta":{},"index":0,"finish_reason":"stop"}],"usage":{...}}
data: [DONE]
字段说明:
- 首个 delta 包含
role: assistant - 开启 thinking 时会输出
delta.reasoning_content - 普通文本输出
delta.content - 最后一段包含
finish_reason和usage - token 计数优先透传上游 DeepSeek SSE(如
accumulated_token_usage/token_usage);仅在上游缺失时回退本地估算。失败/中断型结束(例如response.failed)可能不会携带usage
Tool Calls
若
tools.enabled=false(默认),请求里的tools会被忽略,响应不会包含tool_calls。下面描述的行为只在开启工具调用后生效,开启风险见 docs/prompt-compatibility.md。
当请求中含 tools 时,DS2API 做防泄漏处理:
非流式:识别到工具调用时,返回 message.tool_calls,设置 finish_reason=tool_calls,message.content=null。
{
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_xxx",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\":\"beijing\"}"
}
}
]
},
"finish_reason": "tool_calls"
}
]
}
流式:命中高置信特征后立即输出 delta.tool_calls(不等待完整工具参数闭合),并持续发送 arguments 增量;已确认的工具调用片段不会回流到 delta.content。
补充说明:
- 非代码块上下文下,工具负载即使与普通文本混合,也会按特征识别并产出可执行 tool call(前后普通文本仍可透传)。
- 解析器当前把推荐半角管道符 DSML 外壳(
<|DSML|tool_calls>/<|DSML|invoke name="...">/<|DSML|parameter name="...">)、DSML wrapper 别名(<dsml|tool_calls>、<|tool_calls>)、常见 DSML 分隔符漏写形态(如<|DSML tool_calls>/<|DSML invoke>/<|DSML parameter>)、DSML与工具标签名黏连的常见 typo(如<DSMLtool_calls>/<DSMLinvoke>/<DSMLparameter>)、控制分隔符漂移(如<DSML␂tool_calls>/ 原始 STX\x02)、CJK 尖括号、全角感叹号、顿号、PascalCase 本地名、弯引号属性值与属性尾部分隔符漂移(如<DSM|parameter name="command"|>...〈/DSM|parameter〉/<!DSML!invoke name=“Bash”>/<、DSML、tool_calls>/<DSmartToolCalls>/<DSMLtool_calls※>)、任意协议前缀壳(如<proto💥tool_calls>)和旧式 canonical XML 工具块(<tool_calls>/<invoke name="...">/<parameter name="...">)作为可执行调用解析;这些非结构性分隔符壳会先归一化回 XML,内部仍以 XML 解析语义为准,CDATA 开头也会容错<![CDATA[/<、[CDATA[。旧式<tools>、<tool_call>、<tool_name>、<param>、<function_call>、tool_use、antml 风格与纯 JSONtool_calls片段默认都会按普通文本处理;完整但 malformed 的 wrapper 同样会作为普通文本释放。 - 解析层不会因为参数值为空而丢弃工具调用;显式空字符串或纯空白参数会按空字符串进入结构化
tool_calls。Prompt 会要求模型不要主动输出空参数,缺参/空命令的拒绝应由工具执行侧或客户端 schema 校验负责。 - 当最终可见正文为空但思维链里包含可执行工具调用时,Chat / Responses 会在收尾阶段补发标准 OpenAI
tool_calls/function_call输出;如果客户端未开启 thinking / reasoning,该思维链只用于检测,不会作为可见正文或reasoning_content暴露。 - Markdown fenced code block(例如
json ...)和行内 code span(例如`<tool_calls>...</tool_calls>`)中的tool_calls仅视为示例文本,不会被执行。
GET /v1/models/{id}
无需鉴权。入参支持 alias(例如 gpt-4o),返回的是映射后的 DeepSeek 模型对象。
POST /v1/responses
OpenAI Responses 风格接口,兼容 input 或 messages。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
model |
string | ✅ | 支持原生模型 + alias 自动映射 |
input |
string/array/object | ❌ | 与 messages 二选一 |
messages |
array | ❌ | 与 input 二选一 |
instructions |
string | ❌ | 自动前置为 system 消息 |
stream |
boolean | ❌ | 默认 false |
tools |
array | ❌ | 与 chat 同样的工具识别与转译策略(含代码块示例豁免) |
tool_choice |
string/object | ❌ | 支持 auto/none/required 与强制函数({"type":"function","name":"..."}) |
非流式响应:返回标准 response 对象,id 形如 resp_xxx,并写入内存 TTL 存储。
当 tool_choice=required 且未产出有效工具调用时,返回 HTTP 422(error.code=tool_choice_violation)。
流式响应(SSE):最小事件序列如下。
event: response.created
data: {"type":"response.created","id":"resp_xxx","status":"in_progress",...}
event: response.output_item.added
data: {"type":"response.output_item.added","response_id":"resp_xxx","item":{"type":"message|function_call",...},...}
event: response.content_part.added
data: {"type":"response.content_part.added","response_id":"resp_xxx","part":{"type":"output_text",...},...}
event: response.output_text.delta
data: {"type":"response.output_text.delta","response_id":"resp_xxx","item_id":"msg_xxx","output_index":0,"content_index":0,"delta":"..."}
event: response.function_call_arguments.delta
data: {"type":"response.function_call_arguments.delta","response_id":"resp_xxx","call_id":"call_xxx","delta":"..."}
event: response.function_call_arguments.done
data: {"type":"response.function_call_arguments.done","response_id":"resp_xxx","call_id":"call_xxx","name":"tool","arguments":"{...}"}
event: response.content_part.done
data: {"type":"response.content_part.done","response_id":"resp_xxx",...}
event: response.output_item.done
data: {"type":"response.output_item.done","response_id":"resp_xxx","item":{"type":"message|function_call",...},...}
event: response.completed
data: {"type":"response.completed","response":{...}}
data: [DONE]
流式场景下若 tool_choice=required 违规,会返回 response.failed 后结束(不再发送 response.completed)。
当前版本说明:解析层默认“尽量提取结构化 tool call”,未启用基于
toolsallow-list 的硬拒绝;是否执行仍应由你的工具执行器做白名单校验。
GET /v1/responses/{response_id}
需要业务鉴权。查询 POST /v1/responses 生成并缓存的 response 对象(按调用方鉴权隔离,仅同一 key/token 可读取)。
当前为内存 TTL 存储,默认过期时间
900s(可用responses.store_ttl_seconds调整)。
POST /v1/embeddings
需要业务鉴权。返回 OpenAI Embeddings 兼容结构。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
model |
string | ✅ | 支持原生模型 + alias 自动映射 |
input |
string/array | ✅ | 支持字符串、字符串数组、token 数组 |
需配置
embeddings.provider。当前支持:mock/deterministic/builtin(三者都走同一套本地确定性实现)。未配置或不支持时返回标准错误结构(HTTP 501)。
POST /v1/files
需要业务鉴权。兼容 OpenAI Files 上传接口,当前仅支持 multipart/form-data。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
file |
file | ✅ | 上传文件二进制 |
purpose |
string | ❌ | 透传到上游用途字段 |
约束与行为:
- 请求必须为
multipart/form-data,否则返回400。 - 请求体总大小上限 100 MiB(超限返回
413)。 - 成功返回 OpenAI
file对象(id/object/bytes/filename/purpose/status等字段),并附带account_id便于定位来源账号。
GET /v1/files/{file_id}
需要业务鉴权。查询 DeepSeek 上传文件的当前状态,并返回 OpenAI file 对象;未找到匹配文件时返回 404。
Claude 兼容接口
除标准路径 /anthropic/v1/* 外,还支持快捷路径 /v1/messages、/messages、/v1/messages/count_tokens、/messages/count_tokens。
实现上统一走 OpenAI Chat Completions 解析与回译链路,避免多套解析逻辑分叉维护。
GET /anthropic/v1/models
无需鉴权。
响应示例:
{
"object": "list",
"data": [
{"id": "claude-sonnet-4-6", "object": "model", "created": 1715635200, "owned_by": "anthropic"},
{"id": "claude-sonnet-4-6-nothinking", "object": "model", "created": 1715635200, "owned_by": "anthropic"},
{"id": "claude-haiku-4-5", "object": "model", "created": 1715635200, "owned_by": "anthropic"},
{"id": "claude-haiku-4-5-nothinking", "object": "model", "created": 1715635200, "owned_by": "anthropic"},
{"id": "claude-opus-4-6", "object": "model", "created": 1715635200, "owned_by": "anthropic"},
{"id": "claude-opus-4-6-nothinking", "object": "model", "created": 1715635200, "owned_by": "anthropic"}
],
"first_id": "claude-opus-4-6",
"last_id": "claude-3-haiku-20240307-nothinking",
"has_more": false
}
说明:示例仅展示部分模型;实际返回除当前主别名外,还包含 Claude 4.x snapshots、3.x 历史模型 ID 与常见别名,并为这些可映射模型额外提供
-nothinking变体。
POST /anthropic/v1/messages
请求头:
x-api-key: your-api-key
Content-Type: application/json
anthropic-version: 2023-06-01
anthropic-version可省略,服务端会自动补为2023-06-01。
请求体:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
model |
string | ✅ | 例如 claude-sonnet-4-6 / claude-opus-4-6 / claude-haiku-4-5(兼容 claude-sonnet-4-5、claude-3-5-haiku-latest),并支持历史 Claude 模型 ID;若模型名带 -nothinking 后缀,则强制关闭 thinking / reasoning |
messages |
array | ✅ | Claude 风格消息数组 |
max_tokens |
number | ❌ | 缺省自动补 8192;当前实现不会硬性截断上游输出 |
stream |
boolean | ❌ | 默认 false |
system |
string | ❌ | 可选系统提示 |
tools |
array | ❌ | Claude tool 定义 |
thinking |
object | ❌ | Anthropic thinking 配置;会转译为下游 reasoning 控制,-nothinking 模型会忽略 |
temperature |
number | ❌ | 透传到下游;若同时提供 top_p,以 temperature 为准 |
top_p |
number | ❌ | 当未提供 temperature 时透传到下游 |
stop_sequences |
array | ❌ | 透传到下游停用序列 |
tool_choice |
string/object | ❌ | 支持 auto / none / required / {"type":"function","name":"..."},并会转译为下游工具选择 |
说明:上述
thinking、temperature、top_p、stop_sequences、tool_choice都会走兼容层转译;最终是否生效仍取决于当前模型和上游能力。temperature与top_p同时存在时,temperature优先。思考与联网的解析优先级与 OpenAI 接口完全一致,见 思考与联网搜索解析策略。若tools.enabled=false(默认),请求里的tools会被忽略,响应不会包含tool_calls(Claude 侧即tool_use块)。
非流式响应
{
"id": "msg_1738400000000000000",
"type": "message",
"role": "assistant",
"model": "claude-sonnet-4-6",
"content": [
{"type": "text", "text": "回复内容"}
],
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
"input_tokens": 12,
"output_tokens": 34
}
}
若识别到工具调用,stop_reason=tool_use,content 中返回 tool_use block。
流式响应(stream=true)
SSE 使用 event: + data: 双行格式,JSON 中保留 type 字段。
event: message_start
data: {"type":"message_start","message":{...}}
event: content_block_start
data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"hello"}}
event: ping
data: {"type":"ping"}
event: content_block_stop
data: {"type":"content_block_stop","index":0}
event: message_delta
data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"output_tokens":12}}
event: message_stop
data: {"type":"message_stop"}
说明:
- 默认支持 thinking 的模型会输出
thinkingblock /thinking_delta;请求显式关闭 thinking 或使用-nothinking模型时不会输出 - 带
-nothinking后缀的模型会强制关闭 thinking,即使请求显式传了thinking/reasoning/reasoning_effort也不会输出thinking_delta - 不会输出
signature_delta(上游 DeepSeek 未提供可验证签名) tools场景优先避免泄露原始工具 JSON,不强制发送input_json_delta
POST /anthropic/v1/messages/count_tokens
请求:
{
"model": "claude-sonnet-4-6",
"messages": [
{"role": "user", "content": "你好"}
]
}
响应:
{
"input_tokens": 5
}
Gemini 兼容接口
支持路径:
/v1beta/models/{model}:generateContent/v1beta/models/{model}:streamGenerateContent/v1/models/{model}:generateContent(兼容路径)/v1/models/{model}:streamGenerateContent(兼容路径)
鉴权方式同业务接口(Authorization: Bearer <token> 或 x-api-key)。
实现上统一走 OpenAI Chat Completions 解析与回译链路,避免多套解析逻辑分叉维护。
POST /v1beta/models/{model}:generateContent
请求体兼容 Gemini contents / tools 字段,模型名可用 alias 自动映射到 DeepSeek 模型;若路径中的模型名带 -nothinking 后缀,则最终会映射到对应的无思考模型。思考与联网的解析优先级与 OpenAI 接口一致,见 思考与联网搜索解析策略。若 tools.enabled=false(默认),请求里的 tools 会被忽略,响应不会包含 tool_calls(Gemini 侧即 functionCall)。
响应为 Gemini 兼容结构,核心字段包括:
candidates[].content.parts[].textcandidates[].content.parts[].thought=true(thinking 输出)candidates[].content.parts[].functionCall(工具调用时)usageMetadata(promptTokenCount/candidatesTokenCount/totalTokenCount)
POST /v1beta/models/{model}:streamGenerateContent
返回 SSE(text/event-stream),每个 chunk 为一条 data: <json>:
- 常规文本:持续返回增量文本 chunk
- thinking:持续返回
parts[].thought=true的增量 chunk tools场景:会缓冲并在结束时输出functionCall结构- 结束 chunk:包含
finishReason: "STOP"与usageMetadata - token 计数优先透传上游 DeepSeek SSE(如
accumulated_token_usage/token_usage);仅在上游缺失时回退本地估算
Ollama 兼容接口
POST /api/show请求体:{"model":"<model-id>"}。GET /api/tags只列出 flash 系列的 4 个模型 ID(deepseek-v4-flash、deepseek-v4-flash-search及各自-nothinking变体),与/v1/models保持一致;已下线的 pro / vision 名字不再出现,但仍可作为model入参被请求。- 响应字段使用小写
id(不是ID),并返回capabilities数组,便于与 Ollama 风格客户端/严格 schema 对齐。
示例响应:
{
"id": "deepseek-v4-flash",
"capabilities": ["tools", "thinking"]
}
Admin 接口
POST /admin/login
无需鉴权。
请求:
{
"admin_key": "admin",
"expire_hours": 24
}
expire_hours 可省略,默认 24。
响应:
{
"success": true,
"token": "<jwt>",
"expires_in": 86400
}
GET /admin/verify
需要 JWT:Authorization: Bearer <jwt>
响应:
{
"valid": true,
"expires_at": 1738400000,
"remaining_seconds": 72000
}
GET /admin/vercel/config
返回 Vercel 预配置状态。优先读取环境变量,其次回退到已保存的 vercel 配置块。
{
"has_token": true,
"token_preview": "vc****en",
"token_source": "config",
"project_id": "prj_xxx",
"team_id": null
}
GET /admin/config
返回脱敏后的配置,包含 keys 与 api_keys。
{
"keys": ["k1", "k2"],
"api_keys": [
{"key": "k1", "name": "主 Key", "remark": "生产流量"},
{"key": "k2", "name": "备用 Key", "remark": "压测"}
],
"env_backed": false,
"env_source_present": true,
"env_writeback_enabled": true,
"config_path": "/data/config.json",
"vercel": {
"has_token": true,
"token_preview": "vc****en",
"project_id": "prj_xxx",
"team_id": ""
},
"accounts": [
{
"identifier": "user@example.com",
"email": "user@example.com",
"mobile": "",
"has_password": true,
"has_token": true,
"token_preview": "abcde..."
}
],
"model_aliases": {
"claude-sonnet-4-6": "deepseek-v4-flash",
"claude-opus-4-6": "deepseek-v4-flash"
}
}
POST /admin/config
只更新 keys、api_keys、accounts、model_aliases。
如果同时发送 api_keys 与 keys,优先保留 api_keys 中的结构化 name / remark;keys 仅作为旧格式兼容回退。
请求:
{
"keys": ["k1", "k2"],
"api_keys": [
{"key": "k1", "name": "主 Key", "remark": "生产流量"},
{"key": "k2", "name": "备用 Key", "remark": "压测"}
],
"accounts": [
{"email": "user@example.com", "password": "pwd", "token": ""}
],
"model_aliases": {
"claude-sonnet-4-6": "deepseek-v4-flash",
"claude-opus-4-6": "deepseek-v4-flash"
}
}
api_keys[].thinking_injection(单 Key 覆盖思考格式注入)
api_keys 的每一项都可以带 thinking_injection,用于给单个 API Key 定制注入提示词;keys 旧格式不支持该字段。
{
"api_keys": [
{
"key": "k1",
"name": "主 Key",
"remark": "生产流量",
"thinking_injection": {
"custom": true,
"enabled": true,
"prompt": "回答前先列清楚已知条件,再给结论。"
}
},
{"key": "k2", "name": "备用 Key"}
]
}
| 字段 | 说明 |
|---|---|
custom |
默认 false。为 false(或整段省略)时完全跟随全局 thinking_injection |
enabled |
仅在 custom=true 时生效;省略表示继续跟随全局开关 |
prompt |
仅在 custom=true 时生效;为空或全空白表示继续跟随全局提示词 |
全局开关与默认提示词见 GET /admin/settings 的 thinking_injection;注入位置与默认提示词内容见 docs/prompt-compatibility.md。
api_keys[].prompt_profile(单 Key 覆盖提示词特化层)
api_keys 的每一项都可以带 prompt_profile,用于给单个 API Key 指定特化层预设或正文,与 thinking_injection、skills 并列;keys 旧格式不支持该字段。该字段目前只在配置文件/导入导出里生效,后台面板只提供全局段(见下)。
{
"api_keys": [
{
"key": "k1",
"name": "写作 Key",
"prompt_profile": {"custom": true, "enabled": true, "name": "writing"}
},
{"key": "k2", "name": "默认 Key"}
]
}
| 字段 | 说明 |
|---|---|
custom |
默认 false;为 false 或整段省略时完全跟随全局 prompt_profile |
enabled |
仅在 custom=true 时生效;为 false 表示该 Key 不注入特化层 |
name |
仅在 custom=true 时生效;default / writing,非空时覆盖全局预设 |
prompt |
仅在 custom=true 时生效;非空时覆盖预设正文(上限 20000 字符) |
全局 prompt_profile.enabled=false 时上表全部不生效,任何 Key 都不注入。
prompt_style(内置提示词风格,全局)
顶层字符串字段,只影响内置文案,不影响你自己配置的提示词:
{"prompt_style": "legacy"}
| 值 | 行为 |
|---|---|
v2(缺省,也接受空串) |
历史拆分后的衔接指令带「执行要求」(上文要求逐条满足)并注入内置执行契约(思考注入) |
legacy |
衔接指令换回 ce5a019 时代的短文案,且不注入内置执行契约;显式配置过 thinking_injection.prompt 时仍然注入自定义内容 |
未知值不会让配置加载失败:按 v2 处理并打一条告警。后台 PUT /admin/settings 传未知值直接返回 400。
model_profiles(自定义模型列表,全局)
给同一个 flash 基模挂出多个对外模型名,每个名字带自己的提示词。客户端请求里写哪个模型名,就用哪一份提示词,互不影响。
{
"model_profiles": [
{
"id": "deepseek-writer",
"base": "deepseek-v4-flash",
"name": "写作",
"remark": "长文与改稿",
"prompt": "## 写作要求\n…"
}
]
}
| 字段 | 说明 |
|---|---|
id |
必填,对外模型名。字符集 [A-Za-z0-9._-],长度 1-64,不可与内置模型名(如 deepseek-v4-flash)冲突,条目间不可重复(大小写不敏感) |
base |
必填,基模,只能是 deepseek-v4-flash / -search / -nothinking / -search-nothinking |
name / remark |
可选,仅用于后台与导出可读性 |
prompt |
可选,该模型专属提示词,上限 20000 字符;为空表示不额外注入 |
解析与注入语义:
- 模型名解析优先于内置别名映射:
id命中自定义模型时按它的base调用上游(/v1/models、/v1/models/{id}、/v1/chat/completions、Claude / Gemini 入口共用同一套解析)。 /v1/models会把自定义模型一起列出来,owned_by为ds2api。- 提示词注入点与
prompt_profile相同(历史拆分之后、技能层之前),两者互斥:该模型配了非空prompt就用它,否则回落到全局prompt_profile。 - 数量上限 50 条。接口见
GET/PUT /admin/model_profiles。
api_keys[].skills(单 Key 覆盖技能注入)
api_keys 的每一项都可以带 skills,用来指定这个 API Key 注入哪些技能,与 thinking_injection 并列;keys 旧格式不支持该字段。
{
"api_keys": [
{"key": "k1", "name": "主 Key", "skills": {"custom": true, "enabled": true, "ids": ["sql-report"]}},
{"key": "k2", "name": "备用 Key"}
]
}
| 字段 | 说明 |
|---|---|
custom |
默认 false;为 false 或整段省略时使用全部 skills.items |
enabled |
仅在 custom=true 时生效;为 false 表示该 Key 不注入 |
ids |
仅在 custom=true 时生效;非空时只用命中的技能,为空时该 Key 不注入 |
custom |
enabled |
ids |
结果 |
|---|---|---|---|
省略或 false |
任意 | 任意 | 使用全部 items |
true |
false |
任意 | 不注入 |
true |
省略或 true |
非空 | 只用命中的 items |
true |
省略或 true |
空 | 不注入 |
全局 skills.enabled=false 时上表全部不生效,任何 Key 都不注入。技能正文与管理接口见 GET / PUT /admin/skills。
GET /admin/settings
读取运行时设置与状态,返回:
successadmin(has_password_hash、jwt_expire_hours、jwt_valid_after_unix、default_password_warning)runtime(account_max_inflight、account_max_queue、global_max_inflight、token_refresh_interval_hours)runtime.token_refresh_interval_hours的语义是「会话体检间隔」:到期只做一次无密码复核(GET /api/v0/users/current),只有服务端明确拒绝会话时才回退到密码登录config_snapshot:原始配置快照(admin/runtime/responses/embeddings/auto_delete/current_input_file/thinking_injection/prompt_profile/prompt_style/model_profiles/model_aliases),用于区分环境变量覆盖前的配置responses/embeddingsauto_delete(mode:none/single/all;旧配置sessions=true仍按all处理)current_input_file(enabled默认返回true、min_chars)thinking_injection(enabled缺省为true、prompt、default_prompt);prompt为空时使用内置默认提示词,单 Key 可用api_keys[].thinking_injection覆盖prompt_profile(enabled缺省false、enabled_default固定false、name、prompt、presets);presets是[{name, body}]的内置预设清单,body为该预设正文,供面板在输入框里显示「留空时使用的默认正文」prompt_style(mode:生效风格v2/legacy;modes:可选值;default:v2;value_raw:配置里原样存的值)model_profiles(count、max_count、base_models、max_id_chars、max_prompt_chars);条目本身用/admin/model_profiles读写model_aliasestools(enabled:工具调用总开关,缺省false;default_enabled:固定false,供面板显示默认值)env_backed、needs_vercel_synctoolcall策略已固定为feature_match + high,不再通过 settings 返回或修改
PUT /admin/settings
热更新运行时设置。支持更新:
admin.jwt_expire_hoursruntime.account_max_inflight/runtime.account_max_queue/runtime.global_max_inflight/runtime.token_refresh_interval_hoursresponses.store_ttl_secondsembeddings.providerauto_delete.modecurrent_input_file.enabled/current_input_file.min_charsthinking_injection.enabled/thinking_injection.promptprompt_profile.enabled/prompt_profile.name/prompt_profile.promptprompt_styletools.enabledmodel_aliasestoolcall策略已固定,不再作为可写入字段
prompt_profile 是本次新增的「提示词特化层」,缺省关闭;name 只接受 default / writing(其他值返回 400),prompt 上限 20000 字符(超限返回 400),只传部分字段时其余字段保持不变:
{"prompt_profile": {"enabled": true, "name": "writing"}}
注入位置、拼接顺序与预设正文见 docs/prompt-compatibility.md 与 docs/prompt-design.md。
prompt_style 是顶层字符串(不是对象),只接受 v2 / legacy / 空串(空串等于回到 v2):
{"prompt_style": "legacy"}
自定义模型列表不走 settings,用下面的 /admin/model_profiles 整体替换。
tools.enabled 只改请求体里传了的字段,其它设置的现有语义不变:
{"tools": {"enabled": true}}
关闭(默认)时带 tools 的请求按纯对话处理;DSML 工具调用可能触发上游风控导致账号封禁,是否开启请自行评估,风险说明见 docs/prompt-compatibility.md。
POST /admin/settings/password
更新管理密码并使旧 JWT 失效。
请求示例:
{"new_password":"your-new-password"}
也兼容 {"password":"your-new-password"}。
GET / PUT /admin/skills
纯文本技能库,实验性功能,缺省关闭(skills.enabled=false)。技能正文由服务端拼进 prompt,模型不需要工具调用就能按固定套路工作。
- 全局配置:
skills.enabled(缺省false)、skills.max_chars(缺省8000)、skills.items - 单 Key 覆盖:
api_keys[].skills,语义见上文「api_keys[].skills(单 Key 覆盖技能注入)」 - 注入形态、注入顺序与「为什么不用工具调用」:docs/prompt-compatibility.md 的「技能注入(skills)」
GET 响应:
{
"enabled": false,
"max_chars": 8000,
"items": [
{
"id": "sql-report",
"name": "SQL 报表",
"description": "按固定口径写 SQL",
"body": "# 技能正文 markdown\n..."
}
]
}
PUT 请求:与 GET 同形状,整体替换,不做增量合并。
{
"enabled": true,
"max_chars": 8000,
"items": [
{
"id": "sql-report",
"name": "SQL 报表",
"description": "按固定口径写 SQL",
"body": "# 报表口径\n先按天聚合,再按渠道拆分。"
}
]
}
PUT 成功响应:
{"success": true, "count": 1}
校验规则(任一不满足返回 400,响应体为 {"detail": "..."}):
| 字段 | 规则 |
|---|---|
items |
最多 50 条 |
id |
必填、唯一,字符集 [a-z0-9_-] |
body |
必填;单条 ≤ 100000 字符,全部条目合计 ≤ 500000 字符 |
max_chars |
缺省 8000;按字符(rune)截断,截断时追加「(技能内容已截断)」;0 表示不限制 |
校验在写入时执行,与 enabled 无关:先关着开关把内容配好、改好,再打开即可。超限、重复 id、非法 id 都不会被静默修正。
GET / PUT /admin/model_profiles
自定义模型列表(模型名 + 基模 + 该模型专属提示词)。与技能库同样是独立接口、整体替换语义:PUT 请求里的 items 就是最终列表,传空数组表示清空。
GET 响应:
{
"items": [
{"id": "deepseek-writer", "base": "deepseek-v4-flash", "name": "写作", "remark": "长文", "prompt": "## 写作要求\n…"}
],
"base_models": ["deepseek-v4-flash", "deepseek-v4-flash-search", "deepseek-v4-flash-nothinking", "deepseek-v4-flash-search-nothinking"],
"max_count": 50,
"max_id_chars": 64,
"max_prompt_chars": 20000
}
PUT 请求:
{"items": [{"id": "deepseek-writer", "base": "deepseek-v4-flash", "prompt": "…"}]}
校验规则(任一条不满足返回 400,响应体为 {"detail": "..."}):
| 字段 | 规则 |
|---|---|
items |
最多 50 条 |
id |
必填、唯一(大小写不敏感),字符集 [A-Za-z0-9._-],长度 1-64,不可与内置模型名冲突 |
base |
必填,只能是 deepseek-v4-flash 系列的四个名字之一 |
prompt |
可空;≤ 20000 字符 |
保存后立即生效:/v1/models 列表与模型名解析都会包含新条目,无需重启。
POST /admin/config/import
导入完整配置,支持:
mode=merge(默认)mode=replace
请求可直接传配置对象,或使用 {"config": {...}, "mode":"merge"} 包裹格式。
也支持在查询参数里传 ?mode=merge / ?mode=replace。
replace 模式会按完整配置结构替换(保留 Vercel 同步元信息);merge 模式会合并 keys、api_keys、accounts、model_aliases,并覆盖 admin、runtime、responses、embeddings 中的非空字段。auto_delete、current_input_file 建议通过 /admin/settings 或配置文件管理;compat 与 toolcall 相关字段会被忽略。
注意:
merge模式不会更新auto_delete、current_input_file。
GET /admin/config/export
导出完整配置,返回 config、json、base64 三种格式。
响应示例:
注:
_vercel_sync_hash和_vercel_sync_time为内部同步元数据字段,用于 Vercel 配置漂移检测。
POST /admin/keys
{"key": "new-api-key", "name": "主 Key", "remark": "生产流量"}
响应:{"success": true, "total_keys": 3}
PUT /admin/keys/{key}
更新指定 API key 的 name / remark,路径参数中的 key 为只读标识,不可修改。
{"name": "备用 Key", "remark": "压测"}
响应:{"success": true, "total_keys": 3}
DELETE /admin/keys/{key}
响应:{"success": true, "total_keys": 2}
GET /admin/proxies
列出代理配置(密码不回传,仅返回 has_password 标记)。
POST /admin/proxies
新增代理。请求体支持 id(可选,未传则自动生成)、name、type(http / socks5)、host、port、username、password。
PUT /admin/proxies/{proxyID}
更新指定代理。若请求中 password 为空字符串,则保留原密码。
DELETE /admin/proxies/{proxyID}
删除代理,并自动清空所有引用该代理账号的 proxy_id。
POST /admin/proxies/test
测试代理连通性:传 proxy_id 时测试已保存代理;不传时按请求体代理字段做临时连通性测试。
GET /admin/accounts
查询参数:
| 参数 | 默认 | 范围 |
|---|---|---|
page |
1 |
≥ 1 |
page_size |
10 |
1–5000 |
q |
空 | 按 identifier / email / mobile 过滤 |
响应:
{
"items": [
{
"identifier": "user@example.com",
"email": "user@example.com",
"mobile": "",
"has_password": true,
"has_token": true,
"token_preview": "abc...",
"test_status": "ok"
}
],
"total": 25,
"page": 1,
"page_size": 10,
"total_pages": 3
}
POST /admin/accounts
{"email": "user@example.com", "password": "pwd"}
响应:{"success": true, "total_accounts": 6}
PUT /admin/accounts/{identifier}
更新指定账号的 name / remark。路径参数中的 identifier 可以是 email 或 mobile,且不可修改。
{"name": "主账号", "remark": "团队共享"}
响应:{"success": true, "total_accounts": 6}
DELETE /admin/accounts/{identifier}
identifier 可为 email、mobile,或 token-only 账号的合成标识(token:<hash>)。
响应:{"success": true, "total_accounts": 5}
PUT /admin/accounts/{identifier}/proxy
更新指定账号绑定代理。
- 请求体:
{"proxy_id":"..."}; proxy_id传空字符串时表示解绑代理;identifier支持 email / mobile / token-only 合成标识。
GET /admin/queue/status
{
"available": 3,
"in_use": 1,
"total": 4,
"available_accounts": ["a@example.com"],
"in_use_accounts": ["b@example.com"],
"max_inflight_per_account": 2,
"global_max_inflight": 8,
"recommended_concurrency": 8,
"waiting": 0,
"max_queue_size": 8
}
| 字段 | 说明 |
|---|---|
available |
仍有剩余并发槽位的账号数 |
in_use |
当前已占用的 in-flight 槽位数 |
total |
总账号数 |
available_accounts |
仍有剩余并发槽位的账号 ID 列表 |
in_use_accounts |
当前处于使用中的账号 ID 列表 |
max_inflight_per_account |
每账号并发上限 |
global_max_inflight |
全局并发上限 |
recommended_concurrency |
建议并发值(total × max_inflight_per_account) |
waiting |
当前等待中的请求数 |
max_queue_size |
等待队列上限 |
POST /admin/accounts/test
| 字段 | 必填 | 说明 |
|---|---|---|
identifier |
✅ | email / mobile / token-only 合成标识 |
model |
❌ | 默认 deepseek-v4-flash |
message |
❌ | 空字符串时仅测试会话创建 |
响应:
{
"account": "user@example.com",
"success": true,
"response_time": 1240,
"message": "API 测试成功(仅会话创建)",
"model": "deepseek-v4-flash",
"session_count": 0,
"config_writable": true,
"config_warning": ""
}
如果传入 message,还会附带 thinking(当上游返回思考内容时)。
当部署环境配置文件路径不可写(例如容器内默认 /app/config.json 只读)时,登录与会话测试仍可继续;此时会返回 config_warning 提示 token 仅保存在内存、重启后丢失。
POST /admin/accounts/test-all
可选请求字段:model
{
"total": 5,
"success": 4,
"failed": 1,
"results": [...]
}
内部并发上限当前固定为 5。
POST /admin/accounts/sessions/delete-all
清空指定账号的所有 DeepSeek 会话。请求体示例:
{"identifier":"user@example.com"}
响应:
{"success": true, "message": "删除成功"}
如果账号不存在或删除失败,success 会是 false,message 会返回错误原因。
POST /admin/import
批量导入 keys 与 accounts。
请求:
{
"keys": ["k1", "k2"],
"accounts": [
{"email": "user@example.com", "password": "pwd", "token": ""}
]
}
响应:
{
"success": true,
"imported_keys": 2,
"imported_accounts": 1
}
POST /admin/test
测试当前 API 可用性(通过自身接口调用)。
| 字段 | 必填 | 默认值 |
|---|---|---|
model |
❌ | deepseek-v4-flash |
message |
❌ | 你好 |
api_key |
❌ | 配置中第一个 key |
响应:
{
"success": true,
"status_code": 200,
"response": {"id": "..."}
}
POST /admin/dev/raw-samples/capture
直接通过服务自身发起一次 /v1/chat/completions 请求,并把请求元信息和上游原始 SSE 保存到 tests/raw_stream_samples/<sample-id>/。
常用请求字段:
| 字段 | 必填 | 默认值 | 说明 |
|---|---|---|---|
message |
否 | 你好 |
便捷单轮用户消息 |
messages |
否 | 自动由 message 生成 |
OpenAI 风格消息数组 |
model |
否 | deepseek-v4-flash |
目标模型 |
stream |
否 | true |
建议保留流式,以记录原始 SSE |
api_key |
否 | 配置中第一个 key | 调用业务接口使用的 key |
sample_id |
否 | 自动生成 | 样本目录名 |
成功时会在响应头里附带:
X-Ds2-Sample-IdX-Ds2-Sample-DirX-Ds2-Sample-MetaX-Ds2-Sample-Upstream
如果请求本身成功,但当前进程没有记录到新的上游抓包,会返回:
{"detail":"no upstream capture was recorded"}
GET /admin/dev/raw-samples/query
按关键词查询当前进程内存里的抓包记录,并按 chat_session_id 归并 completion + continue 链。
查询参数:
| 参数 | 默认值 | 说明 |
|---|---|---|
q |
空 | 按请求体/响应体关键词模糊匹配 |
limit |
20 |
返回链条数上限 |
响应字段包含:
items[].chain_keyitems[].capture_idsitems[].round_countitems[].initial_labelitems[].request_previewitems[].response_preview
POST /admin/dev/raw-samples/save
把当前内存中的某条抓包链落盘为 tests/raw_stream_samples/<sample-id>/。
支持以下任一种选中方式:
{"chain_key":"session:xxxx","sample_id":"tmp-from-memory"}
{"capture_id":"cap_xxx","sample_id":"tmp-from-memory"}
{"query":"广州天气","sample_id":"tmp-from-memory"}
成功响应会返回 sample_id、dir、meta_path、upstream_path。
POST /admin/vercel/sync
| 字段 | 必填 | 说明 |
|---|---|---|
vercel_token |
❌ | 空或 __USE_PRECONFIG__ 则读环境变量,再回退到已保存配置 |
project_id |
❌ | 空则读 VERCEL_PROJECT_ID,再回退到已保存配置 |
team_id |
❌ | 空则读 VERCEL_TEAM_ID,再回退到已保存配置 |
auto_validate |
❌ | 默认 true |
save_credentials |
❌ | 默认 true;保存本次显式填写的 Vercel 凭据,供下次同步复用 |
config_override |
❌ | 传入完整配置对象,覆盖当前内存配置用于同步 |
成功响应:
{
"success": true,
"validated_accounts": 3,
"message": "配置已同步,正在重新部署...",
"deployment_url": "https://..."
}
或需要手动部署:
{
"success": true,
"validated_accounts": 3,
"message": "配置已同步到 Vercel,请手动触发重新部署",
"manual_deploy_required": true
}
失败校验的账号会通过 failed_accounts 返回;成功保存到 Vercel 的凭据会通过 saved_credentials 返回。
GET /admin/vercel/status
也支持 POST /admin/vercel/status,可提交 config_override 用于比对草稿是否与已同步配置一致。
{
"synced": true,
"last_sync_time": 1738400000,
"has_synced_before": true,
"env_backed": false,
"config_hash": "....",
"last_synced_hash": "....",
"draft_hash": "....",
"draft_differs": false
}
POST /admin/vercel/status 还可以携带 config_override,用于对比“草稿配置”和当前已同步配置。
GET /admin/export
{
"json": "{...}",
"base64": "ey4uLn0="
}
该接口与 GET /admin/config/export 返回相同内容,只是路径更短。
GET /admin/version
查询当前构建版本与 GitHub 最新 Release:
{
"success": true,
"current_version": "3.0.0",
"current_tag": "v3.0.0",
"source": "file:VERSION",
"checked_at": "2026-03-29T00:00:00Z",
"latest_tag": "v3.0.0",
"latest_version": "3.0.0",
"release_url": "https://github.com/CJackHwang/ds2api/releases/tag/v3.0.0",
"published_at": "2026-03-28T12:00:00Z",
"has_update": false
}
如果 GitHub API 不可用,响应里会额外包含 check_error,但 HTTP 状态仍为 200。
GET /admin/dev/captures
查看本地抓包状态与最近记录(需 Admin 鉴权):
enabledlimitmax_body_bytesitems
DELETE /admin/dev/captures
清空抓包记录,返回:
{"success":true,"detail":"capture logs cleared"}
GET /admin/chat-history
查询服务器端对话记录,支持筛选与分页。
查询参数:
| 参数 | 默认 | 匹配方式 |
|---|---|---|
caller |
空 | 与 caller_id 全等(取值用响应里的 callers[].caller_id,不是 Key 名称) |
status |
空 | 与记录状态不区分大小写全等:streaming / success / error / stopped |
surface |
空 | 与来源接口不区分大小写全等,如 openai.chat_completions、openai.responses、claude.messages、gemini.generate_content |
q |
空 | 不区分大小写包含匹配,命中 user_input / preview / model / caller_label / error |
offset |
0 |
分页偏移;offset 与 limit 都为 0 表示不分页 |
limit |
0 |
单页条数;0 表示不限制条数(不按 settings 的保留条数分页) |
非法或负数的 offset / limit 按 0 处理。
响应:
{
"version": 2,
"limit": 20,
"revision": 128,
"total": 137,
"callers": [
{"caller_id": "caller:9f2c1a2b3c4d5e6f", "label": "主 Key", "configured": true, "count": 96},
{"caller_id": "caller:41ab0c9d8e7f6a5b", "label": "备用 Key", "configured": true, "count": 0}
],
"items": [
{
"id": "chat_20260418_093012_ab12",
"status": "success",
"caller_id": "caller:9f2c1a2b3c4d5e6f",
"caller_label": "主 Key",
"account_id": "user@example.com",
"surface": "openai.chat_completions",
"model": "deepseek-v4-flash",
"stream": true,
"user_input": "今天的新闻",
"preview": "……",
"status_code": 200,
"elapsed_ms": 4210,
"finish_reason": "stop",
"created_at": 1777000000000,
"updated_at": 1777000004000,
"detail_revision": 3
}
],
"path": "/data/chat_history.json"
}
total是筛选后的总条数,items是分页后的当前页;不带任何筛选/分页参数时items仍是全部摘要。caller_label是发起方 API Key 的可读标签(名称 / 备注 / 脱敏 Key),没有备注时退回脱敏值,避免面板里只剩一串哈希。callers汇总记录里出现过的caller_id,并补上配置里已登记但还没有记录的 Key(count为0,configured=true),按count倒序、label升序排列。- 时间戳字段(
created_at/updated_at/completed_at)单位是毫秒。 If-None-Match+ETag的304协商只在不带任何筛选/分页参数时生效,因为带参数的响应内容本身随查询条件变化。
GET /admin/chat-history/{id}
查看单条记录全文,响应为 {"item": {...}}。除摘要字段外还包含 messages、history_text(历史拆分上传的对话记录全文)、final_prompt、reasoning_content、content、error、usage、file_infos(上传文件 ID、审核结果、来源)等。同样支持 ETag / 304。
DELETE /admin/chat-history
清空全部服务器端记录,返回 {"success": true}。
DELETE /admin/chat-history/{id}
删除单条记录,返回 {"success": true};记录不存在返回 404。
PUT /admin/chat-history/settings
更新保留条数。limit 接受 0..500 的任意整数,不再是固定档位。
{"limit": 120}
| 取值 | 行为 |
|---|---|
0 |
关闭记录:不再写新记录 |
1..500 |
保留最近该数量的记录,超出部分按创建时间淘汰 |
| 其他 | 返回 400,detail 为 unsupported chat history limit: <n> |
响应带 success、limit、revision 与裁剪后的 items。
GET /admin/chat-history/export
按与列表接口相同的筛选与分页参数(caller / status / surface / q / offset / limit)导出记录,响应是文件下载。
| 参数 | 说明 |
|---|---|
format=json(默认) |
JSON 附件,结构为 {exported_at, count, filters, items};exported_at 为 RFC3339,filters 回显本次筛选条件,items 是完整记录(含 messages / final_prompt / history_text 等大字段) |
format=markdown |
Markdown 附件,标题 # ds2api 响应记录导出,逐条记录 ## N. <记录 ID> 分节,含时间、接口、模型、账号、调用方、状态、耗时、结束原因,以及 UserInput / FinalPrompt / ReasoningContent / Content / Error 代码块 |
| 其他值 | 返回 400,detail 为 unsupported export format: <format> |
响应头带 Content-Disposition: attachment(文件名为 ds2api-logs-<YYYYMMDD-HHMMSS>.json|.md)与 Cache-Control: no-store。导出过程中若某条记录已被删除,该条会被跳过,count 反映实际导出的条数。
错误响应格式
兼容路由(/v1/*、/anthropic/*)统一使用以下结构:
{
"error": {
"message": "...",
"type": "invalid_request_error",
"code": "invalid_request",
"param": null
}
}
Admin 接口保持 {"detail":"..."}。
Gemini 路由使用 Google 风格错误结构:
{
"error": {
"code": 400,
"message": "invalid json",
"status": "INVALID_ARGUMENT"
}
}
建议客户端处理逻辑:检查 HTTP 状态码 + 解析 error 或 detail 字段。
常见状态码:
| 状态码 | 说明 |
|---|---|
401 |
鉴权失败(key/token 无效,或 Admin JWT 过期) |
429 |
请求过多(超出并发上限 + 等待队列,或上游账号 thinking-only 后仍无可见输出;托管账号模式会先尝试一次切号 fresh retry;当前不附带 Retry-After 头) |
503 |
模型不可用或上游服务异常 |
cURL 示例
OpenAI 非流式
curl http://localhost:5001/v1/chat/completions \
-H "Authorization: Bearer your-api-key" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-v4-flash",
"messages": [{"role": "user", "content": "你好"}],
"stream": false
}'
OpenAI 流式
curl http://localhost:5001/v1/chat/completions \
-H "Authorization: Bearer your-api-key" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-v4-flash",
"messages": [{"role": "user", "content": "解释一下量子纠缠"}],
"stream": true
}'
OpenAI Responses(流式)
curl http://localhost:5001/v1/responses \
-H "Authorization: Bearer your-api-key" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.3-codex",
"input": "写一个 golang 的 hello world",
"stream": true
}'
OpenAI Embeddings
curl http://localhost:5001/v1/embeddings \
-H "Authorization: Bearer your-api-key" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-4o",
"input": ["第一段文本", "第二段文本"]
}'
OpenAI 带搜索
curl http://localhost:5001/v1/chat/completions \
-H "Authorization: Bearer your-api-key" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-v4-flash-search",
"messages": [{"role": "user", "content": "今天的新闻"}],
"stream": true
}'
OpenAI Tool Calling
curl http://localhost:5001/v1/chat/completions \
-H "Authorization: Bearer your-api-key" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-v4-flash",
"messages": [{"role": "user", "content": "北京今天天气怎么样?"}],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "获取指定城市的天气",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "城市名"}
},
"required": ["city"]
}
}
}
]
}'
Gemini 非流式
curl "http://localhost:5001/v1beta/models/gemini-2.5-pro:generateContent" \
-H "Authorization: Bearer your-api-key" \
-H "Content-Type: application/json" \
-d '{
"contents": [
{
"role": "user",
"parts": [{"text": "用三句话介绍 Go 语言"}]
}
]
}'
Gemini 流式
curl "http://localhost:5001/v1beta/models/gemini-2.5-flash:streamGenerateContent" \
-H "Authorization: Bearer your-api-key" \
-H "Content-Type: application/json" \
-d '{
"contents": [
{
"role": "user",
"parts": [{"text": "写一个简短摘要"}]
}
]
}'
Claude 非流式
curl http://localhost:5001/anthropic/v1/messages \
-H "x-api-key: your-api-key" \
-H "Content-Type: application/json" \
-H "anthropic-version: 2023-06-01" \
-d '{
"model": "claude-sonnet-4-6",
"max_tokens": 1024,
"messages": [{"role": "user", "content": "你好"}]
}'
Claude 流式
curl http://localhost:5001/anthropic/v1/messages \
-H "x-api-key: your-api-key" \
-H "Content-Type: application/json" \
-H "anthropic-version: 2023-06-01" \
-d '{
"model": "claude-opus-4-6",
"max_tokens": 1024,
"messages": [{"role": "user", "content": "解释相对论"}],
"stream": true
}'
Admin 登录
curl http://localhost:5001/admin/login \
-H "Content-Type: application/json" \
-d '{"admin_key": "admin"}'
指定账号请求
curl http://localhost:5001/v1/chat/completions \
-H "Authorization: Bearer your-api-key" \
-H "X-Ds2-Target-Account: user@example.com" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-v4-flash",
"messages": [{"role": "user", "content": "你好"}]
}'