@linxiraos/pi-catalog 1.2.1 → 1.3.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.
Files changed (127) hide show
  1. package/CHANGELOG.md +79 -0
  2. package/README.md +4 -2
  3. package/THIRD-PARTY-NOTICES.txt +55 -5
  4. package/dist/types/build.d.ts +3 -2
  5. package/dist/types/compat/auth-ids.d.ts +2 -2
  6. package/dist/types/compat/axes.d.ts +1 -1
  7. package/dist/types/compat/behavior.d.ts +5 -0
  8. package/dist/types/compat/catalog-endpoint.d.ts +8 -0
  9. package/dist/types/compat/image-tokenization.d.ts +9 -1
  10. package/dist/types/compat/judgment-images.d.ts +25 -0
  11. package/dist/types/compat/provider-ids.d.ts +1 -1
  12. package/dist/types/compat/request-compression.d.ts +23 -0
  13. package/dist/types/compat/request-size.d.ts +12 -3
  14. package/dist/types/compat/thinking-replay.d.ts +9 -0
  15. package/dist/types/compat/types.d.ts +4 -0
  16. package/dist/types/discovery/antigravity.d.ts +7 -0
  17. package/dist/types/model-cache.d.ts +7 -0
  18. package/dist/types/model-manager.d.ts +6 -0
  19. package/dist/types/model-thinking.d.ts +11 -1
  20. package/dist/types/models.d.ts +16 -1
  21. package/dist/types/pricing.d.ts +37 -1
  22. package/dist/types/provider-models/openai-compat.d.ts +18 -46
  23. package/dist/types/types.d.ts +63 -12
  24. package/dist/types/wire/codex.d.ts +2 -0
  25. package/package.json +5 -5
  26. package/src/build.d.ts +3 -2
  27. package/src/build.ts +159 -80
  28. package/src/compat/auth-ids.ts +2 -0
  29. package/src/compat/axes.d.ts +1 -1
  30. package/src/compat/axes.ts +129 -4
  31. package/src/compat/behavior.d.ts +5 -0
  32. package/src/compat/behavior.ts +9 -1
  33. package/src/compat/cascade.ts +4 -1
  34. package/src/compat/catalog-endpoint.ts +43 -0
  35. package/src/compat/image-tokenization.ts +28 -10
  36. package/src/compat/judgment-images.ts +83 -0
  37. package/src/compat/provider-ids.d.ts +1 -1
  38. package/src/compat/provider-ids.ts +1 -0
  39. package/src/compat/request-compression.ts +43 -0
  40. package/src/compat/request-size.ts +20 -5
  41. package/src/compat/resolve.ts +90 -53
  42. package/src/compat/rules/README.md +32 -3
  43. package/src/compat/rules/auth/_order.kdl +1 -1
  44. package/src/compat/rules/auth/google-antigravity.kdl +1 -1
  45. package/src/compat/rules/auth/sambanova.kdl +9 -0
  46. package/src/compat/rules/auth/stencil.kdl +1 -1
  47. package/src/compat/rules/classes/anthropic.kdl +42 -2
  48. package/src/compat/rules/classes/deepseek.kdl +4 -0
  49. package/src/compat/rules/classes/gemini.kdl +46 -2
  50. package/src/compat/rules/classes/meta.kdl +7 -0
  51. package/src/compat/rules/classes/minimax.kdl +11 -1
  52. package/src/compat/rules/classes/openai.kdl +34 -3
  53. package/src/compat/rules/classes/xai.kdl +26 -0
  54. package/src/compat/rules/providers/amazon-bedrock.kdl +1 -0
  55. package/src/compat/rules/providers/anthropic.kdl +23 -0
  56. package/src/compat/rules/providers/azure.kdl +2 -0
  57. package/src/compat/rules/providers/cerebras.kdl +3 -0
  58. package/src/compat/rules/providers/cline-pass.kdl +16 -0
  59. package/src/compat/rules/providers/commandcode.kdl +10 -0
  60. package/src/compat/rules/providers/cursor.kdl +50 -1
  61. package/src/compat/rules/providers/deepinfra.kdl +3 -1
  62. package/src/compat/rules/providers/devin.kdl +117 -6
  63. package/src/compat/rules/providers/factory-droid.kdl +6 -0
  64. package/src/compat/rules/providers/firepass.kdl +15 -0
  65. package/src/compat/rules/providers/fireworks.kdl +26 -0
  66. package/src/compat/rules/providers/github-copilot.kdl +24 -0
  67. package/src/compat/rules/providers/google-antigravity.kdl +37 -0
  68. package/src/compat/rules/providers/google-gemini-cli.kdl +1 -0
  69. package/src/compat/rules/providers/google-vertex.kdl +5 -0
  70. package/src/compat/rules/providers/google.kdl +1 -0
  71. package/src/compat/rules/providers/image-tokenization.kdl +2 -1
  72. package/src/compat/rules/providers/local.kdl +8 -1
  73. package/src/compat/rules/providers/meta.kdl +16 -0
  74. package/src/compat/rules/providers/moonshot.kdl +2 -0
  75. package/src/compat/rules/providers/muse-code.kdl +49 -0
  76. package/src/compat/rules/providers/native-tools.kdl +3 -0
  77. package/src/compat/rules/providers/ollama-cloud.kdl +10 -0
  78. package/src/compat/rules/providers/openai-codex.kdl +29 -5
  79. package/src/compat/rules/providers/openai.kdl +1 -0
  80. package/src/compat/rules/providers/opencode-go.kdl +35 -0
  81. package/src/compat/rules/providers/opencode-zen.kdl +4 -0
  82. package/src/compat/rules/providers/openrouter.kdl +19 -0
  83. package/src/compat/rules/providers/sambanova.kdl +60 -0
  84. package/src/compat/rules/providers/tool-call-names.kdl +37 -0
  85. package/src/compat/rules/providers/typesafe.kdl +11 -0
  86. package/src/compat/rules/providers/umans.kdl +7 -0
  87. package/src/compat/rules/providers/venice.kdl +7 -0
  88. package/src/compat/rules/providers/xai-oauth.kdl +16 -6
  89. package/src/compat/rules/providers/xai.kdl +3 -0
  90. package/src/compat/rules/providers/zai.kdl +5 -0
  91. package/src/compat/rules/runtime/behavior.kdl +128 -8
  92. package/src/compat/rules/taxonomy/_collapse.kdl +11 -0
  93. package/src/compat/rules/taxonomy/gemma.kdl +1 -0
  94. package/src/compat/rules/taxonomy/meta.kdl +7 -0
  95. package/src/compat/rules/taxonomy/openai.kdl +2 -0
  96. package/src/compat/rules.json +1 -1
  97. package/src/compat/thinking-replay.ts +33 -0
  98. package/src/compat/types.d.ts +4 -0
  99. package/src/compat/types.ts +4 -0
  100. package/src/discovery/antigravity.d.ts +7 -0
  101. package/src/discovery/antigravity.ts +13 -0
  102. package/src/discovery/codex.ts +0 -4
  103. package/src/discovery/cursor.ts +28 -20
  104. package/src/discovery/devin.ts +7 -8
  105. package/src/model-cache.d.ts +7 -0
  106. package/src/model-cache.ts +8 -0
  107. package/src/model-manager.d.ts +6 -0
  108. package/src/model-manager.ts +41 -21
  109. package/src/model-thinking.d.ts +80 -0
  110. package/src/model-thinking.ts +13 -1
  111. package/src/model-tokenizer.ts +7 -1
  112. package/src/models.d.ts +16 -1
  113. package/src/models.json +1 -1
  114. package/src/models.ts +26 -12
  115. package/src/pricing.d.ts +37 -1
  116. package/src/pricing.ts +123 -12
  117. package/src/provider-models/bundled-references.ts +5 -2
  118. package/src/provider-models/cache-provider-id.ts +1 -1
  119. package/src/provider-models/descriptors.ts +2 -0
  120. package/src/provider-models/google.ts +7 -1
  121. package/src/provider-models/openai-compat.d.ts +18 -46
  122. package/src/provider-models/openai-compat.ts +188 -104
  123. package/src/provider-models/special.ts +1 -0
  124. package/src/types.d.ts +63 -12
  125. package/src/types.ts +71 -9
  126. package/src/wire/codex.d.ts +2 -0
  127. package/src/wire/codex.ts +17 -0
package/src/models.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { classifyModel } from "./compat/taxonomy";
2
2
  import MODELS from "./models.json" with { type: "json" };
3
+ import { dueEffectiveRate } from "./pricing";
3
4
  import type {
4
5
  Api,
5
6
  EffectiveTokenCost,
@@ -65,22 +66,25 @@ export function getBundledModels(provider: GeneratedProvider): Model<Api>[] {
65
66
  }
66
67
  /** Resolve display semantics without mistaking an absent rate card for a free model. */
67
68
  export function getModelPricingStatus(model: Pick<Model<Api>, "cost" | "pricingStatus">): ModelPricingStatus {
68
- const cost = model.cost;
69
+ const cost = getEffectiveTokenCost(model.cost);
69
70
  if (cost.input !== 0 || cost.output !== 0 || cost.cacheRead !== 0 || cost.cacheWrite !== 0) return "fixed";
70
71
  return model.pricingStatus ?? "unknown";
71
72
  }
72
73
 
74
+ /**
75
+ * The base token card in force at `timestamp` (Unix ms, default now): the
76
+ * latest due `timeBased` effective rate, else the row's own legs. Price
77
+ * displays read this rather than the scalar legs, which keep the card current
78
+ * when the row was built while bundles, caches, and long sessions outlive it.
79
+ * Applies no long-context tier or peak/off-peak multiplier.
80
+ */
81
+ export function getEffectiveTokenCost(cost: ModelCost, timestamp = Date.now()): TokenCost {
82
+ return resolveTokenCost(cost, 0, timestamp);
83
+ }
84
+
73
85
  function resolveTokenCost(cost: ModelCost, promptInputTokens: number, timestamp: number | undefined): TokenCost {
74
- let rates: ModelCost | EffectiveTokenCost = cost;
75
- let effectiveFrom = -Infinity;
76
- if (timestamp !== undefined && cost.timeBased?.effectiveRates) {
77
- for (const candidate of cost.timeBased.effectiveRates) {
78
- if (candidate.effectiveFrom <= timestamp && candidate.effectiveFrom > effectiveFrom) {
79
- rates = candidate;
80
- effectiveFrom = candidate.effectiveFrom;
81
- }
82
- }
83
- }
86
+ const rates: ModelCost | EffectiveTokenCost =
87
+ (timestamp === undefined ? undefined : dueEffectiveRate(cost.timeBased, timestamp)) ?? cost;
84
88
  const longContext = rates.longContext;
85
89
  if (!longContext) return rates;
86
90
  const reachesThreshold =
@@ -105,10 +109,20 @@ function timeBasedMultiplier(schedule: TimeBasedCost | undefined, timestamp: num
105
109
  return isPeakPricingPeriod(schedule, timestamp) ? 1 : schedule.offPeakMultiplier;
106
110
  }
107
111
 
112
+ /**
113
+ * Whether a schedule carries a recurring peak/off-peak tariff. A schedule with
114
+ * no peak windows at a 1x off-peak multiplier only carries dated rate cards
115
+ * (`cost-patch`/`cost-fallback` `effective-rates`): its price changes on a
116
+ * date, never by time of day.
117
+ */
118
+ export function hasRecurringTariff(schedule: TimeBasedCost): boolean {
119
+ return schedule.peakWindows.length > 0 || schedule.offPeakMultiplier !== 1;
120
+ }
121
+
108
122
  /** Return the recurring UTC tariff period, independently of its monetary multiplier. */
109
123
  export function getTimeBasedPricingPeriod(cost: ModelCost, timestamp?: number): "peak" | "off-peak" | undefined {
110
124
  const schedule = cost.timeBased;
111
- if (!schedule) return undefined;
125
+ if (!schedule || !hasRecurringTariff(schedule)) return undefined;
112
126
  return isPeakPricingPeriod(schedule, timestamp ?? Date.now()) ? "peak" : "off-peak";
113
127
  }
114
128
 
package/src/pricing.d.ts CHANGED
@@ -1,5 +1,41 @@
1
- import type { TimeBasedCost } from "./types";
1
+ import type { EffectiveTokenCost, ModelCost, TimeBasedCost, TokenCost } from "./types";
2
2
  /** Validate serialized schedules before admitting cached model rows. */
3
3
  export declare function isTimeBasedCost(value: unknown): value is TimeBasedCost;
4
4
  /** KDL object children are named records; normalize them once while constructing the model. */
5
5
  export declare function materializeTimeBasedCost(value: unknown): TimeBasedCost;
6
+ /**
7
+ * The latest `effectiveRates` card of a materialized schedule due at
8
+ * `timestamp`, or undefined before the first one (or without a schedule).
9
+ */
10
+ export declare function dueEffectiveRate(schedule: TimeBasedCost | undefined, timestamp: number): EffectiveTokenCost | undefined;
11
+ /** A `cost-patch`/`cost-fallback` dated card: the legs it overwrites from `effectiveFrom` (Unix ms). */
12
+ export interface DatedCostCard extends Partial<TokenCost> {
13
+ effectiveFrom: number;
14
+ }
15
+ /**
16
+ * Parse the compiled `effective-rates` block of a `cost-patch` or
17
+ * `cost-fallback` payload into dated cards, ascending by date. Throws on a
18
+ * malformed card, so a typo in a rule fails the build instead of silently
19
+ * leaving the old price in force.
20
+ */
21
+ export declare function parseDatedCostCards(costAxis: unknown, axis: "cost-patch" | "cost-fallback"): DatedCostCard[];
22
+ /**
23
+ * Earliest instant a rate card can name (the `Date` range minimum). A leading
24
+ * card from here holds the price in force before the first dated change.
25
+ */
26
+ export declare const EARLIEST_EFFECTIVE_FROM = -8640000000000000;
27
+ /** A schedule of dated rate cards alone: no peak windows, no off-peak discount. */
28
+ export declare const NO_RECURRING_TARIFF: TimeBasedCost;
29
+ /**
30
+ * A flat card plus its dated overrides as a request-time `ModelCost`: the
31
+ * card due at `now` in the scalar legs, and every period (a leading card for
32
+ * the base price, then each dated card over the previous one) in a
33
+ * `timeBased` schedule without a recurring tariff. The plain card when no
34
+ * dated card exists.
35
+ */
36
+ export declare function datedCost(base: TokenCost, cards: readonly DatedCostCard[], now?: number): ModelCost;
37
+ /**
38
+ * Overwrite `cost` legs with the latest dated card due at `now`. Leaves
39
+ * `cost` unchanged before the first card.
40
+ */
41
+ export declare function applyDueDatedCostCard(cost: TokenCost, cards: readonly DatedCostCard[], now?: number): void;
package/src/pricing.ts CHANGED
@@ -1,4 +1,11 @@
1
- import type { EffectiveTokenCost, LongContextTokenCost, PeakPricingWindow, TimeBasedCost, TokenCost } from "./types";
1
+ import type {
2
+ EffectiveTokenCost,
3
+ LongContextTokenCost,
4
+ ModelCost,
5
+ PeakPricingWindow,
6
+ TimeBasedCost,
7
+ TokenCost,
8
+ } from "./types";
2
9
  import { isRecord } from "./utils";
3
10
 
4
11
  function nonnegative(value: unknown): value is number {
@@ -86,6 +93,21 @@ function namedEntries(value: unknown, field: string): unknown[] {
86
93
  return Object.values(value);
87
94
  }
88
95
 
96
+ /** Parse a KDL ISO UTC instant (`YYYY-MM-DDTHH:mm:ssZ`, optional milliseconds) to Unix ms. */
97
+ function parseUtcInstant(date: unknown, axis: string): number {
98
+ if (typeof date !== "string" || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3})?Z$/.test(date)) {
99
+ throw new Error(`Invalid ${axis} effective-from: expected an ISO UTC timestamp`);
100
+ }
101
+ const instant = Date.parse(date);
102
+ if (
103
+ !Number.isFinite(instant) ||
104
+ new Date(instant).toISOString() !== (date.includes(".") ? date : date.replace("Z", ".000Z"))
105
+ ) {
106
+ throw new Error(`Invalid ${axis} effective-from: invalid UTC date`);
107
+ }
108
+ return instant;
109
+ }
110
+
89
111
  /** KDL object children are named records; normalize them once while constructing the model. */
90
112
  export function materializeTimeBasedCost(value: unknown): TimeBasedCost {
91
113
  const source = payload(value, ["offPeakMultiplier", "peakWindows", "effectiveRates"], "schedule");
@@ -105,17 +127,7 @@ export function materializeTimeBasedCost(value: unknown): TimeBasedCost {
105
127
  ["effectiveFrom", "input", "output", "cacheRead", "cacheWrite", "longContext"],
106
128
  "effective-rate",
107
129
  );
108
- const date = rate.effectiveFrom;
109
- if (typeof date !== "string" || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3})?Z$/.test(date)) {
110
- throw new Error("Invalid time-based-cost effective-from: expected an ISO UTC timestamp");
111
- }
112
- const effectiveFrom = Date.parse(date);
113
- if (
114
- !Number.isFinite(effectiveFrom) ||
115
- new Date(effectiveFrom).toISOString() !== (date.includes(".") ? date : date.replace("Z", ".000Z"))
116
- ) {
117
- throw new Error("Invalid time-based-cost effective-from: invalid UTC date");
118
- }
130
+ const effectiveFrom = parseUtcInstant(rate.effectiveFrom, "time-based-cost");
119
131
  if (rate.longContext !== undefined) {
120
132
  payload(
121
133
  rate.longContext,
@@ -134,3 +146,102 @@ export function materializeTimeBasedCost(value: unknown): TimeBasedCost {
134
146
  throw new Error("Invalid time-based-cost schedule: invalid windows, rates, or multiplier");
135
147
  return schedule;
136
148
  }
149
+
150
+ /**
151
+ * The latest `effectiveRates` card of a materialized schedule due at
152
+ * `timestamp`, or undefined before the first one (or without a schedule).
153
+ */
154
+ export function dueEffectiveRate(
155
+ schedule: TimeBasedCost | undefined,
156
+ timestamp: number,
157
+ ): EffectiveTokenCost | undefined {
158
+ let due: EffectiveTokenCost | undefined;
159
+ for (const candidate of schedule?.effectiveRates ?? []) {
160
+ if (candidate.effectiveFrom <= timestamp && (due === undefined || candidate.effectiveFrom > due.effectiveFrom)) {
161
+ due = candidate;
162
+ }
163
+ }
164
+ return due;
165
+ }
166
+
167
+ const TOKEN_COST_LEGS = ["input", "output", "cacheRead", "cacheWrite"] as const;
168
+ const DATED_CARD_KEYS: readonly string[] = ["effectiveFrom", ...TOKEN_COST_LEGS];
169
+
170
+ /** A `cost-patch`/`cost-fallback` dated card: the legs it overwrites from `effectiveFrom` (Unix ms). */
171
+ export interface DatedCostCard extends Partial<TokenCost> {
172
+ effectiveFrom: number;
173
+ }
174
+
175
+ /**
176
+ * Parse the compiled `effective-rates` block of a `cost-patch` or
177
+ * `cost-fallback` payload into dated cards, ascending by date. Throws on a
178
+ * malformed card, so a typo in a rule fails the build instead of silently
179
+ * leaving the old price in force.
180
+ */
181
+ export function parseDatedCostCards(costAxis: unknown, axis: "cost-patch" | "cost-fallback"): DatedCostCard[] {
182
+ const effectiveRates = isRecord(costAxis) ? costAxis.effectiveRates : undefined;
183
+ if (effectiveRates === undefined) return [];
184
+ if (!isRecord(effectiveRates)) throw new Error(`Invalid ${axis} effective-rates: expected named objects`);
185
+ const cards = Object.values(effectiveRates).map(entry => {
186
+ if (!isRecord(entry) || Object.keys(entry).some(key => !DATED_CARD_KEYS.includes(key))) {
187
+ throw new Error(`Invalid ${axis} effective-rate: unknown or malformed fields`);
188
+ }
189
+ const card: DatedCostCard = { effectiveFrom: parseUtcInstant(entry.effectiveFrom, axis) };
190
+ for (const leg of TOKEN_COST_LEGS) {
191
+ const value = entry[leg];
192
+ if (value === undefined) continue;
193
+ if (!nonnegative(value))
194
+ throw new Error(`Invalid ${axis} effective-rate ${leg}: expected a nonnegative number`);
195
+ card[leg] = value;
196
+ }
197
+ return card;
198
+ });
199
+ cards.sort((a, b) => a.effectiveFrom - b.effectiveFrom);
200
+ for (let index = 1; index < cards.length; index++) {
201
+ if (cards[index]!.effectiveFrom === cards[index - 1]!.effectiveFrom) {
202
+ throw new Error(`Invalid ${axis} effective-rates: duplicate effective-from`);
203
+ }
204
+ }
205
+ return cards;
206
+ }
207
+
208
+ /**
209
+ * Earliest instant a rate card can name (the `Date` range minimum). A leading
210
+ * card from here holds the price in force before the first dated change.
211
+ */
212
+ export const EARLIEST_EFFECTIVE_FROM = -8_640_000_000_000_000;
213
+
214
+ /** A schedule of dated rate cards alone: no peak windows, no off-peak discount. */
215
+ export const NO_RECURRING_TARIFF: TimeBasedCost = { offPeakMultiplier: 1, peakWindows: [] };
216
+
217
+ /**
218
+ * A flat card plus its dated overrides as a request-time `ModelCost`: the
219
+ * card due at `now` in the scalar legs, and every period (a leading card for
220
+ * the base price, then each dated card over the previous one) in a
221
+ * `timeBased` schedule without a recurring tariff. The plain card when no
222
+ * dated card exists.
223
+ */
224
+ export function datedCost(base: TokenCost, cards: readonly DatedCostCard[], now = Date.now()): ModelCost {
225
+ if (cards.length === 0) return base;
226
+ const effectiveRates: EffectiveTokenCost[] = [{ effectiveFrom: EARLIEST_EFFECTIVE_FROM, ...base }];
227
+ for (const card of cards) effectiveRates.push({ ...effectiveRates.at(-1)!, ...card });
228
+ const timeBased = { ...NO_RECURRING_TARIFF, effectiveRates };
229
+ const { effectiveFrom: _effectiveFrom, ...due } = dueEffectiveRate(timeBased, now)!;
230
+ return { ...due, timeBased };
231
+ }
232
+
233
+ /**
234
+ * Overwrite `cost` legs with the latest dated card due at `now`. Leaves
235
+ * `cost` unchanged before the first card.
236
+ */
237
+ export function applyDueDatedCostCard(cost: TokenCost, cards: readonly DatedCostCard[], now = Date.now()): void {
238
+ let due: DatedCostCard | undefined;
239
+ for (const card of cards) {
240
+ if (card.effectiveFrom <= now) due = card;
241
+ }
242
+ if (due === undefined) return;
243
+ for (const leg of TOKEN_COST_LEGS) {
244
+ const value = due[leg];
245
+ if (value !== undefined) cost[leg] = value;
246
+ }
247
+ }
@@ -1,5 +1,6 @@
1
1
  import { isBareIdReferenceProvider } from "../compat/behavior";
2
2
  import { isZeroCostXaiOAuthReference } from "../identity/reference";
3
+ import { withoutUnverifiedThinking } from "../model-thinking";
3
4
  import { getBundledModels, getBundledProviders } from "../models";
4
5
  import type { Api, Model, ModelSpec } from "../types";
5
6
 
@@ -17,11 +18,13 @@ export function toModelSpec<TApi extends Api>(model: Model<TApi>): ModelSpec<TAp
17
18
  supportsComputerUseConfig,
18
19
  ...rest
19
20
  } = model;
20
- return {
21
+ // Unverified thinking is derived output too: references must not carry it to
22
+ // another host as reasoning capability.
23
+ return withoutUnverifiedThinking({
21
24
  ...rest,
22
25
  ...(supportsComputerUseConfig !== undefined ? { supportsComputerUse: supportsComputerUseConfig } : {}),
23
26
  compat: compatConfig,
24
- } as ModelSpec<TApi>;
27
+ } as ModelSpec<TApi>);
25
28
  }
26
29
 
27
30
  export function createBundledReferenceMap<TApi extends Api>(
@@ -45,7 +45,7 @@ export function getDefaultModelDiscoveryBaseUrl(providerId: string): string | un
45
45
  case "litellm":
46
46
  return Bun.env.LITELLM_BASE_URL ?? "http://localhost:4000/v1";
47
47
  case "opencode-go":
48
- return "https://opencode.ai/zen/go/v1";
48
+ return "https://opencode.ai/inference/go/v1";
49
49
  case "opencode-zen":
50
50
  return "https://opencode.ai/zen/v1";
51
51
  case "vllm":
@@ -59,6 +59,7 @@ import {
59
59
  qianfanModelManagerOptions,
60
60
  qwenPortalModelManagerOptions,
61
61
  sakanaModelManagerOptions,
62
+ sambanovaModelManagerOptions,
62
63
  siliconflowCnModelManagerOptions,
63
64
  siliconflowModelManagerOptions,
64
65
  singularityApiDevModelManagerOptions,
@@ -146,6 +147,7 @@ const MODEL_MANAGER_FACTORIES: Readonly<Partial<Record<KnownProvider, ModelManag
146
147
  qianfan: config => qianfanModelManagerOptions(config),
147
148
  "qwen-portal": config => qwenPortalModelManagerOptions(config),
148
149
  sakana: config => sakanaModelManagerOptions(config),
150
+ sambanova: config => sambanovaModelManagerOptions(config),
149
151
  siliconflow: config => siliconflowModelManagerOptions(config),
150
152
  "siliconflow-cn": config => siliconflowCnModelManagerOptions(config),
151
153
  "singularityapi-dev": config => singularityApiDevModelManagerOptions(config),
@@ -1,4 +1,5 @@
1
1
  import { logger } from "@linxiraos/pi-utils";
2
+ import { isExcludedModel } from "../compat/behavior";
2
3
  import { reviewedCollapseTable } from "../compat/collapse";
3
4
  import { classifyModel } from "../compat/taxonomy";
4
5
  import { providerEntry } from "../compat/providers";
@@ -127,7 +128,12 @@ export function googleAntigravityModelManagerOptions(
127
128
  });
128
129
  continue;
129
130
  }
130
- catalogs.push({ accountKey, models: result.models });
131
+ // Caches written under older rules fail the compiled-rules hash in
132
+ // the model cache, so filtering fresh rosters covers every source.
133
+ catalogs.push({
134
+ accountKey,
135
+ models: result.models.filter(model => !isExcludedModel("google-antigravity", model.id)),
136
+ });
131
137
  }
132
138
  if (catalogs.length === 0) return null;
133
139
  const tagAccess = catalogs.every(catalog => catalog.accountKey !== undefined);
@@ -100,6 +100,19 @@ export interface CerebrasModelManagerConfig {
100
100
  fetch?: FetchImpl;
101
101
  }
102
102
  export declare function cerebrasModelManagerOptions(config?: CerebrasModelManagerConfig): ModelManagerOptions<"openai-completions">;
103
+ /** SambaNova discovery inputs: `apiKey` enables the live roster; `baseUrl` defaults to SambaNova Cloud. */
104
+ export interface SambaNovaModelManagerConfig {
105
+ apiKey?: string;
106
+ baseUrl?: string;
107
+ fetch?: FetchImpl;
108
+ }
109
+ /**
110
+ * SambaNova Cloud's model manager. `/v1/models` requires a key, so a keyless
111
+ * manager serves only the bundled seed; with a key the live roster replaces
112
+ * it. Rows carry pricing and limits but no capability metadata: reasoning,
113
+ * thinking wire format, and image input come from `providers/sambanova.kdl`.
114
+ */
115
+ export declare function sambanovaModelManagerOptions(config?: SambaNovaModelManagerConfig): ModelManagerOptions<"openai-completions">;
103
116
  export interface HuggingfaceModelManagerConfig {
104
117
  apiKey?: string;
105
118
  baseUrl?: string;
@@ -232,52 +245,6 @@ export interface ZhipuCodingPlanModelManagerConfig {
232
245
  fetch?: FetchImpl;
233
246
  }
234
247
  export declare function zhipuCodingPlanModelManagerOptions(config?: ZhipuCodingPlanModelManagerConfig): ModelManagerOptions<"openai-completions">;
235
- /**
236
- * Fireworks-published cap for the Kimi K2 family. Fireworks' `/v1/models`
237
- * envelope generically reports `max_completion_tokens: 65536` for every Kimi
238
- * deployment, but Kimi K2 (instruct / thinking / turbo) on Fireworks is
239
- * documented to ship long reasoning traces that should be bounded — capping
240
- * at 32,768 prevents handing callers a budget the router cannot honor.
241
- * See https://github.com/can1357/oh-my-pi/issues/1849.
242
- */
243
- export declare const FIREWORKS_KIMI_MAX_TOKENS = 32768;
244
- /**
245
- * Fireworks' output ceiling for Kimi K2.7-Code specifically. Its `/v1/models`
246
- * generic `max_completion_tokens` is 65,536 and Fireworks serves it in full —
247
- * verified with a single completion emitting 58,971 output tokens and
248
- * `max_tokens: 200000` accepted without error. Unlike the older K2.5/K2.6
249
- * family (see {@link FIREWORKS_KIMI_MAX_TOKENS}), K2.7-Code is not clamped to
250
- * 32,768; that ceiling only truncated it.
251
- */
252
- export declare const FIREWORKS_KIMI_K27_CODE_MAX_TOKENS = 65536;
253
- /**
254
- * Returns true for the Kimi K2.5 / K2.6 family served by Fireworks-backed
255
- * providers (`fireworks` direct, `firepass` router) that share the 32,768
256
- * `maxTokens` ceiling. Matches both the public catalog id (`kimi-k2.5`,
257
- * `kimi-k2.6`, `kimi-k2.6-turbo`) and the canonical Fireworks wire id
258
- * (`accounts/fireworks/{models,routers}/kimi-k2…`).
259
- *
260
- * K2.7-Code (incl. `-fast` / `-highspeed`) is deliberately excluded: unlike the
261
- * earlier K2 family it serves its full context on Fireworks — verified with a
262
- * single completion emitting 58,971 output tokens and `max_tokens: 200000`
263
- * accepted without error — so the 32,768 cap would only truncate it. It inherits
264
- * Fireworks' reported `max_completion_tokens` (65,536) instead.
265
- */
266
- export declare function isFireworksKimiK2ModelId(modelId: string): boolean;
267
- /**
268
- * Clamp the Kimi K2 family's `maxTokens` to {@link FIREWORKS_KIMI_MAX_TOKENS}
269
- * on Fireworks-backed providers, leaving every other model untouched.
270
- */
271
- export declare function clampFireworksKimiMaxTokens(modelId: string, candidate: number): number;
272
- export declare function clampFireworksKimiMaxTokens(modelId: string, candidate: number | null): number | null;
273
- /**
274
- * Kimi K2.7 Code's documented recommended output budget. Some provider
275
- * discovery rows report the context-sized `max_completion_tokens` instead.
276
- */
277
- export declare const KIMI_K27_CODE_RECOMMENDED_MAX_TOKENS = 32768;
278
- export declare function isKimiK27CodeModelId(modelId: string): boolean;
279
- export declare function clampKimiK27CodeMaxTokens(modelId: string, candidate: number): number;
280
- export declare function clampKimiK27CodeMaxTokens(modelId: string, candidate: number | null): number | null;
281
248
  /**
282
249
  * Build the Fireworks Fast seed by projecting each base bundled spec into a
283
250
  * `<id>-fast` variant. Pushed into the generated catalog (Fast routers never
@@ -655,6 +622,11 @@ export interface ModelsDevProviderDescriptor {
655
622
  }
656
623
  /** Generic mapper that converts models.dev data using provider descriptors. */
657
624
  export declare function mapModelsDevToModels(data: Record<string, unknown>, descriptors: readonly ModelsDevProviderDescriptor[]): ModelSpec<Api>[];
625
+ /**
626
+ * OpenCode Go endpoint for one request API. The model generator re-bases
627
+ * previous-snapshot rows with it so retained rows follow endpoint moves.
628
+ */
629
+ export declare function openCodeGoBaseUrlForApi(api: Api): string;
658
630
  /** Resolves the transport and endpoint for one Z.AI model catalog entry. */
659
631
  export declare function resolveZaiApi(modelId: string): {
660
632
  api: "anthropic-messages" | "openai-completions";