Bundle
dsh-llm-proxy
Persistent global fetch proxy for DeepSeek Harness LLM requests (undici setGlobalDispatcher + EnvHttpProxyAgent), configured via the llm-proxy settings namespace
- Source
- Ye-Yu-Mo
- stars
- 5 stars
- License
- MIT
- Updated
- Updated 7 days ago
Readme
# dsh-llm-proxy
> DeepSeek Harness (dsh) 的全局 HTTP 代理插件:用 undici `setGlobalDispatcher` + `EnvHttpProxyAgent` 接管 dsh 所有 LLM 请求的代理路由。配置化、热切换、可观测。
[](LICENSE)
[](package.json)
[](package.json)
---
## 为什么需要它
dsh(DeepSeek Harness)用 node 内置 `fetch`(undici)发 LLM 请求,而 undici **默认不读 `http_proxy`/`https_proxy` 环境变量**。如果你的 LLM 端点(如 llm-proxy 网关)必须走代理才能访问,dsh 会直连超时。
本插件在 dsh 进程内用 `setGlobalDispatcher(new EnvHttpProxyAgent(...))` 全局接管 fetch 的代理路由:
- ✅ 让 dsh 所有 LLM 请求走你配置的代理
- ✅ **热切换**:设置变更即时生效,无需重启
- ✅ **可逆**:插件停止/禁用时恢复基线 dispatcher,不留进程级残留
- ✅ **可观测**:`/api/llm-proxy/status` 查看当前生效模式,`/api/llm-proxy/test` 测试任意代理连通性
## 安装
本插件是 **bundle** 包(`dsh.bundle.patch` 清单):`dsh plugin add` 会把它装进 profile 依赖并自动加入 `dsh.profile.bundles`,下次启动自动激活,**无需手动加 patch 行**。
```bash
# 一键安装(发布到 npm 后可直接用包名)
dsh plugin --profile web add dsh-llm-proxy
# 或直接从 GitHub 安装
dsh plugin --profile web add github:Ye-Yu-Mo/dsh-llm-proxy
```
开发调试(本地 link):
```bash
git clone https://github.com/Ye-Yu-Mo/dsh-llm-proxy
dsh plugin --profile web add link:$(pwd)/dsh-llm-proxy
```
安装后**重启 dsh web** 使 bundle 补丁生效;`~/.dsh/dsh.log` 出现 `[llm-proxy]` 日志(未启用时为「恢复基线」一行)即装载成功。
## 配置
通过 dsh 设置页的「LLM 代理」项,或直接编辑 `~/.dsh/settings.yaml`:
```yaml
llm-proxy:
enabled: true
url: http://127.0.0.1:7891 # 你的 HTTP(S) 代理地址
noProxy:
- localhost
- 127.0.0.1
- ::1
```
| 字段 | 类型 | 默认 | 说明 |
|------|------|------|------|
| `enabled` | boolean | `false` | 是否启用代理接管 |
| `url` | string | `''` | HTTP(S) 代理地址,如 `http://127.0.0.1:7891` |
| `noProxy` | string[] | `['localhost','127.0.0.1','::1']` | 不走代理的地址 |
> ⚠️ dsh 设置页只暴露白名单内的配置命名空间。若设置页显示「表单不可用」,需把 `llm-proxy` 加入 `dsh-host-apiproxy` 的 `WEB_SETTINGS_NAMESPACES`(当前为 dsh 已知限制,见下)。
## HTTP API
插件注册两个宿主路由(loopback-only,防 LAN 探测):
### `GET /api/llm-proxy/status`
当前生效模式:
```json
{
"effective": { "mode": "setting", "url": "http://127.0.0.1:7891" },
"env": { "httpProxy": "http://127.0.0.1:7891", "httpsProxy": "http://127.0.0.1:7891" }
}
```
`mode` 取值:
- `setting`:走设置的 `url`
- `env`:读环境变量代理(`HTTP(S)_PROXY`)
- `direct`:直连(无代理)
### `POST /api/llm-proxy/test`
测试任意代理到目标端点的连通性:
```json
// 请求
{ "url": "http://127.0.0.1:7891", "probeUrl": "https://llm-proxy.ftai.chat/v1/models", "timeoutMs": 8000 }
// 响应
{ "ok": true, "latencyMs": 246, "status": 200 }
```
## 开发
```bash
pnpm install
pnpm test # 单元测试(logic/http-utils)
node scripts/... # 见 scripts/
```
目录结构:
```
lib/
index.js # 宿主核心:settings + dispatcher 接管 + HTTP 路由
logic.js # 纯函数配置判定(可单测)
http-utils.js # loopback 校验 / JSON 读写
client.js # 设置面板「LLM 代理」页
test/ # 单元测试
```
## 已知限制
dsh 设置页的配置命名空间白名单(`dsh-host-apiproxy` 的 `WEB_SETTINGS_NAMESPACES`)是硬编码的,第三方插件无法自注册暴露。目前需要手动把 `llm-proxy` 加入白名单才能让设置页表单可用(dsh 官方已将"插件自注册"列为延后工作)。
已在上游提交讨论:[Third-party plugins cannot expose settings namespaces to the web settings page](https://github.com/deepseek-ai/deepseek-harness/discussions/1196)(Ideas 分类)。
## License
MIT
Install
dsh plugin --profile web add github:Ye-Yu-Mo/dsh-llm-proxy#2679d6ccd2375b4c9ac85d8a8a806a18cf906779
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install dsh-llm-proxy from the hub