dsh-opendesign-corouter 0.1.7 → 0.1.8

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
@@ -32,7 +32,7 @@ dsh plugin --profile web add dsh-opendesign-corouter
32
32
  也可打开 [Actions → CI](https://github.com/KouzakiUmi/dsh-opendesign-corouter/actions/workflows/ci.yml),从成功构建下载 **`dsh-opendesign-corouter-build`** artifact。登录 GitHub 后解压 ZIP,取得 `.tgz` 与 `.sha256`,再安装本地包:
33
33
 
34
34
  ```console
35
- dsh plugin --profile web add /absolute/path/to/dsh-opendesign-corouter-0.1.7.tgz
35
+ dsh plugin --profile web add /absolute/path/to/dsh-opendesign-corouter-0.1.8.tgz
36
36
  ```
37
37
 
38
38
  `web` 只是示例,请替换为目标 Profile。Desktop 使用桌面安装器提供的 `dsh` 入口;独立公共 CLI 不能代管保留的 `desktop` Profile。安装后的启用与重载按目标 DSH 的管理流程进行。
@@ -59,6 +59,8 @@ dsh plugin --profile web add /absolute/path/to/dsh-opendesign-corouter-0.1.7.tgz
59
59
 
60
60
  推理协议可选 **OpenAI Chat Completions、OpenAI Responses、Anthropic Messages**。模型发现始终使用当前网关的 OpenAI 兼容 `/models` 接口,不随推理协议切换。
61
61
 
62
+ 思考档位按精确型号的厂商文档维护,并按当前协议筛选。33 款提供离散档位,4 款为固定思考或开关/预算控制,Muse Wonder 的精确别名尚待确认。旧配置中的无效默认档位可在参数设置中改为“继承默认”。[逐模型来源与网关验证状态](docs/reasoning-evidence.md)
63
+
62
64
  > **分组不等于计费隔离。** 赠送额度耗尽后是否转扣套餐余额或钱包,由服务端和账号权益决定。选择“套餐赠送”不能保证绝不消耗钱包余额。
63
65
 
64
66
  ### 四层额度,不重复相加
@@ -79,11 +81,11 @@ dsh plugin --profile web add /absolute/path/to/dsh-opendesign-corouter-0.1.7.tgz
79
81
  <img src="docs/previews/theme-light.png" width="1040" alt="连接与登录页,展示桌面优先开关、独立登录按钮和不回填的 API Key 输入框;模拟数据">
80
82
  </picture>
81
83
 
82
- ### 空轮监控,只作用于赠送模型
84
+ ### DeepSeek V4.1 Flash 空回答检测
83
85
 
84
86
  独立开关,默认开启。参考 [dsh-empty-turn-guard](https://github.com/FYTXY/dsh-empty-turn-guard),将“正常 stop、没有工具调用、答案为空或只剩协议残渣”的工具对话标成 `EMPTY_RESPONSE`。
85
87
 
86
- 检测到空回答后,由 DSH 的重试策略决定是否重试,**重复请求可能额外消耗额度**。该监控仅用于套餐赠送模型的工具对话。
88
+ 检测到空回答后,由 DSH 的重试策略决定是否重试,**重复请求可能额外消耗额度**。该附加监控仅用于 DeepSeek V4.1 Flash 的工具对话,识别 DSML、thinking 及 `<rea:tool_calls>` 残留。
87
89
 
88
90
  ## 使用前先知道
89
91
 
package/lib/catalog.d.ts CHANGED
@@ -6,7 +6,7 @@
6
6
  * 详情页内联 `data-model="{...}"` JSON(公开页面,2026-10-26)。
7
7
  * - group:官网首页套餐卡 `https://corouter.open-design.ai/zh/` 的
8
8
  * `data-model-module="design-plan"` 12 款归 `plan`;其余在售对话模型归 `paid`。
9
- * - `plan` 12 款的 contextWindow / maxOutputTokens / inputModalities / reasoningEfforts
9
+ * - `plan` 原有 12 款的 contextWindow / maxOutputTokens / inputModalities
10
10
  * 来自 Go 侧 `dsh-opendesign-go` 0.1.9 的 MODEL_DEFAULTS(同一 `amr-link` endpoint 部署资料),
11
11
  * 作为部署参考,尚无官网能力数值的独立佐证;只用于对应的原有 12 款模型。
12
12
  * - 客户端桌面安装(Open Design 0.26.1)不内联 CoRouter 目录;其
@@ -26,8 +26,8 @@
26
26
  * - 媒体模型 `GPT Image 2`、`Nano Banana 2`、`Seedream 5 Lite / Pro`:
27
27
  * 走 `/api/v1/media/*`,不应出现在对话模型列表里,避免选中后 chat 请求必败。
28
28
  *
29
- * `paid` 25 款及新增 M3.1 Flash 的能力字段一律缺省:没有可核实的能力数值,
30
- * 未知模态保持缺省;`['text']` 仅表示已确认的纯文本输入。
29
+ * 思考档位按厂商型号资料记录,来源见 docs/reasoning-evidence.md;网关逐路由行为未实测。
30
+ * 其余容量与模态字段仍按已有来源保留,未知值保持缺省。
31
31
  */
32
32
  export interface CatalogModel {
33
33
  id: string;
@@ -36,6 +36,7 @@ export interface CatalogModel {
36
36
  contextWindow?: number;
37
37
  maxOutputTokens?: number;
38
38
  inputModalities?: readonly ('text' | 'image')[];
39
+ /** Undefined means unconfirmed; an empty array means no discrete effort selector. */
39
40
  reasoningEfforts?: readonly string[];
40
41
  }
41
42
  /**
package/lib/client.js CHANGED
@@ -20234,6 +20234,19 @@ var TYPERT_REMOTE = createTypertRemoteInfo({ package: "dsh-opendesign-corouter",
20234
20234
  } }, external_exports);
20235
20235
  var typert_remote_client_default = TYPERT_REMOTE;
20236
20236
 
20237
+ // src/reasoning.ts
20238
+ var PROTOCOL_EFFORTS = {
20239
+ "chat-completions": ["none", "minimal", "low", "medium", "high", "xhigh", "ultra", "max"],
20240
+ responses: ["none", "minimal", "low", "medium", "high", "xhigh", "max"],
20241
+ messages: ["none", "low", "medium", "high", "xhigh", "max"]
20242
+ };
20243
+ function protocolAcceptsEffort(protocol, effort) {
20244
+ return PROTOCOL_EFFORTS[protocol].includes(effort);
20245
+ }
20246
+ function modelReasoningEfforts(model, protocol) {
20247
+ return model.reasoningEfforts?.filter((effort) => protocolAcceptsEffort(protocol, effort));
20248
+ }
20249
+
20237
20250
  // src/client.ts
20238
20251
  var h = React.createElement;
20239
20252
  var SETTINGS_NS = "llm-opendesign-corouter";
@@ -20705,8 +20718,9 @@ function EffortField({ id, model, value, disabled, onChange }) {
20705
20718
  const mounted = useMounted();
20706
20719
  const [error62, setError] = React.useState(void 0);
20707
20720
  const listed = model.reasoningEfforts ?? [];
20708
- const options = value === void 0 || listed.includes(value) ? listed : [...listed, value];
20709
- const unknown2 = options.length === 0;
20721
+ const invalidDefault = value !== void 0 && !listed.includes(value);
20722
+ const options = invalidDefault ? [...listed, value] : listed;
20723
+ const unavailable = listed.length === 0;
20710
20724
  return h(
20711
20725
  "div",
20712
20726
  { className: "odc-field odc-field-compact" },
@@ -20715,8 +20729,9 @@ function EffortField({ id, model, value, disabled, onChange }) {
20715
20729
  id,
20716
20730
  className: "odc-input",
20717
20731
  value: value ?? "",
20718
- disabled: disabled || unknown2,
20732
+ disabled: disabled || unavailable && !invalidDefault,
20719
20733
  "aria-describedby": `${id}-hint`,
20734
+ "aria-invalid": invalidDefault || error62 !== void 0,
20720
20735
  onChange: (event) => {
20721
20736
  const next = event.target.value;
20722
20737
  void (async () => {
@@ -20726,13 +20741,13 @@ function EffortField({ id, model, value, disabled, onChange }) {
20726
20741
  }
20727
20742
  }, [
20728
20743
  h("option", { key: "", value: "" }, "\u7EE7\u627F\u9ED8\u8BA4"),
20729
- ...options.map((effort) => h("option", { key: effort, value: effort }, effort))
20744
+ ...options.map((effort) => h("option", { key: effort, value: effort, disabled: invalidDefault && effort === value }, invalidDefault && effort === value ? `${effort}\uFF08\u5F53\u524D\u4E0D\u53EF\u7528\uFF09` : effort))
20730
20745
  ]),
20731
20746
  h("small", {
20732
20747
  className: error62 === void 0 ? "odc-hint" : "odc-hint odc-field-error",
20733
20748
  id: `${id}-hint`,
20734
20749
  ...error62 === void 0 ? {} : { role: "alert" }
20735
- }, error62 ?? (unknown2 ? "\u8BE5\u6A21\u578B\u76EE\u5F55\u672A\u63D0\u4F9B\u601D\u8003\u6863\u4F4D\uFF0C\u65E0\u6CD5\u8986\u76D6\u3002" : "\u53EA\u5217\u51FA\u8BE5\u6A21\u578B\u5DF2\u77E5\u6863\u4F4D\uFF1B\u7559\u7A7A\u7EE7\u627F\u9ED8\u8BA4\u3002"))
20750
+ }, error62 ?? (invalidDefault ? "\u5DF2\u4FDD\u5B58\u7684\u6863\u4F4D\u4E0D\u9002\u7528\u4E8E\u5F53\u524D\u6A21\u578B\u6216\u534F\u8BAE\uFF0C\u8BF7\u9009\u62E9\u5176\u4ED6\u6863\u4F4D\u6216\u7EE7\u627F\u9ED8\u8BA4\u3002" : unavailable ? "\u5F53\u524D\u6A21\u578B\u6216\u534F\u8BAE\u672A\u63D0\u4F9B\u53EF\u8C03\u6574\u7684\u601D\u8003\u6863\u4F4D\u3002" : "\u7559\u7A7A\u4F7F\u7528\u670D\u52A1\u7AEF\u9ED8\u8BA4\u6863\u4F4D\u3002"))
20736
20751
  );
20737
20752
  }
20738
20753
  function ModelRow({ model, settings, authoritative, writable, pending, onCommit }) {
@@ -20902,7 +20917,11 @@ function Models({ status, formSnapshot, setFormField, pendingField, onRefreshMod
20902
20917
  };
20903
20918
  const source = status?.modelsSource === "openai" ? "\u5F53\u524D\u7F51\u5173" : status?.modelsSource === "builtin" ? "\u5185\u7F6E\u76EE\u5F55" : "\u5C1A\u672A\u786E\u8BA4";
20904
20919
  const updated = status?.modelsUpdatedAt === void 0 ? "" : ` \xB7 \u66F4\u65B0\u4E8E ${formatTime(status.modelsUpdatedAt)}`;
20905
- const models = status?.models ?? [];
20920
+ const protocol = formSnapshot.value?.apiProtocol ?? status?.config.apiProtocol ?? "chat-completions";
20921
+ const models = (status?.models ?? []).map((model) => {
20922
+ const reasoningEfforts = modelReasoningEfforts(model, protocol);
20923
+ return { ...model, ...reasoningEfforts === void 0 ? {} : { reasoningEfforts } };
20924
+ });
20906
20925
  return h(
20907
20926
  React.Fragment,
20908
20927
  null,
@@ -21027,7 +21046,7 @@ function Advanced({ status, formSnapshot, setFormField, pendingField }) {
21027
21046
  "section",
21028
21047
  { className: "odc-card" },
21029
21048
  h("h3", null, "\u7A7A\u8F6E\u76D1\u63A7"),
21030
- h("p", { className: "odc-hint" }, "\u68C0\u6D4B\u5957\u9910\u8D60\u9001\u6A21\u578B\u5728\u5DE5\u5177\u5BF9\u8BDD\u4E2D\u8FD4\u56DE\u7684\u7A7A\u56DE\u7B54\u3002"),
21049
+ h("p", { className: "odc-hint" }, "\u68C0\u6D4B DeepSeek V4.1 Flash \u5DE5\u5177\u5BF9\u8BDD\u4E2D\u7684\u7A7A\u56DE\u7B54\u6216\u534F\u8BAE\u6807\u8BB0\u6B8B\u7559\u3002"),
21031
21050
  h("div", { style: { marginTop: 14 } }, h(Switch, {
21032
21051
  checked: formSnapshot.value?.emptyTurnGuard ?? status?.config.emptyTurnGuard ?? true,
21033
21052
  disabled: !writable || pendingField.includes("emptyTurnGuard"),
@@ -3,7 +3,7 @@ export interface DeviceTokens {
3
3
  runtimeKey: string;
4
4
  }
5
5
  export declare const DEVICE_AUTH_ENDPOINT = "https://amr-api.open-design.ai/api/v1/cli/device-authorizations";
6
- export declare const DEVICE_AUTH_PLUGIN_VERSION = "0.1.7";
6
+ export declare const DEVICE_AUTH_PLUGIN_VERSION = "0.1.8";
7
7
  export declare const DEVICE_REQUEST_TIMEOUT_MS = 20000;
8
8
  /** One bounded launch step of the temporary activation window. */
9
9
  export declare const DEVICE_OPEN_TIMEOUT_MS = 30000;
package/lib/index.js CHANGED
@@ -65,13 +65,17 @@ import {
65
65
  // src/catalog.ts
66
66
  var TEXT_ONLY = ["text"];
67
67
  var TEXT_AND_IMAGE = ["text", "image"];
68
- var EFFORTS_FULL = ["none", "minimal", "low", "medium", "high", "xhigh", "ultra", "max"];
69
- var EFFORTS_STD = ["none", "minimal", "low", "medium", "high", "xhigh", "max"];
68
+ var EFFORTS_REASONING = ["low", "medium", "high", "xhigh", "max"];
69
+ var EFFORTS_OPENAI = ["none", ...EFFORTS_REASONING];
70
+ var EFFORTS_GLM52 = ["none", "minimal", "low", "medium", "high", "xhigh", "max"];
70
71
  var EFFORTS_LOW_HIGH = ["low", "high", "max"];
72
+ var EFFORTS_LEVELS = ["low", "medium", "high"];
73
+ var EFFORTS_GROK = [...EFFORTS_LEVELS, "xhigh"];
74
+ var EFFORTS_MUSE = ["minimal", ...EFFORTS_REASONING];
71
75
  var MODELS = [
72
76
  // ── OpenAI(7) ─────────────────────────────────────────────
73
- { id: "gpt-6.1-sol", name: "GPT-6.1 Sol", group: "paid" },
74
- { id: "gpt-6-astra", name: "GPT-6 Astra", group: "paid" },
77
+ { id: "gpt-6.1-sol", name: "GPT-6.1 Sol", group: "paid", reasoningEfforts: EFFORTS_REASONING },
78
+ { id: "gpt-6-astra", name: "GPT-6 Astra", group: "paid", reasoningEfforts: EFFORTS_REASONING },
75
79
  {
76
80
  id: "gpt-6-luna",
77
81
  name: "GPT-6 Luna",
@@ -79,20 +83,20 @@ var MODELS = [
79
83
  contextWindow: 105e4,
80
84
  maxOutputTokens: 128e3,
81
85
  inputModalities: TEXT_AND_IMAGE,
82
- reasoningEfforts: EFFORTS_FULL
86
+ reasoningEfforts: EFFORTS_OPENAI
83
87
  },
84
- { id: "gpt-6-sol", name: "GPT-6 Sol", group: "paid" },
85
- { id: "gpt-5.6-luna", name: "GPT-5.6 Luna", group: "paid" },
86
- { id: "gpt-5.6-sol", name: "GPT-5.6 Sol", group: "paid" },
87
- { id: "gpt-5.6-terra", name: "GPT-5.6 Terra", group: "paid" },
88
+ { id: "gpt-6-sol", name: "GPT-6 Sol", group: "paid", reasoningEfforts: EFFORTS_OPENAI },
89
+ { id: "gpt-5.6-luna", name: "GPT-5.6 Luna", group: "paid", reasoningEfforts: EFFORTS_OPENAI },
90
+ { id: "gpt-5.6-sol", name: "GPT-5.6 Sol", group: "paid", reasoningEfforts: EFFORTS_OPENAI },
91
+ { id: "gpt-5.6-terra", name: "GPT-5.6 Terra", group: "paid", reasoningEfforts: EFFORTS_OPENAI },
88
92
  // ── Anthropic(8) ──────────────────────────────────────────
89
- { id: "claude-fable-5.1", name: "Claude Fable 5.1", group: "paid" },
90
- { id: "claude-fable-5", name: "Claude Fable 5", group: "paid" },
91
- { id: "claude-opus-5.5", name: "Claude Opus 5.5", group: "paid" },
92
- { id: "claude-opus-5", name: "Claude Opus 5", group: "paid" },
93
- { id: "claude-opus-4.8", name: "Claude Opus 4.8", group: "paid" },
94
- { id: "claude-sonnet-5.5", name: "Claude Sonnet 5.5", group: "paid" },
95
- { id: "claude-sonnet-5", name: "Claude Sonnet 5", group: "paid" },
93
+ { id: "claude-fable-5.1", name: "Claude Fable 5.1", group: "paid", reasoningEfforts: EFFORTS_REASONING },
94
+ { id: "claude-fable-5", name: "Claude Fable 5", group: "paid", reasoningEfforts: EFFORTS_REASONING },
95
+ { id: "claude-opus-5.5", name: "Claude Opus 5.5", group: "paid", reasoningEfforts: EFFORTS_REASONING },
96
+ { id: "claude-opus-5", name: "Claude Opus 5", group: "paid", reasoningEfforts: EFFORTS_REASONING },
97
+ { id: "claude-opus-4.8", name: "Claude Opus 4.8", group: "paid", reasoningEfforts: EFFORTS_REASONING },
98
+ { id: "claude-sonnet-5.5", name: "Claude Sonnet 5.5", group: "paid", reasoningEfforts: EFFORTS_REASONING },
99
+ { id: "claude-sonnet-5", name: "Claude Sonnet 5", group: "paid", reasoningEfforts: EFFORTS_REASONING },
96
100
  {
97
101
  id: "claude-haiku-5.5",
98
102
  name: "Claude Haiku 5.5",
@@ -100,7 +104,7 @@ var MODELS = [
100
104
  contextWindow: 1e6,
101
105
  maxOutputTokens: 128e3,
102
106
  inputModalities: TEXT_AND_IMAGE,
103
- reasoningEfforts: EFFORTS_STD
107
+ reasoningEfforts: EFFORTS_REASONING
104
108
  },
105
109
  // ── DeepSeek(4) ───────────────────────────────────────────
106
110
  {
@@ -110,7 +114,7 @@ var MODELS = [
110
114
  contextWindow: 1e6,
111
115
  maxOutputTokens: 384e3,
112
116
  inputModalities: TEXT_AND_IMAGE,
113
- reasoningEfforts: EFFORTS_FULL
117
+ reasoningEfforts: EFFORTS_LOW_HIGH
114
118
  },
115
119
  {
116
120
  id: "deepseek-v4-pro",
@@ -119,7 +123,7 @@ var MODELS = [
119
123
  contextWindow: 1e6,
120
124
  maxOutputTokens: 384e3,
121
125
  inputModalities: TEXT_ONLY,
122
- reasoningEfforts: EFFORTS_FULL
126
+ reasoningEfforts: EFFORTS_LOW_HIGH
123
127
  },
124
128
  {
125
129
  id: "deepseek-v4-flash",
@@ -128,7 +132,7 @@ var MODELS = [
128
132
  contextWindow: 1e6,
129
133
  maxOutputTokens: 384e3,
130
134
  inputModalities: TEXT_AND_IMAGE,
131
- reasoningEfforts: EFFORTS_FULL
135
+ reasoningEfforts: EFFORTS_LOW_HIGH
132
136
  },
133
137
  {
134
138
  id: "deepseek-v4-flash-vision-exp",
@@ -137,7 +141,7 @@ var MODELS = [
137
141
  contextWindow: 1e6,
138
142
  maxOutputTokens: 384e3,
139
143
  inputModalities: TEXT_AND_IMAGE,
140
- reasoningEfforts: EFFORTS_FULL
144
+ reasoningEfforts: EFFORTS_LOW_HIGH
141
145
  },
142
146
  // ── Z.ai(3) ───────────────────────────────────────────────
143
147
  {
@@ -158,7 +162,7 @@ var MODELS = [
158
162
  inputModalities: TEXT_AND_IMAGE,
159
163
  reasoningEfforts: EFFORTS_LOW_HIGH
160
164
  },
161
- { id: "glm-5.2", name: "GLM-5.2", group: "paid" },
165
+ { id: "glm-5.2", name: "GLM-5.2", group: "paid", reasoningEfforts: EFFORTS_GLM52 },
162
166
  // ── Moonshot(3) ───────────────────────────────────────────
163
167
  {
164
168
  id: "kimi-k2.7-code",
@@ -167,18 +171,18 @@ var MODELS = [
167
171
  contextWindow: 262144,
168
172
  maxOutputTokens: 262144,
169
173
  inputModalities: TEXT_AND_IMAGE,
170
- reasoningEfforts: EFFORTS_STD
174
+ reasoningEfforts: []
171
175
  },
172
- { id: "kimi-k3", name: "Kimi K3", group: "paid" },
173
- { id: "kimi-k3.1-pro", name: "Kimi K3.1 Pro", group: "paid" },
176
+ { id: "kimi-k3", name: "Kimi K3", group: "paid", reasoningEfforts: EFFORTS_LOW_HIGH },
177
+ { id: "kimi-k3.1-pro", name: "Kimi K3.1 Pro", group: "paid", reasoningEfforts: EFFORTS_LOW_HIGH },
174
178
  // ── Google(3) ─────────────────────────────────────────────
175
- { id: "gemini-4-argon", name: "Gemini 4 Argon", group: "paid" },
176
- { id: "gemini-3.8-flash", name: "Gemini 3.8 Flash", group: "paid" },
177
- { id: "gemini-3.7-flash", name: "Gemini 3.7 Flash", group: "paid" },
179
+ { id: "gemini-4-argon", name: "Gemini 4 Argon", group: "paid", reasoningEfforts: EFFORTS_LEVELS },
180
+ { id: "gemini-3.8-flash", name: "Gemini 3.8 Flash", group: "paid", reasoningEfforts: EFFORTS_LEVELS },
181
+ { id: "gemini-3.7-flash", name: "Gemini 3.7 Flash", group: "paid", reasoningEfforts: EFFORTS_LEVELS },
178
182
  // ── xAI(3) ────────────────────────────────────────────────
179
- { id: "grok-4.7", name: "Grok 4.7", group: "paid" },
180
- { id: "grok-4.6", name: "Grok 4.6", group: "paid" },
181
- { id: "grok-4.5", name: "Grok 4.5", group: "paid" },
183
+ { id: "grok-4.7", name: "Grok 4.7", group: "paid", reasoningEfforts: EFFORTS_GROK },
184
+ { id: "grok-4.6", name: "Grok 4.6", group: "paid", reasoningEfforts: EFFORTS_GROK },
185
+ { id: "grok-4.5", name: "Grok 4.5", group: "paid", reasoningEfforts: EFFORTS_LEVELS },
182
186
  // ── Xiaomi(2) ─────────────────────────────────────────────
183
187
  {
184
188
  id: "mimo-v2.6-pro",
@@ -187,7 +191,7 @@ var MODELS = [
187
191
  contextWindow: 1e6,
188
192
  maxOutputTokens: 128e3,
189
193
  inputModalities: TEXT_AND_IMAGE,
190
- reasoningEfforts: EFFORTS_STD
194
+ reasoningEfforts: []
191
195
  },
192
196
  {
193
197
  id: "mimo-v2.6-flash",
@@ -196,7 +200,7 @@ var MODELS = [
196
200
  contextWindow: 1e6,
197
201
  maxOutputTokens: 128e3,
198
202
  inputModalities: TEXT_AND_IMAGE,
199
- reasoningEfforts: EFFORTS_STD
203
+ reasoningEfforts: []
200
204
  },
201
205
  // ── Tencent(1) ────────────────────────────────────────────
202
206
  {
@@ -206,13 +210,13 @@ var MODELS = [
206
210
  contextWindow: 1e6,
207
211
  maxOutputTokens: 64e3,
208
212
  inputModalities: TEXT_ONLY,
209
- reasoningEfforts: EFFORTS_STD
213
+ reasoningEfforts: ["none", "high"]
210
214
  },
211
215
  // ── Alibaba(1) ────────────────────────────────────────────
212
- { id: "qwen3.7-max", name: "Qwen3.7 Max", group: "paid" },
216
+ { id: "qwen3.7-max", name: "Qwen3.7 Max", group: "paid", reasoningEfforts: [] },
213
217
  // Official API additions; display labels for undocumented entries are local formatting.
214
- { id: "minimax-m3.1-flash", name: "MiniMax M3.1 Flash", group: "plan" },
215
- { id: "muse-spark-1.3", name: "Muse Spark 1.3", group: "paid" },
218
+ { id: "minimax-m3.1-flash", name: "MiniMax M3.1 Flash", group: "plan", reasoningEfforts: EFFORTS_REASONING },
219
+ { id: "muse-spark-1.3", name: "Muse Spark 1.3", group: "paid", reasoningEfforts: EFFORTS_MUSE },
216
220
  { id: "muse-wonder", name: "Muse Wonder", group: "paid" }
217
221
  ];
218
222
  var BY_ID = new Map(MODELS.map((model) => [model.id, model]));
@@ -294,6 +298,15 @@ function isModelEnabled(id, settings = {}) {
294
298
  return settings[id]?.enabled !== false;
295
299
  }
296
300
 
301
+ // src/schema-serialization.ts
302
+ function serializableTransform(callback) {
303
+ return Object.assign(callback, {
304
+ toJSON() {
305
+ return `(${serializableTransform.toString()})(${callback.toString()})`;
306
+ }
307
+ });
308
+ }
309
+
297
310
  // src/config.ts
298
311
  var SETTINGS_NS = "llm-opendesign-corouter";
299
312
  var PLAN_PROVIDER = "opendesign-corouter-plan";
@@ -301,11 +314,11 @@ var PAID_PROVIDER = "opendesign-corouter-paid";
301
314
  var DEFAULT_BASE_URL = "https://amr-link.open-design.ai/v1";
302
315
  var Config = Schema.object({
303
316
  apiKeyEnv: Schema.string().max(128).pattern(/^[A-Za-z_][A-Za-z0-9_]*$/u).default("COROUTER_API_KEY").description("CoRouter API Key \u7684\u73AF\u5883\u53D8\u91CF\u6216 DSH \u51ED\u636E\u5F15\u7528\u540D").volatile(),
304
- baseURL: Schema.transform(Schema.string(), assertBaseURL).default(DEFAULT_BASE_URL).description("CoRouter \u7F51\u5173\u5730\u5740\uFF1B\u4E0D\u7528\u4E8E\u63A7\u5236\u53F0\u767B\u5F55\u6216\u989D\u5EA6\u67E5\u8BE2").volatile(),
317
+ baseURL: Schema.transform(Schema.string(), serializableTransform(assertBaseURL)).default(DEFAULT_BASE_URL).description("CoRouter \u7F51\u5173\u5730\u5740\uFF1B\u4E0D\u7528\u4E8E\u63A7\u5236\u53F0\u767B\u5F55\u6216\u989D\u5EA6\u67E5\u8BE2").volatile(),
305
318
  apiProtocol: Schema.union(API_PROTOCOL_IDS).default("chat-completions").description("\u7F51\u5173\u652F\u6301\u7684\u63A8\u7406\u534F\u8BAE\uFF1B\u6A21\u578B\u76EE\u5F55\u901A\u8FC7\u8BE5\u7F51\u5173\u7684\u517C\u5BB9\u63A5\u53E3\u8BFB\u53D6").volatile(),
306
- modelSettings: Schema.transform(Schema.dict(Schema.any()), parseModelSettingsMap).default({}).description("\u9010\u6A21\u578B\u542F\u7528\u72B6\u6001\u548C\u8BF7\u6C42\u53C2\u6570").volatile(),
319
+ modelSettings: Schema.transform(Schema.dict(Schema.any()), serializableTransform(parseModelSettingsMap)).default({}).description("\u9010\u6A21\u578B\u542F\u7528\u72B6\u6001\u548C\u8BF7\u6C42\u53C2\u6570").volatile(),
307
320
  useDesktopCredential: Schema.boolean().default(true).description("\u4F18\u5148\u8BFB\u53D6 OpenDesign \u684C\u9762\u7AEF\u767B\u5F55\u6001\u67E5\u8BE2\u989D\u5EA6").volatile(),
308
- emptyTurnGuard: Schema.boolean().default(true).description("\u76D1\u63A7\u5957\u9910\u8D60\u9001\u6A21\u578B\u7684\u7A7A\u8F6E/\u534F\u8BAE\u6B8B\u6E23\uFF1B\u7531\u5BBF\u4E3B\u91CD\u8BD5\u7B56\u7565\u5904\u7406\uFF0C\u53EF\u80FD\u989D\u5916\u6D88\u8017\u8D60\u9001\u989D\u5EA6").volatile(),
321
+ emptyTurnGuard: Schema.boolean().default(true).description("\u68C0\u6D4B DeepSeek V4.1 Flash \u5DE5\u5177\u5BF9\u8BDD\u7684\u7A7A\u56DE\u7B54\uFF1B\u7531\u5BBF\u4E3B\u91CD\u8BD5\u7B56\u7565\u5904\u7406\uFF0C\u53EF\u80FD\u989D\u5916\u6D88\u8017\u989D\u5EA6").volatile(),
309
322
  quotaRefreshSeconds: Schema.natural().min(5).max(3600).default(60).description("\u989D\u5EA6\u81EA\u52A8\u5237\u65B0\u95F4\u9694\uFF08\u79D2\uFF09").volatile(),
310
323
  requestTimeoutMs: Schema.natural().min(1e3).max(36e5).default(18e5).description("\u63A8\u7406\u8BF7\u6C42\u603B\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09").volatile(),
311
324
  streamIdleTimeoutMs: Schema.natural().min(1e3).max(18e5).default(3e5).description("\u6D41\u5F0F\u54CD\u5E94\u65E0\u6570\u636E\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09").volatile()
@@ -809,6 +822,7 @@ var RESIDUE_PATTERNS = [
809
822
  /<\s*\/?\s*||DSML||[^>]*>/gu,
810
823
  /||DSML||[^\n<>]*>?/gu,
811
824
  /<\s*\/?\s*thinking\s*>/giu,
825
+ /<\s*\/?\s*rea:tool_calls\s*>/giu,
812
826
  /<\s*\/?\s*返璞归真\s*>/gu
813
827
  ];
814
828
  var ONLY_MARKUP_PUNCTUATION = /^[\s<>/\\||=_-]*$/u;
@@ -943,6 +957,19 @@ function streamHeaders(apiKey, extra = {}) {
943
957
  };
944
958
  }
945
959
 
960
+ // src/reasoning.ts
961
+ var PROTOCOL_EFFORTS = {
962
+ "chat-completions": ["none", "minimal", "low", "medium", "high", "xhigh", "ultra", "max"],
963
+ responses: ["none", "minimal", "low", "medium", "high", "xhigh", "max"],
964
+ messages: ["none", "low", "medium", "high", "xhigh", "max"]
965
+ };
966
+ function protocolAcceptsEffort(protocol, effort) {
967
+ return PROTOCOL_EFFORTS[protocol].includes(effort);
968
+ }
969
+ function modelReasoningEfforts(model, protocol) {
970
+ return model.reasoningEfforts?.filter((effort) => protocolAcceptsEffort(protocol, effort));
971
+ }
972
+
946
973
  // src/protocols/chat-completions.ts
947
974
  function buildChatCompletionsRequest(ctx) {
948
975
  const { options, model } = ctx;
@@ -957,7 +984,10 @@ function buildChatCompletionsRequest(ctx) {
957
984
  if (!Number.isSafeInteger(options.maxTokens) || options.maxTokens <= 0) throw new LlmError5("maxTokens \u5FC5\u987B\u662F\u6B63\u6574\u6570", "INVALID_REQUEST");
958
985
  body.max_tokens = options.maxTokens;
959
986
  }
960
- if (options.reasoningEffort !== void 0) body.reasoning_effort = options.reasoningEffort;
987
+ if (options.reasoningEffort !== void 0) {
988
+ if (!protocolAcceptsEffort("chat-completions", options.reasoningEffort)) throw new LlmError5("Chat Completions \u534F\u8BAE\u4E0D\u63A5\u53D7\u8BE5\u601D\u8003\u6DF1\u5EA6", "UNSUPPORTED_OPTION");
989
+ body.reasoning_effort = options.reasoningEffort;
990
+ }
961
991
  if (options.stop?.length) body.stop = options.stop;
962
992
  return { url: inferenceEndpoint(ctx.baseURL, "chat-completions"), headers: streamHeaders(ctx.apiKey), body };
963
993
  }
@@ -1041,7 +1071,6 @@ function anthropicUsage(raw) {
1041
1071
  // src/protocols/messages.ts
1042
1072
  var MESSAGES_DEFAULT_MAX_TOKENS = 4096;
1043
1073
  var ANTHROPIC_VERSION = "2023-06-01";
1044
- var THINKING_EFFORTS = ["low", "medium", "high", "xhigh", "max"];
1045
1074
  var DATA_URL = /^data:([^;,]+);base64,(.*)$/su;
1046
1075
  function messagesURL(baseURL) {
1047
1076
  return inferenceEndpoint(baseURL, "messages");
@@ -1133,7 +1162,7 @@ function buildMessagesRequest(ctx) {
1133
1162
  if (options.reasoningEffort !== void 0) {
1134
1163
  const effort = String(options.reasoningEffort);
1135
1164
  if (effort === "none") body.thinking = { type: "disabled" };
1136
- else if (THINKING_EFFORTS.includes(effort)) {
1165
+ else if (protocolAcceptsEffort("messages", effort)) {
1137
1166
  body.thinking = { type: "adaptive" };
1138
1167
  body.output_config = { effort };
1139
1168
  } else throw new LlmError7(`Anthropic Messages \u534F\u8BAE\u4E0D\u63A5\u53D7\u601D\u8003\u6DF1\u5EA6\u300C${effort}\u300D`, "UNSUPPORTED_OPTION");
@@ -1331,7 +1360,6 @@ import {
1331
1360
  LlmError as LlmError8,
1332
1361
  ToolCallId as ToolCallId3
1333
1362
  } from "@deepseek-ai/dsh-llm";
1334
- var REASONING_EFFORTS = ["none", "minimal", "low", "medium", "high", "xhigh", "max"];
1335
1363
  function userContent(text2, images) {
1336
1364
  if (!images.length) return text2;
1337
1365
  return [
@@ -1392,7 +1420,7 @@ function buildResponsesRequest(ctx) {
1392
1420
  }
1393
1421
  if (options.reasoningEffort !== void 0) {
1394
1422
  const effort = String(options.reasoningEffort);
1395
- if (!REASONING_EFFORTS.includes(effort)) throw new LlmError8(`Responses \u534F\u8BAE\u4E0D\u63A5\u53D7\u601D\u8003\u6DF1\u5EA6\u300C${effort}\u300D`, "UNSUPPORTED_OPTION");
1423
+ if (!protocolAcceptsEffort("responses", effort)) throw new LlmError8(`Responses \u534F\u8BAE\u4E0D\u63A5\u53D7\u601D\u8003\u6DF1\u5EA6\u300C${effort}\u300D`, "UNSUPPORTED_OPTION");
1396
1424
  body.reasoning = { effort, summary: "auto" };
1397
1425
  }
1398
1426
  if (options.stop?.length) throw new LlmError8("Responses \u534F\u8BAE\u4E0D\u652F\u6301 stop \u5E8F\u5217", "UNSUPPORTED_OPTION");
@@ -1701,7 +1729,8 @@ function outputReserve(model, protocol) {
1701
1729
  function metadata(provider, row, config) {
1702
1730
  const setting = config.modelSettings?.[row.id];
1703
1731
  const reserve = setting?.maxTokens ?? outputReserve(row, config.apiProtocol ?? "chat-completions");
1704
- const efforts = row.reasoningEfforts ?? (setting?.reasoningEffort ? [setting.reasoningEffort] : void 0);
1732
+ const efforts = modelReasoningEfforts(row, config.apiProtocol ?? "chat-completions");
1733
+ const defaultEffort = setting?.reasoningEffort !== void 0 && efforts?.includes(setting.reasoningEffort) ? setting.reasoningEffort : void 0;
1705
1734
  return {
1706
1735
  provider,
1707
1736
  id: row.id,
@@ -1711,7 +1740,7 @@ function metadata(provider, row, config) {
1711
1740
  ...reserve === void 0 ? {} : { defaultMaxTokens: reserve },
1712
1741
  ...efforts?.length ? { reasoning: {
1713
1742
  efforts: efforts.map((id) => ({ id: ReasoningEffortId(id), name: { none: "\u5173\u95ED", minimal: "\u6700\u4F4E", low: "\u4F4E", medium: "\u4E2D", high: "\u9AD8", xhigh: "\u8F83\u9AD8", ultra: "\u6781\u9AD8", max: "\u6700\u9AD8" }[id] ?? id })),
1714
- ...setting?.reasoningEffort ? { defaultEffort: ReasoningEffortId(setting.reasoningEffort) } : {}
1743
+ ...defaultEffort === void 0 ? {} : { defaultEffort: ReasoningEffortId(defaultEffort) }
1715
1744
  } } : {}
1716
1745
  };
1717
1746
  }
@@ -1758,11 +1787,13 @@ var CoRouterAdapter = class extends LlmAdapter {
1758
1787
  async *#stream(options, config, row) {
1759
1788
  const signal = AbortSignal.any([combinedSignal(options.signal, config.requestTimeoutMs), this.#lifetime.signal]);
1760
1789
  signal.throwIfAborted();
1790
+ const setting = config.modelSettings?.[row.id] ?? {};
1791
+ const protocolId = config.apiProtocol ?? "chat-completions";
1792
+ const effort = options.reasoningEffort ?? (setting.reasoningEffort ? ReasoningEffortId(setting.reasoningEffort) : void 0);
1793
+ if (effort !== void 0 && !modelReasoningEfforts(row, protocolId)?.includes(effort)) throw new LlmError10("\u6240\u9009\u601D\u8003\u6DF1\u5EA6\u4E0D\u9002\u7528\u4E8E\u5F53\u524D\u6A21\u578B\u6216\u534F\u8BAE\uFF1B\u8BF7\u4FEE\u6539\u9ED8\u8BA4\u503C\u6216\u9009\u62E9\u5176\u4ED6\u6863\u4F4D", "UNSUPPORTED_OPTION");
1761
1794
  const raw = await this.options.apiKey(config.apiKeyEnv, config.baseURL);
1762
1795
  if (!raw) throw new LlmError10("\u672A\u914D\u7F6E\u5F53\u524D\u7F51\u5173\u7684\u63A8\u7406\u51ED\u636E\uFF1B\u8BF7\u5B8C\u6210\u8BBE\u5907\u6388\u6743\u6216\u914D\u7F6E\u5BF9\u5E94 API Key", "MISSING_CREDENTIAL");
1763
1796
  const key = assertUsableApiKey(raw, "dsh-opendesign-corouter", config.apiKeyEnv);
1764
- const setting = config.modelSettings?.[row.id] ?? {};
1765
- const protocolId = config.apiProtocol ?? "chat-completions";
1766
1797
  const images = /* @__PURE__ */ new Map();
1767
1798
  const attemptedImages = /* @__PURE__ */ new Set();
1768
1799
  const unreadableImages = /* @__PURE__ */ new Set();
@@ -1796,8 +1827,6 @@ var CoRouterAdapter = class extends LlmAdapter {
1796
1827
  if (room < 1) throw new LlmError10("\u5F53\u524D\u8F93\u5165\u6CA1\u6709\u4E3A\u8F93\u51FA\u7559\u4E0B\u4E0A\u4E0B\u6587\u7A7A\u95F4", "CONTEXT_WINDOW_EXCEEDED");
1797
1828
  maxTokens = Math.min(capped, Math.floor(room));
1798
1829
  }
1799
- const effort = options.reasoningEffort ?? (setting.reasoningEffort ? ReasoningEffortId(setting.reasoningEffort) : void 0);
1800
- if (effort !== void 0 && row.reasoningEfforts && !row.reasoningEfforts.includes(effort)) throw new LlmError10("\u8BE5\u6A21\u578B\u4E0D\u652F\u6301\u6240\u9009\u601D\u8003\u6DF1\u5EA6", "UNSUPPORTED_OPTION");
1801
1830
  const effective = {
1802
1831
  ...projectedOptions,
1803
1832
  ...(options.temperature ?? setting.temperature) === void 0 ? {} : { temperature: options.temperature ?? setting.temperature },
@@ -1846,11 +1875,12 @@ var CoRouterAdapter = class extends LlmAdapter {
1846
1875
  throw gatewayError(response.status, error);
1847
1876
  }
1848
1877
  if (!response.body) throw new LlmError10("CoRouter \u8FD4\u56DE\u7A7A\u54CD\u5E94", "EMPTY_RESPONSE");
1849
- const tracker = new EmptyTurnTracker({ enabled: row.group === "plan" && config.emptyTurnGuard && Boolean(options.tools?.length), onEmptyTurn: () => this.options.onEmptyTurn?.() });
1878
+ const monitor = row.group === "plan" && row.id === "deepseek-v4.1-flash" && config.emptyTurnGuard && Boolean(options.tools?.length);
1879
+ const tracker = new EmptyTurnTracker({ enabled: monitor, onEmptyTurn: () => this.options.onEmptyTurn?.() });
1850
1880
  try {
1851
1881
  for await (const chunk of protocol.stream(response.body, signal, config.streamIdleTimeoutMs)) {
1852
1882
  tracker.observe(chunk);
1853
- if (chunk.type === "finish" && row.group === "plan" && config.emptyTurnGuard) yield { ...chunk, reason: tracker.resolve(chunk.reason) };
1883
+ if (chunk.type === "finish" && monitor) yield { ...chunk, reason: tracker.resolve(chunk.reason) };
1854
1884
  else yield chunk;
1855
1885
  }
1856
1886
  } catch (error) {
@@ -2200,7 +2230,7 @@ import { randomUUID } from "node:crypto";
2200
2230
 
2201
2231
  // src/device-auth.ts
2202
2232
  var DEVICE_AUTH_ENDPOINT = "https://amr-api.open-design.ai/api/v1/cli/device-authorizations";
2203
- var DEVICE_AUTH_PLUGIN_VERSION = "0.1.7";
2233
+ var DEVICE_AUTH_PLUGIN_VERSION = "0.1.8";
2204
2234
  var DEVICE_REQUEST_TIMEOUT_MS = 2e4;
2205
2235
  var DEVICE_RESPONSE_MAX_BYTES = 65536;
2206
2236
  var DEVICE_POLL_MIN_MS = 1e3;
@@ -3495,15 +3525,19 @@ var OpenDesignCoRouterService = class extends (_a = TypertRemoteService, _status
3495
3525
  login: this._login.status(),
3496
3526
  config: state.config,
3497
3527
  guardedTurns: this.guardedTurns,
3498
- models: this._models.map(({ id, name: name2, group: group2, contextWindow, maxOutputTokens, reasoningEfforts }) => ({
3499
- id,
3500
- name: name2,
3501
- group: group2,
3502
- enabled: isModelEnabled(id, state.config.modelSettings),
3503
- ...contextWindow === void 0 ? {} : { contextWindow },
3504
- ...maxOutputTokens === void 0 ? {} : { maxOutputTokens },
3505
- ...reasoningEfforts === void 0 ? {} : { reasoningEfforts }
3506
- })),
3528
+ models: this._models.map((row) => {
3529
+ const { id, name: name2, group: group2, contextWindow, maxOutputTokens } = row;
3530
+ const reasoningEfforts = modelReasoningEfforts(row, state.config.apiProtocol ?? "chat-completions");
3531
+ return {
3532
+ id,
3533
+ name: name2,
3534
+ group: group2,
3535
+ enabled: isModelEnabled(id, state.config.modelSettings),
3536
+ ...contextWindow === void 0 ? {} : { contextWindow },
3537
+ ...maxOutputTokens === void 0 ? {} : { maxOutputTokens },
3538
+ ...reasoningEfforts === void 0 ? {} : { reasoningEfforts }
3539
+ };
3540
+ }),
3507
3541
  modelsSource: this._modelsUpdatedAt ? "openai" : "builtin",
3508
3542
  ...this._modelsUpdatedAt ? { modelsUpdatedAt: this._modelsUpdatedAt } : {},
3509
3543
  ...this._modelsError ? { modelsError: this._modelsError } : {}