cascade-ai 0.70.0 → 0.71.0
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/cli.cjs +205 -340
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +205 -340
- package/dist/cli.js.map +1 -1
- package/dist/desktop-core.cjs +164 -294
- package/dist/index.cjs +164 -294
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +45 -15
- package/dist/index.d.ts +45 -15
- package/dist/index.js +164 -292
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1908,7 +1908,7 @@ function reconcilePrice(model, live) {
|
|
|
1908
1908
|
}
|
|
1909
1909
|
|
|
1910
1910
|
// src/constants.ts
|
|
1911
|
-
var CASCADE_VERSION = "0.
|
|
1911
|
+
var CASCADE_VERSION = "0.71.0";
|
|
1912
1912
|
var CASCADE_CONFIG_DIR = ".cascade";
|
|
1913
1913
|
var CASCADE_MD_FILE = "CASCADE.md";
|
|
1914
1914
|
var CASCADE_IGNORE_FILE = ".cascadeignore";
|
|
@@ -2315,9 +2315,6 @@ var DEFAULT_THEME = "cascade";
|
|
|
2315
2315
|
var OLLAMA_BASE_URL = "http://localhost:11434";
|
|
2316
2316
|
var LM_STUDIO_BASE_URL = "http://localhost:1234";
|
|
2317
2317
|
var AZURE_BASE_URL_TEMPLATE = "https://{resource}.openai.azure.com";
|
|
2318
|
-
var GITHUB_MODELS_INFERENCE_URL = "https://models.github.ai/inference";
|
|
2319
|
-
var GITHUB_MODELS_CATALOG_URL = "https://models.github.ai/catalog/models";
|
|
2320
|
-
var GITHUB_MODELS_API_VERSION = "2026-03-10";
|
|
2321
2318
|
var TOOL_NAMES = {
|
|
2322
2319
|
SHELL: "shell",
|
|
2323
2320
|
FILE_READ: "file_read",
|
|
@@ -2352,8 +2349,7 @@ var PROVIDER_DISPLAY_NAMES = {
|
|
|
2352
2349
|
gemini: "Google Gemini",
|
|
2353
2350
|
azure: "Azure OpenAI",
|
|
2354
2351
|
"openai-compatible": "OpenAI-Compatible",
|
|
2355
|
-
ollama: "Ollama (Local)"
|
|
2356
|
-
"github-models": "GitHub Models"
|
|
2352
|
+
ollama: "Ollama (Local)"
|
|
2357
2353
|
};
|
|
2358
2354
|
|
|
2359
2355
|
// src/utils/cost.ts
|
|
@@ -3396,206 +3392,6 @@ async function nodeHttpFetch(input, init = {}, redirectCount = 0, opts = {}) {
|
|
|
3396
3392
|
});
|
|
3397
3393
|
}
|
|
3398
3394
|
|
|
3399
|
-
// src/providers/github-models.ts
|
|
3400
|
-
var GITHUB_MODELS_MAX_OUTPUT_TOKENS = 4e3;
|
|
3401
|
-
var GITHUB_MODELS_MAX_INPUT_TOKENS = 8e3;
|
|
3402
|
-
var DEFAULT_CONTEXT_WINDOW = 128e3;
|
|
3403
|
-
var GITHUB_MODELS_MAX_CATALOG_PAGES = 20;
|
|
3404
|
-
var GITHUB_MODELS_CATALOG_ORIGIN = new URL(GITHUB_MODELS_CATALOG_URL).origin;
|
|
3405
|
-
function stripModelOwnerPrefix(id) {
|
|
3406
|
-
const slash = (id || "").indexOf("/");
|
|
3407
|
-
return slash === -1 ? id : id.slice(slash + 1);
|
|
3408
|
-
}
|
|
3409
|
-
function pickString(o, keys) {
|
|
3410
|
-
for (const k of keys) {
|
|
3411
|
-
const v = o[k];
|
|
3412
|
-
if (typeof v === "string" && v.length > 0) return v;
|
|
3413
|
-
}
|
|
3414
|
-
return void 0;
|
|
3415
|
-
}
|
|
3416
|
-
function pickNumber(o, keys) {
|
|
3417
|
-
for (const k of keys) {
|
|
3418
|
-
const v = k.includes(".") ? o[k.split(".")[0]]?.[k.split(".")[1]] : o[k];
|
|
3419
|
-
if (typeof v === "number" && Number.isFinite(v) && v > 0) return v;
|
|
3420
|
-
}
|
|
3421
|
-
return void 0;
|
|
3422
|
-
}
|
|
3423
|
-
function looksVisionCapable(o) {
|
|
3424
|
-
for (const k of ["supported_input_modalities", "input_modalities", "modalities", "capabilities"]) {
|
|
3425
|
-
const v = o[k];
|
|
3426
|
-
if (Array.isArray(v) && v.some((x) => typeof x === "string" && /image|vision/i.test(x))) return true;
|
|
3427
|
-
}
|
|
3428
|
-
return false;
|
|
3429
|
-
}
|
|
3430
|
-
function looksToolCapable(o) {
|
|
3431
|
-
const params = o["supported_parameters"];
|
|
3432
|
-
if (Array.isArray(params) && params.some((x) => typeof x === "string" && /^tools?$|function.?call/i.test(x))) {
|
|
3433
|
-
return true;
|
|
3434
|
-
}
|
|
3435
|
-
for (const k of ["capabilities", "supported_output_modalities"]) {
|
|
3436
|
-
const v = o[k];
|
|
3437
|
-
if (Array.isArray(v) && v.some((x) => typeof x === "string" && /tool|function.?call/i.test(x))) return true;
|
|
3438
|
-
}
|
|
3439
|
-
const caps = o["capabilities"];
|
|
3440
|
-
if (caps && typeof caps === "object" && !Array.isArray(caps)) {
|
|
3441
|
-
const c = caps;
|
|
3442
|
-
if (c["function_calling"] === true || c["tool_calling"] === true || c["tools"] === true) return true;
|
|
3443
|
-
}
|
|
3444
|
-
return false;
|
|
3445
|
-
}
|
|
3446
|
-
function parseNextLink(linkHeader) {
|
|
3447
|
-
if (!linkHeader) return null;
|
|
3448
|
-
for (const part of linkHeader.split(",")) {
|
|
3449
|
-
const match = part.match(/<([^>]+)>\s*;\s*rel="next"/);
|
|
3450
|
-
if (match) return match[1];
|
|
3451
|
-
}
|
|
3452
|
-
return null;
|
|
3453
|
-
}
|
|
3454
|
-
var GitHubModelsProvider = class extends OpenAIProvider {
|
|
3455
|
-
constructor(config, model) {
|
|
3456
|
-
super({ ...config, apiKey: config.apiKey ?? "" }, model);
|
|
3457
|
-
this.useMaxCompletionTokens = isReasoningModel(stripModelOwnerPrefix(model.id));
|
|
3458
|
-
this.client = new OpenAI__default.default({
|
|
3459
|
-
apiKey: config.apiKey ?? "",
|
|
3460
|
-
baseURL: GITHUB_MODELS_INFERENCE_URL,
|
|
3461
|
-
defaultHeaders: { "X-GitHub-Api-Version": GITHUB_MODELS_API_VERSION, "User-Agent": "Cascade-AI" }
|
|
3462
|
-
});
|
|
3463
|
-
}
|
|
3464
|
-
/**
|
|
3465
|
-
* `OpenAIProvider.generateStream()` builds its request budget as
|
|
3466
|
-
* `options.maxTokens ?? this.model.maxOutputTokens` — an explicit per-call
|
|
3467
|
-
* `maxTokens` (T1Administrator's final compilation asks for 8,000, well
|
|
3468
|
-
* above GitHub's real ~4K hard cap) wins outright and is sent unclamped.
|
|
3469
|
-
* GitHub then rejects the request instead of answering, so a run routed to
|
|
3470
|
-
* this provider for that step fails outright rather than getting a
|
|
3471
|
-
* (perfectly serviceable, just shorter) completion. Clamp here, once, right
|
|
3472
|
-
* before delegating to the inherited implementation, rather than teaching
|
|
3473
|
-
* every caller GitHub's specific cap.
|
|
3474
|
-
*/
|
|
3475
|
-
async generateStream(options, onChunk) {
|
|
3476
|
-
const clamped = options.maxTokens !== void 0 && options.maxTokens > GITHUB_MODELS_MAX_OUTPUT_TOKENS ? { ...options, maxTokens: GITHUB_MODELS_MAX_OUTPUT_TOKENS } : options;
|
|
3477
|
-
return super.generateStream(clamped, onChunk);
|
|
3478
|
-
}
|
|
3479
|
-
/**
|
|
3480
|
-
* The catalog is a GitHub REST resource, not an OpenAI `/models` list, and
|
|
3481
|
-
* wants GitHub's own header trio — the plain `Accept: application/json` the
|
|
3482
|
-
* openai-compatible provider sends is not the same request.
|
|
3483
|
-
*/
|
|
3484
|
-
catalogHeaders() {
|
|
3485
|
-
return {
|
|
3486
|
-
Accept: "application/vnd.github+json",
|
|
3487
|
-
Authorization: `Bearer ${this.config.apiKey ?? ""}`,
|
|
3488
|
-
"X-GitHub-Api-Version": GITHUB_MODELS_API_VERSION,
|
|
3489
|
-
// GitHub's REST API rejects any request with no User-Agent — a plain 403
|
|
3490
|
-
// regardless of how valid the PAT is. nodeHttpFetch is a thin wrapper
|
|
3491
|
-
// over node:http/https, neither of which sets one the way a browser's
|
|
3492
|
-
// fetch or a full HTTP client library would, so this has to be explicit.
|
|
3493
|
-
"User-Agent": "Cascade-AI"
|
|
3494
|
-
};
|
|
3495
|
-
}
|
|
3496
|
-
async listModels() {
|
|
3497
|
-
const raw = [];
|
|
3498
|
-
let url = GITHUB_MODELS_CATALOG_URL;
|
|
3499
|
-
let pages = 0;
|
|
3500
|
-
const visited = /* @__PURE__ */ new Set();
|
|
3501
|
-
while (url && pages < GITHUB_MODELS_MAX_CATALOG_PAGES) {
|
|
3502
|
-
if (visited.has(url)) break;
|
|
3503
|
-
visited.add(url);
|
|
3504
|
-
const res = await nodeHttpFetch(url, { headers: this.catalogHeaders() }, 0, {
|
|
3505
|
-
allowedRedirectOrigin: GITHUB_MODELS_CATALOG_ORIGIN
|
|
3506
|
-
});
|
|
3507
|
-
if (!res.ok) throw new Error(`GitHub Models catalog ${url} returned HTTP ${res.status}`);
|
|
3508
|
-
const body = await res.json();
|
|
3509
|
-
const pageItems = Array.isArray(body) ? body : Array.isArray(body?.models) ? body.models : Array.isArray(body?.data) ? body.data : [];
|
|
3510
|
-
raw.push(...pageItems);
|
|
3511
|
-
const next = parseNextLink(res.headers.get("link"));
|
|
3512
|
-
if (next) {
|
|
3513
|
-
const resolved = new URL(next, url);
|
|
3514
|
-
if (resolved.origin !== GITHUB_MODELS_CATALOG_ORIGIN) {
|
|
3515
|
-
throw new Error(`Refusing cross-origin GitHub Models pagination URL: ${resolved.origin}`);
|
|
3516
|
-
}
|
|
3517
|
-
url = resolved.href;
|
|
3518
|
-
} else {
|
|
3519
|
-
url = null;
|
|
3520
|
-
}
|
|
3521
|
-
pages++;
|
|
3522
|
-
}
|
|
3523
|
-
const entries = raw.map((m) => {
|
|
3524
|
-
if (typeof m === "string") {
|
|
3525
|
-
return { id: m, name: m, contextWindow: DEFAULT_CONTEXT_WINDOW, vision: false, tools: false };
|
|
3526
|
-
}
|
|
3527
|
-
if (!m || typeof m !== "object") return void 0;
|
|
3528
|
-
const o = m;
|
|
3529
|
-
const id = pickString(o, ["id", "name", "model"]);
|
|
3530
|
-
if (!id) return void 0;
|
|
3531
|
-
return {
|
|
3532
|
-
id,
|
|
3533
|
-
name: pickString(o, ["friendly_name", "display_name", "name"]) ?? id,
|
|
3534
|
-
contextWindow: pickNumber(o, [
|
|
3535
|
-
"context_window",
|
|
3536
|
-
"context_length",
|
|
3537
|
-
"max_input_tokens",
|
|
3538
|
-
"limits.max_input_tokens",
|
|
3539
|
-
"limits.max_context_tokens"
|
|
3540
|
-
]) ?? DEFAULT_CONTEXT_WINDOW,
|
|
3541
|
-
vision: looksVisionCapable(o),
|
|
3542
|
-
tools: looksToolCapable(o)
|
|
3543
|
-
};
|
|
3544
|
-
}).filter((e) => e !== void 0);
|
|
3545
|
-
if (entries.length === 0) return [];
|
|
3546
|
-
const chat = entries.filter((e) => isChatModel(e.id));
|
|
3547
|
-
return (chat.length ? chat : entries).map((e) => ({
|
|
3548
|
-
id: e.id,
|
|
3549
|
-
// full `owner/model` — what the API is addressed by
|
|
3550
|
-
name: e.name,
|
|
3551
|
-
provider: "github-models",
|
|
3552
|
-
// GitHub's real per-request input cap, not the base model's larger one
|
|
3553
|
-
// (see GITHUB_MODELS_MAX_INPUT_TOKENS above) — the same reasoning
|
|
3554
|
-
// already applied to maxOutputTokens below, for the input side.
|
|
3555
|
-
contextWindow: Math.min(e.contextWindow, GITHUB_MODELS_MAX_INPUT_TOKENS),
|
|
3556
|
-
isVisionCapable: e.vision,
|
|
3557
|
-
// A REAL $0, not a missing number — usage is bundled into the quota of
|
|
3558
|
-
// the plan the PAT belongs to and is never billed per token, so
|
|
3559
|
-
// `pricingUnknown: false` is the honest value (see the flag's contract in
|
|
3560
|
-
// core/router/pricing.ts: 0 alone is ambiguous). Set here rather than in
|
|
3561
|
-
// pricing-data.json because resolvePricing() matches exactly on
|
|
3562
|
-
// provider|model|region with no wildcard: every catalog model would need
|
|
3563
|
-
// a hand-curated row, and the next one GitHub adds would read as "cost
|
|
3564
|
-
// unknown" — an inconsistent picture for a billing reality that is
|
|
3565
|
-
// identical across the entire catalog.
|
|
3566
|
-
//
|
|
3567
|
-
// `isLocal` is the OTHER way to reach a real $0, and it is the wrong one:
|
|
3568
|
-
// it means "runs on this machine" and carries consequences that are
|
|
3569
|
-
// simply false for a hosted API — the shared concurrency-1
|
|
3570
|
-
// LocalRequestQueue (serialising these calls behind actual Ollama GPU
|
|
3571
|
-
// inference), the 300s local rather than 120s cloud inference timeout,
|
|
3572
|
-
// and a literal "[local]" tag in `cascade models`.
|
|
3573
|
-
inputCostPer1kTokens: 0,
|
|
3574
|
-
outputCostPer1kTokens: 0,
|
|
3575
|
-
pricingUnknown: false,
|
|
3576
|
-
maxOutputTokens: GITHUB_MODELS_MAX_OUTPUT_TOKENS,
|
|
3577
|
-
supportsStreaming: true,
|
|
3578
|
-
// Not every catalog model supports function/tool calling — a hardcoded
|
|
3579
|
-
// `true` sent `tools` to models that reject the parameter outright and
|
|
3580
|
-
// never engaged t3-worker.ts's existing text-tool fallback (only an
|
|
3581
|
-
// explicit `false` does). See looksToolCapable's own comment for why
|
|
3582
|
-
// "not positively advertised" defaults to false rather than true here.
|
|
3583
|
-
supportsToolUse: e.tools,
|
|
3584
|
-
isLocal: false
|
|
3585
|
-
}));
|
|
3586
|
-
}
|
|
3587
|
-
async isAvailable() {
|
|
3588
|
-
try {
|
|
3589
|
-
const res = await nodeHttpFetch(GITHUB_MODELS_CATALOG_URL, { headers: this.catalogHeaders() }, 0, {
|
|
3590
|
-
allowedRedirectOrigin: GITHUB_MODELS_CATALOG_ORIGIN
|
|
3591
|
-
});
|
|
3592
|
-
return res.ok;
|
|
3593
|
-
} catch {
|
|
3594
|
-
return false;
|
|
3595
|
-
}
|
|
3596
|
-
}
|
|
3597
|
-
};
|
|
3598
|
-
|
|
3599
3395
|
// src/providers/ollama.ts
|
|
3600
3396
|
var TOOL_CAPABLE_FAMILIES = [
|
|
3601
3397
|
"llama3.1",
|
|
@@ -4104,7 +3900,7 @@ var ModelSelector = class {
|
|
|
4104
3900
|
if (overrideModelId.includes(":")) {
|
|
4105
3901
|
const parts = overrideModelId.split(":");
|
|
4106
3902
|
const prefix = parts[0].toLowerCase();
|
|
4107
|
-
const validProviders = ["anthropic", "openai", "gemini", "azure", "openai-compatible", "ollama"
|
|
3903
|
+
const validProviders = ["anthropic", "openai", "gemini", "azure", "openai-compatible", "ollama"];
|
|
4108
3904
|
if (validProviders.includes(prefix)) {
|
|
4109
3905
|
providerStr = prefix;
|
|
4110
3906
|
actualId = parts.slice(1).join(":");
|
|
@@ -4124,31 +3920,6 @@ var ModelSelector = class {
|
|
|
4124
3920
|
else if (this.availableProviders.has("openai-compatible")) providerStr = "openai-compatible";
|
|
4125
3921
|
else if (this.availableProviders.size === 1) providerStr = Array.from(this.availableProviders)[0];
|
|
4126
3922
|
}
|
|
4127
|
-
if (providerStr === "github-models" && this.availableProviders.has("github-models")) {
|
|
4128
|
-
const dynamicModel = {
|
|
4129
|
-
id: actualId,
|
|
4130
|
-
name: actualId,
|
|
4131
|
-
provider: "github-models",
|
|
4132
|
-
contextWindow: GITHUB_MODELS_MAX_INPUT_TOKENS,
|
|
4133
|
-
isVisionCapable: false,
|
|
4134
|
-
inputCostPer1kTokens: 0,
|
|
4135
|
-
outputCostPer1kTokens: 0,
|
|
4136
|
-
pricingUnknown: false,
|
|
4137
|
-
maxOutputTokens: GITHUB_MODELS_MAX_OUTPUT_TOKENS,
|
|
4138
|
-
supportsStreaming: true,
|
|
4139
|
-
// Unconfirmed (no live catalog entry to read a real capability from)
|
|
4140
|
-
// must default to false, not left undefined: t3-worker.ts's text-tool
|
|
4141
|
-
// fallback only engages on a strict `=== false`, so an undefined
|
|
4142
|
-
// value here sends native `tools` to an unverified multi-vendor
|
|
4143
|
-
// model and any that don't support function calling reject the
|
|
4144
|
-
// request outright. Same default-to-false-when-unconfirmed policy
|
|
4145
|
-
// listModels() already applies for a real catalog entry.
|
|
4146
|
-
supportsToolUse: false,
|
|
4147
|
-
isLocal: false
|
|
4148
|
-
};
|
|
4149
|
-
this.addDynamicModel(dynamicModel);
|
|
4150
|
-
return dynamicModel;
|
|
4151
|
-
}
|
|
4152
3923
|
if (providerStr && this.availableProviders.has(providerStr)) {
|
|
4153
3924
|
const dynamicModel = withResolvedPricing({
|
|
4154
3925
|
id: actualId,
|
|
@@ -4241,20 +4012,7 @@ var DEFAULT_PROVIDER_TPM = {
|
|
|
4241
4012
|
gemini: 6e4,
|
|
4242
4013
|
azure: 3e4,
|
|
4243
4014
|
"openai-compatible": 3e4,
|
|
4244
|
-
ollama: Number.POSITIVE_INFINITY
|
|
4245
|
-
// An order of magnitude below the others, on purpose. GitHub Models is
|
|
4246
|
-
// rate-limited by REQUESTS (Free ≈10 RPM, Pro ≈20, plus low daily caps), and
|
|
4247
|
-
// this bucket meters TOKENS — there is no request-count limiter in this
|
|
4248
|
-
// codebase, and building one is not in scope. The approximation works because
|
|
4249
|
-
// the router reserves `model.maxOutputTokens + 512` per call, and the
|
|
4250
|
-
// provider reports GitHub's real ~4K per-request cap rather than the
|
|
4251
|
-
// underlying model's larger one — so every call withdraws a near-constant
|
|
4252
|
-
// ~4.5K regardless of how short the reply actually turns out to be. At 8K/min
|
|
4253
|
-
// that lands around 1–2 calls per minute of headroom: comfortably inside even
|
|
4254
|
-
// the Free tier, with room for the daily cap to matter more than this does.
|
|
4255
|
-
// Users on a higher tier can raise it with no code change through the generic
|
|
4256
|
-
// `config.rateLimits.providerTpm['github-models']` override.
|
|
4257
|
-
"github-models": 8e3
|
|
4015
|
+
ollama: Number.POSITIVE_INFINITY
|
|
4258
4016
|
};
|
|
4259
4017
|
var TpmLimiter = class {
|
|
4260
4018
|
buckets = /* @__PURE__ */ new Map();
|
|
@@ -4610,7 +4368,6 @@ var ModelProfiler = class {
|
|
|
4610
4368
|
(m) => !alreadyProfiled.has(m.id) && !SKIP_PATTERN.test(m.id) && !SKIP_PATTERN.test(m.name)
|
|
4611
4369
|
);
|
|
4612
4370
|
if (toProfile.length === 0) return;
|
|
4613
|
-
const probesHitGithubModels = this.router?.getModelForTier("T3")?.provider === "github-models";
|
|
4614
4371
|
const openRouterModels = await fetchOpenRouterModels();
|
|
4615
4372
|
const orByNormalizedId = /* @__PURE__ */ new Map();
|
|
4616
4373
|
for (const m of openRouterModels) {
|
|
@@ -4625,7 +4382,7 @@ var ModelProfiler = class {
|
|
|
4625
4382
|
if (orMatch?.description) {
|
|
4626
4383
|
specializations = extractSpecializations(orMatch.description);
|
|
4627
4384
|
}
|
|
4628
|
-
if (specializations.length === 0 && this.router
|
|
4385
|
+
if (specializations.length === 0 && this.router) {
|
|
4629
4386
|
specializations = await queryModelDirectly(this.router);
|
|
4630
4387
|
}
|
|
4631
4388
|
this.store.saveModelProfile(model.id, model.provider, specializations);
|
|
@@ -4845,7 +4602,6 @@ var LiveDataProvider = class {
|
|
|
4845
4602
|
*/
|
|
4846
4603
|
applyLivePricing(models) {
|
|
4847
4604
|
return models.map((m) => {
|
|
4848
|
-
if (m.provider === "github-models") return m;
|
|
4849
4605
|
const p = this.getLivePrice(m.baseModelId ?? m.id);
|
|
4850
4606
|
if (!p) return m;
|
|
4851
4607
|
const reconciled = reconcilePrice(m, p);
|
|
@@ -4884,7 +4640,6 @@ var LiveDataProvider = class {
|
|
|
4884
4640
|
*/
|
|
4885
4641
|
applyLiveCapabilities(models) {
|
|
4886
4642
|
return models.map((m) => {
|
|
4887
|
-
if (m.provider === "github-models") return m;
|
|
4888
4643
|
const c = this.getCapability(m.baseModelId ?? m.id);
|
|
4889
4644
|
if (!c) return m;
|
|
4890
4645
|
const next = { ...m };
|
|
@@ -5188,16 +4943,13 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter__default.default {
|
|
|
5188
4943
|
for (const model of azureDeployments) this.selector.addDynamicModel(model);
|
|
5189
4944
|
availableProviders.add("azure");
|
|
5190
4945
|
}
|
|
5191
|
-
if (availableProviders.has("github-models")) {
|
|
5192
|
-
await this.discoverGitHubModelsCatalog(config);
|
|
5193
|
-
}
|
|
5194
4946
|
await this.validateCloudProviderModels(config);
|
|
5195
4947
|
for (const tier of ["T1", "T2", "T3"]) {
|
|
5196
4948
|
const override = tier === "T1" ? config.models.t1 : tier === "T2" ? config.models.t2 : config.models.t3;
|
|
5197
4949
|
if (!override || override === "auto") continue;
|
|
5198
4950
|
const model = this.selector.selectForTier(tier, override);
|
|
5199
4951
|
if (!model) {
|
|
5200
|
-
const knownProviders = ["anthropic", "openai", "gemini", "azure", "openai-compatible", "ollama"
|
|
4952
|
+
const knownProviders = ["anthropic", "openai", "gemini", "azure", "openai-compatible", "ollama"];
|
|
5201
4953
|
const hasProviderPrefix = override.includes(":") && knownProviders.some((p) => override.startsWith(p + ":"));
|
|
5202
4954
|
if (hasProviderPrefix) {
|
|
5203
4955
|
const provider = override.split(":")[0];
|
|
@@ -5352,7 +5104,7 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter__default.default {
|
|
|
5352
5104
|
* models without a package upgrade. Mirrors discoverOllamaModels.
|
|
5353
5105
|
*/
|
|
5354
5106
|
async discoverProviderModels() {
|
|
5355
|
-
const cloud = ["anthropic", "openai", "gemini", "azure", "openai-compatible"
|
|
5107
|
+
const cloud = ["anthropic", "openai", "gemini", "azure", "openai-compatible"];
|
|
5356
5108
|
const tasks = cloud.map(async (type) => {
|
|
5357
5109
|
if (!this.selector.isProviderAvailable(type)) return;
|
|
5358
5110
|
const seed = this.getAnyModelForProvider(type);
|
|
@@ -5867,26 +5619,6 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter__default.default {
|
|
|
5867
5619
|
return false;
|
|
5868
5620
|
}
|
|
5869
5621
|
}
|
|
5870
|
-
/**
|
|
5871
|
-
* Registers GitHub Models' real catalog ids via addDynamicModel so init()'s
|
|
5872
|
-
* tier-fill loop can pick one. Registration alone does not create a bound
|
|
5873
|
-
* BaseProvider instance (see ensureProvider) — the tier-fill loop's own
|
|
5874
|
-
* ensureProvider call does that for whichever model it actually selects,
|
|
5875
|
-
* which is why this runs BEFORE that loop rather than only in the
|
|
5876
|
-
* background discoverProviderModels() refresh.
|
|
5877
|
-
*/
|
|
5878
|
-
async discoverGitHubModelsCatalog(config) {
|
|
5879
|
-
const cfg = config.providers.find((p) => p.type === "github-models") ?? { type: "github-models" };
|
|
5880
|
-
const seed = this.getAnyModelForProvider("github-models");
|
|
5881
|
-
if (!seed) return;
|
|
5882
|
-
try {
|
|
5883
|
-
const provider = new GitHubModelsProvider(cfg, seed);
|
|
5884
|
-
const models = await provider.listModels();
|
|
5885
|
-
for (const m of models) this.selector.addDynamicModel(m);
|
|
5886
|
-
} catch (err) {
|
|
5887
|
-
console.warn("[router] GitHub Models catalog discovery failed:", err instanceof Error ? err.message : err);
|
|
5888
|
-
}
|
|
5889
|
-
}
|
|
5890
5622
|
ensureProvider(model, configs) {
|
|
5891
5623
|
const key = `${model.provider}:${model.id}`;
|
|
5892
5624
|
if (this.providers.has(key)) return;
|
|
@@ -5911,8 +5643,6 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter__default.default {
|
|
|
5911
5643
|
return new OllamaProvider(cfg, model);
|
|
5912
5644
|
case "openai-compatible":
|
|
5913
5645
|
return new OpenAICompatibleProvider(cfg, model);
|
|
5914
|
-
case "github-models":
|
|
5915
|
-
return new GitHubModelsProvider(cfg, model);
|
|
5916
5646
|
default:
|
|
5917
5647
|
throw new Error(`Unsupported provider type: ${String(cfg.type)}`);
|
|
5918
5648
|
}
|
|
@@ -5920,7 +5650,7 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter__default.default {
|
|
|
5920
5650
|
getAnyModelForProvider(type) {
|
|
5921
5651
|
const fromCatalog = Object.values(MODELS).find((m) => m.provider === type);
|
|
5922
5652
|
if (fromCatalog) return fromCatalog;
|
|
5923
|
-
if (type === "openai-compatible" || type === "azure"
|
|
5653
|
+
if (type === "openai-compatible" || type === "azure") {
|
|
5924
5654
|
return {
|
|
5925
5655
|
id: type,
|
|
5926
5656
|
name: type,
|
|
@@ -14053,7 +13783,7 @@ var PermissionEscalator = class extends EventEmitter__default.default {
|
|
|
14053
13783
|
}
|
|
14054
13784
|
};
|
|
14055
13785
|
var ProviderConfigSchema = zod.z.object({
|
|
14056
|
-
type: zod.z.enum(["anthropic", "openai", "gemini", "azure", "openai-compatible", "ollama"
|
|
13786
|
+
type: zod.z.enum(["anthropic", "openai", "gemini", "azure", "openai-compatible", "ollama"]),
|
|
14057
13787
|
label: zod.z.string().optional(),
|
|
14058
13788
|
apiKey: zod.z.string().optional(),
|
|
14059
13789
|
baseUrl: zod.z.string().url().optional(),
|
|
@@ -19329,6 +19059,19 @@ Original error: ${err.message}`
|
|
|
19329
19059
|
const rows = provider ? this.db.prepare("SELECT metadata FROM model_cache WHERE provider = ?").all(provider) : this.db.prepare("SELECT metadata FROM model_cache").all();
|
|
19330
19060
|
return rows.map((r) => JSON.parse(r.metadata));
|
|
19331
19061
|
}
|
|
19062
|
+
/**
|
|
19063
|
+
* Deletes cached rows for a provider type that no longer exists in
|
|
19064
|
+
* `ProviderType`, so it takes a raw string rather than the union.
|
|
19065
|
+
*
|
|
19066
|
+
* Needed because the cache outlives the code: the REPL's loadCache() only
|
|
19067
|
+
* re-discovers when the cache is EMPTY or older than 24h, so rows left
|
|
19068
|
+
* behind by a removed provider read as "cache populated" and suppress
|
|
19069
|
+
* discovery for the providers that replaced it — `/model` then shows a
|
|
19070
|
+
* configured provider with zero models until the rows age out.
|
|
19071
|
+
*/
|
|
19072
|
+
purgeCachedModelsForRetiredProvider(providerType) {
|
|
19073
|
+
return this.db.prepare("DELETE FROM model_cache WHERE provider = ?").run(providerType).changes;
|
|
19074
|
+
}
|
|
19332
19075
|
clearModelCache(provider) {
|
|
19333
19076
|
if (provider) {
|
|
19334
19077
|
this.db.prepare("DELETE FROM model_cache WHERE provider = ?").run(provider);
|
|
@@ -19676,7 +19419,73 @@ function mergeGlobalCredentials(workspaceProviders, globalProviders) {
|
|
|
19676
19419
|
return merged;
|
|
19677
19420
|
}
|
|
19678
19421
|
|
|
19422
|
+
// src/config/retired-providers.ts
|
|
19423
|
+
var RETIRED_PROVIDER_TYPES = {
|
|
19424
|
+
"github-models": "GitHub retired GitHub Models on 30 July 2026; models.github.ai no longer responds."
|
|
19425
|
+
};
|
|
19426
|
+
function isRetiredProviderType(type) {
|
|
19427
|
+
return typeof type === "string" && Object.hasOwn(RETIRED_PROVIDER_TYPES, type);
|
|
19428
|
+
}
|
|
19429
|
+
function didCleanupChangeAnything(c) {
|
|
19430
|
+
return c.removed.length > 0 || c.clearedPins.length > 0;
|
|
19431
|
+
}
|
|
19432
|
+
function stripRetiredProviders(raw) {
|
|
19433
|
+
const cleanup = { removed: [], clearedPins: [] };
|
|
19434
|
+
if (typeof raw !== "object" || raw === null) return cleanup;
|
|
19435
|
+
const cfg = raw;
|
|
19436
|
+
if (Array.isArray(cfg["providers"])) {
|
|
19437
|
+
const kept = cfg["providers"].filter((p) => {
|
|
19438
|
+
const type = p?.type;
|
|
19439
|
+
if (!isRetiredProviderType(type)) return true;
|
|
19440
|
+
if (!cleanup.removed.includes(type)) cleanup.removed.push(type);
|
|
19441
|
+
return false;
|
|
19442
|
+
});
|
|
19443
|
+
cfg["providers"] = kept;
|
|
19444
|
+
}
|
|
19445
|
+
cleanup.clearedPins = clearRetiredPins(cfg["models"]);
|
|
19446
|
+
return cleanup;
|
|
19447
|
+
}
|
|
19448
|
+
function clearRetiredPins(models) {
|
|
19449
|
+
const cleared = [];
|
|
19450
|
+
if (typeof models !== "object" || models === null) return cleared;
|
|
19451
|
+
const tiers = models;
|
|
19452
|
+
for (const tier of ["t1", "t2", "t3"]) {
|
|
19453
|
+
const pin = tiers[tier];
|
|
19454
|
+
if (typeof pin !== "string") continue;
|
|
19455
|
+
const prefix = pin.slice(0, pin.indexOf(":")).toLowerCase();
|
|
19456
|
+
if (pin.includes(":") && isRetiredProviderType(prefix)) {
|
|
19457
|
+
delete tiers[tier];
|
|
19458
|
+
cleared.push(tier);
|
|
19459
|
+
}
|
|
19460
|
+
}
|
|
19461
|
+
return cleared;
|
|
19462
|
+
}
|
|
19463
|
+
function filterRetiredCredentials(providers) {
|
|
19464
|
+
const removed = [];
|
|
19465
|
+
const kept = providers.filter((p) => {
|
|
19466
|
+
if (!isRetiredProviderType(p.type)) return true;
|
|
19467
|
+
if (!removed.includes(p.type)) removed.push(p.type);
|
|
19468
|
+
return false;
|
|
19469
|
+
});
|
|
19470
|
+
return { kept, removed };
|
|
19471
|
+
}
|
|
19472
|
+
function describeCleanup(c) {
|
|
19473
|
+
const parts = [];
|
|
19474
|
+
for (const type of c.removed) {
|
|
19475
|
+
parts.push(`removed the retired "${type}" provider (${RETIRED_PROVIDER_TYPES[type]})`);
|
|
19476
|
+
}
|
|
19477
|
+
if (c.clearedPins.length > 0) {
|
|
19478
|
+
parts.push(`reset ${c.clearedPins.map((t) => t.toUpperCase()).join("/")} to Auto, since the pin named it`);
|
|
19479
|
+
}
|
|
19480
|
+
return `Cascade config migration: ${parts.join("; ")}.`;
|
|
19481
|
+
}
|
|
19482
|
+
|
|
19679
19483
|
// src/config/index.ts
|
|
19484
|
+
var KEY_OPTIONAL_PROVIDER_TYPES = /* @__PURE__ */ new Set(["ollama", "openai-compatible"]);
|
|
19485
|
+
function hasUsableProvider(providers) {
|
|
19486
|
+
if (!providers?.length) return false;
|
|
19487
|
+
return providers.some((p) => KEY_OPTIONAL_PROVIDER_TYPES.has(p.type) || !!p.apiKey || !!p.authToken);
|
|
19488
|
+
}
|
|
19680
19489
|
var ConfigManager = class {
|
|
19681
19490
|
config;
|
|
19682
19491
|
keystore;
|
|
@@ -19685,6 +19494,29 @@ var ConfigManager = class {
|
|
|
19685
19494
|
cascadeMd = null;
|
|
19686
19495
|
workspacePath;
|
|
19687
19496
|
globalDir;
|
|
19497
|
+
/**
|
|
19498
|
+
* What THIS load migrated out, if anything. Strictly per-load state: reset
|
|
19499
|
+
* at the top of every `load()`, because two places read it as a statement
|
|
19500
|
+
* about the load in progress — `injectEnvKeys()` asks whether an empty
|
|
19501
|
+
* provider list was emptied by a retirement, and the end of `load()` warns
|
|
19502
|
+
* and builds the user-facing notice from it. Left set, a second `load()` on
|
|
19503
|
+
* the same instance (`startRepl()` does one after its setup wizard) would
|
|
19504
|
+
* re-announce a migration that already happened and suppress the Ollama
|
|
19505
|
+
* fallback for a list that is empty for some unrelated reason.
|
|
19506
|
+
*/
|
|
19507
|
+
retiredCleanup;
|
|
19508
|
+
/**
|
|
19509
|
+
* Human-readable migration notice held for a UI to display. `console.warn`
|
|
19510
|
+
* is not sufficient: the REPL clears the TTY immediately after load(), and
|
|
19511
|
+
* the desktop emits from the main process where nothing is rendered.
|
|
19512
|
+
*/
|
|
19513
|
+
pendingRetiredNotice;
|
|
19514
|
+
/** Returns the pending migration notice once, then forgets it. */
|
|
19515
|
+
takeRetiredNotice() {
|
|
19516
|
+
const n = this.pendingRetiredNotice;
|
|
19517
|
+
this.pendingRetiredNotice = void 0;
|
|
19518
|
+
return n;
|
|
19519
|
+
}
|
|
19688
19520
|
/** `globalDirOverride` exists for tests — never point it at the real home dir there. */
|
|
19689
19521
|
constructor(workspacePath = process.cwd(), globalDirOverride) {
|
|
19690
19522
|
this.workspacePath = workspacePath;
|
|
@@ -19704,10 +19536,32 @@ var ConfigManager = class {
|
|
|
19704
19536
|
this.cascadeMd = await loadCascadeMd(this.workspacePath);
|
|
19705
19537
|
this.keystore = new Keystore(path26__default.default.join(this.globalDir, GLOBAL_KEYSTORE_FILE));
|
|
19706
19538
|
this.store = new MemoryStore(path26__default.default.join(this.workspacePath, CASCADE_DB_FILE));
|
|
19539
|
+
const globalCreds = filterRetiredCredentials(loadGlobalCredentials(this.globalDir));
|
|
19540
|
+
if (globalCreds.removed.length > 0) {
|
|
19541
|
+
try {
|
|
19542
|
+
saveGlobalCredentials(this.globalDir, globalCreds.kept);
|
|
19543
|
+
} catch (err) {
|
|
19544
|
+
console.warn(`Could not rewrite the global credential store: ${err instanceof Error ? err.message : String(err)}`);
|
|
19545
|
+
}
|
|
19546
|
+
this.retiredCleanup = {
|
|
19547
|
+
removed: [.../* @__PURE__ */ new Set([...this.retiredCleanup?.removed ?? [], ...globalCreds.removed])],
|
|
19548
|
+
clearedPins: this.retiredCleanup?.clearedPins ?? []
|
|
19549
|
+
};
|
|
19550
|
+
}
|
|
19707
19551
|
await this.injectEnvKeys();
|
|
19708
|
-
this.config.providers = mergeGlobalCredentials(this.config.providers,
|
|
19552
|
+
this.config.providers = mergeGlobalCredentials(this.config.providers, globalCreds.kept);
|
|
19553
|
+
for (const type of Object.keys(RETIRED_PROVIDER_TYPES)) {
|
|
19554
|
+
try {
|
|
19555
|
+
this.store.purgeCachedModelsForRetiredProvider(type);
|
|
19556
|
+
} catch {
|
|
19557
|
+
}
|
|
19558
|
+
}
|
|
19709
19559
|
await this.ensureDefaultIdentity();
|
|
19710
19560
|
if (mcpNamesChanged) await this.save();
|
|
19561
|
+
if (this.retiredCleanup) {
|
|
19562
|
+
console.warn(describeCleanup(this.retiredCleanup));
|
|
19563
|
+
this.pendingRetiredNotice = describeCleanup(this.retiredCleanup);
|
|
19564
|
+
}
|
|
19711
19565
|
}
|
|
19712
19566
|
getConfig() {
|
|
19713
19567
|
return this.config;
|
|
@@ -19746,14 +19600,13 @@ var ConfigManager = class {
|
|
|
19746
19600
|
anthropic: "ANTHROPIC_API_KEY",
|
|
19747
19601
|
openai: "OPENAI_API_KEY",
|
|
19748
19602
|
gemini: "GOOGLE_API_KEY",
|
|
19749
|
-
azure: "AZURE_OPENAI_KEY"
|
|
19603
|
+
azure: "AZURE_OPENAI_KEY"
|
|
19750
19604
|
// Deliberately NOT `GITHUB_TOKEN`: that one is injected automatically by
|
|
19751
19605
|
// GitHub Actions and is commonly exported in developer shells for `gh`
|
|
19752
19606
|
// and git auth, and it will essentially never carry the fine-grained
|
|
19753
19607
|
// `models: read` scope this provider needs. Adopting it would wire up a
|
|
19754
19608
|
// provider that 403s on its first real call — a confusing failure for
|
|
19755
19609
|
// something that is meant to be explicit opt-in BYOK.
|
|
19756
|
-
"github-models": "GITHUB_MODELS_TOKEN"
|
|
19757
19610
|
};
|
|
19758
19611
|
const envKey = envMap[provider];
|
|
19759
19612
|
if (envKey && process.env[envKey]) return process.env[envKey];
|
|
@@ -19765,10 +19618,21 @@ var ConfigManager = class {
|
|
|
19765
19618
|
return configProvider?.apiKey;
|
|
19766
19619
|
}
|
|
19767
19620
|
async loadConfig() {
|
|
19621
|
+
this.retiredCleanup = void 0;
|
|
19768
19622
|
const configPath = path26__default.default.join(this.workspacePath, CASCADE_CONFIG_FILE);
|
|
19769
19623
|
try {
|
|
19770
19624
|
const raw = await fs5__default.default.readFile(configPath, "utf-8");
|
|
19771
|
-
|
|
19625
|
+
const parsed = JSON.parse(raw);
|
|
19626
|
+
const cleanup = stripRetiredProviders(parsed);
|
|
19627
|
+
if (didCleanupChangeAnything(cleanup)) {
|
|
19628
|
+
this.retiredCleanup = cleanup;
|
|
19629
|
+
try {
|
|
19630
|
+
await fs5__default.default.writeFile(configPath, JSON.stringify(parsed, null, 2), "utf-8");
|
|
19631
|
+
} catch (err) {
|
|
19632
|
+
console.warn(`Could not persist the retired-provider migration: ${err instanceof Error ? err.message : String(err)}`);
|
|
19633
|
+
}
|
|
19634
|
+
}
|
|
19635
|
+
return validateConfig(parsed);
|
|
19772
19636
|
} catch (err) {
|
|
19773
19637
|
if (err.code === "ENOENT") {
|
|
19774
19638
|
return validateConfig({});
|
|
@@ -19777,27 +19641,25 @@ var ConfigManager = class {
|
|
|
19777
19641
|
}
|
|
19778
19642
|
}
|
|
19779
19643
|
async injectEnvKeys() {
|
|
19780
|
-
const
|
|
19644
|
+
const wasEmpty = this.config.providers.length === 0;
|
|
19645
|
+
const emptiedByRetirement = wasEmpty && !!this.retiredCleanup;
|
|
19781
19646
|
const envProviders = [
|
|
19782
19647
|
{ env: "ANTHROPIC_API_KEY", type: "anthropic" },
|
|
19783
19648
|
{ env: "OPENAI_API_KEY", type: "openai" },
|
|
19784
19649
|
{ env: "GOOGLE_API_KEY", type: "gemini" },
|
|
19785
|
-
{ env: "AZURE_OPENAI_KEY", type: "azure" }
|
|
19786
|
-
// See getApiKey() above for why this is GITHUB_MODELS_TOKEN and never
|
|
19787
|
-
// the ambient GITHUB_TOKEN.
|
|
19788
|
-
{ env: "GITHUB_MODELS_TOKEN", type: "github-models" }
|
|
19650
|
+
{ env: "AZURE_OPENAI_KEY", type: "azure" }
|
|
19789
19651
|
];
|
|
19790
19652
|
for (const { env, type } of envProviders) {
|
|
19791
19653
|
const key = process.env[env];
|
|
19792
19654
|
if (!key) continue;
|
|
19793
19655
|
const existing = this.config.providers.find((p) => p.type === type);
|
|
19794
|
-
if (!existing &&
|
|
19656
|
+
if (!existing && wasEmpty) {
|
|
19795
19657
|
this.config.providers.push({ type, apiKey: key });
|
|
19796
19658
|
} else if (existing && !existing.apiKey) {
|
|
19797
19659
|
existing.apiKey = key;
|
|
19798
19660
|
}
|
|
19799
19661
|
}
|
|
19800
|
-
if (
|
|
19662
|
+
if (wasEmpty && !emptiedByRetirement && !this.config.providers.find((p) => p.type === "ollama")) {
|
|
19801
19663
|
this.config.providers.push({ type: "ollama" });
|
|
19802
19664
|
}
|
|
19803
19665
|
}
|
|
@@ -22434,7 +22296,9 @@ function gatherSyncBundle(config) {
|
|
|
22434
22296
|
if (Object.keys(prefs).length) bundle.prefs = prefs;
|
|
22435
22297
|
return bundle;
|
|
22436
22298
|
}
|
|
22437
|
-
function applySyncBundle(bundle, config) {
|
|
22299
|
+
function applySyncBundle(bundle, config, cleanup) {
|
|
22300
|
+
const dropped = stripRetiredProviders(bundle);
|
|
22301
|
+
if (cleanup) cleanup.removed = dropped.removed;
|
|
22438
22302
|
const next = { ...config, tools: { ...config.tools } };
|
|
22439
22303
|
if (bundle.providers) next.providers = mergeByKey(config.providers, bundle.providers, providerSig) ?? [];
|
|
22440
22304
|
if (bundle.webSearch) next.tools.webSearch = { ...config.tools?.webSearch, ...bundle.webSearch };
|
|
@@ -22450,6 +22314,14 @@ function applySyncBundle(bundle, config) {
|
|
|
22450
22314
|
}
|
|
22451
22315
|
if (bundle.models) next.models = { ...config.models, ...bundle.models };
|
|
22452
22316
|
if (bundle.budget) next.budget = { ...config.budget, ...bundle.budget };
|
|
22317
|
+
const merged = { ...next.models };
|
|
22318
|
+
const clearedFromMerge = clearRetiredPins(merged);
|
|
22319
|
+
if (clearedFromMerge.length > 0) next.models = merged;
|
|
22320
|
+
if (cleanup) {
|
|
22321
|
+
cleanup.clearedPins = ["t1", "t2", "t3"].filter(
|
|
22322
|
+
(t) => (dropped.clearedPins.includes(t) || clearedFromMerge.includes(t)) && next.models?.[t] === void 0
|
|
22323
|
+
);
|
|
22324
|
+
}
|
|
22453
22325
|
if (bundle.prefs) {
|
|
22454
22326
|
const p = bundle.prefs;
|
|
22455
22327
|
if (p.autoBias !== void 0) next.autoBias = p.autoBias;
|
|
@@ -22496,9 +22368,6 @@ exports.DOCUMENT_MIME = DOCUMENT_MIME;
|
|
|
22496
22368
|
exports.DashboardServer = DashboardServer;
|
|
22497
22369
|
exports.DependencyScheduler = DependencyScheduler;
|
|
22498
22370
|
exports.FileMcpOAuthStore = FileMcpOAuthStore;
|
|
22499
|
-
exports.GITHUB_MODELS_API_VERSION = GITHUB_MODELS_API_VERSION;
|
|
22500
|
-
exports.GITHUB_MODELS_CATALOG_URL = GITHUB_MODELS_CATALOG_URL;
|
|
22501
|
-
exports.GITHUB_MODELS_INFERENCE_URL = GITHUB_MODELS_INFERENCE_URL;
|
|
22502
22371
|
exports.GLOBAL_CONFIG_DIR = GLOBAL_CONFIG_DIR;
|
|
22503
22372
|
exports.GLOBAL_CREDENTIALS_FILE = GLOBAL_CREDENTIALS_FILE;
|
|
22504
22373
|
exports.GLOBAL_DB_FILE = GLOBAL_DB_FILE;
|
|
@@ -22571,6 +22440,7 @@ exports.failures = failures;
|
|
|
22571
22440
|
exports.fileExt = fileExt;
|
|
22572
22441
|
exports.fileOAuthProvider = fileOAuthProvider;
|
|
22573
22442
|
exports.gatherSyncBundle = gatherSyncBundle;
|
|
22443
|
+
exports.hasUsableProvider = hasUsableProvider;
|
|
22574
22444
|
exports.hashContent = hashContent;
|
|
22575
22445
|
exports.heuristicCodeChunker = heuristicCodeChunker;
|
|
22576
22446
|
exports.inferAzureBaseModel = inferAzureBaseModel;
|