cascade-ai 0.48.0 → 0.49.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/index.d.cts CHANGED
@@ -17,6 +17,21 @@ interface ModelInfo {
17
17
  maxOutputTokens: number;
18
18
  supportsStreaming: boolean;
19
19
  isLocal: boolean;
20
+ /**
21
+ * True when Cascade has NO price for this model — the cost fields above are
22
+ * 0 only because there is nothing to put in them.
23
+ *
24
+ * This exists because 0 is ambiguous: a local Ollama model genuinely costs
25
+ * nothing, while a newly-released cloud preview model that isn't in the
26
+ * pricing dataset costs real money we can't quote. Treating the second like
27
+ * the first made unknown-priced models report $0.00 spend, put them outside
28
+ * the reach of the per-run cost cap, and gave them the best possible score in
29
+ * the value-based rankers. Anything that reads the cost fields must consult
30
+ * this flag first (see `isPricingUnknown` in core/router/pricing.ts).
31
+ *
32
+ * Never set on local models — their $0 is real.
33
+ */
34
+ pricingUnknown?: boolean;
20
35
  minSizeB?: number;
21
36
  /** Tool-use capability. False for Ollama; true for all cloud providers. */
22
37
  supportsToolUse?: boolean;
@@ -46,6 +61,25 @@ interface ProviderConfig {
46
61
  authToken?: string;
47
62
  /** Where an adopted credential came from, e.g. "Claude Code". Informational. */
48
63
  credentialSource?: string;
64
+ /**
65
+ * Local-vs-hosted toggle. `true` declares "this endpoint runs on hardware I
66
+ * already pay for, so inference genuinely costs $0"; `false` declares it is a
67
+ * paid hosted endpoint, so an unpriced model there reports "cost not tracked"
68
+ * rather than free.
69
+ *
70
+ * Only meaningful for `ollama` and `openai-compatible`, which can each point
71
+ * at either. Left unset, it defaults sensibly: ollama ⇒ local, and
72
+ * openai-compatible ⇒ local when `baseUrl` is loopback/LAN (llama.cpp, LM
73
+ * Studio, vLLM on your own box) and hosted otherwise (Together, Groq, …).
74
+ * See `isLocalEndpoint` in core/router/pricing.ts.
75
+ */
76
+ local?: boolean;
77
+ /**
78
+ * Deployment region for providers whose price varies by region (Azure lists
79
+ * different rates for `global`, `us` and `eu` deployments of the same model).
80
+ * Matched against the pricing dataset's per-region entries.
81
+ */
82
+ region?: string;
49
83
  }
50
84
  interface StreamChunk {
51
85
  text: string;
@@ -57,6 +91,13 @@ interface TokenUsage {
57
91
  outputTokens: number;
58
92
  totalTokens: number;
59
93
  estimatedCostUsd: number;
94
+ /**
95
+ * True when `estimatedCostUsd` is 0 because the model's price is UNKNOWN, not
96
+ * because the call was free. Cost readouts must say "not tracked" for these
97
+ * rather than "$0.00", and budget code must know the spend it just recorded
98
+ * is an undercount.
99
+ */
100
+ costUnknown?: boolean;
60
101
  }
61
102
  interface GenerateOptions {
62
103
  messages: ConversationMessage[];
@@ -1095,6 +1136,25 @@ interface DelegationSavings {
1095
1136
 
1096
1137
  type BenchmarkProfile = Partial<Record<Exclude<TaskType, 'mixed'>, number>>;
1097
1138
 
1139
+ interface PriceDisagreement {
1140
+ modelId: string;
1141
+ provider: string;
1142
+ /** Per-1k rates the bundled dataset asserts. */
1143
+ dataset: {
1144
+ input: number;
1145
+ output: number;
1146
+ };
1147
+ /** Per-1k rates the live source asserts. */
1148
+ live: {
1149
+ input: number;
1150
+ output: number;
1151
+ };
1152
+ /** live/dataset ratio on the blended rate; >1 means live is more expensive. */
1153
+ ratio: number;
1154
+ datasetAsOf: string;
1155
+ datasetSource: string;
1156
+ }
1157
+
1098
1158
  type DataSource = 'live' | 'cache' | 'bundled';
1099
1159
  interface PriceEntry {
1100
1160
  input: number;
@@ -1126,6 +1186,8 @@ declare class LiveDataProvider {
1126
1186
  private snapshot;
1127
1187
  private prices;
1128
1188
  private capabilities;
1189
+ /** Dataset-vs-live price mismatches, deduped by `provider:modelId`. */
1190
+ private disagreements;
1129
1191
  private source;
1130
1192
  private fetchedAt;
1131
1193
  private loaded;
@@ -1156,8 +1218,25 @@ declare class LiveDataProvider {
1156
1218
  /**
1157
1219
  * Returns a price-corrected copy of each model when live pricing is known,
1158
1220
  * leaving the original untouched (so the shared catalog is never mutated).
1221
+ *
1222
+ * Reconciliation policy (see reconcilePrice in pricing.ts): the bundled
1223
+ * dataset is the authoritative baseline, and a live quote overrides it
1224
+ * because it is fresher by construction — but the disagreement is RECORDED,
1225
+ * not swallowed. A dataset/live mismatch is the best signal available that
1226
+ * the committed prices have gone stale, so it must be visible rather than
1227
+ * silently resolved in either direction. A live quote of $0 for a non-local
1228
+ * model is rejected outright: that is the very failure mode this change
1229
+ * exists to prevent, and a marketplace's free-tier alias must never zero out
1230
+ * the price of the paid model Cascade is actually calling.
1159
1231
  */
1160
1232
  applyLivePricing(models: ModelInfo[]): ModelInfo[];
1233
+ private recordDisagreement;
1234
+ /**
1235
+ * Dataset-vs-live price mismatches seen this session, newest-model-first
1236
+ * insertion order. Surfaced by `cascade models --verbose`; a non-empty list
1237
+ * means src/core/router/pricing-data.json is probably due a refresh.
1238
+ */
1239
+ getPriceDisagreements(): PriceDisagreement[];
1161
1240
  /** Current capability facts for a model id, or null when unknown. */
1162
1241
  getCapability(modelId: string): CapabilityEntry | null;
1163
1242
  /**
@@ -1345,6 +1424,14 @@ interface RouterStats {
1345
1424
  outputTokensByTier: Record<string, number>;
1346
1425
  /** Accumulated cost (USD) broken down by feature tag (e.g. T2 section names). */
1347
1426
  costByFeature: Record<string, number>;
1427
+ /**
1428
+ * Calls made against models Cascade has no price for. Their spend is NOT in
1429
+ * `totalCostUsd` — it can't be — so a non-zero count here means the totals
1430
+ * above (and any budget check derived from them) are an undercount.
1431
+ */
1432
+ untrackedCostCalls: number;
1433
+ /** Ids of the models responsible, for "cost not tracked for: …" readouts. */
1434
+ untrackedCostModels: string[];
1348
1435
  }
1349
1436
  declare class CascadeRouter extends EventEmitter {
1350
1437
  private selector;
package/dist/index.d.ts CHANGED
@@ -17,6 +17,21 @@ interface ModelInfo {
17
17
  maxOutputTokens: number;
18
18
  supportsStreaming: boolean;
19
19
  isLocal: boolean;
20
+ /**
21
+ * True when Cascade has NO price for this model — the cost fields above are
22
+ * 0 only because there is nothing to put in them.
23
+ *
24
+ * This exists because 0 is ambiguous: a local Ollama model genuinely costs
25
+ * nothing, while a newly-released cloud preview model that isn't in the
26
+ * pricing dataset costs real money we can't quote. Treating the second like
27
+ * the first made unknown-priced models report $0.00 spend, put them outside
28
+ * the reach of the per-run cost cap, and gave them the best possible score in
29
+ * the value-based rankers. Anything that reads the cost fields must consult
30
+ * this flag first (see `isPricingUnknown` in core/router/pricing.ts).
31
+ *
32
+ * Never set on local models — their $0 is real.
33
+ */
34
+ pricingUnknown?: boolean;
20
35
  minSizeB?: number;
21
36
  /** Tool-use capability. False for Ollama; true for all cloud providers. */
22
37
  supportsToolUse?: boolean;
@@ -46,6 +61,25 @@ interface ProviderConfig {
46
61
  authToken?: string;
47
62
  /** Where an adopted credential came from, e.g. "Claude Code". Informational. */
48
63
  credentialSource?: string;
64
+ /**
65
+ * Local-vs-hosted toggle. `true` declares "this endpoint runs on hardware I
66
+ * already pay for, so inference genuinely costs $0"; `false` declares it is a
67
+ * paid hosted endpoint, so an unpriced model there reports "cost not tracked"
68
+ * rather than free.
69
+ *
70
+ * Only meaningful for `ollama` and `openai-compatible`, which can each point
71
+ * at either. Left unset, it defaults sensibly: ollama ⇒ local, and
72
+ * openai-compatible ⇒ local when `baseUrl` is loopback/LAN (llama.cpp, LM
73
+ * Studio, vLLM on your own box) and hosted otherwise (Together, Groq, …).
74
+ * See `isLocalEndpoint` in core/router/pricing.ts.
75
+ */
76
+ local?: boolean;
77
+ /**
78
+ * Deployment region for providers whose price varies by region (Azure lists
79
+ * different rates for `global`, `us` and `eu` deployments of the same model).
80
+ * Matched against the pricing dataset's per-region entries.
81
+ */
82
+ region?: string;
49
83
  }
50
84
  interface StreamChunk {
51
85
  text: string;
@@ -57,6 +91,13 @@ interface TokenUsage {
57
91
  outputTokens: number;
58
92
  totalTokens: number;
59
93
  estimatedCostUsd: number;
94
+ /**
95
+ * True when `estimatedCostUsd` is 0 because the model's price is UNKNOWN, not
96
+ * because the call was free. Cost readouts must say "not tracked" for these
97
+ * rather than "$0.00", and budget code must know the spend it just recorded
98
+ * is an undercount.
99
+ */
100
+ costUnknown?: boolean;
60
101
  }
61
102
  interface GenerateOptions {
62
103
  messages: ConversationMessage[];
@@ -1095,6 +1136,25 @@ interface DelegationSavings {
1095
1136
 
1096
1137
  type BenchmarkProfile = Partial<Record<Exclude<TaskType, 'mixed'>, number>>;
1097
1138
 
1139
+ interface PriceDisagreement {
1140
+ modelId: string;
1141
+ provider: string;
1142
+ /** Per-1k rates the bundled dataset asserts. */
1143
+ dataset: {
1144
+ input: number;
1145
+ output: number;
1146
+ };
1147
+ /** Per-1k rates the live source asserts. */
1148
+ live: {
1149
+ input: number;
1150
+ output: number;
1151
+ };
1152
+ /** live/dataset ratio on the blended rate; >1 means live is more expensive. */
1153
+ ratio: number;
1154
+ datasetAsOf: string;
1155
+ datasetSource: string;
1156
+ }
1157
+
1098
1158
  type DataSource = 'live' | 'cache' | 'bundled';
1099
1159
  interface PriceEntry {
1100
1160
  input: number;
@@ -1126,6 +1186,8 @@ declare class LiveDataProvider {
1126
1186
  private snapshot;
1127
1187
  private prices;
1128
1188
  private capabilities;
1189
+ /** Dataset-vs-live price mismatches, deduped by `provider:modelId`. */
1190
+ private disagreements;
1129
1191
  private source;
1130
1192
  private fetchedAt;
1131
1193
  private loaded;
@@ -1156,8 +1218,25 @@ declare class LiveDataProvider {
1156
1218
  /**
1157
1219
  * Returns a price-corrected copy of each model when live pricing is known,
1158
1220
  * leaving the original untouched (so the shared catalog is never mutated).
1221
+ *
1222
+ * Reconciliation policy (see reconcilePrice in pricing.ts): the bundled
1223
+ * dataset is the authoritative baseline, and a live quote overrides it
1224
+ * because it is fresher by construction — but the disagreement is RECORDED,
1225
+ * not swallowed. A dataset/live mismatch is the best signal available that
1226
+ * the committed prices have gone stale, so it must be visible rather than
1227
+ * silently resolved in either direction. A live quote of $0 for a non-local
1228
+ * model is rejected outright: that is the very failure mode this change
1229
+ * exists to prevent, and a marketplace's free-tier alias must never zero out
1230
+ * the price of the paid model Cascade is actually calling.
1159
1231
  */
1160
1232
  applyLivePricing(models: ModelInfo[]): ModelInfo[];
1233
+ private recordDisagreement;
1234
+ /**
1235
+ * Dataset-vs-live price mismatches seen this session, newest-model-first
1236
+ * insertion order. Surfaced by `cascade models --verbose`; a non-empty list
1237
+ * means src/core/router/pricing-data.json is probably due a refresh.
1238
+ */
1239
+ getPriceDisagreements(): PriceDisagreement[];
1161
1240
  /** Current capability facts for a model id, or null when unknown. */
1162
1241
  getCapability(modelId: string): CapabilityEntry | null;
1163
1242
  /**
@@ -1345,6 +1424,14 @@ interface RouterStats {
1345
1424
  outputTokensByTier: Record<string, number>;
1346
1425
  /** Accumulated cost (USD) broken down by feature tag (e.g. T2 section names). */
1347
1426
  costByFeature: Record<string, number>;
1427
+ /**
1428
+ * Calls made against models Cascade has no price for. Their spend is NOT in
1429
+ * `totalCostUsd` — it can't be — so a non-zero count here means the totals
1430
+ * above (and any budget check derived from them) are an undercount.
1431
+ */
1432
+ untrackedCostCalls: number;
1433
+ /** Ids of the models responsible, for "cost not tracked for: …" readouts. */
1434
+ untrackedCostModels: string[];
1348
1435
  }
1349
1436
  declare class CascadeRouter extends EventEmitter {
1350
1437
  private selector;