cascade-ai 0.53.0 → 0.54.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 +173 -2
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +173 -2
- package/dist/cli.js.map +1 -1
- package/dist/desktop-core.cjs +167 -2
- package/dist/index.cjs +173 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +77 -0
- package/dist/index.d.ts +77 -0
- package/dist/index.js +173 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -904,6 +904,16 @@ declare class ModelSelector {
|
|
|
904
904
|
*/
|
|
905
905
|
private discovered;
|
|
906
906
|
constructor(availableProviders: Set<ProviderType>);
|
|
907
|
+
/**
|
|
908
|
+
* Verdicts from earlier runs about ids that 404 on this account. Consulted
|
|
909
|
+
* BEFORE a model enters the pool, which is the only placement that helps: a
|
|
910
|
+
* T3 wave runs concurrently, so filtering after the first failure still lets
|
|
911
|
+
* every other worker in the wave call the dead id.
|
|
912
|
+
*/
|
|
913
|
+
private deadModels?;
|
|
914
|
+
setDeadModelStore(store: {
|
|
915
|
+
isDead(provider: string, modelId: string): boolean;
|
|
916
|
+
}): void;
|
|
907
917
|
addDynamicModel(model: ModelInfo): void;
|
|
908
918
|
/** Record the ids a provider's API actually serves (discovery). Empty ⇒ ignored. */
|
|
909
919
|
setValidatedModels(provider: ProviderType, ids: string[]): void;
|
|
@@ -1077,6 +1087,60 @@ declare class TaskAnalyzer {
|
|
|
1077
1087
|
static clearCache(): void;
|
|
1078
1088
|
}
|
|
1079
1089
|
|
|
1090
|
+
interface DeadModelRecord {
|
|
1091
|
+
/** `provider:modelId` — the same key shape the failover event reports. */
|
|
1092
|
+
key: string;
|
|
1093
|
+
provider: string;
|
|
1094
|
+
modelId: string;
|
|
1095
|
+
/** Provider's own words, kept verbatim so the user can act on them. */
|
|
1096
|
+
reason: string;
|
|
1097
|
+
firstSeenAt: number;
|
|
1098
|
+
lastSeenAt: number;
|
|
1099
|
+
/** How many times this model has been found dead. */
|
|
1100
|
+
hits: number;
|
|
1101
|
+
}
|
|
1102
|
+
/**
|
|
1103
|
+
* Where records live between runs. Desktop/CLI back this with a JSON file,
|
|
1104
|
+
* the cloud server with a per-user table. Both are synchronous-optional: a
|
|
1105
|
+
* store that fails to load must never stop a run.
|
|
1106
|
+
*/
|
|
1107
|
+
interface DeadModelPersistence {
|
|
1108
|
+
load(): DeadModelRecord[];
|
|
1109
|
+
save(records: DeadModelRecord[]): void;
|
|
1110
|
+
}
|
|
1111
|
+
declare class DeadModelStore {
|
|
1112
|
+
private persistence?;
|
|
1113
|
+
private records;
|
|
1114
|
+
private readonly ttlMs;
|
|
1115
|
+
private readonly now;
|
|
1116
|
+
constructor(persistence?: DeadModelPersistence | undefined, opts?: {
|
|
1117
|
+
ttlMs?: number;
|
|
1118
|
+
now?: () => number;
|
|
1119
|
+
});
|
|
1120
|
+
private restore;
|
|
1121
|
+
private isExpired;
|
|
1122
|
+
private flush;
|
|
1123
|
+
/**
|
|
1124
|
+
* Record that a model was found dead. Returns true when this is the first
|
|
1125
|
+
* time (so the caller can log it once rather than once per concurrent
|
|
1126
|
+
* worker in the wave that discovered it).
|
|
1127
|
+
*/
|
|
1128
|
+
record(provider: string, modelId: string, reason: string): boolean;
|
|
1129
|
+
/** True when this model is known dead and the verdict has not expired. */
|
|
1130
|
+
isDead(provider: string, modelId: string): boolean;
|
|
1131
|
+
/** Why a model is considered dead, for /why and the models command. */
|
|
1132
|
+
reasonFor(provider: string, modelId: string): string | null;
|
|
1133
|
+
/** Live records, expired ones filtered out. */
|
|
1134
|
+
list(): DeadModelRecord[];
|
|
1135
|
+
/**
|
|
1136
|
+
* Forget a verdict — used when the user fixes the underlying problem (adds
|
|
1137
|
+
* the model to their project, gets quota) and wants it retried now rather
|
|
1138
|
+
* than after the TTL.
|
|
1139
|
+
*/
|
|
1140
|
+
forget(provider: string, modelId: string): boolean;
|
|
1141
|
+
clear(): void;
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1080
1144
|
declare class MemoryStore {
|
|
1081
1145
|
private db;
|
|
1082
1146
|
constructor(dbPath: string);
|
|
@@ -1509,6 +1573,19 @@ declare class CascadeRouter extends EventEmitter {
|
|
|
1509
1573
|
private localQueue;
|
|
1510
1574
|
private taskAnalyzer?;
|
|
1511
1575
|
private pendingFeedbackSource?;
|
|
1576
|
+
/** Models known dead for this account (404 / retired / not enabled). */
|
|
1577
|
+
private deadModels;
|
|
1578
|
+
/**
|
|
1579
|
+
* Back the dead-model memory with durable storage (a JSON file on desktop, a
|
|
1580
|
+
* per-user table in the cloud). Without one the verdicts still work, but only
|
|
1581
|
+
* for the life of the process — so tomorrow's run pays the same wave of 404s
|
|
1582
|
+
* to learn the same fact.
|
|
1583
|
+
*/
|
|
1584
|
+
setDeadModelStore(store: DeadModelStore): void;
|
|
1585
|
+
/** Models known dead for this account, for `cascade models` and /why. */
|
|
1586
|
+
listDeadModels(): ReturnType<DeadModelStore['list']>;
|
|
1587
|
+
/** Retry a model the user has since fixed, without waiting out the TTL. */
|
|
1588
|
+
forgetDeadModel(provider: string, modelId: string): boolean;
|
|
1512
1589
|
private worldStateDB?;
|
|
1513
1590
|
private privacyPaths?;
|
|
1514
1591
|
private guidanceQueue?;
|
package/dist/index.d.ts
CHANGED
|
@@ -904,6 +904,16 @@ declare class ModelSelector {
|
|
|
904
904
|
*/
|
|
905
905
|
private discovered;
|
|
906
906
|
constructor(availableProviders: Set<ProviderType>);
|
|
907
|
+
/**
|
|
908
|
+
* Verdicts from earlier runs about ids that 404 on this account. Consulted
|
|
909
|
+
* BEFORE a model enters the pool, which is the only placement that helps: a
|
|
910
|
+
* T3 wave runs concurrently, so filtering after the first failure still lets
|
|
911
|
+
* every other worker in the wave call the dead id.
|
|
912
|
+
*/
|
|
913
|
+
private deadModels?;
|
|
914
|
+
setDeadModelStore(store: {
|
|
915
|
+
isDead(provider: string, modelId: string): boolean;
|
|
916
|
+
}): void;
|
|
907
917
|
addDynamicModel(model: ModelInfo): void;
|
|
908
918
|
/** Record the ids a provider's API actually serves (discovery). Empty ⇒ ignored. */
|
|
909
919
|
setValidatedModels(provider: ProviderType, ids: string[]): void;
|
|
@@ -1077,6 +1087,60 @@ declare class TaskAnalyzer {
|
|
|
1077
1087
|
static clearCache(): void;
|
|
1078
1088
|
}
|
|
1079
1089
|
|
|
1090
|
+
interface DeadModelRecord {
|
|
1091
|
+
/** `provider:modelId` — the same key shape the failover event reports. */
|
|
1092
|
+
key: string;
|
|
1093
|
+
provider: string;
|
|
1094
|
+
modelId: string;
|
|
1095
|
+
/** Provider's own words, kept verbatim so the user can act on them. */
|
|
1096
|
+
reason: string;
|
|
1097
|
+
firstSeenAt: number;
|
|
1098
|
+
lastSeenAt: number;
|
|
1099
|
+
/** How many times this model has been found dead. */
|
|
1100
|
+
hits: number;
|
|
1101
|
+
}
|
|
1102
|
+
/**
|
|
1103
|
+
* Where records live between runs. Desktop/CLI back this with a JSON file,
|
|
1104
|
+
* the cloud server with a per-user table. Both are synchronous-optional: a
|
|
1105
|
+
* store that fails to load must never stop a run.
|
|
1106
|
+
*/
|
|
1107
|
+
interface DeadModelPersistence {
|
|
1108
|
+
load(): DeadModelRecord[];
|
|
1109
|
+
save(records: DeadModelRecord[]): void;
|
|
1110
|
+
}
|
|
1111
|
+
declare class DeadModelStore {
|
|
1112
|
+
private persistence?;
|
|
1113
|
+
private records;
|
|
1114
|
+
private readonly ttlMs;
|
|
1115
|
+
private readonly now;
|
|
1116
|
+
constructor(persistence?: DeadModelPersistence | undefined, opts?: {
|
|
1117
|
+
ttlMs?: number;
|
|
1118
|
+
now?: () => number;
|
|
1119
|
+
});
|
|
1120
|
+
private restore;
|
|
1121
|
+
private isExpired;
|
|
1122
|
+
private flush;
|
|
1123
|
+
/**
|
|
1124
|
+
* Record that a model was found dead. Returns true when this is the first
|
|
1125
|
+
* time (so the caller can log it once rather than once per concurrent
|
|
1126
|
+
* worker in the wave that discovered it).
|
|
1127
|
+
*/
|
|
1128
|
+
record(provider: string, modelId: string, reason: string): boolean;
|
|
1129
|
+
/** True when this model is known dead and the verdict has not expired. */
|
|
1130
|
+
isDead(provider: string, modelId: string): boolean;
|
|
1131
|
+
/** Why a model is considered dead, for /why and the models command. */
|
|
1132
|
+
reasonFor(provider: string, modelId: string): string | null;
|
|
1133
|
+
/** Live records, expired ones filtered out. */
|
|
1134
|
+
list(): DeadModelRecord[];
|
|
1135
|
+
/**
|
|
1136
|
+
* Forget a verdict — used when the user fixes the underlying problem (adds
|
|
1137
|
+
* the model to their project, gets quota) and wants it retried now rather
|
|
1138
|
+
* than after the TTL.
|
|
1139
|
+
*/
|
|
1140
|
+
forget(provider: string, modelId: string): boolean;
|
|
1141
|
+
clear(): void;
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1080
1144
|
declare class MemoryStore {
|
|
1081
1145
|
private db;
|
|
1082
1146
|
constructor(dbPath: string);
|
|
@@ -1509,6 +1573,19 @@ declare class CascadeRouter extends EventEmitter {
|
|
|
1509
1573
|
private localQueue;
|
|
1510
1574
|
private taskAnalyzer?;
|
|
1511
1575
|
private pendingFeedbackSource?;
|
|
1576
|
+
/** Models known dead for this account (404 / retired / not enabled). */
|
|
1577
|
+
private deadModels;
|
|
1578
|
+
/**
|
|
1579
|
+
* Back the dead-model memory with durable storage (a JSON file on desktop, a
|
|
1580
|
+
* per-user table in the cloud). Without one the verdicts still work, but only
|
|
1581
|
+
* for the life of the process — so tomorrow's run pays the same wave of 404s
|
|
1582
|
+
* to learn the same fact.
|
|
1583
|
+
*/
|
|
1584
|
+
setDeadModelStore(store: DeadModelStore): void;
|
|
1585
|
+
/** Models known dead for this account, for `cascade models` and /why. */
|
|
1586
|
+
listDeadModels(): ReturnType<DeadModelStore['list']>;
|
|
1587
|
+
/** Retry a model the user has since fixed, without waiting out the TTL. */
|
|
1588
|
+
forgetDeadModel(provider: string, modelId: string): boolean;
|
|
1512
1589
|
private worldStateDB?;
|
|
1513
1590
|
private privacyPaths?;
|
|
1514
1591
|
private guidanceQueue?;
|
package/dist/index.js
CHANGED
|
@@ -41,6 +41,12 @@ import cron from 'node-cron';
|
|
|
41
41
|
// Cascade AI — Multi-tier AI Orchestration System
|
|
42
42
|
var __defProp = Object.defineProperty;
|
|
43
43
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
44
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
45
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
46
|
+
}) : x)(function(x) {
|
|
47
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
48
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
49
|
+
});
|
|
44
50
|
var __esm = (fn, res) => function __init() {
|
|
45
51
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
46
52
|
};
|
|
@@ -1845,7 +1851,7 @@ function reconcilePrice(model, live) {
|
|
|
1845
1851
|
}
|
|
1846
1852
|
|
|
1847
1853
|
// src/constants.ts
|
|
1848
|
-
var CASCADE_VERSION = "0.
|
|
1854
|
+
var CASCADE_VERSION = "0.54.0";
|
|
1849
1855
|
var CASCADE_CONFIG_DIR = ".cascade";
|
|
1850
1856
|
var CASCADE_MD_FILE = "CASCADE.md";
|
|
1851
1857
|
var CASCADE_IGNORE_FILE = ".cascadeignore";
|
|
@@ -3658,7 +3664,21 @@ var ModelSelector = class {
|
|
|
3658
3664
|
this.availableProviders = availableProviders;
|
|
3659
3665
|
this.availableModels = new Map(Object.entries(MODELS));
|
|
3660
3666
|
}
|
|
3667
|
+
/**
|
|
3668
|
+
* Verdicts from earlier runs about ids that 404 on this account. Consulted
|
|
3669
|
+
* BEFORE a model enters the pool, which is the only placement that helps: a
|
|
3670
|
+
* T3 wave runs concurrently, so filtering after the first failure still lets
|
|
3671
|
+
* every other worker in the wave call the dead id.
|
|
3672
|
+
*/
|
|
3673
|
+
deadModels;
|
|
3674
|
+
setDeadModelStore(store) {
|
|
3675
|
+
this.deadModels = store;
|
|
3676
|
+
for (const [id, m] of [...this.availableModels]) {
|
|
3677
|
+
if (this.deadModels.isDead(m.provider, m.id)) this.availableModels.delete(id);
|
|
3678
|
+
}
|
|
3679
|
+
}
|
|
3661
3680
|
addDynamicModel(model) {
|
|
3681
|
+
if (this.deadModels?.isDead(model.provider, model.id)) return;
|
|
3662
3682
|
this.availableModels.set(model.id, model);
|
|
3663
3683
|
if (!(model.id in MODELS)) this.discovered.add(normalizeModelId(model.id));
|
|
3664
3684
|
}
|
|
@@ -4065,6 +4085,121 @@ var LocalRequestQueue = class {
|
|
|
4065
4085
|
}
|
|
4066
4086
|
};
|
|
4067
4087
|
|
|
4088
|
+
// src/core/router/dead-models.ts
|
|
4089
|
+
var DEAD_MODEL_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
4090
|
+
function deadModelKey(provider, modelId) {
|
|
4091
|
+
return `${provider}:${modelId}`;
|
|
4092
|
+
}
|
|
4093
|
+
var DeadModelStore = class {
|
|
4094
|
+
constructor(persistence, opts = {}) {
|
|
4095
|
+
this.persistence = persistence;
|
|
4096
|
+
this.ttlMs = opts.ttlMs ?? DEAD_MODEL_TTL_MS;
|
|
4097
|
+
this.now = opts.now ?? (() => Date.now());
|
|
4098
|
+
this.restore();
|
|
4099
|
+
}
|
|
4100
|
+
persistence;
|
|
4101
|
+
records = /* @__PURE__ */ new Map();
|
|
4102
|
+
ttlMs;
|
|
4103
|
+
now;
|
|
4104
|
+
restore() {
|
|
4105
|
+
if (!this.persistence) return;
|
|
4106
|
+
try {
|
|
4107
|
+
for (const r of this.persistence.load()) {
|
|
4108
|
+
if (!this.isExpired(r)) this.records.set(r.key, r);
|
|
4109
|
+
}
|
|
4110
|
+
} catch {
|
|
4111
|
+
}
|
|
4112
|
+
}
|
|
4113
|
+
isExpired(r) {
|
|
4114
|
+
return this.now() - r.lastSeenAt > this.ttlMs;
|
|
4115
|
+
}
|
|
4116
|
+
flush() {
|
|
4117
|
+
if (!this.persistence) return;
|
|
4118
|
+
try {
|
|
4119
|
+
this.persistence.save([...this.records.values()]);
|
|
4120
|
+
} catch {
|
|
4121
|
+
}
|
|
4122
|
+
}
|
|
4123
|
+
/**
|
|
4124
|
+
* Record that a model was found dead. Returns true when this is the first
|
|
4125
|
+
* time (so the caller can log it once rather than once per concurrent
|
|
4126
|
+
* worker in the wave that discovered it).
|
|
4127
|
+
*/
|
|
4128
|
+
record(provider, modelId, reason) {
|
|
4129
|
+
const key = deadModelKey(provider, modelId);
|
|
4130
|
+
const now = this.now();
|
|
4131
|
+
const existing = this.records.get(key);
|
|
4132
|
+
if (existing && !this.isExpired(existing)) {
|
|
4133
|
+
existing.lastSeenAt = now;
|
|
4134
|
+
existing.hits += 1;
|
|
4135
|
+
this.flush();
|
|
4136
|
+
return false;
|
|
4137
|
+
}
|
|
4138
|
+
this.records.set(key, {
|
|
4139
|
+
key,
|
|
4140
|
+
provider,
|
|
4141
|
+
modelId,
|
|
4142
|
+
reason,
|
|
4143
|
+
firstSeenAt: now,
|
|
4144
|
+
lastSeenAt: now,
|
|
4145
|
+
hits: 1
|
|
4146
|
+
});
|
|
4147
|
+
this.flush();
|
|
4148
|
+
return true;
|
|
4149
|
+
}
|
|
4150
|
+
/** True when this model is known dead and the verdict has not expired. */
|
|
4151
|
+
isDead(provider, modelId) {
|
|
4152
|
+
const r = this.records.get(deadModelKey(provider, modelId));
|
|
4153
|
+
if (!r) return false;
|
|
4154
|
+
if (this.isExpired(r)) {
|
|
4155
|
+
this.records.delete(r.key);
|
|
4156
|
+
this.flush();
|
|
4157
|
+
return false;
|
|
4158
|
+
}
|
|
4159
|
+
return true;
|
|
4160
|
+
}
|
|
4161
|
+
/** Why a model is considered dead, for /why and the models command. */
|
|
4162
|
+
reasonFor(provider, modelId) {
|
|
4163
|
+
const r = this.records.get(deadModelKey(provider, modelId));
|
|
4164
|
+
return r && !this.isExpired(r) ? r.reason : null;
|
|
4165
|
+
}
|
|
4166
|
+
/** Live records, expired ones filtered out. */
|
|
4167
|
+
list() {
|
|
4168
|
+
const live = [...this.records.values()].filter((r) => !this.isExpired(r));
|
|
4169
|
+
return live.sort((a, b) => b.lastSeenAt - a.lastSeenAt);
|
|
4170
|
+
}
|
|
4171
|
+
/**
|
|
4172
|
+
* Forget a verdict — used when the user fixes the underlying problem (adds
|
|
4173
|
+
* the model to their project, gets quota) and wants it retried now rather
|
|
4174
|
+
* than after the TTL.
|
|
4175
|
+
*/
|
|
4176
|
+
forget(provider, modelId) {
|
|
4177
|
+
const removed = this.records.delete(deadModelKey(provider, modelId));
|
|
4178
|
+
if (removed) this.flush();
|
|
4179
|
+
return removed;
|
|
4180
|
+
}
|
|
4181
|
+
clear() {
|
|
4182
|
+
this.records.clear();
|
|
4183
|
+
this.flush();
|
|
4184
|
+
}
|
|
4185
|
+
};
|
|
4186
|
+
function fileDeadModelPersistence(filePath) {
|
|
4187
|
+
return {
|
|
4188
|
+
load() {
|
|
4189
|
+
const fs25 = __require("fs");
|
|
4190
|
+
if (!fs25.existsSync(filePath)) return [];
|
|
4191
|
+
const raw = JSON.parse(fs25.readFileSync(filePath, "utf-8"));
|
|
4192
|
+
return Array.isArray(raw) ? raw : [];
|
|
4193
|
+
},
|
|
4194
|
+
save(records) {
|
|
4195
|
+
const fs25 = __require("fs");
|
|
4196
|
+
const path26 = __require("path");
|
|
4197
|
+
fs25.mkdirSync(path26.dirname(filePath), { recursive: true });
|
|
4198
|
+
fs25.writeFileSync(filePath, JSON.stringify(records, null, 2));
|
|
4199
|
+
}
|
|
4200
|
+
};
|
|
4201
|
+
}
|
|
4202
|
+
|
|
4068
4203
|
// src/utils/retry.ts
|
|
4069
4204
|
var CascadeCancelledError = class extends Error {
|
|
4070
4205
|
constructor(reason) {
|
|
@@ -4658,6 +4793,26 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter {
|
|
|
4658
4793
|
localQueue;
|
|
4659
4794
|
taskAnalyzer;
|
|
4660
4795
|
pendingFeedbackSource;
|
|
4796
|
+
/** Models known dead for this account (404 / retired / not enabled). */
|
|
4797
|
+
deadModels = new DeadModelStore();
|
|
4798
|
+
/**
|
|
4799
|
+
* Back the dead-model memory with durable storage (a JSON file on desktop, a
|
|
4800
|
+
* per-user table in the cloud). Without one the verdicts still work, but only
|
|
4801
|
+
* for the life of the process — so tomorrow's run pays the same wave of 404s
|
|
4802
|
+
* to learn the same fact.
|
|
4803
|
+
*/
|
|
4804
|
+
setDeadModelStore(store) {
|
|
4805
|
+
this.deadModels = store;
|
|
4806
|
+
this.selector.setDeadModelStore(store);
|
|
4807
|
+
}
|
|
4808
|
+
/** Models known dead for this account, for `cascade models` and /why. */
|
|
4809
|
+
listDeadModels() {
|
|
4810
|
+
return this.deadModels.list();
|
|
4811
|
+
}
|
|
4812
|
+
/** Retry a model the user has since fixed, without waiting out the TTL. */
|
|
4813
|
+
forgetDeadModel(provider, modelId) {
|
|
4814
|
+
return this.deadModels.forget(provider, modelId);
|
|
4815
|
+
}
|
|
4661
4816
|
worldStateDB;
|
|
4662
4817
|
privacyPaths;
|
|
4663
4818
|
guidanceQueue;
|
|
@@ -5022,6 +5177,8 @@ var CascadeRouter = class _CascadeRouter extends EventEmitter {
|
|
|
5022
5177
|
}
|
|
5023
5178
|
if (isModelNotFoundError(errMsg)) {
|
|
5024
5179
|
this.selector.removeModel(model.id);
|
|
5180
|
+
const firstSighting = this.deadModels.record(model.provider, model.id, errMsg);
|
|
5181
|
+
if (firstSighting) this.emit("model:dead", { provider: model.provider, modelId: model.id, reason: errMsg });
|
|
5025
5182
|
const depth = (options._notFoundDepth ?? 0) + 1;
|
|
5026
5183
|
const next = depth <= 3 ? this.selector.selectForTier(tier) : null;
|
|
5027
5184
|
if (next && next.id !== model.id) {
|
|
@@ -8817,9 +8974,16 @@ var CAPABILITIES = [
|
|
|
8817
8974
|
api: "openai-images"
|
|
8818
8975
|
},
|
|
8819
8976
|
{
|
|
8977
|
+
// Imagen, not `gemini-3-pro-image`. That id appears on Google's PRICING
|
|
8978
|
+
// page but 404s on `:predict` ("not found for API version v1beta, or is not
|
|
8979
|
+
// supported for predict") — the Gemini image models are called through
|
|
8980
|
+
// generateContent with an image response modality, while `:predict` is the
|
|
8981
|
+
// Imagen shape this executor implements. A billing line item is not proof
|
|
8982
|
+
// of a callable endpoint, which is the assumption that put the wrong id
|
|
8983
|
+
// here in the first place.
|
|
8820
8984
|
modality: "image",
|
|
8821
8985
|
provider: "gemini",
|
|
8822
|
-
modelId: "
|
|
8986
|
+
modelId: "imagen-4.0-generate-001",
|
|
8823
8987
|
api: "gemini-predict"
|
|
8824
8988
|
},
|
|
8825
8989
|
// ── Speech (text → audio) ──
|
|
@@ -14391,6 +14555,13 @@ var Cascade = class _Cascade extends EventEmitter {
|
|
|
14391
14555
|
}
|
|
14392
14556
|
});
|
|
14393
14557
|
this.toolRegistry = new ToolRegistry(this.config.tools, workspacePath);
|
|
14558
|
+
try {
|
|
14559
|
+
const nodePath = __require("path");
|
|
14560
|
+
this.router.setDeadModelStore(new DeadModelStore(
|
|
14561
|
+
fileDeadModelPersistence(nodePath.join(workspacePath, ".cascade", "dead-models.json"))
|
|
14562
|
+
));
|
|
14563
|
+
} catch {
|
|
14564
|
+
}
|
|
14394
14565
|
this.multimodal = new MultimodalRegistry(
|
|
14395
14566
|
(this.config.providers ?? []).map((p) => p.type)
|
|
14396
14567
|
);
|