@apinggithub/mydsh-auto-model-router 0.3.3 → 0.3.4
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 +6 -4
- package/README.zh.md +4 -3
- package/config.mjs +8 -8
- package/index.mjs +26 -15
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -63,10 +63,12 @@ LLM dispatch
|
|
|
63
63
|
| `L2` | writing code, review, refactoring | strong |
|
|
64
64
|
| `L3` | complex bug fixes, multi-step tasks, reports | most powerful |
|
|
65
65
|
|
|
66
|
-
Each level is a user-assigned provider/model route with
|
|
67
|
-
`reasoningEffort
|
|
68
|
-
|
|
69
|
-
|
|
66
|
+
Each level is a user-assigned provider/model route with an **optional**
|
|
67
|
+
`reasoningEffort`: omitting it (or writing `null`) sends no effort at all —
|
|
68
|
+
required for models without a thinking mode (GLM/Qwen flash), which the host
|
|
69
|
+
otherwise rejects; `off` explicitly disables thinking; `high`/`max` set its
|
|
70
|
+
intensity. Levels may be left unconfigured; the router then falls to the nearest
|
|
71
|
+
configured level (direction depends on the cost
|
|
70
72
|
mode).
|
|
71
73
|
|
|
72
74
|
## Auto mode in the model selector
|
package/README.zh.md
CHANGED
|
@@ -52,9 +52,10 @@ LLM 分发
|
|
|
52
52
|
| `L2` | 写代码、审查、重构 | 较强 |
|
|
53
53
|
| `L3` | 复杂 BUG 修复、多步任务、汇报 | 最强 |
|
|
54
54
|
|
|
55
|
-
每个层级是一个用户自行指定的 provider/model
|
|
56
|
-
`reasoningEffort
|
|
57
|
-
|
|
55
|
+
每个层级是一个用户自行指定的 provider/model 路由,**可选**附带
|
|
56
|
+
`reasoningEffort`:不写(或写 `null`)= 不发送该字段——GLM/Qwen flash 这类没有
|
|
57
|
+
思考模式的模型**必须**这样,否则宿主会拒绝请求;`off` = 显式关闭思考;`high`/`max`
|
|
58
|
+
= 思考强度。层级可以留空不配置;路由会落到最近的已配置层级(方向由成本模式决定)。
|
|
58
59
|
|
|
59
60
|
## 模型选择框中的 Auto 模式
|
|
60
61
|
|
package/config.mjs
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
// fallbackChain: [route, ...] (model-level, appended with the default)
|
|
23
23
|
// maxRetries: per-route retry budget on request-error
|
|
24
24
|
|
|
25
|
-
export const PLUGIN_VERSION = '0.3.
|
|
25
|
+
export const PLUGIN_VERSION = '0.3.4'
|
|
26
26
|
|
|
27
27
|
export const LEVEL_IDS = Object.freeze(['L0', 'L1', 'L2', 'L3'])
|
|
28
28
|
export const COST_MODES = Object.freeze(['cost-first', 'quality-first', 'balanced'])
|
|
@@ -382,13 +382,14 @@ function normalizeLevels(levels) {
|
|
|
382
382
|
}
|
|
383
383
|
|
|
384
384
|
function normalizeLevelRoute(route, where) {
|
|
385
|
-
//
|
|
386
|
-
//
|
|
387
|
-
//
|
|
388
|
-
//
|
|
389
|
-
|
|
385
|
+
// A level states a reasoning effort only when the config states one.
|
|
386
|
+
//
|
|
387
|
+
// This used to default to 'off', which broke twice over: providers whose
|
|
388
|
+
// models have no thinking mode (GLM/Qwen flash) reject *any* effort with
|
|
389
|
+
// UNSUPPORTED_REASONING_EFFORT, and `apply()` normalises the config a second
|
|
390
|
+
// time, so an omitted field came back as 'off' even when the config had said
|
|
391
|
+
// `null`. Absent now stays absent, which makes normalisation idempotent.
|
|
390
392
|
const normalized = normalizeRoute(route, where)
|
|
391
|
-
// Every other level carries an explicit effort; absent → 'off' (no thinking).
|
|
392
393
|
const contextWindow = route.contextWindow === undefined || route.contextWindow === null
|
|
393
394
|
? undefined
|
|
394
395
|
: Number(route.contextWindow)
|
|
@@ -397,7 +398,6 @@ function normalizeLevelRoute(route, where) {
|
|
|
397
398
|
}
|
|
398
399
|
return {
|
|
399
400
|
...normalized,
|
|
400
|
-
...omitEffort ? {} : { reasoningEffort: normalized.reasoningEffort ?? 'off' },
|
|
401
401
|
// A level may declare that its route is a free-quota route. The flag rides
|
|
402
402
|
// along with the route so the router can prefer/demote it.
|
|
403
403
|
...route.freeQuota === true ? { freeQuota: true } : {},
|
package/index.mjs
CHANGED
|
@@ -294,8 +294,29 @@ export function apply(ctx, input = {}) {
|
|
|
294
294
|
}
|
|
295
295
|
})
|
|
296
296
|
|
|
297
|
-
|
|
298
|
-
|
|
297
|
+
/**
|
|
298
|
+
* Rewrite a host proposal onto one routed model.
|
|
299
|
+
*
|
|
300
|
+
* The incoming proposal still carries the *session's* reasoning effort — the
|
|
301
|
+
* host's model-selection listener re-applies it on every step — and that
|
|
302
|
+
* value belongs to the virtual `auto` model, not to the model we route to.
|
|
303
|
+
* Spreading it through sends a thinking parameter to a model that has no
|
|
304
|
+
* thinking mode, and the host rejects the request with
|
|
305
|
+
* `provider "zhipu" model "glm-4.7-flash" does not support reasoning effort "off"`.
|
|
306
|
+
* So the inherited effort is dropped unless the level itself declares one.
|
|
307
|
+
*
|
|
308
|
+
* @param {object} proposed - config from the host's own waterfall
|
|
309
|
+
* @param {object} route - the level's route
|
|
310
|
+
* @returns {object} the rewritten config
|
|
311
|
+
*/
|
|
312
|
+
const withRoute = (proposed, route) => {
|
|
313
|
+
const next = { ...proposed, provider: route.provider, model: route.model }
|
|
314
|
+
delete next.reasoningEffort
|
|
315
|
+
if (route.reasoningEffort !== undefined) next.reasoningEffort = route.reasoningEffort
|
|
316
|
+
return next
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// ── ★ agent/request: reuse the per-input decision ────────────────────── // Only a NEW user input triggers a fresh decision (and only when the
|
|
299
320
|
// session picked the virtual auto provider). Steps within the same input
|
|
300
321
|
// reuse state.current — no per-step re-judging, no per-step classifier.
|
|
301
322
|
ctx.on('agent/request', async (payload, next) => {
|
|
@@ -333,15 +354,13 @@ export function apply(ctx, input = {}) {
|
|
|
333
354
|
}
|
|
334
355
|
// fallback: user dismissed → stay in heuristic mode.
|
|
335
356
|
const route = router.current(sessionId)
|
|
336
|
-
return
|
|
337
|
-
...route.reasoningEffort === undefined ? {} : { reasoningEffort: route.reasoningEffort } }
|
|
357
|
+
return withRoute(proposed, route)
|
|
338
358
|
}
|
|
339
359
|
if (decision.needsAsk === 'fixed-reelect') {
|
|
340
360
|
const picked = await askFixedLevel(ctx, agentOf(payload), router, sessionId)
|
|
341
361
|
if (picked) router.setMode(sessionId, 'fixed', { fixedLevel: picked })
|
|
342
362
|
const route = router.current(sessionId)
|
|
343
|
-
return
|
|
344
|
-
...route.reasoningEffort === undefined ? {} : { reasoningEffort: route.reasoningEffort } }
|
|
363
|
+
return withRoute(proposed, route)
|
|
345
364
|
}
|
|
346
365
|
}
|
|
347
366
|
|
|
@@ -363,15 +382,7 @@ export function apply(ctx, input = {}) {
|
|
|
363
382
|
if (demotion && config.freeQuota.notify) {
|
|
364
383
|
await notifyFreeQuotaFallback(agentOf(payload), sessionId, demotion, resolveLocale(ctx))
|
|
365
384
|
}
|
|
366
|
-
return
|
|
367
|
-
...proposed,
|
|
368
|
-
provider: route.provider,
|
|
369
|
-
model: route.model,
|
|
370
|
-
// A level that omits `reasoningEffort` (configured as `null`) has no
|
|
371
|
-
// thinking mode: sending an effort to a model without reasoning
|
|
372
|
-
// metadata would be rejected with UNSUPPORTED_REASONING_EFFORT.
|
|
373
|
-
...route.reasoningEffort === undefined ? {} : { reasoningEffort: route.reasoningEffort },
|
|
374
|
-
}
|
|
385
|
+
return withRoute(proposed, route)
|
|
375
386
|
} catch (error) {
|
|
376
387
|
// The router must never be able to fail a user request: hand the host's
|
|
377
388
|
// own proposal back untouched and surface the reason once.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@apinggithub/mydsh-auto-model-router",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.4",
|
|
4
4
|
"description": "Hybrid model router for DeepSeek Harness: declarative rules, user-configurable cost control (cost-first / quality-first / balanced), optional LLM task classifier, and failure fallback chains.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.mjs",
|