cascade-ai 0.73.0 → 0.74.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 +106 -14
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +106 -14
- package/dist/cli.js.map +1 -1
- package/dist/desktop-core.cjs +104 -13
- package/dist/index.cjs +104 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +15 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +104 -13
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -2063,6 +2063,21 @@ declare class CascadeRouter extends EventEmitter {
|
|
|
2063
2063
|
* Used to populate the desktop model pickers with the user's real models.
|
|
2064
2064
|
*/
|
|
2065
2065
|
getAvailableModels(): ModelInfo[];
|
|
2066
|
+
/**
|
|
2067
|
+
* Why each configured provider failed its availability probe, kept so the
|
|
2068
|
+
* surfaces can say something better than "add an API key" to a user who
|
|
2069
|
+
* already has one. Cleared and refilled by each detectAvailableProviders().
|
|
2070
|
+
*/
|
|
2071
|
+
private probeFailures;
|
|
2072
|
+
/**
|
|
2073
|
+
* What stopped the configured providers from being usable, if anything.
|
|
2074
|
+
* Empty when every configured provider passed — or when none was configured,
|
|
2075
|
+
* which is a different problem with a different answer.
|
|
2076
|
+
*/
|
|
2077
|
+
providerProbeFailures(): Array<{
|
|
2078
|
+
provider: ProviderType;
|
|
2079
|
+
reason: string;
|
|
2080
|
+
}>;
|
|
2066
2081
|
/** Logs why a configured provider failed its availability probe. */
|
|
2067
2082
|
private emitProbeFailure;
|
|
2068
2083
|
private detectAvailableProviders;
|
package/dist/index.d.ts
CHANGED
|
@@ -2063,6 +2063,21 @@ declare class CascadeRouter extends EventEmitter {
|
|
|
2063
2063
|
* Used to populate the desktop model pickers with the user's real models.
|
|
2064
2064
|
*/
|
|
2065
2065
|
getAvailableModels(): ModelInfo[];
|
|
2066
|
+
/**
|
|
2067
|
+
* Why each configured provider failed its availability probe, kept so the
|
|
2068
|
+
* surfaces can say something better than "add an API key" to a user who
|
|
2069
|
+
* already has one. Cleared and refilled by each detectAvailableProviders().
|
|
2070
|
+
*/
|
|
2071
|
+
private probeFailures;
|
|
2072
|
+
/**
|
|
2073
|
+
* What stopped the configured providers from being usable, if anything.
|
|
2074
|
+
* Empty when every configured provider passed — or when none was configured,
|
|
2075
|
+
* which is a different problem with a different answer.
|
|
2076
|
+
*/
|
|
2077
|
+
providerProbeFailures(): Array<{
|
|
2078
|
+
provider: ProviderType;
|
|
2079
|
+
reason: string;
|
|
2080
|
+
}>;
|
|
2066
2081
|
/** Logs why a configured provider failed its availability probe. */
|
|
2067
2082
|
private emitProbeFailure;
|
|
2068
2083
|
private detectAvailableProviders;
|
package/dist/index.js
CHANGED
|
@@ -1871,7 +1871,7 @@ function reconcilePrice(model, live) {
|
|
|
1871
1871
|
}
|
|
1872
1872
|
|
|
1873
1873
|
// src/constants.ts
|
|
1874
|
-
var CASCADE_VERSION = "0.
|
|
1874
|
+
var CASCADE_VERSION = "0.74.0";
|
|
1875
1875
|
var CASCADE_CONFIG_DIR = ".cascade";
|
|
1876
1876
|
var CASCADE_MD_FILE = "CASCADE.md";
|
|
1877
1877
|
var CASCADE_IGNORE_FILE = ".cascadeignore";
|
|
@@ -2377,6 +2377,13 @@ function buildTokenUsage(inputTokens, outputTokens, model) {
|
|
|
2377
2377
|
}
|
|
2378
2378
|
|
|
2379
2379
|
// src/providers/base.ts
|
|
2380
|
+
var ProviderUnreachableError = class extends Error {
|
|
2381
|
+
transient = true;
|
|
2382
|
+
constructor(message) {
|
|
2383
|
+
super(message);
|
|
2384
|
+
this.name = "ProviderUnreachableError";
|
|
2385
|
+
}
|
|
2386
|
+
};
|
|
2380
2387
|
var BaseProvider = class {
|
|
2381
2388
|
config;
|
|
2382
2389
|
model;
|
|
@@ -3097,6 +3104,18 @@ function toGeminiFunctionCalls(toolCalls) {
|
|
|
3097
3104
|
}
|
|
3098
3105
|
}));
|
|
3099
3106
|
}
|
|
3107
|
+
function isGeminiAuthStatus(status) {
|
|
3108
|
+
return status === 400 || status === 401 || status === 403;
|
|
3109
|
+
}
|
|
3110
|
+
async function describeGeminiError(resp) {
|
|
3111
|
+
try {
|
|
3112
|
+
const body = await resp.json();
|
|
3113
|
+
const detail = body?.error?.message ?? body?.error?.status;
|
|
3114
|
+
return detail ? ` \u2014 ${detail}` : "";
|
|
3115
|
+
} catch {
|
|
3116
|
+
return "";
|
|
3117
|
+
}
|
|
3118
|
+
}
|
|
3100
3119
|
var GeminiProvider = class extends BaseProvider {
|
|
3101
3120
|
client;
|
|
3102
3121
|
constructor(config, model) {
|
|
@@ -3180,9 +3199,7 @@ var GeminiProvider = class extends BaseProvider {
|
|
|
3180
3199
|
}
|
|
3181
3200
|
async listModels() {
|
|
3182
3201
|
try {
|
|
3183
|
-
const resp = await
|
|
3184
|
-
`https://generativelanguage.googleapis.com/v1beta/models?key=${this.config.apiKey}`
|
|
3185
|
-
);
|
|
3202
|
+
const resp = await this.fetchModelList();
|
|
3186
3203
|
if (!resp.ok) {
|
|
3187
3204
|
return Object.values(MODELS).filter((m) => m.provider === "gemini");
|
|
3188
3205
|
}
|
|
@@ -3213,18 +3230,61 @@ var GeminiProvider = class extends BaseProvider {
|
|
|
3213
3230
|
return Object.values(MODELS).filter((m) => m.provider === "gemini");
|
|
3214
3231
|
}
|
|
3215
3232
|
}
|
|
3233
|
+
/**
|
|
3234
|
+
* Is this KEY usable — not "does one particular model answer".
|
|
3235
|
+
*
|
|
3236
|
+
* This used to call countTokens() against `this.model.id`, which the router
|
|
3237
|
+
* fills from the first Gemini entry in the bundled catalogue. That made the
|
|
3238
|
+
* whole provider's fate depend on one hard-coded model id: a key that cannot
|
|
3239
|
+
* reach that one model — retired, not enabled for the key's project, served
|
|
3240
|
+
* on a different API version — failed the probe, and because every later step
|
|
3241
|
+
* is gated on the result (`validateCloudProviderModels` and
|
|
3242
|
+
* `discoverProviderModels` both return early for an unavailable provider) the
|
|
3243
|
+
* real model list was never fetched. Gemini vanished entirely, and the CLI
|
|
3244
|
+
* reported "add a provider API key first" about a key that was present and
|
|
3245
|
+
* working.
|
|
3246
|
+
*
|
|
3247
|
+
* Listing models asks the account-level question instead, the same one
|
|
3248
|
+
* OpenAI's probe asks, and it is the same request `listModels()` already
|
|
3249
|
+
* makes for discovery.
|
|
3250
|
+
*
|
|
3251
|
+
* Throws rather than returning false. The router logs the reason it catches,
|
|
3252
|
+
* and "bad key, wrong endpoint/deployment, or unreachable" — three guesses,
|
|
3253
|
+
* no answer — is what made this take a user report to find. A swallowed probe
|
|
3254
|
+
* error is the same defect that once hid a misconfigured Azure deployment.
|
|
3255
|
+
*/
|
|
3216
3256
|
async isAvailable() {
|
|
3257
|
+
if (!this.config.apiKey) throw new Error("no Gemini API key configured");
|
|
3258
|
+
let resp;
|
|
3217
3259
|
try {
|
|
3218
|
-
await this.
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
} catch {
|
|
3224
|
-
return false;
|
|
3260
|
+
resp = await this.fetchModelList();
|
|
3261
|
+
} catch (err) {
|
|
3262
|
+
throw new ProviderUnreachableError(
|
|
3263
|
+
`could not reach the Gemini API: ${err instanceof Error ? err.message : String(err)}`
|
|
3264
|
+
);
|
|
3225
3265
|
}
|
|
3266
|
+
if (resp.ok) return true;
|
|
3267
|
+
const status = `HTTP ${resp.status} ${resp.statusText}`.trim();
|
|
3268
|
+
const detail = await describeGeminiError(resp);
|
|
3269
|
+
if (isGeminiAuthStatus(resp.status)) {
|
|
3270
|
+
throw new Error(`Gemini rejected the API key: ${status}${detail}`);
|
|
3271
|
+
}
|
|
3272
|
+
throw new ProviderUnreachableError(`Gemini model list failed: ${status}${detail}`);
|
|
3226
3273
|
}
|
|
3227
3274
|
// ── Private ──────────────────────────────────
|
|
3275
|
+
/**
|
|
3276
|
+
* The account's model list. One place so the availability probe and
|
|
3277
|
+
* discovery cannot ask different questions of the same endpoint.
|
|
3278
|
+
*
|
|
3279
|
+
* The key goes in a header, not the query string: a URL carries into proxy
|
|
3280
|
+
* logs, error reports and shell history, and this one would carry the key
|
|
3281
|
+
* with it.
|
|
3282
|
+
*/
|
|
3283
|
+
fetchModelList() {
|
|
3284
|
+
return fetch("https://generativelanguage.googleapis.com/v1beta/models", {
|
|
3285
|
+
headers: { "x-goog-api-key": this.config.apiKey ?? "" }
|
|
3286
|
+
});
|
|
3287
|
+
}
|
|
3228
3288
|
buildContents(messages, extraImages) {
|
|
3229
3289
|
const contents = [];
|
|
3230
3290
|
for (const m of messages) {
|
|
@@ -5949,12 +6009,28 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter {
|
|
|
5949
6009
|
return this.selector?.getAllAvailableModels() ?? [];
|
|
5950
6010
|
}
|
|
5951
6011
|
// ── Private ──────────────────────────────────
|
|
6012
|
+
/**
|
|
6013
|
+
* Why each configured provider failed its availability probe, kept so the
|
|
6014
|
+
* surfaces can say something better than "add an API key" to a user who
|
|
6015
|
+
* already has one. Cleared and refilled by each detectAvailableProviders().
|
|
6016
|
+
*/
|
|
6017
|
+
probeFailures = /* @__PURE__ */ new Map();
|
|
6018
|
+
/**
|
|
6019
|
+
* What stopped the configured providers from being usable, if anything.
|
|
6020
|
+
* Empty when every configured provider passed — or when none was configured,
|
|
6021
|
+
* which is a different problem with a different answer.
|
|
6022
|
+
*/
|
|
6023
|
+
providerProbeFailures() {
|
|
6024
|
+
return [...this.probeFailures].map(([provider, reason]) => ({ provider, reason }));
|
|
6025
|
+
}
|
|
5952
6026
|
/** Logs why a configured provider failed its availability probe. */
|
|
5953
6027
|
emitProbeFailure(type, reason) {
|
|
6028
|
+
this.probeFailures.set(type, reason);
|
|
5954
6029
|
console.warn(`[router] provider "${type}" is not available: ${reason}`);
|
|
5955
6030
|
}
|
|
5956
6031
|
async detectAvailableProviders(configs) {
|
|
5957
6032
|
const available = /* @__PURE__ */ new Set();
|
|
6033
|
+
this.probeFailures.clear();
|
|
5958
6034
|
const checks = configs.map(async (cfg) => {
|
|
5959
6035
|
try {
|
|
5960
6036
|
const testModel = this.getAnyModelForProvider(cfg.type);
|
|
@@ -5964,7 +6040,13 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter {
|
|
|
5964
6040
|
if (ok) available.add(cfg.type);
|
|
5965
6041
|
else this.emitProbeFailure(cfg.type, "availability check returned false (bad key, wrong endpoint/deployment, or unreachable)");
|
|
5966
6042
|
} catch (err) {
|
|
5967
|
-
|
|
6043
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
6044
|
+
if (err instanceof ProviderUnreachableError) {
|
|
6045
|
+
available.add(cfg.type);
|
|
6046
|
+
console.warn(`[router] provider "${cfg.type}" probe did not complete: ${reason} \u2014 continuing with it enabled`);
|
|
6047
|
+
return;
|
|
6048
|
+
}
|
|
6049
|
+
this.emitProbeFailure(cfg.type, reason);
|
|
5968
6050
|
}
|
|
5969
6051
|
});
|
|
5970
6052
|
await Promise.allSettled(checks);
|
|
@@ -18653,7 +18735,16 @@ ${finalOutput}` : ""
|
|
|
18653
18735
|
const selector = this.router.getSelector();
|
|
18654
18736
|
const model = options.fastAnswerModel ? selector.selectForTier(tier, options.fastAnswerModel) : selector.getCandidatesForTier(tier)[0] ?? selector.selectForTier(tier);
|
|
18655
18737
|
if (!model) {
|
|
18656
|
-
|
|
18738
|
+
const failures2 = this.router.providerProbeFailures();
|
|
18739
|
+
const context = failures2.length ? ` Configured providers: ${failures2.map((f) => `${f.provider}: ${f.reason}`).join("; ")}` : "";
|
|
18740
|
+
if (options.fastAnswerModel) {
|
|
18741
|
+
throw new Error(
|
|
18742
|
+
`No model is available for a fast answer: the pinned model "${options.fastAnswerModel}" could not be resolved \u2014 check it is spelled correctly and served by a configured provider.${context}`
|
|
18743
|
+
);
|
|
18744
|
+
}
|
|
18745
|
+
throw new Error(
|
|
18746
|
+
failures2.length ? `No model is available for a fast answer.${context}` : "No model is available for a fast answer \u2014 add a provider API key first."
|
|
18747
|
+
);
|
|
18657
18748
|
}
|
|
18658
18749
|
this.recordDecision(
|
|
18659
18750
|
"model",
|