- Главная
- Плагины
- Модели и провайдеры
- dsh-openai-gateway
dsh-openai-gateway
pigman18/dsh-openai-gateway
Exposes every model of every provider configured in DeepSeek Harness through one OpenAI-compatible endpoint on a fixed loopback port, with a sidebar page for the base URL, key, port and model ids.
Установка
dsh plugin --profile web add github:pigman18/dsh-openai-gatewayREADME
dsh-openai-gateway
把 DeepSeek Harness 里已配置的全部 provider、全部模型,暴露成一个固定的 OpenAI 兼容接口 —— 端口与密钥都固化在本地,重启不变。
English · 安装 · 兼容的 provider 插件 · 工作原理 · 端口与密钥 · 更新日志
一个独立的 DeepSeek Harness (DSH) bundle 插件。它在本地回环上监听一个固定端口,把所有已注册 provider 的模型按 provider/model 暴露为 OpenAI 兼容接口;同时往左侧边栏加一个 模型网关 页面,用来查看地址与密钥、改端口、换密钥、以及逐个复制模型 ID。
没有任何按厂商重写的转发逻辑。 请求走宿主的 ctx.llm 服务,因此每个请求进入的正是该 provider 已有的那个 adapter —— 它自己的凭据、请求头、重试与账号池行为、模型门禁都原地保留。这也是它能「顺带」支持所有 provider 插件的原因:只要有插件把 provider 注册进 ctx.llm,它就自动出现在网关里,不需要任何适配。
功能特性
- 一个入口,全部模型 ——
/v1/models列出所有 provider 的所有模型,ID 为provider/model(如workbuddy/deepseek-v4.1-flash、opencode2dsh/deepseek-v4.1-flash-free、local/qwen3.5:4b)。当某个裸 ID 只被一个 provider 提供时也接受;多个 provider 撞名时明确报错并列出候选,不做猜测。 - 端口固化 —— 网关自己监听
127.0.0.1:8787,与界面端口无关(界面端口每次启动都可能变,网关的地址不会)。端口被占用时自动落到系统分配的随机端口继续服务,页面会标明「随机(8787 被占用)」,而不是起不来。 - 密钥固化 —— 首次启动生成
sk-dsh-…并写入$DSH_HOME/openai-gateway/settings.json(0600),跨重启不变;页面上的 生成 按钮可换新(两步确认,旧钥立即失效)。旧版本的api-key文件会被自动迁移,已配置的客户端不受影响。 - 侧边栏模型网关页 —— 打开即见接口地址、密钥(打码 / 显示 / 复制)、端口(可直接改,回车即应用),以及按 provider 折叠分组的完整模型清单:每个模型 ID 一个复制按钮,每组可整组复制,也可一键复制全部。
- 流式与非流式都支持 ——
"stream": true返回标准 SSE(data: …逐帧,[DONE]收尾),非流式返回完整chat.completion。 - 工具调用 ——
tools正常转发,结果以tool_calls返回,arguments的分片会被拼装完整,finish_reason为tool_calls;tool消息按其tool_call_id回到对应调用。assistant 侧漏了id时会按结果的 id 对齐,而不是另造一个(上游对「没有结果的调用」只会回空回复)。 - 图片与文件 ——
image_url/input_image与file/input_file都支持。图片:data:URL 直接解码,http(s)图片由网关抓取(上限 25 MiB),然后存进宿主的附件库(ctx.attachments),模型拿到的是持久化的图片块而不是只有调用方够得着的 URL;一条 user 消息里的多张图按一条批次入库,附件的张数与字节上限照旧生效,模型不支持图片时由宿主降级成一行说明文字。文件:能当文本读的直接内联成文本([attached file: name]+ 内容,2 MiB 以内、UTF-8 且无 NUL 字节),其余(PDF、图片、压缩包等)存进附件库并作为file块交给模型。没有附件服务的 profile 会明确回 400,而不是悄悄只发文本。 - 推理内容与用量 —— 模型产出思考时以
reasoning_content单独返回;usage映射为prompt_tokens/completion_tokens/total_tokens,有 reasoning / cache 细分时一并带上。 reasoning_effort按目标模型真实档位映射 —— 映射到该模型自己声明的档位集合;不支持的档位降到最接近的或直接丢弃,不会因此拒掉整个请求。- 对
stop被拒的上游自动降级 —— 某些 adapter 不接受stop,会先返回UNSUPPORTED_OPTION;网关识别该情况后去掉stop重试一次,而不是把请求判死。 - 浏览器直连 ——
/v1/*自带 CORS 预检(OPTIONS无需鉴权即答 204),前端项目可以把它当普通 OpenAI 端点用。 - 只绑回环 —— 仅监听
127.0.0.1,不是网络服务。页面用的控制接口还额外要求同源、POST与 JSON Content-Type。
兼容的 provider 插件
网关不认识任何厂商,只读宿主已注册的 provider。以下是真实环境里跑通过的组合,也就是 1.0.0 的实测范围:
| provider ID | 由谁提供 | 说明 |
|---|---|---|
workbuddy / workbuddy-global | dsh-connect-workbuddy | 国内版与国际版 WorkBuddy,含账号池换号 |
workbuddy-xdpool | dsh-workbuddy-xdpool | WorkBuddy 账号池插件 |
opencode2dsh | @opencode2dsh/dsh-plugin | 免费额度模型(如 deepseek-v4.1-flash-free、glm-5.3-flash-free) |
local | @deepseek-ai/dsh-llm-pi-ai 的本地路由 | 例如指向 Ollama(http://127.0.0.1:11434/v1) |
deepseek-official / deepseek-account | DSH 内置 DeepSeek provider | 官方通道与账号通道 |
任何其它把 provider 注册进 ctx.llm 的插件(自建 OpenAI 兼容后端、其它厂商连接器)同样自动可用;上表只列出实测过的。转发时不需要你手动补任何 header:上游需要的请求头由各自 adapter 负责,网关只把 OpenAI 形状的请求翻译成 adapter 的调用。
安装
从 GitHub 安装:
dsh plugin --profile <profile> add github:pigman18/dsh-openai-gateway
开发时直接链本地目录:
dsh plugin --profile <profile> add link:/path/to/dsh-openai-gateway
安装或更新后重启 DSH:宿主侧插件在启动时装载,浏览器前半段也只在启动时装配。
使用
- 侧边栏点 模型网关,页面顶部就是接口地址(默认
http://127.0.0.1:8787/v1)与 API Key。 - 把它当普通 OpenAI 端点填进任意客户端:
curl http://127.0.0.1:8787/v1/chat/completions \
-H "Authorization: Bearer <key>" \
-H "Content-Type: application/json" \
-d '{"model":"workbuddy/deepseek-v4.1-flash","messages":[{"role":"user","content":"hi"}]}'
-
模型名直接从页面复制,或在
/v1/models里查。 -
图片照 OpenAI 的写法内联即可(
data:URL 或http(s)图片地址都行):
{
"model": "workbuddy/glm-5.3-flash",
"messages": [
{
"role": "user",
"content": [
{ "type": "text", "text": "这张图里是什么?" },
{ "type": "image_url", "image_url": { "url": "data:image/png;base64,iVBORw0KGgo…" } }
]
}
]
}
文件同理,用 {"type":"file","file":{"filename":"notes.txt","file_data":"data:text/plain;base64,…"}}。
接口
/v1/* 由网关自己的固定端口提供;/plugins/… 三条控制接口挂在宿主 web server 上(页面同源、回环校验)。
| 方法 | 路径 | 用途 |
|---|---|---|
GET | /v1/models | 所有 provider 的所有模型(?refresh=1 跳过 60 秒缓存) |
GET | /v1/models/<provider>/<model> | 单个模型 |
POST | /v1/chat/completions | 对话补全,流式或非流式 |
POST | /v1/completions | 同一处理器,兼容只认旧路径的客户端 |
OPTIONS | /v1/* | CORS 预检,无需鉴权 |
GET | /plugins/dsh-openai-gateway/status | 页面读取的状态文档,附带完整模型清单(超过 60 秒缓存会先重建;?refresh=1 强制重建)与 recent:最近 20 次请求的耗时、首字节、尝试次数与结束原因 |
POST | /plugins/dsh-openai-gateway/settings | {"port": 8787} 改端口,端口被占返回 409 |
POST | /plugins/dsh-openai-gateway/key | {"action":"regenerate"} 换密钥 |
鉴权为 Authorization: Bearer <key>,比较使用常量时间比较。
工作原理
OpenAI 客户端 ──▶ http://127.0.0.1:8787/v1 ──▶ ctx.llm ──▶ provider adapter ──▶ 上游
│ (workbuddy / opencode2dsh /
│ pi-ai 本地路由 / …)
└── /v1/models 把它们全列出来
- 请求体被翻译成
ctx.llm.prepareCall(config, signal, configure)的调用:system/developer消息折进 system 提示,user/assistant/tool转成 LLM 消息块,tools转成函数声明。 - 响应流(
text-delta/reasoning-delta/tool-call-delta/usage/finish)再拼回 OpenAI 的chat.completion或 SSE 帧。 - 一个准备好的调用只允许派发一次,这点由宿主保证;网关对每个请求单独准备。
端口与密钥
两者都持久化在一个插件自有文件里,重启后无需任何配置:
$DSH_HOME/openai-gateway/settings.json # 默认 ~/.dsh/openai-gateway/settings.json
{ "port": 8787, "apiKey": "sk-dsh-…" } # 权限 0600
- 端口:优先
8787;启动时若已被占用,落到系统分配的随机端口继续服务(页面显示被占用的原端口)。页面改端口立即生效;改到被占用端口返回 409 且继续在原端口服务,不会把自己搞挂。 - 密钥:首次启动生成;生成 之后旧钥立刻失效。删掉
settings.json即回到默认(会重新生成密钥)。 - 密钥不写进 profile,只能通过回环状态接口读回。
配置
bundle 行上可选:
| 键 | 默认 | 含义 |
|---|---|---|
enabled | true | false 时插件空转:页面显示未启用,/v1/* 返回 503 |
port | 已存值 / 8787 | 首选监听端口,显式值优先于已存值 |
apiKey | 已存值 | 固定密钥,优先于已存值 |
bodyLimitBytes | 8388608 | 请求体上限 |
catalogTtlMs | 60000 | /v1/models 缓存时长 |
retryMaxRetries | 5 | 瞬时失败(RATE_LIMIT/SERVER/TIMEOUT/TRANSPORT/EMPTY_RESPONSE)的重试次数,与宿主 llm-retry 默认一致 |
retryInitialDelayMs | 500 | 退避起始间隔 |
retryMaxDelayMs | 10000 | 退避上限;上游给出的重置点超过它就按「请稍后再来」直接失败 |
silentFlushMs | 10000 | 流式响应静默多久后提交响应头并开始保活 |
keepAliveMs | 15000 | 保活 : keep-alive 注释的间隔 |
maxInputTokens | 0(关) | 估算输入超过它就回 413 CONTEXT_WINDOW_EXCEEDED;0 时改用模型声明的 contextWindow(provider 没声明就不拦) |
maxMessages | 0(关) | 历史超过这么多条就丢弃最早的若干条,且保证保留部分从 user 开始 |
已知边界
- 图片按宿主的策略处理:入库时会做一次规范化(宿主把 PNG/JPEG/WebP/GIF 转成统一栅格),所以同一张图两次请求的字节可能不同;媒体类型、张数与总字节上限由附件库决定,超限回 400 并带出原因。
file只接受内联的file_data:OpenAI 的 Files API(file_id)把文件存在调用方账号里,本机 harness 拿不到,因此这种情况会明确报错而不是假装成功。- 二进制文件只能给到路径:宿主交给模型的「文件」是一份只读副本的路径(这是它的设计:模型用自己的文件工具去读)。外部客户端调用的模型没有那些工具,所以二进制文件(PDF / docx / 压缩包)只能让模型知道「有这么个文件、多大、在哪」,读不到内容 —— 文本类文件才走内联。
- provider 注入的提示会原样透传:例如账号池换号时上游返回的一行说明,会作为普通 assistant 文本出现,不会被网关吞掉或改写。
- 没有 web server 的 profile(headless):没有页面与控制接口,但
/v1/*仍按固定端口提供服务。 - 长任务建议用
stream: true:非流式请求在整段生成期间不会写出任何字节,任何读超时都会命中。 - 超过
silentFlushMs才失败的流式请求,错误以流内事件到达:响应头一旦提交(为了保活),首字之前的失败只能写成data: {"error": …}+data: [DONE],不再有 HTTP 状态码。 - 只绑定
127.0.0.1,不要把它转发到网络上。
许可
MIT · 见 LICENSE
Похожие плагины
dsh-our-free-model
ebony-vinyl/dsh-our-free-model
dsh-routing-suite
yjh051108/dsh-routing-suite
dsh-workbuddy-connect
corrinehu/dsh-workbuddy-connect
dsh-connect-workbuddy
dingminhua/dsh-connect-workbuddy