@yeaft/webchat-agent 0.1.976 → 0.1.978
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/llm-model-discovery.js +2 -0
- package/package.json +1 -1
- package/yeaft/llm/router.js +81 -7
- package/yeaft/models.js +20 -0
package/llm-model-discovery.js
CHANGED
package/package.json
CHANGED
package/yeaft/llm/router.js
CHANGED
|
@@ -231,6 +231,84 @@ export class AdapterRouter extends LLMAdapter {
|
|
|
231
231
|
}
|
|
232
232
|
}
|
|
233
233
|
|
|
234
|
+
/**
|
|
235
|
+
* Resolve an explicit provider/model ref against a provider row even when
|
|
236
|
+
* the local model catalog is stale. The provider name still must exist; the
|
|
237
|
+
* fallback only skips the per-provider models[] membership check.
|
|
238
|
+
*
|
|
239
|
+
* @param {string} modelRef
|
|
240
|
+
* @returns {{provider: object, entry: {id: string, protocol?: string}} | null}
|
|
241
|
+
*/
|
|
242
|
+
#resolveProviderQualifiedFallback(modelRef) {
|
|
243
|
+
const parsed = parseModelRef(modelRef);
|
|
244
|
+
if (!parsed.providerName || !parsed.modelId) return null;
|
|
245
|
+
|
|
246
|
+
const candidates = this.#providers.filter(p => p && p.name === parsed.providerName);
|
|
247
|
+
if (candidates.length === 0) return null;
|
|
248
|
+
|
|
249
|
+
const inferred = inferProtocolFromModelId(parsed.modelId);
|
|
250
|
+
if (!inferred) return null;
|
|
251
|
+
const entry = { id: parsed.modelId, protocol: inferred };
|
|
252
|
+
|
|
253
|
+
// Provider-qualified refs are explicit enough to tolerate a stale
|
|
254
|
+
// providers[].models catalog. Prefer a provider row that already serves
|
|
255
|
+
// this protocol through at least one model entry. That covers the common
|
|
256
|
+
// Copilot shape: provider.protocol=openai-responses plus per-model
|
|
257
|
+
// protocol='anthropic' for Claude entries.
|
|
258
|
+
for (const provider of candidates) {
|
|
259
|
+
const models = Array.isArray(provider.models) ? provider.models : [];
|
|
260
|
+
for (const raw of models) {
|
|
261
|
+
const existing = normalizeModelEntry(raw);
|
|
262
|
+
if (!existing) continue;
|
|
263
|
+
try {
|
|
264
|
+
if (this.#effectiveProtocol(provider, existing) === inferred) {
|
|
265
|
+
return { provider, entry };
|
|
266
|
+
}
|
|
267
|
+
} catch {
|
|
268
|
+
// Ignore invalid existing entries while looking for a compatible row.
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// If no existing entry proves mixed-protocol support, fall back to rows
|
|
274
|
+
// whose provider-level protocol is absent or directly compatible.
|
|
275
|
+
for (const provider of candidates) {
|
|
276
|
+
if (!provider.protocol || provider.protocol === inferred) {
|
|
277
|
+
return { provider, entry };
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return null;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
#unknownModelError(modelRef) {
|
|
285
|
+
const parsed = parseModelRef(modelRef);
|
|
286
|
+
if (parsed.providerName) {
|
|
287
|
+
const providerModels = [];
|
|
288
|
+
let sawProvider = false;
|
|
289
|
+
for (const provider of this.#providers) {
|
|
290
|
+
if (!provider || provider.name !== parsed.providerName) continue;
|
|
291
|
+
sawProvider = true;
|
|
292
|
+
for (const raw of Array.isArray(provider.models) ? provider.models : []) {
|
|
293
|
+
const entry = normalizeModelEntry(raw);
|
|
294
|
+
if (entry) providerModels.push(entry.id);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
if (sawProvider) {
|
|
298
|
+
return new Error(
|
|
299
|
+
`Model "${modelRef}" is not listed under provider "${parsed.providerName}" and no compatible protocol row could be inferred. ` +
|
|
300
|
+
`Available models for this provider: ${providerModels.join(', ') || '(none)'}. ` +
|
|
301
|
+
`Add "${parsed.modelId}" to config.json providers[].models or refresh the model catalog.`
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return new Error(
|
|
306
|
+
`Model "${modelRef}" not found in any provider. ` +
|
|
307
|
+
`Available models: ${[...this.#modelToProvider.keys()].join(', ') || '(none)'}. ` +
|
|
308
|
+
`Check your config.json providers[].models arrays.`
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
|
|
234
312
|
/**
|
|
235
313
|
* Resolve the effective wire protocol for a (provider, model) pair.
|
|
236
314
|
*
|
|
@@ -283,13 +361,9 @@ export class AdapterRouter extends LLMAdapter {
|
|
|
283
361
|
* @returns {Promise<{adapter: LLMAdapter, modelId: string}>}
|
|
284
362
|
*/
|
|
285
363
|
async #resolveAdapter(modelRef) {
|
|
286
|
-
const hit = this.#modelToProvider.get(modelRef);
|
|
364
|
+
const hit = this.#modelToProvider.get(modelRef) || this.#resolveProviderQualifiedFallback(modelRef);
|
|
287
365
|
if (!hit) {
|
|
288
|
-
throw
|
|
289
|
-
`Model "${modelRef}" not found in any provider. ` +
|
|
290
|
-
`Available models: ${[...this.#modelToProvider.keys()].join(', ') || '(none)'}. ` +
|
|
291
|
-
`Check your config.json providers[].models arrays.`
|
|
292
|
-
);
|
|
366
|
+
throw this.#unknownModelError(modelRef);
|
|
293
367
|
}
|
|
294
368
|
const { provider, entry } = hit;
|
|
295
369
|
|
|
@@ -435,7 +509,7 @@ export class AdapterRouter extends LLMAdapter {
|
|
|
435
509
|
* @returns {object|null} — Provider config or null
|
|
436
510
|
*/
|
|
437
511
|
getProviderForModel(modelId) {
|
|
438
|
-
const hit = this.#modelToProvider.get(modelId);
|
|
512
|
+
const hit = this.#modelToProvider.get(modelId) || this.#resolveProviderQualifiedFallback(modelId);
|
|
439
513
|
return hit ? hit.provider : null;
|
|
440
514
|
}
|
|
441
515
|
|
package/yeaft/models.js
CHANGED
|
@@ -67,6 +67,26 @@ export const MODEL_REGISTRY = new Map([
|
|
|
67
67
|
defaultEffort: null,
|
|
68
68
|
maxBudgetTokens: 64000,
|
|
69
69
|
}],
|
|
70
|
+
['claude-opus-4-8', {
|
|
71
|
+
provider: 'anthropic',
|
|
72
|
+
adapter: 'anthropic',
|
|
73
|
+
baseUrl: 'https://api.anthropic.com',
|
|
74
|
+
displayName: 'Claude Opus 4.8',
|
|
75
|
+
supportsThinking: true,
|
|
76
|
+
thinkingProtocol: 'anthropic',
|
|
77
|
+
defaultEffort: null,
|
|
78
|
+
maxBudgetTokens: 64000,
|
|
79
|
+
}],
|
|
80
|
+
['claude-opus-4.8', {
|
|
81
|
+
provider: 'anthropic',
|
|
82
|
+
adapter: 'anthropic',
|
|
83
|
+
baseUrl: 'https://api.anthropic.com',
|
|
84
|
+
displayName: 'Claude Opus 4.8',
|
|
85
|
+
supportsThinking: true,
|
|
86
|
+
thinkingProtocol: 'anthropic',
|
|
87
|
+
defaultEffort: null,
|
|
88
|
+
maxBudgetTokens: 64000,
|
|
89
|
+
}],
|
|
70
90
|
['claude-haiku-3-20250414', {
|
|
71
91
|
provider: 'anthropic',
|
|
72
92
|
adapter: 'anthropic',
|