dsh-opendesign-corouter 0.1.6 → 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.
@@ -122,9 +122,9 @@ function amount(value, label, negative = false) {
122
122
  if (typeof value !== "number" || !Number.isFinite(value) || !negative && value < 0) throw new Error(`${label} \u5FC5\u987B\u662F${negative ? "" : "\u975E\u8D1F"}\u6709\u9650\u6570\u5B57`);
123
123
  return value;
124
124
  }
125
- function positiveInteger(value, label, max) {
125
+ function positiveInteger(value, label, max, min = 1) {
126
126
  const result = count(value, label);
127
- if (result === 0 || result > max) throw new Error(`${label} \u8D85\u51FA\u8303\u56F4`);
127
+ if (result < min || result > max) throw new Error(`${label} \u8D85\u51FA\u8303\u56F4`);
128
128
  return result;
129
129
  }
130
130
  function parseEfforts(value) {
@@ -227,9 +227,9 @@ function parseStatusConfig(value) {
227
227
  apiKeyEnv,
228
228
  ...value.apiProtocol === void 0 ? {} : { apiProtocol: member(value.apiProtocol, API_PROTOCOL_IDS, "\u63A8\u7406\u534F\u8BAE") },
229
229
  ...value.modelSettings === void 0 ? {} : { modelSettings: parseModelSettingsMap(value.modelSettings) },
230
- ...value.requestTimeoutMs === void 0 ? {} : { requestTimeoutMs: positiveInteger(value.requestTimeoutMs, "\u8BF7\u6C42\u8D85\u65F6", 36e5) },
231
- ...value.streamIdleTimeoutMs === void 0 ? {} : { streamIdleTimeoutMs: positiveInteger(value.streamIdleTimeoutMs, "\u6D41\u7A7A\u95F2\u8D85\u65F6", 18e5) },
232
- ...value.quotaRefreshSeconds === void 0 ? {} : { quotaRefreshSeconds: positiveInteger(value.quotaRefreshSeconds, "\u81EA\u52A8\u5237\u65B0\u95F4\u9694", 3600) }
230
+ ...value.requestTimeoutMs === void 0 ? {} : { requestTimeoutMs: positiveInteger(value.requestTimeoutMs, "\u8BF7\u6C42\u8D85\u65F6", 36e5, 1e3) },
231
+ ...value.streamIdleTimeoutMs === void 0 ? {} : { streamIdleTimeoutMs: positiveInteger(value.streamIdleTimeoutMs, "\u6D41\u7A7A\u95F2\u8D85\u65F6", 18e5, 1e3) },
232
+ ...value.quotaRefreshSeconds === void 0 ? {} : { quotaRefreshSeconds: positiveInteger(value.quotaRefreshSeconds, "\u81EA\u52A8\u5237\u65B0\u95F4\u9694", 3600, 5) }
233
233
  };
234
234
  }
235
235
  function parseStatus(value) {
@@ -242,9 +242,13 @@ function parseStatus(value) {
242
242
  if (ids.has(model.id)) throw new Error(`\u6A21\u578B\u5217\u8868\u91CD\u590D\u4E86 id "${model.id}"`);
243
243
  ids.add(model.id);
244
244
  }
245
+ const apiKeyConfigured = flag(value.apiKeyConfigured, "\u51ED\u636E\u662F\u5426\u5C31\u7EEA");
246
+ const apiKeySource = value.apiKeySource === void 0 ? void 0 : member(value.apiKeySource, ["reference", "stored", "device"], "\u63A8\u7406\u51ED\u636E\u6765\u6E90");
247
+ if (!apiKeyConfigured && apiKeySource !== void 0) throw new Error("\u672A\u914D\u7F6E\u51ED\u636E\u65F6\u4E0D\u80FD\u6307\u5B9A\u63A8\u7406\u51ED\u636E\u6765\u6E90");
245
248
  return {
246
249
  revision: text(value.revision, "\u914D\u7F6E\u7248\u672C", MAX_REVISION),
247
- apiKeyConfigured: flag(value.apiKeyConfigured, "\u51ED\u636E\u662F\u5426\u5C31\u7EEA"),
250
+ apiKeyConfigured,
251
+ ...apiKeySource === void 0 ? {} : { apiKeySource },
248
252
  ...value.sessionSource === void 0 ? {} : { sessionSource: member(value.sessionSource, SESSION_SOURCES, "\u767B\u5F55\u6001\u6765\u6E90") },
249
253
  quota: parseQuotaSnapshot(value.quota),
250
254
  login: parseLoginState(value.login),
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(),
305
- apiProtocol: Schema.union(API_PROTOCOL_IDS).default("chat-completions").description("\u63A8\u7406 API \u534F\u8BAE\uFF1B\u6A21\u578B\u53D1\u73B0\u59CB\u7EC8\u4F7F\u7528 OpenAI models \u63A5\u53E3").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(),
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(),
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(),
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.6";
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;
@@ -3171,9 +3201,9 @@ function amount(value, label, negative = false) {
3171
3201
  if (typeof value !== "number" || !Number.isFinite(value) || !negative && value < 0) throw new Error(`${label} \u5FC5\u987B\u662F${negative ? "" : "\u975E\u8D1F"}\u6709\u9650\u6570\u5B57`);
3172
3202
  return value;
3173
3203
  }
3174
- function positiveInteger(value, label, max) {
3204
+ function positiveInteger(value, label, max, min = 1) {
3175
3205
  const result = count(value, label);
3176
- if (result === 0 || result > max) throw new Error(`${label} \u8D85\u51FA\u8303\u56F4`);
3206
+ if (result < min || result > max) throw new Error(`${label} \u8D85\u51FA\u8303\u56F4`);
3177
3207
  return result;
3178
3208
  }
3179
3209
  function parseEfforts(value) {
@@ -3276,9 +3306,9 @@ function parseStatusConfig(value) {
3276
3306
  apiKeyEnv,
3277
3307
  ...value.apiProtocol === void 0 ? {} : { apiProtocol: member(value.apiProtocol, API_PROTOCOL_IDS, "\u63A8\u7406\u534F\u8BAE") },
3278
3308
  ...value.modelSettings === void 0 ? {} : { modelSettings: parseModelSettingsMap(value.modelSettings) },
3279
- ...value.requestTimeoutMs === void 0 ? {} : { requestTimeoutMs: positiveInteger(value.requestTimeoutMs, "\u8BF7\u6C42\u8D85\u65F6", 36e5) },
3280
- ...value.streamIdleTimeoutMs === void 0 ? {} : { streamIdleTimeoutMs: positiveInteger(value.streamIdleTimeoutMs, "\u6D41\u7A7A\u95F2\u8D85\u65F6", 18e5) },
3281
- ...value.quotaRefreshSeconds === void 0 ? {} : { quotaRefreshSeconds: positiveInteger(value.quotaRefreshSeconds, "\u81EA\u52A8\u5237\u65B0\u95F4\u9694", 3600) }
3309
+ ...value.requestTimeoutMs === void 0 ? {} : { requestTimeoutMs: positiveInteger(value.requestTimeoutMs, "\u8BF7\u6C42\u8D85\u65F6", 36e5, 1e3) },
3310
+ ...value.streamIdleTimeoutMs === void 0 ? {} : { streamIdleTimeoutMs: positiveInteger(value.streamIdleTimeoutMs, "\u6D41\u7A7A\u95F2\u8D85\u65F6", 18e5, 1e3) },
3311
+ ...value.quotaRefreshSeconds === void 0 ? {} : { quotaRefreshSeconds: positiveInteger(value.quotaRefreshSeconds, "\u81EA\u52A8\u5237\u65B0\u95F4\u9694", 3600, 5) }
3282
3312
  };
3283
3313
  }
3284
3314
  function parseStatus(value) {
@@ -3291,9 +3321,13 @@ function parseStatus(value) {
3291
3321
  if (ids.has(model.id)) throw new Error(`\u6A21\u578B\u5217\u8868\u91CD\u590D\u4E86 id "${model.id}"`);
3292
3322
  ids.add(model.id);
3293
3323
  }
3324
+ const apiKeyConfigured = flag(value.apiKeyConfigured, "\u51ED\u636E\u662F\u5426\u5C31\u7EEA");
3325
+ const apiKeySource = value.apiKeySource === void 0 ? void 0 : member(value.apiKeySource, ["reference", "stored", "device"], "\u63A8\u7406\u51ED\u636E\u6765\u6E90");
3326
+ if (!apiKeyConfigured && apiKeySource !== void 0) throw new Error("\u672A\u914D\u7F6E\u51ED\u636E\u65F6\u4E0D\u80FD\u6307\u5B9A\u63A8\u7406\u51ED\u636E\u6765\u6E90");
3294
3327
  return {
3295
3328
  revision: text(value.revision, "\u914D\u7F6E\u7248\u672C", MAX_REVISION),
3296
- apiKeyConfigured: flag(value.apiKeyConfigured, "\u51ED\u636E\u662F\u5426\u5C31\u7EEA"),
3329
+ apiKeyConfigured,
3330
+ ...apiKeySource === void 0 ? {} : { apiKeySource },
3297
3331
  ...value.sessionSource === void 0 ? {} : { sessionSource: member(value.sessionSource, SESSION_SOURCES, "\u767B\u5F55\u6001\u6765\u6E90") },
3298
3332
  quota: parseQuotaSnapshot(value.quota),
3299
3333
  login: parseLoginState(value.login),
@@ -3332,7 +3366,8 @@ var inject = ["llm"];
3332
3366
  var API_KEY = credentialKey(name, "api-key");
3333
3367
  var SESSION = credentialKey(name, "console");
3334
3368
  var hash = (value) => createHash("sha256").update(JSON.stringify(value) ?? "").digest("hex");
3335
- var needLogin = () => ({ state: "need-login", updatedAt: (/* @__PURE__ */ new Date()).toISOString(), windows: [], balances: {}, note: "\u5C1A\u672A\u8BFB\u53D6\u989D\u5EA6\uFF1B\u53EF\u4F7F\u7528\u684C\u9762\u767B\u5F55\u6001\u6216\u5728\u8FDE\u63A5\u9875\u767B\u5F55" });
3369
+ var needLogin = () => ({ state: "need-login", updatedAt: (/* @__PURE__ */ new Date()).toISOString(), windows: [], balances: {}, note: "\u8BF7\u5728\u8FDE\u63A5\u4E0E\u767B\u5F55\u9875\u5B8C\u6210\u8D26\u53F7\u6388\u6743\u540E\u8BFB\u53D6\u989D\u5EA6\u3002" });
3370
+ var unreadQuota = () => ({ state: "partial", updatedAt: (/* @__PURE__ */ new Date()).toISOString(), windows: [], balances: {}, note: "\u989D\u5EA6\u5C1A\u672A\u8BFB\u53D6\uFF0C\u8BF7\u5237\u65B0\u989D\u5EA6\u3002" });
3336
3371
  function grantOf(value) {
3337
3372
  if (value?.kind !== "grant" || !record(value.payload)) return;
3338
3373
  if (value.payload.kind === "device") return deviceGrant(value.payload);
@@ -3351,7 +3386,7 @@ var OpenDesignCoRouterService = class extends (_a = TypertRemoteService, _status
3351
3386
  __publicField(this, "_lifetime", new AbortController());
3352
3387
  __publicField(this, "_revision", randomUUID2());
3353
3388
  __publicField(this, "_fingerprint", "");
3354
- __publicField(this, "_quota", needLogin());
3389
+ __publicField(this, "_quota", unreadQuota());
3355
3390
  __publicField(this, "_quotaAt", 0);
3356
3391
  __publicField(this, "_quotaConfig", "");
3357
3392
  __publicField(this, "_quotaIdentity");
@@ -3417,15 +3452,15 @@ var OpenDesignCoRouterService = class extends (_a = TypertRemoteService, _status
3417
3452
  async _key(ref, baseURL, apiRecord, sessionRecord) {
3418
3453
  const credentials = this.ctx.get("credentials");
3419
3454
  const ambient = credentials ? (await credentials.resolve(credentialRef(ref)))?.value : launchEnvironmentOf(this.ctx).get(ref)?.value;
3420
- if (ambient?.trim()) return ambient;
3421
- if (apiRecord?.kind === "api-key" && apiRecord.key?.trim()) return apiRecord.key;
3455
+ if (ambient?.trim()) return { key: ambient, source: "reference" };
3456
+ if (apiRecord?.kind === "api-key" && apiRecord.key?.trim()) return { key: apiRecord.key, source: "stored" };
3422
3457
  const grant = grantOf(sessionRecord);
3423
- return isOfficialRuntimeUrl(baseURL) && grant && "kind" in grant && grant.kind === "device" ? grant.runtimeKey : void 0;
3458
+ return isOfficialRuntimeUrl(baseURL) && grant && "kind" in grant && grant.kind === "device" ? { key: grant.runtimeKey, source: "device" } : void 0;
3424
3459
  }
3425
3460
  async resolveApiKey(ref, baseURL = this._config().baseURL) {
3426
3461
  const credentials = this.ctx.get("credentials");
3427
3462
  const [apiRecord, sessionRecord] = await Promise.all([credentials?.readRecord(API_KEY), credentials?.readRecord(SESSION)]);
3428
- return this._key(ref, baseURL, apiRecord, sessionRecord);
3463
+ return (await this._key(ref, baseURL, apiRecord, sessionRecord))?.key;
3429
3464
  }
3430
3465
  _invalidateModels() {
3431
3466
  const changed = this._modelsUpdatedAt !== void 0;
@@ -3445,8 +3480,9 @@ var OpenDesignCoRouterService = class extends (_a = TypertRemoteService, _status
3445
3480
  async _state() {
3446
3481
  const credentials = this.ctx.get("credentials"), config = this._config();
3447
3482
  const [apiRecord, sessionRecord] = await Promise.all([credentials?.readRecord(API_KEY), credentials?.readRecord(SESSION)]);
3448
- const key = await this._key(config.apiKeyEnv, config.baseURL, apiRecord, sessionRecord);
3449
- const state = { apiRecord, sessionRecord, key, config };
3483
+ const resolved = await this._key(config.apiKeyEnv, config.baseURL, apiRecord, sessionRecord);
3484
+ const key = resolved?.key;
3485
+ const state = { apiRecord, sessionRecord, key, keySource: resolved?.source, config };
3450
3486
  const modelsFingerprint = hash({ key, baseURL: config.baseURL });
3451
3487
  if (modelsFingerprint !== this._modelsFingerprint) {
3452
3488
  this._modelsFingerprint = modelsFingerprint;
@@ -3468,7 +3504,7 @@ var OpenDesignCoRouterService = class extends (_a = TypertRemoteService, _status
3468
3504
  _invalidateQuota() {
3469
3505
  this._quotaAt = 0;
3470
3506
  this._quotaEpoch++;
3471
- this._quota = needLogin();
3507
+ this._quota = unreadQuota();
3472
3508
  this._quotaIdentity = void 0;
3473
3509
  this._quotaController?.abort();
3474
3510
  this._quotaPending = void 0;
@@ -3483,20 +3519,25 @@ var OpenDesignCoRouterService = class extends (_a = TypertRemoteService, _status
3483
3519
  return parseStatus({
3484
3520
  revision: this._revision,
3485
3521
  apiKeyConfigured: Boolean(state.key?.trim()),
3522
+ ...state.keySource ? { apiKeySource: state.keySource } : {},
3486
3523
  ...this._quota.source ? { sessionSource: this._quota.source } : {},
3487
3524
  quota: this._quota,
3488
3525
  login: this._login.status(),
3489
3526
  config: state.config,
3490
3527
  guardedTurns: this.guardedTurns,
3491
- models: this._models.map(({ id, name: name2, group: group2, contextWindow, maxOutputTokens, reasoningEfforts }) => ({
3492
- id,
3493
- name: name2,
3494
- group: group2,
3495
- enabled: isModelEnabled(id, state.config.modelSettings),
3496
- ...contextWindow === void 0 ? {} : { contextWindow },
3497
- ...maxOutputTokens === void 0 ? {} : { maxOutputTokens },
3498
- ...reasoningEfforts === void 0 ? {} : { reasoningEfforts }
3499
- })),
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
+ }),
3500
3541
  modelsSource: this._modelsUpdatedAt ? "openai" : "builtin",
3501
3542
  ...this._modelsUpdatedAt ? { modelsUpdatedAt: this._modelsUpdatedAt } : {},
3502
3543
  ...this._modelsError ? { modelsError: this._modelsError } : {}
@@ -3507,7 +3548,7 @@ var OpenDesignCoRouterService = class extends (_a = TypertRemoteService, _status
3507
3548
  if (this._modelsPending) return this._modelsPending;
3508
3549
  const key = state.key;
3509
3550
  if (!key?.trim()) {
3510
- this._modelsError = "\u8BF7\u5148\u914D\u7F6E API Key\uFF0C\u518D\u4ECE OpenAI \u7AEF\u53E3\u62C9\u53D6\u6A21\u578B\u3002";
3551
+ this._modelsError = "\u8BF7\u5148\u914D\u7F6E API Key\uFF0C\u518D\u4ECE\u5F53\u524D\u7F51\u5173\u8BFB\u53D6\u6A21\u578B\u76EE\u5F55\u3002";
3511
3552
  return this.status();
3512
3553
  }
3513
3554
  const epoch = this._modelsEpoch;
@@ -3519,10 +3560,18 @@ var OpenDesignCoRouterService = class extends (_a = TypertRemoteService, _status
3519
3560
  const models = await (this.dependencies.fetchModels ?? fetchModelCatalog)(state.config.baseURL, key, signal);
3520
3561
  await this._state();
3521
3562
  if (signal.aborted || epoch !== this._modelsEpoch) return this.status();
3563
+ const previousModels = this._models;
3564
+ const previousUpdatedAt = this._modelsUpdatedAt;
3522
3565
  this._models = models;
3523
3566
  this._modelsUpdatedAt = (/* @__PURE__ */ new Date()).toISOString();
3524
3567
  this._modelsError = void 0;
3525
- this.dependencies.modelsChanged?.();
3568
+ try {
3569
+ this.dependencies.modelsChanged?.();
3570
+ } catch {
3571
+ this._models = previousModels;
3572
+ this._modelsUpdatedAt = previousUpdatedAt;
3573
+ this._modelsError = "\u6A21\u578B\u76EE\u5F55\u5DF2\u8BFB\u53D6\uFF0C\u4F46\u6A21\u578B\u9009\u62E9\u5668\u66F4\u65B0\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002";
3574
+ }
3526
3575
  } catch (error) {
3527
3576
  if (!signal.aborted && epoch === this._modelsEpoch) this._modelsError = error instanceof ModelDiscoveryError ? error.message : "\u6A21\u578B\u5217\u8868\u66F4\u65B0\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002";
3528
3577
  }