@apinggithub/mydsh-auto-model-router 0.2.3 → 0.2.5
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/auto-adapter.mjs +45 -6
- package/config.mjs +5 -5
- package/package.json +1 -1
package/auto-adapter.mjs
CHANGED
|
@@ -107,6 +107,49 @@ function reasoningEfforts(info) {
|
|
|
107
107
|
return FALLBACK_EFFORTS.map((effort) => ({ ...effort }))
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
/**
|
|
111
|
+
* Pick the reasoning effort to send to the routed model.
|
|
112
|
+
*
|
|
113
|
+
* The session's model selection keeps its own `reasoningEffort` and the host's
|
|
114
|
+
* selection listener re-applies it after every waterfall rewrite, so the value
|
|
115
|
+
* that reaches this adapter belongs to the *virtual* model, not to the model it
|
|
116
|
+
* forwards to. Passing it through unchanged sends a thinking parameter to a
|
|
117
|
+
* model with no thinking mode, which the provider rejects outright
|
|
118
|
+
* (`does not support reasoning effort "low"`).
|
|
119
|
+
*
|
|
120
|
+
* @param {object} llm - ctx.llm
|
|
121
|
+
* @param {object} route - the routed provider/model
|
|
122
|
+
* @param {unknown} requested - effort the caller asked for (usually the UI choice)
|
|
123
|
+
* @returns {Promise<string | undefined>} an effort the target advertises, or undefined
|
|
124
|
+
*/
|
|
125
|
+
async function supportedEffort(llm, route, requested) {
|
|
126
|
+
const info = await underlyingInfo(llm, route)
|
|
127
|
+
const efforts = Array.isArray(info?.reasoning?.efforts) ? info.reasoning.efforts : []
|
|
128
|
+
if (efforts.length === 0) return undefined
|
|
129
|
+
const ids = new Set(efforts.map((effort) => effort.id))
|
|
130
|
+
if (typeof route.reasoningEffort === 'string' && ids.has(route.reasoningEffort)) return route.reasoningEffort
|
|
131
|
+
if (typeof requested === 'string' && ids.has(requested)) return requested
|
|
132
|
+
const fallback = info?.reasoning?.defaultEffort
|
|
133
|
+
return typeof fallback === 'string' && ids.has(fallback) ? fallback : undefined
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Request options for the routed model: same call, different provider/model,
|
|
138
|
+
* and only the reasoning effort that model actually supports.
|
|
139
|
+
*
|
|
140
|
+
* @param {object} llm - ctx.llm
|
|
141
|
+
* @param {object} route - the routed provider/model
|
|
142
|
+
* @param {object} options - options the runtime handed to `stream()`
|
|
143
|
+
* @returns {Promise<object>}
|
|
144
|
+
*/
|
|
145
|
+
async function forwardOptions(llm, route, options) {
|
|
146
|
+
const forwarded = { ...options, provider: route.provider, model: route.model }
|
|
147
|
+
delete forwarded.reasoningEffort
|
|
148
|
+
const effort = await supportedEffort(llm, route, options?.reasoningEffort)
|
|
149
|
+
if (effort !== undefined) forwarded.reasoningEffort = effort
|
|
150
|
+
return forwarded
|
|
151
|
+
}
|
|
152
|
+
|
|
110
153
|
/**
|
|
111
154
|
* Build the virtual adapter for one plugin mount.
|
|
112
155
|
*
|
|
@@ -194,13 +237,9 @@ export function createAutoAdapter(resolve = {}) {
|
|
|
194
237
|
// runtime; the adapter contract requires we stream chunks back.
|
|
195
238
|
const llm = readLlm()
|
|
196
239
|
if (typeof llm?.stream === 'function') {
|
|
240
|
+
const forwarded = await forwardOptions(llm, route, options)
|
|
197
241
|
// ctx.llm.stream returns the AsyncIterable directly (not { stream }).
|
|
198
|
-
const stream = llm.stream(
|
|
199
|
-
...options,
|
|
200
|
-
provider: route.provider,
|
|
201
|
-
model: route.model,
|
|
202
|
-
...route.reasoningEffort === undefined ? {} : { reasoningEffort: route.reasoningEffort },
|
|
203
|
-
})
|
|
242
|
+
const stream = llm.stream(forwarded)
|
|
204
243
|
for await (const chunk of stream) yield chunk
|
|
205
244
|
return
|
|
206
245
|
}
|
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.2.
|
|
25
|
+
export const PLUGIN_VERSION = '0.2.5'
|
|
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'])
|
|
@@ -213,12 +213,12 @@ function normalizeFreeQuota(freeQuota) {
|
|
|
213
213
|
`mydsh-auto-model-router: ${where}.maxLevel must be one of ${LEVEL_IDS.join(', ')}, got "${maxLevel}"`,
|
|
214
214
|
)
|
|
215
215
|
}
|
|
216
|
-
//
|
|
217
|
-
//
|
|
218
|
-
// thinking
|
|
216
|
+
// Free routes state an effort only when the user asks for one: the host
|
|
217
|
+
// rejects an effort on a model without reasoning metadata, and free tiers
|
|
218
|
+
// are exactly where non-thinking models (Qwen/GLM flash) live.
|
|
219
219
|
return {
|
|
220
220
|
...normalized,
|
|
221
|
-
reasoningEffort: normalized.reasoningEffort
|
|
221
|
+
...normalized.reasoningEffort === undefined ? {} : { reasoningEffort: normalized.reasoningEffort },
|
|
222
222
|
maxLevel,
|
|
223
223
|
}
|
|
224
224
|
})
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@apinggithub/mydsh-auto-model-router",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
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",
|