claudish 10.1.0 → 10.1.1
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/dist/index.js +613 -483
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -715,7 +715,7 @@ var init_onepassword_config = __esm(() => {
|
|
|
715
715
|
});
|
|
716
716
|
|
|
717
717
|
// src/version.ts
|
|
718
|
-
var VERSION = "10.1.
|
|
718
|
+
var VERSION = "10.1.1";
|
|
719
719
|
|
|
720
720
|
// src/logger.ts
|
|
721
721
|
import { appendFile, existsSync as existsSync2, mkdirSync, readdirSync, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
@@ -18340,7 +18340,6 @@ var init_catalog_route_bindings = __esm(() => {
|
|
|
18340
18340
|
"qwen-coding": { routeId: "qwen", routeProfileId: "modelstudio-coding-plan" },
|
|
18341
18341
|
"qwen-token-plan": { routeId: "qwen", routeProfileId: "qwencloud-token-plan" },
|
|
18342
18342
|
"qwen-payg": { routeId: "qwen", routeProfileId: "dashscope-direct" },
|
|
18343
|
-
qwen: { routeId: "qwen", routeProfileId: "dashscope-direct" },
|
|
18344
18343
|
"opencode-zen-go": { routeId: "opencode", routeProfileId: "go-subscription" },
|
|
18345
18344
|
"opencode-zen": { routeId: "opencode", routeProfileId: "zen" },
|
|
18346
18345
|
zen: { routeId: "opencode", routeProfileId: "zen" },
|
|
@@ -20290,10 +20289,7 @@ class BaseAPIFormat {
|
|
|
20290
20289
|
const effort = this.resolveEffortLevel(originalRequest);
|
|
20291
20290
|
if (!effort)
|
|
20292
20291
|
return request;
|
|
20293
|
-
if (effort
|
|
20294
|
-
if (reasoning?.mandatory) {
|
|
20295
|
-
return this.enableAnthropicEffort(request, effort, reasoning, "mandatory reasoning");
|
|
20296
|
-
}
|
|
20292
|
+
if (this.meansReasoningOff(effort, reasoning)) {
|
|
20297
20293
|
request.thinking = { type: "disabled" };
|
|
20298
20294
|
log(`[${this.getName()}] effort ${effort} -> thinking.type: disabled for ${this.modelId}`);
|
|
20299
20295
|
return request;
|
|
@@ -20348,9 +20344,23 @@ class BaseAPIFormat {
|
|
|
20348
20344
|
if (Object.keys(request.output_config).length === 0)
|
|
20349
20345
|
delete request.output_config;
|
|
20350
20346
|
}
|
|
20347
|
+
meansReasoningOff(effort, reasoning) {
|
|
20348
|
+
if (effort !== "none" && effort !== "minimal")
|
|
20349
|
+
return false;
|
|
20350
|
+
if (reasoning?.mandatory)
|
|
20351
|
+
return false;
|
|
20352
|
+
if (effort === "none")
|
|
20353
|
+
return true;
|
|
20354
|
+
const advertised = (reasoning?.efforts ?? []).filter(isEffortLevel);
|
|
20355
|
+
if (advertised.length === 0)
|
|
20356
|
+
return true;
|
|
20357
|
+
const lowest = advertised.reduce((a, b) => EFFORT_ORDER.indexOf(a) <= EFFORT_ORDER.indexOf(b) ? a : b);
|
|
20358
|
+
return lowest === "none";
|
|
20359
|
+
}
|
|
20351
20360
|
clampToAdvertisedEffort(requested, reasoning) {
|
|
20352
|
-
if (this.pinnedEffort)
|
|
20361
|
+
if (this.pinnedEffort && this.pinnedEffort !== "none" && this.pinnedEffort !== "minimal") {
|
|
20353
20362
|
return this.pinnedEffort;
|
|
20363
|
+
}
|
|
20354
20364
|
const advertised = (reasoning.efforts ?? []).filter(isEffortLevel);
|
|
20355
20365
|
if (advertised.length === 0) {
|
|
20356
20366
|
return isEffortLevel(reasoning.defaultEffort) ? reasoning.defaultEffort : undefined;
|
|
@@ -20395,6 +20405,9 @@ class BaseAPIFormat {
|
|
|
20395
20405
|
return 38912;
|
|
20396
20406
|
case "max":
|
|
20397
20407
|
return;
|
|
20408
|
+
case "none":
|
|
20409
|
+
case "minimal":
|
|
20410
|
+
return MIN_THINKING_BUDGET;
|
|
20398
20411
|
default:
|
|
20399
20412
|
return 8192;
|
|
20400
20413
|
}
|
|
@@ -23302,7 +23315,7 @@ var init_deepseek_model_dialect = __esm(() => {
|
|
|
23302
23315
|
applyNativeReasoning(request, originalRequest) {
|
|
23303
23316
|
const effort = this.resolveEffortLevel(originalRequest);
|
|
23304
23317
|
if (effort && this.acceptsReasoningControls()) {
|
|
23305
|
-
if (effort
|
|
23318
|
+
if (this.meansReasoningOff(effort, this.lookupReasoningCapability())) {
|
|
23306
23319
|
request.thinking = { type: "disabled" };
|
|
23307
23320
|
if (request.reasoning_effort !== undefined)
|
|
23308
23321
|
delete request.reasoning_effort;
|
|
@@ -23359,7 +23372,7 @@ var init_glm_model_dialect = __esm(() => {
|
|
|
23359
23372
|
const effort = this.resolveEffortLevel(originalRequest);
|
|
23360
23373
|
const reasoning = this.lookupReasoningCapability();
|
|
23361
23374
|
if (effort && this.acceptsThinkingToggle(reasoning)) {
|
|
23362
|
-
if (effort
|
|
23375
|
+
if (this.meansReasoningOff(effort, reasoning)) {
|
|
23363
23376
|
request.thinking = { type: "disabled" };
|
|
23364
23377
|
if (request.reasoning_effort !== undefined)
|
|
23365
23378
|
delete request.reasoning_effort;
|
|
@@ -23758,7 +23771,7 @@ var init_qwen_model_dialect = __esm(() => {
|
|
|
23758
23771
|
const effort = this.resolveEffortLevel(originalRequest);
|
|
23759
23772
|
if (!effort)
|
|
23760
23773
|
return request;
|
|
23761
|
-
if (effort
|
|
23774
|
+
if (this.meansReasoningOff(effort, this.lookupReasoningCapability())) {
|
|
23762
23775
|
request.enable_thinking = false;
|
|
23763
23776
|
log(`[QwenModelDialect] effort ${effort} -> enable_thinking: false for ${this.modelId}`);
|
|
23764
23777
|
} else {
|
|
@@ -25821,8 +25834,8 @@ async function fetchOllamaModels(options = {}) {
|
|
|
25821
25834
|
const data = await response.json();
|
|
25822
25835
|
const models = data.models || [];
|
|
25823
25836
|
const enriched = await Promise.all(models.map(async (m) => {
|
|
25824
|
-
let capabilities = [];
|
|
25825
|
-
if (enrichCapabilities) {
|
|
25837
|
+
let capabilities = Array.isArray(m.capabilities) ? m.capabilities : [];
|
|
25838
|
+
if (enrichCapabilities && capabilities.length === 0) {
|
|
25826
25839
|
try {
|
|
25827
25840
|
const showResponse = await fetch(`${host}/api/show`, {
|
|
25828
25841
|
method: "POST",
|
|
@@ -25836,9 +25849,8 @@ async function fetchOllamaModels(options = {}) {
|
|
|
25836
25849
|
}
|
|
25837
25850
|
} catch {}
|
|
25838
25851
|
}
|
|
25839
|
-
const nameLower = String(m.name).toLowerCase();
|
|
25840
25852
|
const supportsTools = capabilities.includes("tools");
|
|
25841
|
-
const isEmbeddingModel = capabilities.includes("embedding")
|
|
25853
|
+
const isEmbeddingModel = capabilities.includes("embedding");
|
|
25842
25854
|
const sizeInfo = m.details?.parameter_size || "unknown size";
|
|
25843
25855
|
const toolsIndicator = supportsTools ? "\u2713 tools" : "\u2717 no tools";
|
|
25844
25856
|
return {
|
|
@@ -30287,6 +30299,461 @@ var init_devin = __esm(() => {
|
|
|
30287
30299
|
SPEED_SUFFIXES = ["fast", "priority"];
|
|
30288
30300
|
});
|
|
30289
30301
|
|
|
30302
|
+
// src/providers/transport/probe-discovery.ts
|
|
30303
|
+
function ollamaReported(row) {
|
|
30304
|
+
const caps = Array.isArray(row.capabilities) ? row.capabilities : [];
|
|
30305
|
+
if (caps.includes("completion"))
|
|
30306
|
+
return "chat";
|
|
30307
|
+
if (caps.includes("embedding"))
|
|
30308
|
+
return "not-chat";
|
|
30309
|
+
return;
|
|
30310
|
+
}
|
|
30311
|
+
function lmStudioReported(row) {
|
|
30312
|
+
if (row.type === "llm" || row.type === "vlm")
|
|
30313
|
+
return "chat";
|
|
30314
|
+
if (row.type === "embeddings" || row.type === "embedding")
|
|
30315
|
+
return "not-chat";
|
|
30316
|
+
return;
|
|
30317
|
+
}
|
|
30318
|
+
function isSmallName(name) {
|
|
30319
|
+
return SMALL_MODEL_PATTERNS.some((re) => re.test(name));
|
|
30320
|
+
}
|
|
30321
|
+
function _clearChatCapabilityIndex() {
|
|
30322
|
+
_catalogChatIndex.clear();
|
|
30323
|
+
}
|
|
30324
|
+
function catalogKey(name) {
|
|
30325
|
+
const lower = name.toLowerCase();
|
|
30326
|
+
return lower.includes("/") ? lower.slice(lower.lastIndexOf("/") + 1) : lower;
|
|
30327
|
+
}
|
|
30328
|
+
function indexOutputModality(index, keys, modalities) {
|
|
30329
|
+
if (!Array.isArray(modalities) || modalities.length === 0)
|
|
30330
|
+
return;
|
|
30331
|
+
const target = modalities.includes("text") ? index.textOutput : index.nonTextOutput;
|
|
30332
|
+
for (const k of keys)
|
|
30333
|
+
target.add(k);
|
|
30334
|
+
}
|
|
30335
|
+
function indexInputModality(index, keys, modalities) {
|
|
30336
|
+
if (!Array.isArray(modalities) || modalities.length === 0)
|
|
30337
|
+
return;
|
|
30338
|
+
if (modalities.includes("text"))
|
|
30339
|
+
return;
|
|
30340
|
+
for (const k of keys)
|
|
30341
|
+
index.nonTextInput.add(k);
|
|
30342
|
+
}
|
|
30343
|
+
function catalogCapabilityIndex(cachePath) {
|
|
30344
|
+
const key = cachePath ?? "";
|
|
30345
|
+
const hit = _catalogChatIndex.get(key);
|
|
30346
|
+
if (hit && hit.expiresAt > Date.now())
|
|
30347
|
+
return hit.index;
|
|
30348
|
+
const index = {
|
|
30349
|
+
chat: new Set,
|
|
30350
|
+
textOutput: new Set,
|
|
30351
|
+
nonTextOutput: new Set,
|
|
30352
|
+
nonTextInput: new Set,
|
|
30353
|
+
videoOutput: new Set,
|
|
30354
|
+
videoOutputKnown: new Set,
|
|
30355
|
+
known: new Set
|
|
30356
|
+
};
|
|
30357
|
+
for (const entry of readAllModelsCache(cachePath)?.entries ?? []) {
|
|
30358
|
+
const keys = [catalogKey(entry.modelId), ...(entry.aliases ?? []).map(catalogKey)];
|
|
30359
|
+
for (const k of keys)
|
|
30360
|
+
index.known.add(k);
|
|
30361
|
+
indexOutputModality(index, keys, entry.outputModalities);
|
|
30362
|
+
indexInputModality(index, keys, entry.inputModalities);
|
|
30363
|
+
if (entry.videoOutput !== undefined) {
|
|
30364
|
+
for (const k of keys)
|
|
30365
|
+
index.videoOutputKnown.add(k);
|
|
30366
|
+
}
|
|
30367
|
+
if (entry.videoOutput === true) {
|
|
30368
|
+
for (const k of keys)
|
|
30369
|
+
index.videoOutput.add(k);
|
|
30370
|
+
continue;
|
|
30371
|
+
}
|
|
30372
|
+
const chatShaped = entry.supportsTools === true || entry.supportsThinking === true;
|
|
30373
|
+
if (chatShaped)
|
|
30374
|
+
for (const k of keys)
|
|
30375
|
+
index.chat.add(k);
|
|
30376
|
+
}
|
|
30377
|
+
_catalogChatIndex.set(key, { index, expiresAt: Date.now() + CATALOG_CHAT_INDEX_TTL_MS });
|
|
30378
|
+
return index;
|
|
30379
|
+
}
|
|
30380
|
+
function classifyChatCapability(name, cachePath, reported) {
|
|
30381
|
+
if (name.includes("*"))
|
|
30382
|
+
return "not-chat";
|
|
30383
|
+
if (reported)
|
|
30384
|
+
return reported;
|
|
30385
|
+
const index = catalogCapabilityIndex(cachePath);
|
|
30386
|
+
const key = catalogKey(name);
|
|
30387
|
+
if (index.nonTextOutput.has(key))
|
|
30388
|
+
return "not-chat";
|
|
30389
|
+
if (index.nonTextInput.has(key))
|
|
30390
|
+
return "not-chat";
|
|
30391
|
+
if (index.textOutput.has(key))
|
|
30392
|
+
return "chat";
|
|
30393
|
+
if (index.videoOutput.has(key))
|
|
30394
|
+
return "not-chat";
|
|
30395
|
+
if (index.chat.has(key))
|
|
30396
|
+
return "chat";
|
|
30397
|
+
return "unknown";
|
|
30398
|
+
}
|
|
30399
|
+
function isChatCapable(name) {
|
|
30400
|
+
return classifyChatCapability(name) === "chat";
|
|
30401
|
+
}
|
|
30402
|
+
function isReportedChatCapable(name, reported) {
|
|
30403
|
+
return classifyChatCapability(name, undefined, reported) === "chat";
|
|
30404
|
+
}
|
|
30405
|
+
function unavailableForMissingCapability(names, reportedFor, cachePath) {
|
|
30406
|
+
const catalogSilent = [];
|
|
30407
|
+
const providerSilent = [];
|
|
30408
|
+
for (const name of names) {
|
|
30409
|
+
const reported = reportedFor?.(name);
|
|
30410
|
+
if (classifyChatCapability(name, cachePath, reported) !== "unknown")
|
|
30411
|
+
continue;
|
|
30412
|
+
const index = catalogCapabilityIndex(cachePath);
|
|
30413
|
+
if (index.known.has(catalogKey(name)))
|
|
30414
|
+
catalogSilent.push(name);
|
|
30415
|
+
else
|
|
30416
|
+
providerSilent.push(name);
|
|
30417
|
+
}
|
|
30418
|
+
return { catalogSilent, providerSilent };
|
|
30419
|
+
}
|
|
30420
|
+
function describeNoCandidates(ids, reportedFor, cachePath) {
|
|
30421
|
+
const { catalogSilent, providerSilent } = unavailableForMissingCapability(ids, reportedFor, cachePath);
|
|
30422
|
+
const undescribed = catalogSilent.length + providerSilent.length;
|
|
30423
|
+
if (undescribed === 0) {
|
|
30424
|
+
return `all ${ids.length} listed models are described as non-chat (image, embedding, audio)`;
|
|
30425
|
+
}
|
|
30426
|
+
const sample = [...catalogSilent, ...providerSilent].slice(0, 3).join(", ");
|
|
30427
|
+
const more = undescribed > 3 ? `, +${undescribed - 3} more` : "";
|
|
30428
|
+
const whose = providerSilent.length === 0 ? "the models catalog publishes them without modalities" : catalogSilent.length === 0 ? "this endpoint publishes no capability field" : "neither the models catalog nor this endpoint describes them";
|
|
30429
|
+
return `no capability data for ${undescribed} of ${ids.length} listed models \u2014 ${whose} (${sample}${more})`;
|
|
30430
|
+
}
|
|
30431
|
+
function isStandardName(name) {
|
|
30432
|
+
if (!name.includes("/"))
|
|
30433
|
+
return true;
|
|
30434
|
+
return STANDARD_VENDOR_PREFIXES.some((p) => name.toLowerCase().startsWith(p));
|
|
30435
|
+
}
|
|
30436
|
+
function rankProbeCandidates(names) {
|
|
30437
|
+
return names.filter((name) => !name.includes("*")).sort((a, b) => {
|
|
30438
|
+
const aStd = isStandardName(a);
|
|
30439
|
+
const bStd = isStandardName(b);
|
|
30440
|
+
if (aStd !== bStd)
|
|
30441
|
+
return aStd ? -1 : 1;
|
|
30442
|
+
const aSmall = isSmallName(a);
|
|
30443
|
+
const bSmall = isSmallName(b);
|
|
30444
|
+
if (aSmall !== bSmall)
|
|
30445
|
+
return aSmall ? -1 : 1;
|
|
30446
|
+
return a.localeCompare(b);
|
|
30447
|
+
});
|
|
30448
|
+
}
|
|
30449
|
+
function cacheGet(key, exclude = new Set) {
|
|
30450
|
+
const hit = _cache.get(key);
|
|
30451
|
+
if (!hit)
|
|
30452
|
+
return;
|
|
30453
|
+
if (Date.now() > hit.expiresAt) {
|
|
30454
|
+
_cache.delete(key);
|
|
30455
|
+
return;
|
|
30456
|
+
}
|
|
30457
|
+
if (hit.ranked.length === 0) {
|
|
30458
|
+
return { model: null, reason: hit.reason };
|
|
30459
|
+
}
|
|
30460
|
+
const pick = hit.ranked.find((m) => !exclude.has(m));
|
|
30461
|
+
if (!pick) {
|
|
30462
|
+
return {
|
|
30463
|
+
model: null,
|
|
30464
|
+
reason: `all ${hit.ranked.length} candidate model(s) already tried`
|
|
30465
|
+
};
|
|
30466
|
+
}
|
|
30467
|
+
return { model: pick };
|
|
30468
|
+
}
|
|
30469
|
+
function cacheSetFailure(key, reason) {
|
|
30470
|
+
_cache.set(key, { ranked: [], reason, expiresAt: Date.now() + CACHE_TTL_MS });
|
|
30471
|
+
}
|
|
30472
|
+
function cacheSetRanked(key, ranked) {
|
|
30473
|
+
_cache.set(key, { ranked, expiresAt: Date.now() + CACHE_TTL_MS });
|
|
30474
|
+
}
|
|
30475
|
+
async function discoverViaOpenAIModels(endpoint, headers, cacheKey) {
|
|
30476
|
+
const cached = cacheGet(cacheKey.key, cacheKey.exclude);
|
|
30477
|
+
if (cached !== undefined)
|
|
30478
|
+
return cached;
|
|
30479
|
+
let response;
|
|
30480
|
+
try {
|
|
30481
|
+
response = await fetch(endpoint, {
|
|
30482
|
+
method: "GET",
|
|
30483
|
+
headers,
|
|
30484
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
30485
|
+
});
|
|
30486
|
+
} catch (e) {
|
|
30487
|
+
const reason = classifyFetchError(e, endpoint);
|
|
30488
|
+
log(`[probe-discovery${cacheKey.displayName ? `:${cacheKey.displayName}` : ""}] fetch failed: ${reason}`);
|
|
30489
|
+
cacheSetFailure(cacheKey.key, reason);
|
|
30490
|
+
return { model: null, reason };
|
|
30491
|
+
}
|
|
30492
|
+
if (!response.ok) {
|
|
30493
|
+
const who = cacheKey.displayName ?? "this provider";
|
|
30494
|
+
const authFailure = response.status === 401 || response.status === 403 ? cacheKey.hasApiKey === false ? `the server requires an API key and none is configured for ${who}` : `the server rejected the configured API key for ${who}` : "";
|
|
30495
|
+
const reason = authFailure ? `HTTP ${response.status} from ${endpoint} \u2014 ${authFailure}` : `HTTP ${response.status} from ${endpoint}`;
|
|
30496
|
+
log(`[probe-discovery${cacheKey.displayName ? `:${cacheKey.displayName}` : ""}] ${reason}`);
|
|
30497
|
+
cacheSetFailure(cacheKey.key, reason);
|
|
30498
|
+
return { model: null, reason };
|
|
30499
|
+
}
|
|
30500
|
+
let body;
|
|
30501
|
+
try {
|
|
30502
|
+
body = await response.json();
|
|
30503
|
+
} catch {
|
|
30504
|
+
const reason = "invalid /v1/models response (not JSON)";
|
|
30505
|
+
cacheSetFailure(cacheKey.key, reason);
|
|
30506
|
+
return { model: null, reason };
|
|
30507
|
+
}
|
|
30508
|
+
const ids = extractModelIds(body);
|
|
30509
|
+
if (ids.length === 0) {
|
|
30510
|
+
const url = tryParseUrl(endpoint);
|
|
30511
|
+
const host = url?.host ?? endpoint;
|
|
30512
|
+
const reason = `${host} reachable but no models loaded \u2014 load a model in the server UI`;
|
|
30513
|
+
cacheSetFailure(cacheKey.key, reason);
|
|
30514
|
+
return { model: null, reason };
|
|
30515
|
+
}
|
|
30516
|
+
const ranked = rankProbeCandidates(ids.filter(isChatCapable));
|
|
30517
|
+
if (ranked.length === 0) {
|
|
30518
|
+
const reason = describeNoCandidates(ids);
|
|
30519
|
+
cacheSetFailure(cacheKey.key, reason);
|
|
30520
|
+
return { model: null, reason };
|
|
30521
|
+
}
|
|
30522
|
+
cacheSetRanked(cacheKey.key, ranked);
|
|
30523
|
+
const pick = ranked.find((m) => !cacheKey.exclude?.has(m));
|
|
30524
|
+
if (!pick) {
|
|
30525
|
+
return {
|
|
30526
|
+
model: null,
|
|
30527
|
+
reason: `all ${ranked.length} candidate model(s) already tried`
|
|
30528
|
+
};
|
|
30529
|
+
}
|
|
30530
|
+
return { model: pick };
|
|
30531
|
+
}
|
|
30532
|
+
function classifyFetchError(e, endpoint) {
|
|
30533
|
+
const name = e?.name ?? "";
|
|
30534
|
+
const code = e?.cause?.code ?? "";
|
|
30535
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
30536
|
+
const url = tryParseUrl(endpoint);
|
|
30537
|
+
const host = url?.host ?? endpoint;
|
|
30538
|
+
const isLocal = !!url && /^(localhost|127\.0\.0\.1|0\.0\.0\.0|::1)$/i.test(url.hostname);
|
|
30539
|
+
if (name === "TimeoutError" || name === "AbortError" || /timeout/i.test(msg)) {
|
|
30540
|
+
return `${host} unresponsive (>${FETCH_TIMEOUT_MS / 1000}s) \u2014 check if the server is overloaded`;
|
|
30541
|
+
}
|
|
30542
|
+
if (code === "ENOTFOUND" || code === "EAI_AGAIN") {
|
|
30543
|
+
return `cannot resolve host ${url?.hostname ?? endpoint} \u2014 check the URL`;
|
|
30544
|
+
}
|
|
30545
|
+
const isConnRefused = code === "ECONNREFUSED" || code === "ECONNRESET" || /unable to connect|connection refused|fetch failed/i.test(msg);
|
|
30546
|
+
if (isConnRefused) {
|
|
30547
|
+
if (isLocal) {
|
|
30548
|
+
return `${host} not reachable \u2014 is the server running? Press u to change URL.`;
|
|
30549
|
+
}
|
|
30550
|
+
return `${host} not reachable \u2014 check the URL or network. Press u to change.`;
|
|
30551
|
+
}
|
|
30552
|
+
return `${host}: ${msg}`;
|
|
30553
|
+
}
|
|
30554
|
+
function tryParseUrl(s) {
|
|
30555
|
+
try {
|
|
30556
|
+
return new URL(s);
|
|
30557
|
+
} catch {
|
|
30558
|
+
return null;
|
|
30559
|
+
}
|
|
30560
|
+
}
|
|
30561
|
+
function extractModelIds(body) {
|
|
30562
|
+
if (!body || typeof body !== "object")
|
|
30563
|
+
return [];
|
|
30564
|
+
const data = body;
|
|
30565
|
+
if (Array.isArray(data.data)) {
|
|
30566
|
+
return data.data.map((m) => m && typeof m === "object" ? m.id : null).filter((id) => typeof id === "string" && id.length > 0);
|
|
30567
|
+
}
|
|
30568
|
+
if (Array.isArray(data.models)) {
|
|
30569
|
+
return data.models.map((m) => m && typeof m === "object" ? m.id ?? m.model_name : null).filter((id) => typeof id === "string" && id.length > 0);
|
|
30570
|
+
}
|
|
30571
|
+
return [];
|
|
30572
|
+
}
|
|
30573
|
+
function orderByCost(models) {
|
|
30574
|
+
const sized = models.filter((m) => typeof m.size === "number");
|
|
30575
|
+
const unsized = models.filter((m) => typeof m.size !== "number");
|
|
30576
|
+
const bySize = [...sized].sort((a, b) => (a.size ?? Number.POSITIVE_INFINITY) - (b.size ?? Number.POSITIVE_INFINITY)).map((m) => m.name);
|
|
30577
|
+
return [...bySize, ...rankProbeCandidates(unsized.map((m) => m.name))];
|
|
30578
|
+
}
|
|
30579
|
+
async function discoverViaOllama(baseUrl, cacheKey) {
|
|
30580
|
+
const cached = cacheGet(cacheKey.key, cacheKey.exclude);
|
|
30581
|
+
if (cached !== undefined)
|
|
30582
|
+
return cached;
|
|
30583
|
+
const [psResult, tagsResult] = await Promise.allSettled([
|
|
30584
|
+
fetchOllamaModels2(`${baseUrl}/api/ps`),
|
|
30585
|
+
fetchOllamaModels2(`${baseUrl}/api/tags`)
|
|
30586
|
+
]);
|
|
30587
|
+
const loadedRaw = psResult.status === "fulfilled" ? psResult.value : [];
|
|
30588
|
+
const tagsRaw = tagsResult.status === "fulfilled" ? tagsResult.value : [];
|
|
30589
|
+
const connectionError = psResult.status === "rejected" ? classifyFetchError(psResult.reason, `${baseUrl}/api/ps`) : tagsResult.status === "rejected" ? classifyFetchError(tagsResult.reason, `${baseUrl}/api/tags`) : undefined;
|
|
30590
|
+
const loaded = loadedRaw.filter((m) => isReportedChatCapable(m.name, ollamaReported(m)));
|
|
30591
|
+
const loadedNames = new Set(loaded.map((m) => m.name));
|
|
30592
|
+
const rest = tagsRaw.filter((m) => isReportedChatCapable(m.name, ollamaReported(m)) && !loadedNames.has(m.name));
|
|
30593
|
+
if (loaded.length === 0 && rest.length === 0) {
|
|
30594
|
+
const listed = [...loadedRaw, ...tagsRaw];
|
|
30595
|
+
const reportedFor = (name) => {
|
|
30596
|
+
const row = listed.find((m) => m.name === name);
|
|
30597
|
+
return row ? ollamaReported(row) : undefined;
|
|
30598
|
+
};
|
|
30599
|
+
const reason = connectionError ?? (listed.length === 0 ? `no models on ${baseUrl} (pull one: ollama pull llama3.2)` : `${describeNoCandidates(listed.map((m) => m.name), reportedFor)} on ${baseUrl}`);
|
|
30600
|
+
cacheSetFailure(cacheKey.key, reason);
|
|
30601
|
+
return { model: null, reason };
|
|
30602
|
+
}
|
|
30603
|
+
const ranked = [...orderByCost(loaded), ...orderByCost(rest)];
|
|
30604
|
+
if (ranked.length === 0) {
|
|
30605
|
+
const reason = "no chat-capable model on Ollama endpoint";
|
|
30606
|
+
cacheSetFailure(cacheKey.key, reason);
|
|
30607
|
+
return { model: null, reason };
|
|
30608
|
+
}
|
|
30609
|
+
cacheSetRanked(cacheKey.key, ranked);
|
|
30610
|
+
const pick = ranked.find((m) => !cacheKey.exclude?.has(m));
|
|
30611
|
+
if (!pick) {
|
|
30612
|
+
return {
|
|
30613
|
+
model: null,
|
|
30614
|
+
reason: `all ${ranked.length} candidate model(s) already tried`
|
|
30615
|
+
};
|
|
30616
|
+
}
|
|
30617
|
+
return { model: pick };
|
|
30618
|
+
}
|
|
30619
|
+
async function discoverViaLMStudio(baseUrl, headers, cacheKey) {
|
|
30620
|
+
const cached = cacheGet(cacheKey.key, cacheKey.exclude);
|
|
30621
|
+
if (cached !== undefined)
|
|
30622
|
+
return cached;
|
|
30623
|
+
let response;
|
|
30624
|
+
try {
|
|
30625
|
+
response = await fetch(`${baseUrl}/api/v0/models`, {
|
|
30626
|
+
method: "GET",
|
|
30627
|
+
headers,
|
|
30628
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
30629
|
+
});
|
|
30630
|
+
} catch (e) {
|
|
30631
|
+
return discoverViaOpenAIModels(`${baseUrl}/v1/models`, headers, cacheKey);
|
|
30632
|
+
}
|
|
30633
|
+
if (!response.ok) {
|
|
30634
|
+
return discoverViaOpenAIModels(`${baseUrl}/v1/models`, headers, cacheKey);
|
|
30635
|
+
}
|
|
30636
|
+
let body;
|
|
30637
|
+
try {
|
|
30638
|
+
body = await response.json();
|
|
30639
|
+
} catch {
|
|
30640
|
+
const reason = "invalid /api/v0/models response (not JSON)";
|
|
30641
|
+
cacheSetFailure(cacheKey.key, reason);
|
|
30642
|
+
return { model: null, reason };
|
|
30643
|
+
}
|
|
30644
|
+
const models = extractLMStudioModels(body);
|
|
30645
|
+
if (models.length === 0) {
|
|
30646
|
+
const url = tryParseUrl(baseUrl);
|
|
30647
|
+
const host = url?.host ?? baseUrl;
|
|
30648
|
+
const reason = `${host} reachable but no models present \u2014 download one in the LM Studio UI`;
|
|
30649
|
+
cacheSetFailure(cacheKey.key, reason);
|
|
30650
|
+
return { model: null, reason };
|
|
30651
|
+
}
|
|
30652
|
+
const chatModels = models.filter((m) => isReportedChatCapable(m.id, lmStudioReported(m)));
|
|
30653
|
+
const loaded = chatModels.filter((m) => m.state === "loaded");
|
|
30654
|
+
const notLoaded = chatModels.filter((m) => m.state !== "loaded");
|
|
30655
|
+
const ranked = [
|
|
30656
|
+
...rankProbeCandidates(loaded.map((m) => m.id)),
|
|
30657
|
+
...rankProbeCandidates(notLoaded.map((m) => m.id))
|
|
30658
|
+
];
|
|
30659
|
+
if (ranked.length === 0) {
|
|
30660
|
+
const url = tryParseUrl(baseUrl);
|
|
30661
|
+
const host = url?.host ?? baseUrl;
|
|
30662
|
+
const reason = `${host} has ${models.length} model(s) but none are chat-capable`;
|
|
30663
|
+
cacheSetFailure(cacheKey.key, reason);
|
|
30664
|
+
return { model: null, reason };
|
|
30665
|
+
}
|
|
30666
|
+
cacheSetRanked(cacheKey.key, ranked);
|
|
30667
|
+
const pick = ranked.find((m) => !cacheKey.exclude?.has(m));
|
|
30668
|
+
if (!pick) {
|
|
30669
|
+
return {
|
|
30670
|
+
model: null,
|
|
30671
|
+
reason: `all ${ranked.length} candidate model(s) already tried`
|
|
30672
|
+
};
|
|
30673
|
+
}
|
|
30674
|
+
return { model: pick };
|
|
30675
|
+
}
|
|
30676
|
+
function extractLMStudioModels(body) {
|
|
30677
|
+
if (!body || typeof body !== "object")
|
|
30678
|
+
return [];
|
|
30679
|
+
const data = body.data;
|
|
30680
|
+
if (!Array.isArray(data))
|
|
30681
|
+
return [];
|
|
30682
|
+
const out = [];
|
|
30683
|
+
for (const m of data) {
|
|
30684
|
+
if (!m || typeof m !== "object")
|
|
30685
|
+
continue;
|
|
30686
|
+
const r = m;
|
|
30687
|
+
if (typeof r.id !== "string" || !r.id)
|
|
30688
|
+
continue;
|
|
30689
|
+
out.push({
|
|
30690
|
+
id: r.id,
|
|
30691
|
+
state: typeof r.state === "string" ? r.state : undefined,
|
|
30692
|
+
type: typeof r.type === "string" ? r.type : undefined
|
|
30693
|
+
});
|
|
30694
|
+
}
|
|
30695
|
+
return out;
|
|
30696
|
+
}
|
|
30697
|
+
async function fetchOllamaModels2(url) {
|
|
30698
|
+
const response = await fetch(url, {
|
|
30699
|
+
method: "GET",
|
|
30700
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
30701
|
+
});
|
|
30702
|
+
if (!response.ok)
|
|
30703
|
+
return [];
|
|
30704
|
+
const body = await response.json().catch(() => null);
|
|
30705
|
+
if (!body?.models)
|
|
30706
|
+
return [];
|
|
30707
|
+
return body.models.map((m) => ({
|
|
30708
|
+
name: typeof m.name === "string" ? m.name : "",
|
|
30709
|
+
size: typeof m.size === "number" ? m.size : undefined,
|
|
30710
|
+
capabilities: Array.isArray(m.capabilities) ? m.capabilities.filter((c) => typeof c === "string") : undefined
|
|
30711
|
+
})).filter((m) => m.name.length > 0);
|
|
30712
|
+
}
|
|
30713
|
+
function invalidateProbeDiscovery(providerSlug) {
|
|
30714
|
+
for (const key of _cache.keys()) {
|
|
30715
|
+
if (key.startsWith(`${providerSlug}:`)) {
|
|
30716
|
+
_cache.delete(key);
|
|
30717
|
+
}
|
|
30718
|
+
}
|
|
30719
|
+
}
|
|
30720
|
+
var _cache, CACHE_TTL_MS, FETCH_TIMEOUT_MS = 5000, SMALL_MODEL_PATTERNS, CATALOG_CHAT_INDEX_TTL_MS = 60000, _catalogChatIndex, STANDARD_VENDOR_PREFIXES;
|
|
30721
|
+
var init_probe_discovery = __esm(() => {
|
|
30722
|
+
init_logger();
|
|
30723
|
+
init_all_models_cache();
|
|
30724
|
+
_cache = new Map;
|
|
30725
|
+
CACHE_TTL_MS = 5 * 60 * 1000;
|
|
30726
|
+
SMALL_MODEL_PATTERNS = [
|
|
30727
|
+
/\bmini\b/i,
|
|
30728
|
+
/\bnano\b/i,
|
|
30729
|
+
/\bflash\b/i,
|
|
30730
|
+
/\blite\b/i,
|
|
30731
|
+
/\bhaiku\b/i,
|
|
30732
|
+
/\bsmall\b/i,
|
|
30733
|
+
/\btiny\b/i,
|
|
30734
|
+
/\b[12345]b\b/i,
|
|
30735
|
+
/\b[78]b\b/i
|
|
30736
|
+
];
|
|
30737
|
+
_catalogChatIndex = new Map;
|
|
30738
|
+
STANDARD_VENDOR_PREFIXES = [
|
|
30739
|
+
"openai/",
|
|
30740
|
+
"anthropic/",
|
|
30741
|
+
"google/",
|
|
30742
|
+
"gemini/",
|
|
30743
|
+
"meta/",
|
|
30744
|
+
"meta-llama/",
|
|
30745
|
+
"mistralai/",
|
|
30746
|
+
"mistral/",
|
|
30747
|
+
"x-ai/",
|
|
30748
|
+
"deepseek/",
|
|
30749
|
+
"qwen/",
|
|
30750
|
+
"moonshot/",
|
|
30751
|
+
"moonshotai/",
|
|
30752
|
+
"zhipuai/",
|
|
30753
|
+
"z-ai/"
|
|
30754
|
+
];
|
|
30755
|
+
});
|
|
30756
|
+
|
|
30290
30757
|
// src/providers/model-discovery-builtins.ts
|
|
30291
30758
|
function oneLine(text, max = 200) {
|
|
30292
30759
|
const flat = text.replace(/\s+/g, " ").trim();
|
|
@@ -30317,7 +30784,7 @@ async function fetchDevinModelsCatalog() {
|
|
|
30317
30784
|
endpoint,
|
|
30318
30785
|
models: served.map((model) => {
|
|
30319
30786
|
const { wireId, ...rest } = devinModelsCatalogEntry(model);
|
|
30320
|
-
return { id: wireId, ...rest };
|
|
30787
|
+
return { id: wireId, ...rest, reported: "chat" };
|
|
30321
30788
|
})
|
|
30322
30789
|
};
|
|
30323
30790
|
} catch (err) {
|
|
@@ -30356,7 +30823,12 @@ async function fetchAntigravityModelsCatalog() {
|
|
|
30356
30823
|
endpoint,
|
|
30357
30824
|
models: selectable.map((id) => {
|
|
30358
30825
|
const m = meta[id];
|
|
30359
|
-
return m?.contextWindow ? {
|
|
30826
|
+
return m?.contextWindow ? {
|
|
30827
|
+
id,
|
|
30828
|
+
contextWindow: m.contextWindow,
|
|
30829
|
+
ignoreCatalogReleaseDate: true,
|
|
30830
|
+
reported: "chat"
|
|
30831
|
+
} : { id, ignoreCatalogReleaseDate: true, reported: "chat" };
|
|
30360
30832
|
})
|
|
30361
30833
|
};
|
|
30362
30834
|
} catch (err) {
|
|
@@ -30368,19 +30840,18 @@ async function fetchAntigravityModelsCatalog() {
|
|
|
30368
30840
|
}
|
|
30369
30841
|
async function fetchOllamaModelsCatalog() {
|
|
30370
30842
|
await Promise.resolve();
|
|
30843
|
+
await Promise.resolve().then(() => init_probe_discovery());
|
|
30371
30844
|
const endpoint = `${ollamaBaseUrl()}/api/tags`;
|
|
30372
30845
|
try {
|
|
30373
|
-
const installed = await fetchOllamaModels({
|
|
30374
|
-
enrichCapabilities: false,
|
|
30375
|
-
throwOnError: true
|
|
30376
|
-
});
|
|
30846
|
+
const installed = await fetchOllamaModels({ throwOnError: true });
|
|
30377
30847
|
return {
|
|
30378
30848
|
kind: "models",
|
|
30379
30849
|
endpoint,
|
|
30380
30850
|
models: installed.map((model) => ({
|
|
30381
30851
|
id: model.name,
|
|
30382
30852
|
displayName: model.name,
|
|
30383
|
-
supportsTools: model.supportsTools
|
|
30853
|
+
supportsTools: model.supportsTools,
|
|
30854
|
+
reported: ollamaReported(model)
|
|
30384
30855
|
}))
|
|
30385
30856
|
};
|
|
30386
30857
|
} catch (err) {
|
|
@@ -30558,11 +31029,11 @@ async function discoverViaFetcher(providerName, format) {
|
|
|
30558
31029
|
}
|
|
30559
31030
|
_failures.delete(providerName);
|
|
30560
31031
|
log(`[model-discovery:${providerName}] discovered ${models.length} models`);
|
|
30561
|
-
|
|
31032
|
+
_cache2.set(providerName, { models, expiresAt: Date.now() + CACHE_TTL_MS2 });
|
|
30562
31033
|
return { kind: "served", models };
|
|
30563
31034
|
}
|
|
30564
31035
|
async function discoverProviderModelsCatalog(providerName) {
|
|
30565
|
-
const cached =
|
|
31036
|
+
const cached = _cache2.get(providerName);
|
|
30566
31037
|
if (cached && cached.expiresAt > Date.now())
|
|
30567
31038
|
return { kind: "served", models: cached.models };
|
|
30568
31039
|
const def = getProviderByName(providerName);
|
|
@@ -30613,7 +31084,7 @@ async function discoverProviderModelsCatalog(providerName) {
|
|
|
30613
31084
|
response = await fetch(endpoint, {
|
|
30614
31085
|
method: "GET",
|
|
30615
31086
|
headers,
|
|
30616
|
-
signal: AbortSignal.timeout(
|
|
31087
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS2)
|
|
30617
31088
|
});
|
|
30618
31089
|
} catch (e) {
|
|
30619
31090
|
return recordFailure({
|
|
@@ -30656,7 +31127,7 @@ async function discoverProviderModelsCatalog(providerName) {
|
|
|
30656
31127
|
const models = parsed.models;
|
|
30657
31128
|
_failures.delete(providerName);
|
|
30658
31129
|
log(`[model-discovery:${providerName}] discovered ${models.length} models: ` + models.map((m) => `${m.id}(${m.contextWindow ?? "?"})`).join(", "));
|
|
30659
|
-
|
|
31130
|
+
_cache2.set(providerName, { models, expiresAt: Date.now() + CACHE_TTL_MS2 });
|
|
30660
31131
|
return { kind: "served", models };
|
|
30661
31132
|
}
|
|
30662
31133
|
async function discoverProviderModels(providerName) {
|
|
@@ -30674,438 +31145,18 @@ function rankDiscoveredModels(models) {
|
|
|
30674
31145
|
return diff !== 0 ? diff : compareByReleaseDateDesc(a, b);
|
|
30675
31146
|
});
|
|
30676
31147
|
}
|
|
30677
|
-
var _fetchers,
|
|
31148
|
+
var _fetchers, CACHE_TTL_MS2, FETCH_TIMEOUT_MS2 = 5000, _cache2, _failures, MIN_CREATED_SECONDS = 946684800, MAX_CREATED_SECONDS = 4102444800, CONTINUATION_FIELDS;
|
|
30678
31149
|
var init_model_discovery = __esm(() => {
|
|
30679
31150
|
init_authority();
|
|
30680
31151
|
init_logger();
|
|
30681
31152
|
init_provider_definitions();
|
|
30682
31153
|
_fetchers = new Map;
|
|
30683
|
-
|
|
30684
|
-
|
|
31154
|
+
CACHE_TTL_MS2 = 5 * 60 * 1000;
|
|
31155
|
+
_cache2 = new Map;
|
|
30685
31156
|
_failures = new Map;
|
|
30686
31157
|
CONTINUATION_FIELDS = ["has_more", "next", "next_page", "next_page_token"];
|
|
30687
31158
|
});
|
|
30688
31159
|
|
|
30689
|
-
// src/providers/transport/probe-discovery.ts
|
|
30690
|
-
function isSmallName(name) {
|
|
30691
|
-
return SMALL_MODEL_PATTERNS.some((re) => re.test(name));
|
|
30692
|
-
}
|
|
30693
|
-
function catalogKey(name) {
|
|
30694
|
-
const lower = name.toLowerCase();
|
|
30695
|
-
return lower.includes("/") ? lower.slice(lower.lastIndexOf("/") + 1) : lower;
|
|
30696
|
-
}
|
|
30697
|
-
function indexOutputModality(index, keys, modalities) {
|
|
30698
|
-
if (!Array.isArray(modalities) || modalities.length === 0)
|
|
30699
|
-
return;
|
|
30700
|
-
const target = modalities.includes("text") ? index.textOutput : index.nonTextOutput;
|
|
30701
|
-
for (const k of keys)
|
|
30702
|
-
target.add(k);
|
|
30703
|
-
}
|
|
30704
|
-
function catalogCapabilityIndex(cachePath) {
|
|
30705
|
-
const key = cachePath ?? "";
|
|
30706
|
-
const hit = _catalogChatIndex.get(key);
|
|
30707
|
-
if (hit && hit.expiresAt > Date.now())
|
|
30708
|
-
return hit.index;
|
|
30709
|
-
const index = {
|
|
30710
|
-
chat: new Set,
|
|
30711
|
-
textOutput: new Set,
|
|
30712
|
-
nonTextOutput: new Set,
|
|
30713
|
-
videoOutput: new Set,
|
|
30714
|
-
videoOutputKnown: new Set
|
|
30715
|
-
};
|
|
30716
|
-
for (const entry of readAllModelsCache(cachePath)?.entries ?? []) {
|
|
30717
|
-
const keys = [catalogKey(entry.modelId), ...(entry.aliases ?? []).map(catalogKey)];
|
|
30718
|
-
indexOutputModality(index, keys, entry.outputModalities);
|
|
30719
|
-
if (entry.videoOutput !== undefined) {
|
|
30720
|
-
for (const k of keys)
|
|
30721
|
-
index.videoOutputKnown.add(k);
|
|
30722
|
-
}
|
|
30723
|
-
if (entry.videoOutput === true) {
|
|
30724
|
-
for (const k of keys)
|
|
30725
|
-
index.videoOutput.add(k);
|
|
30726
|
-
continue;
|
|
30727
|
-
}
|
|
30728
|
-
const chatShaped = entry.supportsTools === true || entry.supportsThinking === true || entry.supportsVision === true;
|
|
30729
|
-
if (chatShaped)
|
|
30730
|
-
for (const k of keys)
|
|
30731
|
-
index.chat.add(k);
|
|
30732
|
-
}
|
|
30733
|
-
_catalogChatIndex.set(key, { index, expiresAt: Date.now() + CATALOG_CHAT_INDEX_TTL_MS });
|
|
30734
|
-
return index;
|
|
30735
|
-
}
|
|
30736
|
-
function classifyChatCapability(name, cachePath) {
|
|
30737
|
-
if (name.includes("*"))
|
|
30738
|
-
return "not-chat";
|
|
30739
|
-
const index = catalogCapabilityIndex(cachePath);
|
|
30740
|
-
const key = catalogKey(name);
|
|
30741
|
-
if (index.nonTextOutput.has(key))
|
|
30742
|
-
return "not-chat";
|
|
30743
|
-
if (index.textOutput.has(key))
|
|
30744
|
-
return "chat";
|
|
30745
|
-
if (index.videoOutput.has(key))
|
|
30746
|
-
return "not-chat";
|
|
30747
|
-
if (NON_CHAT_PATTERNS.some((re) => re.test(name)))
|
|
30748
|
-
return "not-chat";
|
|
30749
|
-
if (!index.videoOutputKnown.has(key) && VIDEO_OUTPUT_NAME_PATTERNS.some((re) => re.test(name))) {
|
|
30750
|
-
return "not-chat";
|
|
30751
|
-
}
|
|
30752
|
-
if (index.chat.has(key))
|
|
30753
|
-
return "chat";
|
|
30754
|
-
return "unknown";
|
|
30755
|
-
}
|
|
30756
|
-
function isChatCapable(name) {
|
|
30757
|
-
return classifyChatCapability(name) !== "not-chat";
|
|
30758
|
-
}
|
|
30759
|
-
function isStandardName(name) {
|
|
30760
|
-
if (!name.includes("/"))
|
|
30761
|
-
return true;
|
|
30762
|
-
return STANDARD_VENDOR_PREFIXES.some((p) => name.toLowerCase().startsWith(p));
|
|
30763
|
-
}
|
|
30764
|
-
function rankProbeCandidates(names) {
|
|
30765
|
-
return names.filter(isChatCapable).sort((a, b) => {
|
|
30766
|
-
const aStd = isStandardName(a);
|
|
30767
|
-
const bStd = isStandardName(b);
|
|
30768
|
-
if (aStd !== bStd)
|
|
30769
|
-
return aStd ? -1 : 1;
|
|
30770
|
-
const aSmall = isSmallName(a);
|
|
30771
|
-
const bSmall = isSmallName(b);
|
|
30772
|
-
if (aSmall !== bSmall)
|
|
30773
|
-
return aSmall ? -1 : 1;
|
|
30774
|
-
return a.localeCompare(b);
|
|
30775
|
-
});
|
|
30776
|
-
}
|
|
30777
|
-
function cacheGet(key, exclude = new Set) {
|
|
30778
|
-
const hit = _cache2.get(key);
|
|
30779
|
-
if (!hit)
|
|
30780
|
-
return;
|
|
30781
|
-
if (Date.now() > hit.expiresAt) {
|
|
30782
|
-
_cache2.delete(key);
|
|
30783
|
-
return;
|
|
30784
|
-
}
|
|
30785
|
-
if (hit.ranked.length === 0) {
|
|
30786
|
-
return { model: null, reason: hit.reason };
|
|
30787
|
-
}
|
|
30788
|
-
const pick = hit.ranked.find((m) => !exclude.has(m));
|
|
30789
|
-
if (!pick) {
|
|
30790
|
-
return {
|
|
30791
|
-
model: null,
|
|
30792
|
-
reason: `all ${hit.ranked.length} candidate model(s) already tried`
|
|
30793
|
-
};
|
|
30794
|
-
}
|
|
30795
|
-
return { model: pick };
|
|
30796
|
-
}
|
|
30797
|
-
function cacheSetFailure(key, reason) {
|
|
30798
|
-
_cache2.set(key, { ranked: [], reason, expiresAt: Date.now() + CACHE_TTL_MS2 });
|
|
30799
|
-
}
|
|
30800
|
-
function cacheSetRanked(key, ranked) {
|
|
30801
|
-
_cache2.set(key, { ranked, expiresAt: Date.now() + CACHE_TTL_MS2 });
|
|
30802
|
-
}
|
|
30803
|
-
async function discoverViaOpenAIModels(endpoint, headers, cacheKey) {
|
|
30804
|
-
const cached = cacheGet(cacheKey.key, cacheKey.exclude);
|
|
30805
|
-
if (cached !== undefined)
|
|
30806
|
-
return cached;
|
|
30807
|
-
let response;
|
|
30808
|
-
try {
|
|
30809
|
-
response = await fetch(endpoint, {
|
|
30810
|
-
method: "GET",
|
|
30811
|
-
headers,
|
|
30812
|
-
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS2)
|
|
30813
|
-
});
|
|
30814
|
-
} catch (e) {
|
|
30815
|
-
const reason = classifyFetchError(e, endpoint);
|
|
30816
|
-
log(`[probe-discovery${cacheKey.displayName ? `:${cacheKey.displayName}` : ""}] fetch failed: ${reason}`);
|
|
30817
|
-
cacheSetFailure(cacheKey.key, reason);
|
|
30818
|
-
return { model: null, reason };
|
|
30819
|
-
}
|
|
30820
|
-
if (!response.ok) {
|
|
30821
|
-
const who = cacheKey.displayName ?? "this provider";
|
|
30822
|
-
const authFailure = response.status === 401 || response.status === 403 ? cacheKey.hasApiKey === false ? `the server requires an API key and none is configured for ${who}` : `the server rejected the configured API key for ${who}` : "";
|
|
30823
|
-
const reason = authFailure ? `HTTP ${response.status} from ${endpoint} \u2014 ${authFailure}` : `HTTP ${response.status} from ${endpoint}`;
|
|
30824
|
-
log(`[probe-discovery${cacheKey.displayName ? `:${cacheKey.displayName}` : ""}] ${reason}`);
|
|
30825
|
-
cacheSetFailure(cacheKey.key, reason);
|
|
30826
|
-
return { model: null, reason };
|
|
30827
|
-
}
|
|
30828
|
-
let body;
|
|
30829
|
-
try {
|
|
30830
|
-
body = await response.json();
|
|
30831
|
-
} catch {
|
|
30832
|
-
const reason = "invalid /v1/models response (not JSON)";
|
|
30833
|
-
cacheSetFailure(cacheKey.key, reason);
|
|
30834
|
-
return { model: null, reason };
|
|
30835
|
-
}
|
|
30836
|
-
const ids = extractModelIds(body);
|
|
30837
|
-
if (ids.length === 0) {
|
|
30838
|
-
const url = tryParseUrl(endpoint);
|
|
30839
|
-
const host = url?.host ?? endpoint;
|
|
30840
|
-
const reason = `${host} reachable but no models loaded \u2014 load a model in the server UI`;
|
|
30841
|
-
cacheSetFailure(cacheKey.key, reason);
|
|
30842
|
-
return { model: null, reason };
|
|
30843
|
-
}
|
|
30844
|
-
const ranked = rankProbeCandidates(ids);
|
|
30845
|
-
if (ranked.length === 0) {
|
|
30846
|
-
const reason = `no chat-capable model among ${ids.length} listed`;
|
|
30847
|
-
cacheSetFailure(cacheKey.key, reason);
|
|
30848
|
-
return { model: null, reason };
|
|
30849
|
-
}
|
|
30850
|
-
cacheSetRanked(cacheKey.key, ranked);
|
|
30851
|
-
const pick = ranked.find((m) => !cacheKey.exclude?.has(m));
|
|
30852
|
-
if (!pick) {
|
|
30853
|
-
return {
|
|
30854
|
-
model: null,
|
|
30855
|
-
reason: `all ${ranked.length} candidate model(s) already tried`
|
|
30856
|
-
};
|
|
30857
|
-
}
|
|
30858
|
-
return { model: pick };
|
|
30859
|
-
}
|
|
30860
|
-
function classifyFetchError(e, endpoint) {
|
|
30861
|
-
const name = e?.name ?? "";
|
|
30862
|
-
const code = e?.cause?.code ?? "";
|
|
30863
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
30864
|
-
const url = tryParseUrl(endpoint);
|
|
30865
|
-
const host = url?.host ?? endpoint;
|
|
30866
|
-
const isLocal = !!url && /^(localhost|127\.0\.0\.1|0\.0\.0\.0|::1)$/i.test(url.hostname);
|
|
30867
|
-
if (name === "TimeoutError" || name === "AbortError" || /timeout/i.test(msg)) {
|
|
30868
|
-
return `${host} unresponsive (>${FETCH_TIMEOUT_MS2 / 1000}s) \u2014 check if the server is overloaded`;
|
|
30869
|
-
}
|
|
30870
|
-
if (code === "ENOTFOUND" || code === "EAI_AGAIN") {
|
|
30871
|
-
return `cannot resolve host ${url?.hostname ?? endpoint} \u2014 check the URL`;
|
|
30872
|
-
}
|
|
30873
|
-
const isConnRefused = code === "ECONNREFUSED" || code === "ECONNRESET" || /unable to connect|connection refused|fetch failed/i.test(msg);
|
|
30874
|
-
if (isConnRefused) {
|
|
30875
|
-
if (isLocal) {
|
|
30876
|
-
return `${host} not reachable \u2014 is the server running? Press u to change URL.`;
|
|
30877
|
-
}
|
|
30878
|
-
return `${host} not reachable \u2014 check the URL or network. Press u to change.`;
|
|
30879
|
-
}
|
|
30880
|
-
return `${host}: ${msg}`;
|
|
30881
|
-
}
|
|
30882
|
-
function tryParseUrl(s) {
|
|
30883
|
-
try {
|
|
30884
|
-
return new URL(s);
|
|
30885
|
-
} catch {
|
|
30886
|
-
return null;
|
|
30887
|
-
}
|
|
30888
|
-
}
|
|
30889
|
-
function extractModelIds(body) {
|
|
30890
|
-
if (!body || typeof body !== "object")
|
|
30891
|
-
return [];
|
|
30892
|
-
const data = body;
|
|
30893
|
-
if (Array.isArray(data.data)) {
|
|
30894
|
-
return data.data.map((m) => m && typeof m === "object" ? m.id : null).filter((id) => typeof id === "string" && id.length > 0);
|
|
30895
|
-
}
|
|
30896
|
-
if (Array.isArray(data.models)) {
|
|
30897
|
-
return data.models.map((m) => m && typeof m === "object" ? m.id ?? m.model_name : null).filter((id) => typeof id === "string" && id.length > 0);
|
|
30898
|
-
}
|
|
30899
|
-
return [];
|
|
30900
|
-
}
|
|
30901
|
-
function orderByCost(models) {
|
|
30902
|
-
const sized = models.filter((m) => typeof m.size === "number");
|
|
30903
|
-
const unsized = models.filter((m) => typeof m.size !== "number");
|
|
30904
|
-
const bySize = [...sized].sort((a, b) => (a.size ?? Number.POSITIVE_INFINITY) - (b.size ?? Number.POSITIVE_INFINITY)).map((m) => m.name);
|
|
30905
|
-
return [...bySize, ...rankProbeCandidates(unsized.map((m) => m.name))];
|
|
30906
|
-
}
|
|
30907
|
-
async function discoverViaOllama(baseUrl, cacheKey) {
|
|
30908
|
-
const cached = cacheGet(cacheKey.key, cacheKey.exclude);
|
|
30909
|
-
if (cached !== undefined)
|
|
30910
|
-
return cached;
|
|
30911
|
-
const [psResult, tagsResult] = await Promise.allSettled([
|
|
30912
|
-
fetchOllamaModels2(`${baseUrl}/api/ps`),
|
|
30913
|
-
fetchOllamaModels2(`${baseUrl}/api/tags`)
|
|
30914
|
-
]);
|
|
30915
|
-
const loadedRaw = psResult.status === "fulfilled" ? psResult.value : [];
|
|
30916
|
-
const tagsRaw = tagsResult.status === "fulfilled" ? tagsResult.value : [];
|
|
30917
|
-
const connectionError = psResult.status === "rejected" ? classifyFetchError(psResult.reason, `${baseUrl}/api/ps`) : tagsResult.status === "rejected" ? classifyFetchError(tagsResult.reason, `${baseUrl}/api/tags`) : undefined;
|
|
30918
|
-
const loaded = loadedRaw.filter((m) => isChatCapable(m.name));
|
|
30919
|
-
const loadedNames = new Set(loaded.map((m) => m.name));
|
|
30920
|
-
const rest = tagsRaw.filter((m) => isChatCapable(m.name) && !loadedNames.has(m.name));
|
|
30921
|
-
if (loaded.length === 0 && rest.length === 0) {
|
|
30922
|
-
const reason = connectionError ?? (loadedRaw.length === 0 && tagsRaw.length === 0 ? `no models on ${baseUrl} (pull one: ollama pull llama3.2)` : `only embedding/non-chat models on ${baseUrl}`);
|
|
30923
|
-
cacheSetFailure(cacheKey.key, reason);
|
|
30924
|
-
return { model: null, reason };
|
|
30925
|
-
}
|
|
30926
|
-
const ranked = [...orderByCost(loaded), ...orderByCost(rest)];
|
|
30927
|
-
if (ranked.length === 0) {
|
|
30928
|
-
const reason = "no chat-capable model on Ollama endpoint";
|
|
30929
|
-
cacheSetFailure(cacheKey.key, reason);
|
|
30930
|
-
return { model: null, reason };
|
|
30931
|
-
}
|
|
30932
|
-
cacheSetRanked(cacheKey.key, ranked);
|
|
30933
|
-
const pick = ranked.find((m) => !cacheKey.exclude?.has(m));
|
|
30934
|
-
if (!pick) {
|
|
30935
|
-
return {
|
|
30936
|
-
model: null,
|
|
30937
|
-
reason: `all ${ranked.length} candidate model(s) already tried`
|
|
30938
|
-
};
|
|
30939
|
-
}
|
|
30940
|
-
return { model: pick };
|
|
30941
|
-
}
|
|
30942
|
-
async function discoverViaLMStudio(baseUrl, headers, cacheKey) {
|
|
30943
|
-
const cached = cacheGet(cacheKey.key, cacheKey.exclude);
|
|
30944
|
-
if (cached !== undefined)
|
|
30945
|
-
return cached;
|
|
30946
|
-
let response;
|
|
30947
|
-
try {
|
|
30948
|
-
response = await fetch(`${baseUrl}/api/v0/models`, {
|
|
30949
|
-
method: "GET",
|
|
30950
|
-
headers,
|
|
30951
|
-
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS2)
|
|
30952
|
-
});
|
|
30953
|
-
} catch (e) {
|
|
30954
|
-
return discoverViaOpenAIModels(`${baseUrl}/v1/models`, headers, cacheKey);
|
|
30955
|
-
}
|
|
30956
|
-
if (!response.ok) {
|
|
30957
|
-
return discoverViaOpenAIModels(`${baseUrl}/v1/models`, headers, cacheKey);
|
|
30958
|
-
}
|
|
30959
|
-
let body;
|
|
30960
|
-
try {
|
|
30961
|
-
body = await response.json();
|
|
30962
|
-
} catch {
|
|
30963
|
-
const reason = "invalid /api/v0/models response (not JSON)";
|
|
30964
|
-
cacheSetFailure(cacheKey.key, reason);
|
|
30965
|
-
return { model: null, reason };
|
|
30966
|
-
}
|
|
30967
|
-
const models = extractLMStudioModels(body);
|
|
30968
|
-
if (models.length === 0) {
|
|
30969
|
-
const url = tryParseUrl(baseUrl);
|
|
30970
|
-
const host = url?.host ?? baseUrl;
|
|
30971
|
-
const reason = `${host} reachable but no models present \u2014 download one in the LM Studio UI`;
|
|
30972
|
-
cacheSetFailure(cacheKey.key, reason);
|
|
30973
|
-
return { model: null, reason };
|
|
30974
|
-
}
|
|
30975
|
-
const chatModels = models.filter((m) => isChatCapable(m.id) && m.type !== "embeddings" && m.type !== "embedding");
|
|
30976
|
-
const loaded = chatModels.filter((m) => m.state === "loaded");
|
|
30977
|
-
const notLoaded = chatModels.filter((m) => m.state !== "loaded");
|
|
30978
|
-
const ranked = [
|
|
30979
|
-
...rankProbeCandidates(loaded.map((m) => m.id)),
|
|
30980
|
-
...rankProbeCandidates(notLoaded.map((m) => m.id))
|
|
30981
|
-
];
|
|
30982
|
-
if (ranked.length === 0) {
|
|
30983
|
-
const url = tryParseUrl(baseUrl);
|
|
30984
|
-
const host = url?.host ?? baseUrl;
|
|
30985
|
-
const reason = `${host} has ${models.length} model(s) but none are chat-capable`;
|
|
30986
|
-
cacheSetFailure(cacheKey.key, reason);
|
|
30987
|
-
return { model: null, reason };
|
|
30988
|
-
}
|
|
30989
|
-
cacheSetRanked(cacheKey.key, ranked);
|
|
30990
|
-
const pick = ranked.find((m) => !cacheKey.exclude?.has(m));
|
|
30991
|
-
if (!pick) {
|
|
30992
|
-
return {
|
|
30993
|
-
model: null,
|
|
30994
|
-
reason: `all ${ranked.length} candidate model(s) already tried`
|
|
30995
|
-
};
|
|
30996
|
-
}
|
|
30997
|
-
return { model: pick };
|
|
30998
|
-
}
|
|
30999
|
-
function extractLMStudioModels(body) {
|
|
31000
|
-
if (!body || typeof body !== "object")
|
|
31001
|
-
return [];
|
|
31002
|
-
const data = body.data;
|
|
31003
|
-
if (!Array.isArray(data))
|
|
31004
|
-
return [];
|
|
31005
|
-
const out = [];
|
|
31006
|
-
for (const m of data) {
|
|
31007
|
-
if (!m || typeof m !== "object")
|
|
31008
|
-
continue;
|
|
31009
|
-
const r = m;
|
|
31010
|
-
if (typeof r.id !== "string" || !r.id)
|
|
31011
|
-
continue;
|
|
31012
|
-
out.push({
|
|
31013
|
-
id: r.id,
|
|
31014
|
-
state: typeof r.state === "string" ? r.state : undefined,
|
|
31015
|
-
type: typeof r.type === "string" ? r.type : undefined
|
|
31016
|
-
});
|
|
31017
|
-
}
|
|
31018
|
-
return out;
|
|
31019
|
-
}
|
|
31020
|
-
async function fetchOllamaModels2(url) {
|
|
31021
|
-
const response = await fetch(url, {
|
|
31022
|
-
method: "GET",
|
|
31023
|
-
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS2)
|
|
31024
|
-
});
|
|
31025
|
-
if (!response.ok)
|
|
31026
|
-
return [];
|
|
31027
|
-
const body = await response.json().catch(() => null);
|
|
31028
|
-
if (!body?.models)
|
|
31029
|
-
return [];
|
|
31030
|
-
return body.models.map((m) => ({
|
|
31031
|
-
name: typeof m.name === "string" ? m.name : "",
|
|
31032
|
-
size: typeof m.size === "number" ? m.size : undefined
|
|
31033
|
-
})).filter((m) => m.name.length > 0);
|
|
31034
|
-
}
|
|
31035
|
-
function invalidateProbeDiscovery(providerSlug) {
|
|
31036
|
-
for (const key of _cache2.keys()) {
|
|
31037
|
-
if (key.startsWith(`${providerSlug}:`)) {
|
|
31038
|
-
_cache2.delete(key);
|
|
31039
|
-
}
|
|
31040
|
-
}
|
|
31041
|
-
}
|
|
31042
|
-
var _cache2, CACHE_TTL_MS2, FETCH_TIMEOUT_MS2 = 5000, SMALL_MODEL_PATTERNS, NON_CHAT_PATTERNS, VIDEO_OUTPUT_NAME_PATTERNS, CATALOG_CHAT_INDEX_TTL_MS = 60000, _catalogChatIndex, STANDARD_VENDOR_PREFIXES;
|
|
31043
|
-
var init_probe_discovery = __esm(() => {
|
|
31044
|
-
init_logger();
|
|
31045
|
-
init_all_models_cache();
|
|
31046
|
-
_cache2 = new Map;
|
|
31047
|
-
CACHE_TTL_MS2 = 5 * 60 * 1000;
|
|
31048
|
-
SMALL_MODEL_PATTERNS = [
|
|
31049
|
-
/\bmini\b/i,
|
|
31050
|
-
/\bnano\b/i,
|
|
31051
|
-
/\bflash\b/i,
|
|
31052
|
-
/\blite\b/i,
|
|
31053
|
-
/\bhaiku\b/i,
|
|
31054
|
-
/\bsmall\b/i,
|
|
31055
|
-
/\btiny\b/i,
|
|
31056
|
-
/\b[12345]b\b/i,
|
|
31057
|
-
/\b[78]b\b/i
|
|
31058
|
-
];
|
|
31059
|
-
NON_CHAT_PATTERNS = [
|
|
31060
|
-
/\bimage\b/i,
|
|
31061
|
-
/\bembed/i,
|
|
31062
|
-
/\bminilm\b/i,
|
|
31063
|
-
/\bnomic-embed/i,
|
|
31064
|
-
/\bbge-/i,
|
|
31065
|
-
/\bmxbai-embed/i,
|
|
31066
|
-
/\btts\b/i,
|
|
31067
|
-
/\bwhisper\b/i,
|
|
31068
|
-
/\baudio\b/i,
|
|
31069
|
-
/\bvoxtral\b/i,
|
|
31070
|
-
/\bdall-?e\b/i,
|
|
31071
|
-
/\bmoderation\b/i,
|
|
31072
|
-
/\brerank/i,
|
|
31073
|
-
/\bspeech\b/i,
|
|
31074
|
-
/\btranscribe\b/i,
|
|
31075
|
-
/\btranscription\b/i,
|
|
31076
|
-
/\bvoice\b/i,
|
|
31077
|
-
/\brealtime\b/i,
|
|
31078
|
-
/\blive\b/i,
|
|
31079
|
-
/\btranslate\b/i,
|
|
31080
|
-
/\btranslation\b/i,
|
|
31081
|
-
/-(image|tts|audio|embedding|vision-only|transcribe|voice|speech|realtime|live|translate)(-|$)/i
|
|
31082
|
-
];
|
|
31083
|
-
VIDEO_OUTPUT_NAME_PATTERNS = [
|
|
31084
|
-
/\bvideo\b/i,
|
|
31085
|
-
/(^|[-_.])(t2v|i2v|r2v|v2v)([-_.]|$)/i,
|
|
31086
|
-
/\bveo\b/i,
|
|
31087
|
-
/\bsora\b/i
|
|
31088
|
-
];
|
|
31089
|
-
_catalogChatIndex = new Map;
|
|
31090
|
-
STANDARD_VENDOR_PREFIXES = [
|
|
31091
|
-
"openai/",
|
|
31092
|
-
"anthropic/",
|
|
31093
|
-
"google/",
|
|
31094
|
-
"gemini/",
|
|
31095
|
-
"meta/",
|
|
31096
|
-
"meta-llama/",
|
|
31097
|
-
"mistralai/",
|
|
31098
|
-
"mistral/",
|
|
31099
|
-
"x-ai/",
|
|
31100
|
-
"deepseek/",
|
|
31101
|
-
"qwen/",
|
|
31102
|
-
"moonshot/",
|
|
31103
|
-
"moonshotai/",
|
|
31104
|
-
"zhipuai/",
|
|
31105
|
-
"z-ai/"
|
|
31106
|
-
];
|
|
31107
|
-
});
|
|
31108
|
-
|
|
31109
31160
|
// src/providers/transport/provider-model-discovery.ts
|
|
31110
31161
|
async function discoverProviderProbeModel(providerName, displayName, exclude) {
|
|
31111
31162
|
const def = getProviderByName(providerName);
|
|
@@ -31123,7 +31174,7 @@ async function discoverProviderProbeModel(providerName, displayName, exclude) {
|
|
|
31123
31174
|
reason: failure ? `${displayName}: ${describeDiscoveryFailure(failure)}` : `${displayName} listed no models at ${def.modelDiscovery.path} \u2014 check the API key and that the subscription is active`
|
|
31124
31175
|
};
|
|
31125
31176
|
}
|
|
31126
|
-
const ranked = rankDiscoveredModels(discovered).
|
|
31177
|
+
const ranked = rankDiscoveredModels(discovered).filter((model) => isReportedChatCapable(model.id, model.reported)).map((model) => model.id);
|
|
31127
31178
|
if (ranked.length === 0) {
|
|
31128
31179
|
return {
|
|
31129
31180
|
model: null,
|
|
@@ -32945,11 +32996,14 @@ function sanitizeErrorMessage(message, maxLength = MAX_ERROR_MESSAGE_LENGTH) {
|
|
|
32945
32996
|
return flattened;
|
|
32946
32997
|
return `${flattened.slice(0, maxLength - 1).trimEnd()}\u2026`;
|
|
32947
32998
|
}
|
|
32948
|
-
function wrapAnthropicError(status, message, errorType, upstreamStatus) {
|
|
32999
|
+
function wrapAnthropicError(status, message, errorType, upstreamStatus, providerMessage) {
|
|
32949
33000
|
const type = errorType || statusToErrorType(status);
|
|
32950
33001
|
const error = { type, message: sanitizeErrorMessage(message) };
|
|
32951
33002
|
if (upstreamStatus !== undefined)
|
|
32952
33003
|
error.upstream_status = upstreamStatus;
|
|
33004
|
+
const oneLine = (providerMessage ?? "").replace(/\s+/g, " ").trim();
|
|
33005
|
+
if (oneLine)
|
|
33006
|
+
error.provider_message = sanitizeErrorMessage(oneLine);
|
|
32953
33007
|
return { type: "error", error };
|
|
32954
33008
|
}
|
|
32955
33009
|
function extractUpstreamStatus(body) {
|
|
@@ -32963,11 +33017,38 @@ function extractUpstreamStatus(body) {
|
|
|
32963
33017
|
return;
|
|
32964
33018
|
}
|
|
32965
33019
|
}
|
|
33020
|
+
function sseDataPayload(body) {
|
|
33021
|
+
if (!/^\s*(event|data):/.test(body))
|
|
33022
|
+
return;
|
|
33023
|
+
const lines = body.split(/\r?\n/).filter((line) => line.startsWith("data:"));
|
|
33024
|
+
if (lines.length > 0) {
|
|
33025
|
+
const joined = lines.map((line) => line.slice("data:".length).trim()).join("");
|
|
33026
|
+
if (joined.length > 0)
|
|
33027
|
+
return joined;
|
|
33028
|
+
}
|
|
33029
|
+
const at = body.search(/(^|\s)data:/);
|
|
33030
|
+
if (at === -1)
|
|
33031
|
+
return;
|
|
33032
|
+
const payload = body.slice(body.indexOf("data:", at) + "data:".length).trim();
|
|
33033
|
+
return payload.length > 0 ? payload : undefined;
|
|
33034
|
+
}
|
|
32966
33035
|
function extractProviderMessage(body) {
|
|
32967
33036
|
if (body == null)
|
|
32968
33037
|
return "";
|
|
32969
|
-
if (typeof body === "string")
|
|
33038
|
+
if (typeof body === "string") {
|
|
33039
|
+
const payload = sseDataPayload(body);
|
|
33040
|
+
if (payload) {
|
|
33041
|
+
try {
|
|
33042
|
+
const inner = extractProviderMessage(JSON.parse(payload));
|
|
33043
|
+
if (inner)
|
|
33044
|
+
return inner;
|
|
33045
|
+
} catch {
|
|
33046
|
+
return payload;
|
|
33047
|
+
}
|
|
33048
|
+
return payload;
|
|
33049
|
+
}
|
|
32970
33050
|
return body;
|
|
33051
|
+
}
|
|
32971
33052
|
const candidates = [
|
|
32972
33053
|
body?.error?.message,
|
|
32973
33054
|
body?.message,
|
|
@@ -32977,7 +33058,7 @@ function extractProviderMessage(body) {
|
|
|
32977
33058
|
];
|
|
32978
33059
|
for (const c of candidates) {
|
|
32979
33060
|
if (typeof c === "string" && c.length > 0)
|
|
32980
|
-
return c;
|
|
33061
|
+
return extractProviderMessage(c);
|
|
32981
33062
|
if (Array.isArray(c)) {
|
|
32982
33063
|
const first = c.find((e) => typeof e?.msg === "string" && e.msg.length > 0);
|
|
32983
33064
|
if (first)
|
|
@@ -33567,7 +33648,7 @@ var init_stream_head_sniffer = __esm(() => {
|
|
|
33567
33648
|
});
|
|
33568
33649
|
|
|
33569
33650
|
// src/handlers/shared/stream-parsers/anthropic-sse.ts
|
|
33570
|
-
function
|
|
33651
|
+
function sseDataPayload2(line) {
|
|
33571
33652
|
if (!line.startsWith("data:"))
|
|
33572
33653
|
return null;
|
|
33573
33654
|
const rest = line.slice(5);
|
|
@@ -33805,7 +33886,7 @@ data: {"type":"ping"}
|
|
|
33805
33886
|
buffer = lines.pop() || "";
|
|
33806
33887
|
for (const line of lines) {
|
|
33807
33888
|
totalLines++;
|
|
33808
|
-
const payload =
|
|
33889
|
+
const payload = sseDataPayload2(line);
|
|
33809
33890
|
if (filterThinking && payload !== null) {
|
|
33810
33891
|
try {
|
|
33811
33892
|
const data = JSON.parse(payload);
|
|
@@ -35918,7 +35999,8 @@ class ComposedHandler {
|
|
|
35918
35999
|
parsedErrorBody = undefined;
|
|
35919
36000
|
}
|
|
35920
36001
|
const providerMsg = extractProviderMessage(parsedErrorBody ?? errorText);
|
|
35921
|
-
const
|
|
36002
|
+
const oneLineMsg = providerMsg.replace(/\s+/g, " ").trim();
|
|
36003
|
+
const msgTail = oneLineMsg ? ` (${oneLineMsg.length > 200 ? `${oneLineMsg.slice(0, 200)}\u2026` : oneLineMsg})` : "";
|
|
35922
36004
|
logStderr(`Error [${this.provider.displayName}]: HTTP ${response.status}. ${hint}${msgTail}`);
|
|
35923
36005
|
let providerErrorType;
|
|
35924
36006
|
try {
|
|
@@ -35971,7 +36053,7 @@ class ComposedHandler {
|
|
|
35971
36053
|
providerMessage: providerMsg,
|
|
35972
36054
|
leadPhrase: isContextOverflowError(response.status, errorText) ? CONTEXT_OVERFLOW_PHRASE : undefined
|
|
35973
36055
|
});
|
|
35974
|
-
return c.json(wrapAnthropicError(400, surfaced, "invalid_request_error", response.status), 400);
|
|
36056
|
+
return c.json(wrapAnthropicError(400, surfaced, "invalid_request_error", response.status, providerMsg), 400);
|
|
35975
36057
|
}
|
|
35976
36058
|
return c.json(ensureAnthropicErrorFormat(response.status, errorBody), response.status);
|
|
35977
36059
|
}
|
|
@@ -36004,7 +36086,7 @@ class ComposedHandler {
|
|
|
36004
36086
|
invocation_mode: this.options.invocationMode ?? "auto-route"
|
|
36005
36087
|
});
|
|
36006
36088
|
} catch {}
|
|
36007
|
-
return c.json(wrapAnthropicError(503, surfaced, "overloaded_error"), 503);
|
|
36089
|
+
return c.json(wrapAnthropicError(503, surfaced, "overloaded_error", undefined, settled.message), 503);
|
|
36008
36090
|
}
|
|
36009
36091
|
response = settled.response;
|
|
36010
36092
|
}
|
|
@@ -36046,7 +36128,7 @@ class ComposedHandler {
|
|
|
36046
36128
|
invocation_mode: this.options.invocationMode ?? "auto-route"
|
|
36047
36129
|
});
|
|
36048
36130
|
} catch {}
|
|
36049
|
-
return isTerminal ? c.json(wrapAnthropicError(400, surfaced, "invalid_request_error"), 400) : c.json(wrapAnthropicError(503, surfaced, "overloaded_error"), 503);
|
|
36131
|
+
return isTerminal ? c.json(wrapAnthropicError(400, surfaced, "invalid_request_error", undefined, settled.message), 400) : c.json(wrapAnthropicError(503, surfaced, "overloaded_error", undefined, settled.message), 503);
|
|
36050
36132
|
}
|
|
36051
36133
|
response = settled.response;
|
|
36052
36134
|
}
|
|
@@ -38551,7 +38633,7 @@ var init_provider_definitions = __esm(() => {
|
|
|
38551
38633
|
description: "Alibaba Model Studio PAYG (qpay@)"
|
|
38552
38634
|
},
|
|
38553
38635
|
{
|
|
38554
|
-
createHandler:
|
|
38636
|
+
createHandler: noHandler("virtual", "No baseUrl. Exists only so nativeModelPatterns can steer a bare qwen* name; qwen-payg serves dashscope."),
|
|
38555
38637
|
tier: "native",
|
|
38556
38638
|
name: "qwen",
|
|
38557
38639
|
displayName: "Qwen",
|
|
@@ -38881,6 +38963,7 @@ async function refreshCatalog(timeoutMs, options = {}) {
|
|
|
38881
38963
|
catalogGenerationId: generationId
|
|
38882
38964
|
};
|
|
38883
38965
|
writeAllModelsCache(cache, options.cachePath);
|
|
38966
|
+
_clearChatCapabilityIndex();
|
|
38884
38967
|
_memCache = entries;
|
|
38885
38968
|
_warmPromise = Promise.resolve();
|
|
38886
38969
|
return {
|
|
@@ -38957,6 +39040,7 @@ var DEFAULT_CATALOG_URL = "https://us-central1-claudish-6da10.cloudfunctions.net
|
|
|
38957
39040
|
var init_catalog_client = __esm(() => {
|
|
38958
39041
|
init_all_models_cache();
|
|
38959
39042
|
init_catalog_route_bindings();
|
|
39043
|
+
init_probe_discovery();
|
|
38960
39044
|
});
|
|
38961
39045
|
|
|
38962
39046
|
// src/config-schema.ts
|
|
@@ -40094,12 +40178,16 @@ function modelsCatalogHas(ids, wireId) {
|
|
|
40094
40178
|
const needle = wireId.trim().toLowerCase();
|
|
40095
40179
|
return ids.some((id) => id.trim().toLowerCase() === needle);
|
|
40096
40180
|
}
|
|
40181
|
+
function resolveAgainstModelsCatalog(provider, wireId, models) {
|
|
40182
|
+
const entries = models.map(({ id, ...rest }) => ({ wireId: id, ...rest }));
|
|
40183
|
+
return expandSelection(provider, wireId, entries);
|
|
40184
|
+
}
|
|
40097
40185
|
async function providerServesModel(provider, wireId) {
|
|
40098
40186
|
const def = getProviderByName(provider);
|
|
40099
40187
|
if (def?.modelDiscovery) {
|
|
40100
40188
|
const models = await discoverProviderModels(provider);
|
|
40101
40189
|
if (models.length > 0) {
|
|
40102
|
-
return modelsCatalogHas(models.map((m) => m.id), wireId) ? "serves" : "not-served";
|
|
40190
|
+
return modelsCatalogHas(models.map((m) => m.id), resolveAgainstModelsCatalog(provider, wireId, models)) ? "serves" : "not-served";
|
|
40103
40191
|
}
|
|
40104
40192
|
getDiscoveryFailure(provider);
|
|
40105
40193
|
return "unknown";
|
|
@@ -40117,6 +40205,7 @@ var init_model_availability = __esm(() => {
|
|
|
40117
40205
|
init_catalog_route_bindings();
|
|
40118
40206
|
init_catalog_client();
|
|
40119
40207
|
init_model_discovery();
|
|
40208
|
+
init_registry2();
|
|
40120
40209
|
init_provider_definitions();
|
|
40121
40210
|
});
|
|
40122
40211
|
|
|
@@ -44954,17 +45043,32 @@ function truncateKeepingLink(text, max = 400) {
|
|
|
44954
45043
|
const prose = (lastSpace > room * 0.5 ? head.slice(0, lastSpace) : head).trimEnd();
|
|
44955
45044
|
return `${prose}... ${url}`;
|
|
44956
45045
|
}
|
|
45046
|
+
function oneLine4(text) {
|
|
45047
|
+
return text.replace(/\s+/g, " ").trim();
|
|
45048
|
+
}
|
|
45049
|
+
function messageFromJson(text) {
|
|
45050
|
+
try {
|
|
45051
|
+
const parsed = JSON.parse(text);
|
|
45052
|
+
const msg = parsed?.error?.provider_message || parsed?.error?.message || parsed?.error?.error?.message || parsed?.message || parsed?.detail;
|
|
45053
|
+
return typeof msg === "string" && msg.length > 0 ? msg : undefined;
|
|
45054
|
+
} catch {
|
|
45055
|
+
return;
|
|
45056
|
+
}
|
|
45057
|
+
}
|
|
45058
|
+
function unwrapFrame(text) {
|
|
45059
|
+
const payload = sseDataPayload(text);
|
|
45060
|
+
if (!payload)
|
|
45061
|
+
return text;
|
|
45062
|
+
return messageFromJson(payload) ?? payload;
|
|
45063
|
+
}
|
|
44957
45064
|
function extractErrorMessage(body) {
|
|
44958
45065
|
if (!body)
|
|
44959
45066
|
return;
|
|
44960
|
-
|
|
44961
|
-
|
|
44962
|
-
|
|
44963
|
-
|
|
44964
|
-
|
|
44965
|
-
}
|
|
44966
|
-
} catch {}
|
|
44967
|
-
const trimmed = body.trim();
|
|
45067
|
+
const direct = messageFromJson(body);
|
|
45068
|
+
if (direct)
|
|
45069
|
+
return truncateKeepingLink(oneLine4(unwrapFrame(direct)));
|
|
45070
|
+
const unwrapped = unwrapFrame(body);
|
|
45071
|
+
const trimmed = oneLine4(unwrapped);
|
|
44968
45072
|
if (!trimmed)
|
|
44969
45073
|
return;
|
|
44970
45074
|
return truncateKeepingLink(trimmed);
|
|
@@ -45141,7 +45245,10 @@ function isContentEvent(parsed, eventType) {
|
|
|
45141
45245
|
return false;
|
|
45142
45246
|
}
|
|
45143
45247
|
function withDetail(base, message) {
|
|
45144
|
-
return message ? `${base} \u2014 ${message}` : base;
|
|
45248
|
+
return message ? `${base} \u2014 ${stripRedundantHead(message)}` : base;
|
|
45249
|
+
}
|
|
45250
|
+
function stripRedundantHead(message) {
|
|
45251
|
+
return message.replace(/^.{1,40}? error \(HTTP \d{3}\): /, "");
|
|
45145
45252
|
}
|
|
45146
45253
|
function describeProbeState(result) {
|
|
45147
45254
|
const status = result.httpStatus ?? "";
|
|
@@ -59306,7 +59413,7 @@ function logModalityGapOnce(kept) {
|
|
|
59306
59413
|
if (suspect.length === 0)
|
|
59307
59414
|
return;
|
|
59308
59415
|
_modalityGapLogged = true;
|
|
59309
|
-
log(`[Models] ${suspect.length} row(s)
|
|
59416
|
+
log(`[Models] ${suspect.length} row(s) the catalog publishes as chat, from a generator family: ` + `${suspect.slice(0, 8).map((m) => m.id).join(", ")}. If one is not a chat model, its models-index row is wrong.`);
|
|
59310
59417
|
}
|
|
59311
59418
|
async function getFreeModels() {
|
|
59312
59419
|
return [];
|
|
@@ -59762,13 +59869,13 @@ async function buildDiscoveredModelOutcome(provider, displayName, catalog) {
|
|
|
59762
59869
|
}
|
|
59763
59870
|
const served = rankDiscoveredModels(modelsCatalog.models);
|
|
59764
59871
|
const servedCount = served.length;
|
|
59765
|
-
const discovered = served.filter((m) =>
|
|
59872
|
+
const discovered = served.filter((m) => isReportedChatCapable(m.id, m.reported));
|
|
59766
59873
|
const chatCount = discovered.length;
|
|
59767
59874
|
if (chatCount === 0) {
|
|
59768
59875
|
return {
|
|
59769
59876
|
kind: "all-filtered",
|
|
59770
59877
|
servedCount,
|
|
59771
|
-
sampleIds: served.filter((m) => !
|
|
59878
|
+
sampleIds: served.filter((m) => !isReportedChatCapable(m.id, m.reported)).slice(0, 3).map((m) => m.id),
|
|
59772
59879
|
fallbackRows
|
|
59773
59880
|
};
|
|
59774
59881
|
}
|
|
@@ -60848,14 +60955,15 @@ function useProbeStore(store) {
|
|
|
60848
60955
|
}
|
|
60849
60956
|
function deriveLayout(width) {
|
|
60850
60957
|
if (width < 60) {
|
|
60851
|
-
return { barWidth: 0, tokWidth: 0, showBreakdown: false, pillFallback: true };
|
|
60958
|
+
return { barWidth: 0, tokWidth: 0, showBreakdown: false, pillFallback: true, width };
|
|
60852
60959
|
}
|
|
60853
60960
|
if (width < 80) {
|
|
60854
60961
|
return {
|
|
60855
60962
|
barWidth: TIMELINE_BAR_NARROW,
|
|
60856
60963
|
tokWidth: 0,
|
|
60857
60964
|
showBreakdown: false,
|
|
60858
|
-
pillFallback: false
|
|
60965
|
+
pillFallback: false,
|
|
60966
|
+
width
|
|
60859
60967
|
};
|
|
60860
60968
|
}
|
|
60861
60969
|
if (width < 100) {
|
|
@@ -60863,14 +60971,16 @@ function deriveLayout(width) {
|
|
|
60863
60971
|
barWidth: TIMELINE_BAR_FULL,
|
|
60864
60972
|
tokWidth: 0,
|
|
60865
60973
|
showBreakdown: true,
|
|
60866
|
-
pillFallback: false
|
|
60974
|
+
pillFallback: false,
|
|
60975
|
+
width
|
|
60867
60976
|
};
|
|
60868
60977
|
}
|
|
60869
60978
|
return {
|
|
60870
60979
|
barWidth: TIMELINE_BAR_FULL,
|
|
60871
60980
|
tokWidth: TOK_BAR_FULL,
|
|
60872
60981
|
showBreakdown: true,
|
|
60873
|
-
pillFallback: false
|
|
60982
|
+
pillFallback: false,
|
|
60983
|
+
width
|
|
60874
60984
|
};
|
|
60875
60985
|
}
|
|
60876
60986
|
function computeRowWidth(layout, maxNameLen) {
|
|
@@ -60912,8 +61022,12 @@ function padEndSafe(s, n) {
|
|
|
60912
61022
|
return s.slice(0, n);
|
|
60913
61023
|
return s + " ".repeat(n - s.length);
|
|
60914
61024
|
}
|
|
61025
|
+
function clipReason(s, width) {
|
|
61026
|
+
const room = Math.max(8, width - 2);
|
|
61027
|
+
return s.length <= room ? s : `${s.slice(0, room - 1)}\u2026`;
|
|
61028
|
+
}
|
|
60915
61029
|
function stripAnsi3(text) {
|
|
60916
|
-
return text.replace(/\x1b\[[0-9;]*[A-Za-z]/g, "");
|
|
61030
|
+
return text.replace(/\x1b\[[0-9;]*[A-Za-z]/g, "").replace(/\s+/g, " ").trim();
|
|
60917
61031
|
}
|
|
60918
61032
|
function ishGreen() {
|
|
60919
61033
|
return getThemeMode() === "light" ? "#047857" : "#00ff7f";
|
|
@@ -61031,7 +61145,19 @@ function ProgressBar({
|
|
|
61031
61145
|
return /* @__PURE__ */ jsxs9("text", {
|
|
61032
61146
|
children: [
|
|
61033
61147
|
prefix,
|
|
61034
|
-
renderNonLiveStatus(link, false)
|
|
61148
|
+
renderNonLiveStatus(link, false, layout.width)
|
|
61149
|
+
]
|
|
61150
|
+
});
|
|
61151
|
+
}
|
|
61152
|
+
if (link.status === "failed") {
|
|
61153
|
+
const used = ELAPSED_COL + maxNameLen + 2;
|
|
61154
|
+
return /* @__PURE__ */ jsxs9("text", {
|
|
61155
|
+
children: [
|
|
61156
|
+
prefix,
|
|
61157
|
+
/* @__PURE__ */ jsx12("span", {
|
|
61158
|
+
fg: C.red,
|
|
61159
|
+
children: clipReason(`\u2717 ${stripAnsi3(link.error || "failed")}`, layout.width - used)
|
|
61160
|
+
})
|
|
61035
61161
|
]
|
|
61036
61162
|
});
|
|
61037
61163
|
}
|
|
@@ -61044,7 +61170,7 @@ function ProgressBar({
|
|
|
61044
61170
|
fg: C.dim,
|
|
61045
61171
|
children: " "
|
|
61046
61172
|
}),
|
|
61047
|
-
renderNonLiveStatus(link, true)
|
|
61173
|
+
renderNonLiveStatus(link, true, layout.width)
|
|
61048
61174
|
]
|
|
61049
61175
|
});
|
|
61050
61176
|
}
|
|
@@ -61178,7 +61304,7 @@ function renderTimelineSlot(link, animFrame, barWidth) {
|
|
|
61178
61304
|
});
|
|
61179
61305
|
}
|
|
61180
61306
|
}
|
|
61181
|
-
function renderNonLiveStatus(link, hasSlot) {
|
|
61307
|
+
function renderNonLiveStatus(link, hasSlot, width) {
|
|
61182
61308
|
switch (link.status) {
|
|
61183
61309
|
case "probing": {
|
|
61184
61310
|
const elapsedMs = link.startTime ? Date.now() - link.startTime : 0;
|
|
@@ -61193,7 +61319,7 @@ function renderNonLiveStatus(link, hasSlot) {
|
|
|
61193
61319
|
children: "\u2717"
|
|
61194
61320
|
}) : /* @__PURE__ */ jsx12("span", {
|
|
61195
61321
|
fg: C.red,
|
|
61196
|
-
children: `\u2717 ${stripAnsi3(link.error || "failed")}
|
|
61322
|
+
children: clipReason(`\u2717 ${stripAnsi3(link.error || "failed")}`, width)
|
|
61197
61323
|
});
|
|
61198
61324
|
default:
|
|
61199
61325
|
return /* @__PURE__ */ jsx12("span", {
|
|
@@ -61396,6 +61522,7 @@ function DetailLinkRow({
|
|
|
61396
61522
|
]
|
|
61397
61523
|
});
|
|
61398
61524
|
if (!isLive || !probe?.timing) {
|
|
61525
|
+
const used = 2 + 1 + 1 + provW + 2 + 3;
|
|
61399
61526
|
return /* @__PURE__ */ jsxs9("text", {
|
|
61400
61527
|
children: [
|
|
61401
61528
|
lead,
|
|
@@ -61405,7 +61532,7 @@ function DetailLinkRow({
|
|
|
61405
61532
|
}),
|
|
61406
61533
|
/* @__PURE__ */ jsx12("span", {
|
|
61407
61534
|
fg: C.red,
|
|
61408
|
-
children: shortFailureReason(probe, link.hasCredentials)
|
|
61535
|
+
children: clipReason(shortFailureReason(probe, link.hasCredentials), layout.width - used)
|
|
61409
61536
|
})
|
|
61410
61537
|
]
|
|
61411
61538
|
});
|
|
@@ -62141,7 +62268,7 @@ function ProbeApp({
|
|
|
62141
62268
|
]
|
|
62142
62269
|
}, state.phase);
|
|
62143
62270
|
}
|
|
62144
|
-
var ANIM_FRAMES2, TIMELINE_BAR_FULL = 24, TIMELINE_BAR_NARROW = 12, TOK_BAR_FULL = 14, TOTAL_COL = 7, STAGE_NUM_W2 = 6, BREAKDOWN_COL, TOK_VALUE_COL = 7, TRACK_CHAR = "\xB7", BAR_FILL = "\u2588", BANNER_ROWS = 7, SCROLL_HINT_ROWS = 1, LEGEND_ROWS = 2, MIN_LIST_H = 4, TAB_BAR_ROWS = 2;
|
|
62271
|
+
var ANIM_FRAMES2, TIMELINE_BAR_FULL = 24, TIMELINE_BAR_NARROW = 12, TOK_BAR_FULL = 14, TOTAL_COL = 7, ELAPSED_COL = 11, STAGE_NUM_W2 = 6, BREAKDOWN_COL, TOK_VALUE_COL = 7, TRACK_CHAR = "\xB7", BAR_FILL = "\u2588", BANNER_ROWS = 7, SCROLL_HINT_ROWS = 1, LEGEND_ROWS = 2, MIN_LIST_H = 4, TAB_BAR_ROWS = 2;
|
|
62145
62272
|
var init_probe_tui_app = __esm(() => {
|
|
62146
62273
|
init_probe_live();
|
|
62147
62274
|
init_theme_mode();
|
|
@@ -63665,6 +63792,7 @@ async function probeModelRouting(models, jsonOutput, options = { live: true, tim
|
|
|
63665
63792
|
activeTab: "summary"
|
|
63666
63793
|
};
|
|
63667
63794
|
const tui = await startProbeTui(initialState);
|
|
63795
|
+
setStderrQuiet(true);
|
|
63668
63796
|
const addStep = (name, status) => {
|
|
63669
63797
|
tui.store.setState((prev) => ({
|
|
63670
63798
|
...prev,
|
|
@@ -63852,6 +63980,7 @@ async function probeModelRouting(models, jsonOutput, options = { live: true, tim
|
|
|
63852
63980
|
} catch {}
|
|
63853
63981
|
}
|
|
63854
63982
|
await tui.shutdown();
|
|
63983
|
+
setStderrQuiet(false);
|
|
63855
63984
|
}
|
|
63856
63985
|
}
|
|
63857
63986
|
function printHelp2() {
|
|
@@ -64279,6 +64408,7 @@ var __filename3, __dirname3;
|
|
|
64279
64408
|
var init_cli = __esm(() => {
|
|
64280
64409
|
init_base_api_format();
|
|
64281
64410
|
init_config2();
|
|
64411
|
+
init_logger();
|
|
64282
64412
|
init_model_loader();
|
|
64283
64413
|
init_model_selector();
|
|
64284
64414
|
init_probe_results_printer();
|