dsh-magpie-connect 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,76 +1,98 @@
1
1
  # dsh-magpie-connect
2
2
 
3
- **Magpie LAN gateway models, natively inside DSH (DeepSeek Harness).**
3
+ 把 Magpie 局域网网关上的模型接进 DeepSeek Harness(DSH)的模型选择器。
4
4
 
5
- No extra process. Points at your configured gateway origin and serves
6
- every model from the gateway's `GET …/models` in the DSH model picker as provider
7
- `dsh-magpie-connect` (picker label `magpie`).
5
+ 装好之后,你就能在 DSH 里直接选用网关提供的模型,不需要额外跑进程,也不用配代理。
8
6
 
9
- > The gateway is **not** configured out of the box: set the API URL **and**
10
- > API key on the Settings → Magpie page (or via `cordis.patch.yml`). Both
11
- > are required — until then the picker stays empty and calls fail fast with
12
- > a "not configured" error.
7
+ ## 功能
13
8
 
14
- - **双接口** — `/v1/chat/completions` 与 `/v1/responses` 按模型 `native_endpoints`
15
- 自动分流(Muse Spark / Codex / Grok 走 responses,其余走 completions)
16
- - **图片识别** — 声明 `modalities.input: image` 的模型开放图片输入,经 harness
17
- 附件服务取字节后以 OpenAI `image_url` / `input_image` 上行
18
- - **思考级别选择** — 网关 `supported_reasoning_levels` 原样进 picker(含 `none` /
19
- `ultra` 扩展档),直通网关不截断;过期档位按就近原则收敛,不会 400
9
+ - **模型自动同步** — 网关列出的模型全部进入 DSH 模型选择器,想显示哪些由你决定。
10
+ - **图片识别** — 网关声明支持图片的模型可以直接发图。
11
+ - **思考档位** — 网关支持的思考档位(含 `none`、`ultra`)原样出现在选择器里。
12
+ - **接口自动分流** — 网关需要哪种接口就自动用哪种,你不用管。
13
+ - **网关不通也能用** — 选择器用本地缓存兜底,不会整个空掉。
20
14
 
21
- ## Install
15
+ ## 安装
22
16
 
23
17
  ```sh
24
18
  dsh plugin --profile web add dsh-magpie-connect
25
19
  ```
26
20
 
27
- Restart `dsh web` after installing. Requires DSH with a web profile;
28
- Node.js ≥ 20; LAN route to `http://api.lan`.
21
+ 安装后重启 `dsh web`。
29
22
 
30
- ## Settings page
23
+ 已有安装用同一条命令即可更新到最新版本。
31
24
 
32
- Settings → Magpie 网关 (sidebar): edit the API URL and API key, test the
33
- connection (reports how many models the endpoint serves), and choose which
34
- models appear in the model picker. Everything saves to
35
- `~/.dsh-magpie-connect/settings.json` and applies immediately — no restart.
25
+ **需要**:DSH 的 web profile、Node.js ≥ 20、能访问网关所在局域网。
36
26
 
37
- **两种提交时机**:地址与 Key 需要点「保存」(凭证不适合静默写入);**模型列表的
38
- 增删会自动保存**,不需要、也没有保存按钮。因为模型卡片在连接卡片下方,一个管全部
39
- 的保存按钮会让"删了一行"看起来已经生效——它其实没有。自动保存通过队列串行化,
40
- 连点多次删除不会因为响应乱序而把旧集合写回磁盘。
27
+ ## 使用
41
28
 
42
- **API 地址是带版本号的完整前缀**,例如 `http://api.lan/v1`:插件只往后拼资源名
43
- (`/models`、`/chat/completions`、`/responses`),**绝不自己补版本号**。网关以后
44
- 上 `/v2`、`/v3` 时,只改这一个字符串即可,不会拼成 `/v2/v1/models`。尾斜杠会被
45
- 去掉,路径原样保留。
29
+ ### 第一步:填网关地址和 Key
46
30
 
47
- **获取可用模型** 打开候选弹窗:它问的是**表单当前显示的地址与 Key**(包括还没
48
- 保存的),所以填一个新网关是一趟而不是"保存—返回—再看"。弹窗里可搜索、可全选/
49
- 取消全选、逐个勾选,**应用选择**把勾选结果换算成隐藏集合。列表只显示已启用的模型,
50
- 隐藏的只在弹窗里;行尾的「删除」即隐藏,重新加回也在弹窗。保存时会清掉网关已经
51
- 不再提供的陈旧 id。查不通不会卡死——失败信息就显示在列表下方,行仍然可以手改。
31
+ 打开 **设置 → Magpie 网关**,确认两项:
52
32
 
53
- Precedence per field: settings page > `cordis.patch.yml` config > defaults.
54
- Hidden models only come from the page; an empty list shows everything.
33
+ - **API 地址** — 网关地址,**要带版本号**。默认已填好 `http://127.0.0.1:3425/v1`,网关不在本机时改成实际地址。
34
+ - **API Key** — 网关凭据,必填。
55
35
 
56
- Toggling visibility, changing the origin, or a refresh that added/dropped
57
- models publishes `llm/adapters-updated`, the one event DSH's picker listens
58
- to — the browser caches one catalog read per Host generation, so without it
59
- the picker keeps the stale list until `dsh web` restarts.
36
+ 点「保存」后,可以点「测试连接」确认通不通(会告诉你网关上有多少模型)。两项都齐了才会生效——
37
+ 地址有默认值,Key 没有,所以没填 Key 之前模型选择器是空的。
60
38
 
61
- ## Configuration
39
+ > **地址一定要带版本段**(如 `/v1`)。插件只在你填的地址后面接资源名,不会自己补
40
+ > 版本号——所以网关以后升级到 `/v2`、`/v3`,你只要改这一个字符串。
62
41
 
63
- Set the gateway URL first — either on the Settings → Magpie page (recommended,
64
- applies instantly) or via the profile's `cordis.patch.yml`:
42
+ ### 第二步:选择要显示的模型
43
+
44
+ 点 **获取可用模型**,会弹出网关当前的模型列表:勾选你想要的,再点「应用选择」。
45
+ 弹窗里可以搜索、全选、取消全选。
46
+
47
+ 主列表只显示已启用的模型。不想要的点该行末尾的「删除」,它就会从选择器里消失;
48
+ 想加回来,再回到弹窗里勾选。
49
+
50
+ > 弹窗查的是**你当前填的地址和 Key**(哪怕还没保存),所以换网关是一趟的事。
51
+ > 连不上也不会卡住——失败原因会显示在列表下方,已列出的行仍然可以操作。
52
+
53
+ ### 什么时候需要保存
54
+
55
+ - **地址和 Key**:点「保存」才生效。
56
+ - **模型增删**:**自动保存**,不用点任何按钮。
57
+
58
+ ## 常见问题
59
+
60
+ **模型选择器是空的?**
61
+
62
+ 先确认地址和 Key 都填了并且已经保存,再点「测试连接」。最常见的原因是地址缺了版本段
63
+ (比如填成 `http://127.0.0.1:3425`,而应该是 `http://127.0.0.1:3425/v1`)。
64
+
65
+ **删掉的模型怎么加回来?**
66
+
67
+ 点「获取可用模型」,在弹窗里重新勾上,再点「应用选择」。
68
+
69
+ **改了网关地址,模型列表没变?**
70
+
71
+ 保存后列表会重新拉取。如果新网关暂时不通,会先用本地缓存。
72
+
73
+ **页面提示「未配置」?**
74
+
75
+ 地址和 Key 缺任意一项都会这样——此时选择器是空的,调用也会直接失败并给出提示。
76
+
77
+ **发图失败?**
78
+
79
+ 只有网关声明支持图片的模型才能收图。如果给纯文本模型发图,会直接报错而不是静默丢弃。
80
+ 另外,图片需要通过 harness 的附件服务读取,在没有该服务的组合里也会失败。
81
+
82
+ ## 进阶用法
83
+
84
+ ### 用配置文件代替设置页
85
+
86
+ 除了设置页,也可以在 profile 的 `cordis.patch.yml` 里配置:
65
87
 
66
88
  ```yaml
67
89
  - id: dsh-magpie-connect
68
90
  name: 'dsh-magpie-connect'
69
91
  config:
70
92
  providerId: dsh-magpie-connect
71
- displayName: magpie
72
- baseUrl: http://api.lan/v1 # versioned API root — include the version
73
- apiKey: not-needed # LAN gateway needs none
93
+ displayName: Magpie
94
+ baseUrl: http://127.0.0.1:3425/v1 # 带版本号的完整地址
95
+ apiKey: not-needed # 局域网网关通常不校验,但必须非空
74
96
  refreshSeconds: 300
75
97
  maxRetries: 2
76
98
  timeoutMs: 300000
@@ -78,56 +100,61 @@ applies instantly) or via the profile's `cordis.patch.yml`:
78
100
  idleTimeoutMs: 60000
79
101
  ```
80
102
 
81
- | Option | Default | Description |
103
+ | 配置项 | 默认值 | 说明 |
82
104
  | --- | --- | --- |
83
- | `providerId` | `dsh-magpie-connect` | Provider name shown in DSH. |
84
- | `displayName` | `magpie` | Picker grouping label. |
85
- | `baseUrl` | `''` (not configured) | Versioned API root, e.g. `http://api.lan/v1`. The version is part of the value, not appended — a gateway on `/v2` is reached by changing this string. Required. |
86
- | `apiKey` | `''` (not configured) | Bearer key. Required, even if `/models` answers without one. |
87
- | `dataDir` | `~/.dsh-magpie-connect` | State dir (status, settings file, cache). |
88
- | `refreshSeconds` | `300` | Live catalog refresh interval. |
89
- | `maxRetries` | `2` | Connection-setup retries on 429/5xx. |
90
- | `timeoutMs` | `300000` | Overall upstream request cap in ms. |
91
- | `firstEventTimeoutMs` | `90000` | Stall watchdog: max wait for first event. |
92
- | `idleTimeoutMs` | `60000` | Stall watchdog: max silence between events. |
93
-
94
- ## How it works
105
+ | `providerId` | `dsh-magpie-connect` | 注册到 DSH 的 provider 名称。 |
106
+ | `displayName` | `Magpie` | 模型选择器里的分组名。 |
107
+ | `baseUrl` | `http://127.0.0.1:3425/v1` | 带版本号的 API 地址。 |
108
+ | `apiKey` | `''`(未配置) | 网关凭据。必填,即使网关不校验——地址有默认值,Key 没有。 |
109
+ | `dataDir` | `~/.dsh-magpie-connect` | 状态目录(状态快照、设置文件、模型缓存)。 |
110
+ | `refreshSeconds` | `300` | 模型目录刷新间隔(秒)。 |
111
+ | `maxRetries` | `2` | 遇到 429/5xx 时的连接重试次数。 |
112
+ | `timeoutMs` | `300000` | 单次请求的总超时(毫秒)。 |
113
+ | `firstEventTimeoutMs` | `90000` | 等待首个上游事件的超时(毫秒)。 |
114
+ | `idleTimeoutMs` | `60000` | 上游事件之间的静默超时(毫秒)。 |
115
+
116
+ 优先级:设置页 > `cordis.patch.yml` > 默认值。设置页只覆盖你保存过的字段。
117
+
118
+ ### 工作原理
95
119
 
96
120
  ```
97
- DSH session
98
- │ harness chunks (block-start / text-delta / usage / finish …)
121
+ DSH 会话
122
+ │ harness 数据块(block-start / text-delta / usage / finish …)
99
123
  ▼
100
- MagpieAdapter (registered LlmAdapter)
101
- │ pi-ai openai-completions stream (default) /
102
- │ openai-responses stream (responses-only lane)
124
+ MagpieAdapter(注册为 DSH 的 LlmAdapter)
125
+ │ pi-ai openai-completions 流(默认)/
126
+ │ openai-responses 流(仅支持 responses 的模型)
103
127
  ▼
104
- http://api.lan/v1 ← chat/completions or responses per native_endpoints
128
+ http://127.0.0.1:3425/v1 ← 按 native_endpoints 选择 chat/completions 或 responses
105
129
  ```
106
130
 
107
- - **Catalog** — `GET {baseUrl}/models` 全量接入,无付费过滤;磁盘缓存 + 编译期静态
108
- 快照兜底,网关宕机时 picker 仍可用。任何暴露集合的变化(隐藏/显示、换网关
109
- 地址、刷新后模型增删)都会 `emit('llm/adapters-updated')`,让浏览器那份
110
- catalog 缓存立即重读——否则 picker 会一直显示旧列表到下次重启。
111
- - **思考档** — `reasoning=true` 且有 ladder 的模型出 picker;无 ladder 但可思考
112
- 的模型保持 wire 可思考(显式档位直通);`none` 显式关闭思考,`ultra` 直通
113
- 网关,不在 pi-ai 内截断。
114
- - **图片** — 有图的请求经 `ctx.get('attachments')` 取 request 版本字节;
115
- 文本模型误收图时直接失败(harness 按 inputModalities 已做门控)。
116
- - **韧性** — 启动即注册,目录后台预热(失败按短间隔重试);stall 看门狗让静默
117
- 挂起快速失败为 `TIMEOUT`;`length` 截断如实上抛,不做自动续写。
131
+ - **模型目录** — 全量接入网关 `GET {baseUrl}/models`,不做付费过滤;磁盘缓存加编译期
132
+ 静态快照兜底,网关宕机时选择器仍可用。暴露集合一旦变化(隐藏/显示、换网关地址、
133
+ 刷新后模型增删),会发出 `llm/adapters-updated` 让浏览器重读缓存——否则选择器会
134
+ 一直显示旧列表直到下次重启。
135
+ - **思考档位** — 有档位阶梯的模型进入选择器;没有阶梯但可思考的模型保持可思考
136
+ (显式档位直通网关);`none` 关闭思考,`ultra` 直通网关,不在本地截断。
137
+ - **图片** — 带图的请求经 harness 附件服务取字节后上行;文本模型收到图片会直接
138
+ 失败(harness 已按模型声明的输入类型做过门控)。
139
+ - **容错** — 启动即注册,目录在后台预热;静默挂起会被看门狗快速判为超时;
140
+ 上游截断如实上报,不做自动续写。
118
141
 
119
- Health snapshot: `~/.dsh-magpie-connect/adapter-status.json`.
142
+ 运行状态快照写在 `~/.dsh-magpie-connect/adapter-status.json`,设置与缓存在同一目录。
120
143
 
121
- ## Development
144
+ ## 开发
122
145
 
123
146
  ```sh
124
147
  pnpm install
125
- pnpm run check
148
+ pnpm run check # 类型检查 + 测试 + 构建
149
+ pnpm run deploy:local # 构建并同步到 web profile
126
150
  ```
127
151
 
128
- The check ladder is typecheck + tests + build; `lib/` is committed, so profile
129
- installs run without a build.
152
+ `lib/` 是提交进仓库的构建产物,所以 profile 安装时不需要现场构建。
153
+
154
+ 发布由 `.github/workflows/release.yml` 负责:把 `package.json` 和
155
+ `dsh.plugin.json` 的版本号改成一致后提交推送,流水线会打 tag、建 GitHub Release
156
+ 并发布到 npm。版本号带 `-`(如 `0.3.0-rc.1`)时自动标记为预发布。
130
157
 
131
- ## License
158
+ ## 许可证
132
159
 
133
160
  [MIT](./LICENSE)
package/cordis.patch.yml CHANGED
@@ -1,15 +1,15 @@
1
1
  # dsh-magpie-connect DSH plugin — bundle patch: insert the plugin row (deployment-global).
2
2
  #
3
- # The gateway is NOT configured out of the box: set baseUrl on the Settings
4
- # sidebar page ("Magpie") or via the config object below (see src/config.ts).
5
- # Until an API root is set the picker stays empty and calls fail fast.
3
+ # The gateway ships with the conventional local address but no key: set the key
4
+ # on the Settings sidebar page ("Magpie") or via the config object below (see
5
+ # src/config.ts). Until a key is set the picker stays empty and calls fail fast.
6
6
  #
7
7
  # The Settings sidebar page can override baseUrl/apiKey at runtime
8
8
  # (saved to <dataDir>/settings.json, applied without restart) and hides models
9
9
  # from the picker. Page values win per-field; the patch config below stays as
10
10
  # fallback (e.g. headless compositions without the settings page).
11
11
  #
12
- # - baseUrl: versioned API root, e.g. http://api.lan/v1 (required). Include the
12
+ # - baseUrl: versioned API root (default http://127.0.0.1:3425/v1). Include the
13
13
  # version segment: the plugin appends only the resource (/models, /responses),
14
14
  # so a gateway on /v2 or /v3 is reached by changing this one string.
15
15
  # - apiKey: gateway credential (required, even if /models needs none).
package/dsh.plugin.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-magpie-connect",
3
3
  "description": "Magpie LAN gateway models for the DeepSeek Harness web GUI",
4
- "version": "0.2.0",
4
+ "version": "0.2.1",
5
5
  "entry": {
6
6
  "name": "dsh-magpie-connect",
7
7
  "inject": ["llm"]
package/lib/client.js CHANGED
@@ -41,7 +41,7 @@ var EN = {
41
41
  intro: "Connect DSH to the Magpie LAN model gateway. Changes save to this machine and apply immediately \u2014 no restart needed.",
42
42
  connection: "Connection",
43
43
  baseUrl: "API URL",
44
- baseUrlPlaceholder: "http://api.lan/v1",
44
+ baseUrlPlaceholder: "http://127.0.0.1:3425/v1",
45
45
  apiKey: "API key",
46
46
  apiKeyPlaceholder: "required",
47
47
  requiredNote: "API URL and API key are both required. Include the version segment (e.g. /v1) \u2014 the URL is used exactly as written.",
@@ -90,7 +90,7 @@ var ZH = {
90
90
  intro: "\u628A DSH \u63A5\u5165 Magpie \u5C40\u57DF\u7F51\u6A21\u578B\u7F51\u5173\u3002\u4FEE\u6539\u4FDD\u5B58\u5728\u672C\u673A\u5E76\u7ACB\u5373\u751F\u6548\uFF0C\u65E0\u9700\u91CD\u542F\u3002",
91
91
  connection: "\u8FDE\u63A5",
92
92
  baseUrl: "API \u5730\u5740",
93
- baseUrlPlaceholder: "http://api.lan/v1",
93
+ baseUrlPlaceholder: "http://127.0.0.1:3425/v1",
94
94
  apiKey: "API Key",
95
95
  apiKeyPlaceholder: "\u5FC5\u586B",
96
96
  requiredNote: "API \u5730\u5740\u4E0E Key \u5747\u4E3A\u5FC5\u586B\u3002\u5730\u5740\u9700\u5305\u542B\u7248\u672C\u6BB5\uFF08\u5982 /v1\uFF09\uFF0C\u63D2\u4EF6\u6309\u4F60\u586B\u5199\u7684\u539F\u6837\u4F7F\u7528\u3002",
package/lib/index.js CHANGED
@@ -217,7 +217,7 @@ var ModelCatalog = class {
217
217
  }
218
218
  async refreshModels() {
219
219
  if (!this.configured) {
220
- this.#lastError = "magpie gateway baseUrl is not configured \u2014 set it on the Magpie settings page";
220
+ this.#lastError = "magpie gateway API URL or key is not configured \u2014 set both on the Magpie settings page";
221
221
  return;
222
222
  }
223
223
  try {
@@ -282,7 +282,7 @@ var ModelCatalog = class {
282
282
  this.#baseUrl = root;
283
283
  this.#entries = /* @__PURE__ */ new Map();
284
284
  this.#updatedAt = 0;
285
- this.#lastError = root === "" ? "magpie gateway baseUrl is not configured \u2014 set it on the Magpie settings page" : "";
285
+ this.#lastError = root === "" ? "magpie gateway API URL or key is not configured \u2014 set both on the Magpie settings page" : "";
286
286
  this.#announce();
287
287
  }
288
288
  get baseUrl() {
@@ -895,7 +895,7 @@ async function* withStallTimeout(events, timeouts, options = {}) {
895
895
 
896
896
  // src/adapter/magpie-adapter.ts
897
897
  var PROVIDER_ID = "dsh-magpie-connect";
898
- var DEFAULT_DISPLAY_NAME = "magpie";
898
+ var DEFAULT_DISPLAY_NAME = "Magpie";
899
899
  var DEFAULT_CONTEXT_WINDOW = 262144;
900
900
  var DEFAULT_MAX_TOKENS = 32768;
901
901
  var DEFAULT_API_KEY = "not-needed";
@@ -1070,7 +1070,7 @@ var MagpieAdapter = class {
1070
1070
  const raw = this.#runtime?.baseUrl() ?? this.#fallbackBaseUrl;
1071
1071
  const baseUrl = raw.replace(/\/+$/, "");
1072
1072
  if (baseUrl === "") {
1073
- throw new Error("dsh-magpie-connect: Magpie gateway API URL is not configured \u2014 open Settings \u2192 Magpie and set it");
1073
+ throw new Error("dsh-magpie-connect: Magpie gateway API URL or key is not configured \u2014 open Settings \u2192 Magpie and fill in both");
1074
1074
  }
1075
1075
  return baseUrl;
1076
1076
  }
@@ -1150,8 +1150,8 @@ import { homedir } from "node:os";
1150
1150
  import { join as join2 } from "node:path";
1151
1151
  var defaults = {
1152
1152
  providerId: "dsh-magpie-connect",
1153
- displayName: "magpie",
1154
- baseUrl: "",
1153
+ displayName: "Magpie",
1154
+ baseUrl: "http://127.0.0.1:3425/v1",
1155
1155
  apiKey: "",
1156
1156
  refreshSeconds: 300,
1157
1157
  maxRetries: 2,