@agimon-ai/po-cache 0.0.0-alpha.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 (39) hide show
  1. package/LICENSE +46 -0
  2. package/README.md +77 -0
  3. package/dist/apiContracts.cjs +17 -0
  4. package/dist/apiContracts.cjs.map +1 -0
  5. package/dist/apiContracts.d.mts +53 -0
  6. package/dist/apiContracts.d.mts.map +1 -0
  7. package/dist/apiContracts.mjs +13 -0
  8. package/dist/apiContracts.mjs.map +1 -0
  9. package/dist/env.cjs +7 -0
  10. package/dist/env.d.mts +8 -0
  11. package/dist/env.d.mts.map +1 -0
  12. package/dist/env.mjs +2 -0
  13. package/dist/environment-CRhmV8ze.cjs +39 -0
  14. package/dist/environment-CRhmV8ze.cjs.map +1 -0
  15. package/dist/environment-vyw-n84h.mjs +10 -0
  16. package/dist/environment-vyw-n84h.mjs.map +1 -0
  17. package/dist/extensions/mcp.cjs +26 -0
  18. package/dist/extensions/mcp.cjs.map +1 -0
  19. package/dist/extensions/mcp.d.mts +5 -0
  20. package/dist/extensions/mcp.d.mts.map +1 -0
  21. package/dist/extensions/mcp.mjs +21 -0
  22. package/dist/extensions/mcp.mjs.map +1 -0
  23. package/dist/extensions/server.cjs +3706 -0
  24. package/dist/extensions/server.cjs.map +1 -0
  25. package/dist/extensions/server.d.mts +5 -0
  26. package/dist/extensions/server.d.mts.map +1 -0
  27. package/dist/extensions/server.mjs +3701 -0
  28. package/dist/extensions/server.mjs.map +1 -0
  29. package/dist/index.cjs +17 -0
  30. package/dist/index.d.mts +116 -0
  31. package/dist/index.d.mts.map +1 -0
  32. package/dist/index.mjs +2 -0
  33. package/dist/promptCacheTelemetry-BU1fIhb8.cjs +289 -0
  34. package/dist/promptCacheTelemetry-BU1fIhb8.cjs.map +1 -0
  35. package/dist/promptCacheTelemetry-CguL1ZqT.mjs +200 -0
  36. package/dist/promptCacheTelemetry-CguL1ZqT.mjs.map +1 -0
  37. package/llms.txt +11 -0
  38. package/package.json +114 -0
  39. package/src/prompts/po-use-cache/SKILL.md +13 -0
@@ -0,0 +1,3701 @@
1
+ import { a as PO_PROMPT_CACHE_ROOT_SESSION_ENV, i as PO_PROMPT_CACHE_PARENT_NAMESPACE_ENV, r as PO_PROMPT_CACHE_CHILD_PROJECTION_ENV } from "../environment-vyw-n84h.mjs";
2
+ import { c as createPromptCacheModelFingerprint, h as sha256Base64Url, i as rewritePromptCacheKey, l as createRootSessionIdentity, o as createParentPromptCacheNamespace, p as PromptCacheTelemetryService, s as createPromptCacheKey, t as PromptCacheTelemetry, u as canonicalJson } from "../promptCacheTelemetry-CguL1ZqT.mjs";
3
+ import { createHash, randomUUID } from "node:crypto";
4
+ import { composeServerProviderOverrides, defineServerPlugin } from "@agimon-ai/po-core/serverFacet";
5
+ import { composeRootHooks, defineHook, defineRoot } from "@agimon-ai/po-core/extensionFile";
6
+ import { getSessionHarnessState } from "@agimon-ai/po-config/harnessStore";
7
+ import { DOOM_MINOR_MODE_CATALOG_SERVICE, readMinorModeCatalog } from "@agimon-ai/po-minor-mode";
8
+ import { dirname, join } from "node:path";
9
+ import { getAgentDir } from "@earendil-works/pi-coding-agent";
10
+ import { readFileSync, statSync } from "node:fs";
11
+ import { lstat, mkdir, readFile, readdir, rename, rm, stat, unlink, writeFile } from "node:fs/promises";
12
+ import { homedir } from "node:os";
13
+ //#region src/services/environment/index.ts
14
+ const environmentLeases = /* @__PURE__ */ new WeakMap();
15
+ /** Same-value session leases restore only after the final owner leaves. External writes win. */
16
+ function acquireEnvironmentValue(name, value, environment = process.env) {
17
+ let leases = environmentLeases.get(environment);
18
+ if (!leases) {
19
+ leases = /* @__PURE__ */ new Map();
20
+ environmentLeases.set(environment, leases);
21
+ }
22
+ let lease = leases.get(name);
23
+ if (lease && lease.value !== value) throw new Error(`Conflicting environment lease for ${name}.`);
24
+ if (!lease) {
25
+ const owner = new OwnedEnvironmentValue(name, environment);
26
+ owner.set(value);
27
+ lease = {
28
+ count: 0,
29
+ value,
30
+ owner
31
+ };
32
+ leases.set(name, lease);
33
+ }
34
+ lease.count++;
35
+ let released = false;
36
+ return () => {
37
+ if (released) return;
38
+ released = true;
39
+ if (--lease.count > 0) return;
40
+ lease.owner.restore();
41
+ leases.delete(name);
42
+ };
43
+ }
44
+ var OwnedEnvironmentValue = class {
45
+ #environment;
46
+ #name;
47
+ #baseline;
48
+ #ownedValue;
49
+ constructor(name, environment = process.env) {
50
+ this.#name = name;
51
+ this.#environment = environment;
52
+ this.#baseline = {
53
+ present: Object.hasOwn(environment, name),
54
+ value: environment[name]
55
+ };
56
+ }
57
+ set(value) {
58
+ this.#environment[this.#name] = value;
59
+ this.#ownedValue = value;
60
+ }
61
+ restore() {
62
+ if (this.#ownedValue === void 0 || this.#environment[this.#name] !== this.#ownedValue) return;
63
+ if (this.#baseline.present) this.#environment[this.#name] = this.#baseline.value;
64
+ else delete this.#environment[this.#name];
65
+ this.#ownedValue = void 0;
66
+ }
67
+ };
68
+ //#endregion
69
+ //#region src/services/promptCacheUsage/index.ts
70
+ function recordValue(value) {
71
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
72
+ }
73
+ function nonNegativeNumber(value) {
74
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
75
+ }
76
+ function firstNumber(...values) {
77
+ for (const value of values) {
78
+ const number = nonNegativeNumber(value);
79
+ if (number !== void 0) return number;
80
+ }
81
+ }
82
+ function normalizedUsage(usage) {
83
+ const cacheRead = nonNegativeNumber(usage.cacheRead);
84
+ const cacheWrite = nonNegativeNumber(usage.cacheWrite);
85
+ if (cacheRead === void 0 && cacheWrite === void 0) return void 0;
86
+ const input = nonNegativeNumber(usage.input) ?? 0;
87
+ return {
88
+ cacheRead: cacheRead ?? 0,
89
+ cacheWrite: cacheWrite ?? 0,
90
+ totalInput: input + (cacheRead ?? 0) + (cacheWrite ?? 0)
91
+ };
92
+ }
93
+ function openAiUsage(usage) {
94
+ const promptDetails = recordValue(usage.prompt_tokens_details) ?? recordValue(usage.promptTokensDetails);
95
+ const inputDetails = recordValue(usage.input_tokens_details) ?? recordValue(usage.inputTokensDetails);
96
+ const cacheRead = firstNumber(promptDetails?.cached_tokens, promptDetails?.cachedTokens, inputDetails?.cached_tokens, inputDetails?.cachedTokens);
97
+ const cacheWrite = firstNumber(promptDetails?.cache_write_tokens, promptDetails?.cacheWriteTokens, inputDetails?.cache_write_tokens, inputDetails?.cacheWriteTokens);
98
+ if (cacheRead === void 0 && cacheWrite === void 0) return void 0;
99
+ const floor = (cacheRead ?? 0) + (cacheWrite ?? 0);
100
+ const reportedTotal = firstNumber(usage.prompt_tokens, usage.promptTokens, usage.input_tokens, usage.inputTokens);
101
+ return {
102
+ cacheRead: cacheRead ?? 0,
103
+ cacheWrite: cacheWrite ?? 0,
104
+ totalInput: Math.max(reportedTotal ?? floor, floor)
105
+ };
106
+ }
107
+ function anthropicUsage(usage) {
108
+ const cacheRead = firstNumber(usage.cache_read_input_tokens, usage.cacheReadInputTokens);
109
+ const cacheWrite = firstNumber(usage.cache_creation_input_tokens, usage.cacheCreationInputTokens);
110
+ if (cacheRead === void 0 && cacheWrite === void 0) return void 0;
111
+ const input = firstNumber(usage.input_tokens, usage.inputTokens) ?? 0;
112
+ return {
113
+ cacheRead: cacheRead ?? 0,
114
+ cacheWrite: cacheWrite ?? 0,
115
+ totalInput: input + (cacheRead ?? 0) + (cacheWrite ?? 0)
116
+ };
117
+ }
118
+ function geminiUsage(usage) {
119
+ const metadata = recordValue(usage.usage_metadata) ?? recordValue(usage.usageMetadata) ?? usage;
120
+ const cacheRead = firstNumber(metadata.cachedContentTokenCount, metadata.cached_content_token_count);
121
+ if (cacheRead === void 0) return void 0;
122
+ const totalInput = firstNumber(metadata.promptTokenCount, metadata.prompt_token_count, usage.input_tokens, usage.inputTokens, usage.prompt_tokens, usage.promptTokens);
123
+ return {
124
+ cacheRead,
125
+ cacheWrite: 0,
126
+ totalInput: Math.max(totalInput ?? cacheRead, cacheRead)
127
+ };
128
+ }
129
+ /** Normalize only usage values carried by a provider-produced assistant message. */
130
+ function normalizePromptCacheUsage(message) {
131
+ const usage = recordValue(recordValue(message)?.usage);
132
+ if (!usage) return void 0;
133
+ return normalizedUsage(usage) ?? openAiUsage(usage) ?? anthropicUsage(usage) ?? geminiUsage(usage);
134
+ }
135
+ //#endregion
136
+ //#region src/services/cacheRuntime/index.ts
137
+ function stringValue(value) {
138
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
139
+ }
140
+ function minorModeState(record) {
141
+ const state = record.state;
142
+ return {
143
+ source: record.descriptor.source,
144
+ id: record.descriptor.id,
145
+ activation: state.activation,
146
+ ...state.modelContextVariant ? { modelContextVariant: state.modelContextVariant } : {}
147
+ };
148
+ }
149
+ function isErrorMessage(message) {
150
+ const record = typeof message === "object" && message !== null ? message : void 0;
151
+ return record?.stopReason === "error" || record?.stopReason === "aborted";
152
+ }
153
+ function createCacheRuntime(dependencies, environment) {
154
+ const parentNamespaceEnvironment = new OwnedEnvironmentValue(PO_PROMPT_CACHE_PARENT_NAMESPACE_ENV);
155
+ const rootSessionEnvironment = new OwnedEnvironmentValue(PO_PROMPT_CACHE_ROOT_SESSION_ENV);
156
+ const projectedParentNamespace = stringValue(environment[PO_PROMPT_CACHE_PARENT_NAMESPACE_ENV]);
157
+ const childProjection = stringValue(environment[PO_PROMPT_CACHE_CHILD_PROJECTION_ENV]);
158
+ const isProjectedChild = Boolean(projectedParentNamespace && childProjection);
159
+ let rootSessionId;
160
+ let parentNamespace = projectedParentNamespace;
161
+ let minorModes = [];
162
+ const refreshParentNamespace = () => {
163
+ if (isProjectedChild) return parentNamespace;
164
+ if (!rootSessionId) return void 0;
165
+ const harness = getSessionHarnessState(rootSessionId);
166
+ parentNamespace = createParentPromptCacheNamespace({
167
+ rootSessionId,
168
+ compositionFingerprint: harness.compositionFingerprint,
169
+ majorMode: harness.majorMode,
170
+ domains: harness.domains,
171
+ profile: harness.profile,
172
+ persona: harness.personaFile,
173
+ minorModes
174
+ }, sha256Base64Url);
175
+ parentNamespaceEnvironment.set(parentNamespace);
176
+ rootSessionEnvironment.set(createRootSessionIdentity(rootSessionId, sha256Base64Url));
177
+ return parentNamespace;
178
+ };
179
+ const plugin = (cordis) => {
180
+ cordis.inject([DOOM_MINOR_MODE_CATALOG_SERVICE], (context) => {
181
+ if (isProjectedChild) return void 0;
182
+ const catalog = readMinorModeCatalog(context);
183
+ if (!catalog) return void 0;
184
+ const refreshModes = () => {
185
+ minorModes = catalog.getSnapshot().modes.map(minorModeState);
186
+ refreshParentNamespace();
187
+ };
188
+ refreshModes();
189
+ return catalog.subscribe(refreshModes);
190
+ });
191
+ };
192
+ return {
193
+ plugin,
194
+ events: {
195
+ session_start: (_event, ctx) => {
196
+ if (!isProjectedChild) {
197
+ rootSessionId = stringValue(ctx.sessionId);
198
+ refreshParentNamespace();
199
+ }
200
+ },
201
+ message_end: (event) => {
202
+ if (isErrorMessage(event.message)) return;
203
+ const usage = normalizePromptCacheUsage(event.message);
204
+ if (usage) dependencies.telemetry.observe(usage, dependencies.now());
205
+ }
206
+ },
207
+ dispose: () => {
208
+ parentNamespaceEnvironment.restore();
209
+ rootSessionEnvironment.restore();
210
+ dependencies.telemetry.reset();
211
+ }
212
+ };
213
+ }
214
+ //#endregion
215
+ //#region src/services/optimizerSupport/index.ts
216
+ function createOptimizerSupport(STATE_DIR, environment) {
217
+ const MODELS_JSON_PATH = join(STATE_DIR, "models.json");
218
+ function isVirtualRoutingModel(model) {
219
+ return !!model && (isNativeVirtualModel(model) || model.provider.toLowerCase() === "router");
220
+ }
221
+ const ROUTED_FALLBACK_MODEL_SYMBOL = Symbol("pi-cache-optimizer.routed-fallback-model");
222
+ const LOG_PREFIX = "pi-cache-optimizer";
223
+ const STATE_FILE_PATH = join(STATE_DIR, "pi-cache-optimizer-stats.json");
224
+ const LEGACY_STATE_FILE_PATH = join(STATE_DIR, "deepseek-cache-optimizer-stats.json");
225
+ const SHARD_STATE_DIR = join(STATE_DIR, "pi-cache-optimizer-stats.d");
226
+ const SHARD_FILES_DIR = join(SHARD_STATE_DIR, "shards");
227
+ const SHARD_EPOCH_DIR = join(SHARD_STATE_DIR, "epochs");
228
+ const SHARD_MODEL_EPOCH_DIR = join(SHARD_EPOCH_DIR, "models");
229
+ const SHARD_MAINTENANCE_DIR = join(SHARD_STATE_DIR, "maintenance");
230
+ const SHARD_GLOBAL_EPOCH_PATH = join(SHARD_EPOCH_DIR, "global.json");
231
+ const SHARD_CLEANUP_LOCK_PATH = join(SHARD_MAINTENANCE_DIR, "cleanup.lock");
232
+ const SHARD_CLEANUP_MARKER_PATH = join(SHARD_MAINTENANCE_DIR, "last-cleanup");
233
+ const CONFIG_FILE_PATH = join(STATE_DIR, "pi-cache-optimizer-config.json");
234
+ const SHARD_RETENTION_MS = 1728e5;
235
+ const SHARD_TEMP_RETENTION_MS = 864e5;
236
+ const SHARD_CLEANUP_INTERVAL_MS = 216e5;
237
+ const SHARD_CLEANUP_LOCK_STALE_MS = 36e5;
238
+ const FOOTER_MODE_ENV = "PI_CACHE_OPTIMIZER_FOOTER_MODE";
239
+ const PI_VIRTUAL_MODEL_API = "pi-virtual";
240
+ const ASSISTANT_MESSAGE_MODEL_TOKEN_KEYS = ["model", "name"];
241
+ const OPENAI_REASONING_MODEL_PATTERN = /(^|[/\s:_-])o[1345]($|[-_.:/\s])/;
242
+ const XAI_MODEL_PATTERN = /(^|[/\s:_-])xai($|[-_.:/\s])/;
243
+ const MIMO_MODEL_PATTERN = /(^|[/\s:_-])mi-?mo($|[-_.:/\s])/i;
244
+ const PPLX_MODEL_PATTERN = /(^|[/\s:_-])pplx($|[-_.:/\s])/i;
245
+ const NOVA_MODEL_PATTERN = /(^|[/\s:_-])nova($|[-_.:/\s])/i;
246
+ const MPT_MODEL_PATTERN = /(^|[/\s:_-])mpt($|[-_.:/\s])/i;
247
+ const ALEPH_MODEL_PATTERN = /(^|[/\s:_-])aleph($|[-_.:/\s])/i;
248
+ const ARCTIC_MODEL_PATTERN = /(^|[/\s:_-])arctic($|[-_.:/\s])/i;
249
+ const AYA_MODEL_PATTERN = /(^|[/\s:_-])aya($|[-_.:/\s])/i;
250
+ const ORION_MODEL_PATTERN = /(^|[/\s:_-])orion($|[-_.:/\s])/i;
251
+ function hashSessionId(sessionId) {
252
+ return createHash("sha256").update(sessionId).digest("hex").slice(0, 16);
253
+ }
254
+ function firstNonEmptyString(...values) {
255
+ for (const value of values) if (isNonEmptyString(value)) return value.trim();
256
+ }
257
+ function isRoutedFallbackModel(model) {
258
+ return !!model && model[ROUTED_FALLBACK_MODEL_SYMBOL] === true;
259
+ }
260
+ function isNativeVirtualModel(model) {
261
+ return model?.api === PI_VIRTUAL_MODEL_API;
262
+ }
263
+ function makeSessionModelKey(sessionHash, provider, id) {
264
+ return `${sessionHash}:${provider}/${id}`;
265
+ }
266
+ function asRecord(value) {
267
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
268
+ return value;
269
+ }
270
+ function lower(value) {
271
+ return typeof value === "string" ? value.toLowerCase() : "";
272
+ }
273
+ function getNumber(value) {
274
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
275
+ }
276
+ function getNonNegativeNumber(record, key) {
277
+ const value = getNumber(record[key]);
278
+ return value !== void 0 && value >= 0 ? value : void 0;
279
+ }
280
+ const NESTED_COMPAT_KEYS = [
281
+ "openRouterRouting",
282
+ "vercelGatewayRouting",
283
+ "chatTemplateKwargs",
284
+ "chatTemplateArgs"
285
+ ];
286
+ function findLastExactModelDefinition(models, modelId) {
287
+ if (!models) return void 0;
288
+ for (let index = models.length - 1; index >= 0; index--) {
289
+ const candidate = asRecord(models[index]);
290
+ if (candidate?.id === modelId) return candidate;
291
+ }
292
+ }
293
+ function mergeCacheCompat(...sources) {
294
+ const merged = {};
295
+ for (const source of sources) {
296
+ if (!source) continue;
297
+ const previousNested = Object.fromEntries(NESTED_COMPAT_KEYS.map((key) => [key, asRecord(merged[key])]));
298
+ Object.assign(merged, source);
299
+ for (const key of NESTED_COMPAT_KEYS) {
300
+ const baseValue = previousNested[key];
301
+ const overrideValue = asRecord(source[key]);
302
+ if (baseValue || overrideValue) merged[key] = {
303
+ ...baseValue,
304
+ ...overrideValue
305
+ };
306
+ }
307
+ }
308
+ return merged;
309
+ }
310
+ function getEffectiveCompatSources(model, config) {
311
+ const provider = asRecord(asRecord(asRecord(config)?.providers)?.[model.provider]);
312
+ const providerCompat = asRecord(provider?.compat);
313
+ const customModelCompat = asRecord(asRecord(findLastExactModelDefinition(Array.isArray(provider?.models) ? provider.models : void 0, model.id))?.compat);
314
+ const runtimeCompat = asRecord(model.compat);
315
+ const modelOverrideCompat = asRecord(asRecord(asRecord(provider?.modelOverrides)?.[model.id])?.compat);
316
+ return [
317
+ ...providerCompat ? [{
318
+ source: "provider",
319
+ compat: providerCompat
320
+ }] : [],
321
+ ...customModelCompat ? [{
322
+ source: "model",
323
+ compat: customModelCompat
324
+ }] : [],
325
+ ...runtimeCompat ? [{
326
+ source: "runtime",
327
+ compat: runtimeCompat
328
+ }] : [],
329
+ ...modelOverrideCompat ? [{
330
+ source: "modelOverride",
331
+ compat: modelOverrideCompat
332
+ }] : []
333
+ ];
334
+ }
335
+ function resolveEffectiveCompatFromConfig(model, config) {
336
+ return mergeCacheCompat(...getEffectiveCompatSources(model, config).map(({ compat }) => compat));
337
+ }
338
+ function isOptionalString(value) {
339
+ return value === void 0 || typeof value === "string" && value.length > 0;
340
+ }
341
+ function isOptionalBoolean(value) {
342
+ return value === void 0 || typeof value === "boolean";
343
+ }
344
+ function isFiniteNumber(value) {
345
+ return typeof value === "number" && Number.isFinite(value);
346
+ }
347
+ function isRecordOfStrings(value) {
348
+ const record = asRecord(value);
349
+ return !!record && Object.values(record).every((entry) => typeof entry === "string");
350
+ }
351
+ function isThinkingLevelMap(value) {
352
+ const map = asRecord(value);
353
+ if (!map) return false;
354
+ return [
355
+ "off",
356
+ "minimal",
357
+ "low",
358
+ "medium",
359
+ "high",
360
+ "xhigh",
361
+ "max"
362
+ ].every((key) => map[key] === void 0 || map[key] === null || typeof map[key] === "string");
363
+ }
364
+ function isValidModelCostTier(value) {
365
+ const tier = asRecord(value);
366
+ return !!tier && isFiniteNumber(tier.inputTokensAbove) && isFiniteNumber(tier.input) && isFiniteNumber(tier.output) && isFiniteNumber(tier.cacheRead) && isFiniteNumber(tier.cacheWrite);
367
+ }
368
+ function isValidModelCost(value, partial) {
369
+ const cost = asRecord(value);
370
+ if (!cost) return false;
371
+ for (const key of [
372
+ "input",
373
+ "output",
374
+ "cacheRead",
375
+ "cacheWrite"
376
+ ]) if ((!partial || cost[key] !== void 0) && !isFiniteNumber(cost[key])) return false;
377
+ return cost.tiers === void 0 || Array.isArray(cost.tiers) && cost.tiers.every(isValidModelCostTier);
378
+ }
379
+ function isValidModelDefinition(value) {
380
+ const model = asRecord(value);
381
+ if (!model || !isNonEmptyString(model.id)) return false;
382
+ if (!isOptionalString(model.name) || !isOptionalString(model.api) || !isOptionalString(model.baseUrl)) return false;
383
+ if (!isOptionalBoolean(model.reasoning) || !isValidCompatRecord(model.compat)) return false;
384
+ if (model.thinkingLevelMap !== void 0 && !isThinkingLevelMap(model.thinkingLevelMap)) return false;
385
+ if (model.input !== void 0 && (!Array.isArray(model.input) || !model.input.every((entry) => entry === "text" || entry === "image"))) return false;
386
+ if (model.cost !== void 0 && !isValidModelCost(model.cost, false)) return false;
387
+ if (model.contextWindow !== void 0 && !isFiniteNumber(model.contextWindow)) return false;
388
+ if (model.maxTokens !== void 0 && !isFiniteNumber(model.maxTokens)) return false;
389
+ if (model.samplingParams !== void 0 && !asRecord(model.samplingParams)) return false;
390
+ if (model.headers !== void 0 && !isRecordOfStrings(model.headers)) return false;
391
+ return true;
392
+ }
393
+ function isValidModelOverride(value) {
394
+ const override = asRecord(value);
395
+ if (!override || !isValidCompatRecord(override.compat)) return false;
396
+ if (!isOptionalString(override.name) || !isOptionalBoolean(override.reasoning)) return false;
397
+ if (override.thinkingLevelMap !== void 0 && !isThinkingLevelMap(override.thinkingLevelMap)) return false;
398
+ if (override.input !== void 0 && (!Array.isArray(override.input) || !override.input.every((entry) => entry === "text" || entry === "image"))) return false;
399
+ if (override.cost !== void 0 && !isValidModelCost(override.cost, true)) return false;
400
+ if (override.contextWindow !== void 0 && !isFiniteNumber(override.contextWindow)) return false;
401
+ if (override.maxTokens !== void 0 && !isFiniteNumber(override.maxTokens)) return false;
402
+ if (override.samplingParams !== void 0 && !asRecord(override.samplingParams)) return false;
403
+ if (override.headers !== void 0 && !isRecordOfStrings(override.headers)) return false;
404
+ return true;
405
+ }
406
+ function isValidOpenAICompletionsCompat(compat) {
407
+ if ([
408
+ "supportsStore",
409
+ "supportsDeveloperRole",
410
+ "supportsReasoningEffort",
411
+ "supportsUsageInStreaming",
412
+ "requiresToolResultName",
413
+ "requiresAssistantAfterToolResult",
414
+ "requiresThinkingAsText",
415
+ "requiresReasoningContentOnAssistantMessages",
416
+ "supportsOpenAIGrammarTools",
417
+ "supportsStrictMode",
418
+ "sendSessionAffinityHeaders",
419
+ "supportsLongCacheRetention"
420
+ ].some((key) => !isOptionalBoolean(compat[key]))) return false;
421
+ if (compat.maxTokensField !== void 0 && compat.maxTokensField !== "max_completion_tokens" && compat.maxTokensField !== "max_tokens") return false;
422
+ if (compat.thinkingFormat !== void 0 && ![
423
+ "openai",
424
+ "openrouter",
425
+ "together",
426
+ "baseten",
427
+ "deepseek",
428
+ "zai",
429
+ "qwen",
430
+ "chat-template",
431
+ "qwen-chat-template",
432
+ "string-thinking",
433
+ "ant-ling"
434
+ ].includes(typeof compat.thinkingFormat === "string" ? compat.thinkingFormat : "")) return false;
435
+ if (compat.cacheControlFormat !== void 0 && compat.cacheControlFormat !== "anthropic") return false;
436
+ if (compat.deferredToolsMode !== void 0 && compat.deferredToolsMode !== "kimi") return false;
437
+ if (compat.sessionAffinityFormat !== void 0 && ![
438
+ "openai",
439
+ "openai-nosession",
440
+ "openrouter"
441
+ ].includes(typeof compat.sessionAffinityFormat === "string" ? compat.sessionAffinityFormat : "")) return false;
442
+ for (const key of NESTED_COMPAT_KEYS) if (compat[key] !== void 0 && !asRecord(compat[key])) return false;
443
+ return true;
444
+ }
445
+ function isValidOpenAIResponsesCompat(compat) {
446
+ if ([
447
+ "supportsDeveloperRole",
448
+ "supportsLongCacheRetention",
449
+ "supportsStrictMode",
450
+ "supportsOpenAIGrammarTools",
451
+ "supportsAdditionalTools",
452
+ "supportsToolSearch"
453
+ ].some((key) => !isOptionalBoolean(compat[key]))) return false;
454
+ return compat.sessionAffinityFormat === void 0 || [
455
+ "openai",
456
+ "openai-nosession",
457
+ "openrouter"
458
+ ].includes(typeof compat.sessionAffinityFormat === "string" ? compat.sessionAffinityFormat : "");
459
+ }
460
+ function isValidAnthropicMessagesCompat(compat) {
461
+ return [
462
+ "supportsEagerToolInputStreaming",
463
+ "supportsLongCacheRetention",
464
+ "sendSessionAffinityHeaders",
465
+ "supportsCacheControlOnTools",
466
+ "supportsTemperature",
467
+ "forceAdaptiveThinking",
468
+ "allowEmptySignature",
469
+ "supportsStrictTools",
470
+ "supportsToolReferences"
471
+ ].every((key) => isOptionalBoolean(compat[key]));
472
+ }
473
+ function isValidCompatRecord(value) {
474
+ if (value === void 0) return true;
475
+ const compat = asRecord(value);
476
+ return !!compat && !Object.prototype.hasOwnProperty.call(compat, "supportsPromptCacheKey") && (isValidOpenAICompletionsCompat(compat) || isValidOpenAIResponsesCompat(compat) || isValidAnthropicMessagesCompat(compat));
477
+ }
478
+ function isValidModelsConfigForEffectiveCompat(value) {
479
+ const root = asRecord(value);
480
+ const providers = asRecord(root?.providers);
481
+ if (!root || !providers) return false;
482
+ for (const providerValue of Object.values(providers)) {
483
+ const provider = asRecord(providerValue);
484
+ if (!provider) return false;
485
+ if (!isOptionalString(provider.name) || !isOptionalString(provider.baseUrl) || !isOptionalString(provider.apiKey) || !isOptionalString(provider.api)) return false;
486
+ if (!isOptionalBoolean(provider.authHeader) || !isValidCompatRecord(provider.compat)) return false;
487
+ if (provider.oauth !== void 0 && provider.oauth !== "radius") return false;
488
+ if (provider.headers !== void 0 && !isRecordOfStrings(provider.headers)) return false;
489
+ if (provider.models !== void 0 && (!Array.isArray(provider.models) || !provider.models.every(isValidModelDefinition))) return false;
490
+ const overrides = provider.modelOverrides === void 0 ? void 0 : asRecord(provider.modelOverrides);
491
+ if (provider.modelOverrides !== void 0 && (!overrides || !Object.values(overrides).every(isValidModelOverride))) return false;
492
+ }
493
+ return true;
494
+ }
495
+ let modelsConfigCache;
496
+ function getModelsConfigSignature() {
497
+ try {
498
+ const info = statSync(MODELS_JSON_PATH);
499
+ return `${info.dev}:${info.ino}:${info.size}:${info.mtimeMs}:${info.ctimeMs}`;
500
+ } catch {
501
+ return "missing";
502
+ }
503
+ }
504
+ function readEffectiveCompatConfig() {
505
+ const signature = getModelsConfigSignature();
506
+ if (modelsConfigCache?.signature === signature) return modelsConfigCache.value;
507
+ let value;
508
+ if (signature !== "missing") try {
509
+ const parsed = parseJsonc(readFileSync(MODELS_JSON_PATH, "utf8"));
510
+ value = isValidModelsConfigForEffectiveCompat(parsed) ? parsed : void 0;
511
+ } catch {
512
+ value = void 0;
513
+ }
514
+ modelsConfigCache = {
515
+ signature,
516
+ value
517
+ };
518
+ return value;
519
+ }
520
+ function getCompat(model) {
521
+ if (!model) return {};
522
+ return resolveEffectiveCompatFromConfig(model, readEffectiveCompatConfig());
523
+ }
524
+ function joinDisplayPath(base, child, platform = process.platform) {
525
+ const sep = platform.startsWith("win") ? "\\" : "/";
526
+ return `${base.replace(/[\\/]+$/, "")}${sep}${child}`;
527
+ }
528
+ function getAgentDirDisplayPath(platform = process.platform, agentDir = STATE_DIR, homeDir = homedir()) {
529
+ const sep = platform.startsWith("win") ? "\\" : "/";
530
+ const normalizedAgentDir = agentDir.replace(/[\\/]+/g, sep);
531
+ const normalizedHomeDir = homeDir.replace(/[\\/]+/g, sep).replace(/[\\/]+$/, "");
532
+ const homePrefix = `${normalizedHomeDir}${sep}`;
533
+ if (normalizedAgentDir === normalizedHomeDir || normalizedAgentDir.startsWith(homePrefix)) {
534
+ const relative = normalizedAgentDir.slice(normalizedHomeDir.length).replace(/^[\\/]+/, "");
535
+ const homeLabel = platform.startsWith("win") ? "%USERPROFILE%" : "~";
536
+ return relative ? `${homeLabel}${sep}${relative}` : homeLabel;
537
+ }
538
+ return normalizedAgentDir;
539
+ }
540
+ function getModelsJsonDisplayPath(platform = process.platform, agentDir = STATE_DIR, homeDir = homedir()) {
541
+ return joinDisplayPath(getAgentDirDisplayPath(platform, agentDir, homeDir), "models.json", platform);
542
+ }
543
+ function parseFooterStatsMode(value) {
544
+ if (typeof value !== "string") return void 0;
545
+ const normalized = value.trim().toLowerCase();
546
+ return normalized === "session" || normalized === "total" || normalized === "process" ? normalized : void 0;
547
+ }
548
+ function parsePersistedCacheOptimizerConfig(value) {
549
+ const record = asRecord(value);
550
+ if (!record || record.version !== 1 && record.version !== 2 && record.version !== 3) return void 0;
551
+ const allowedTopLevel = new Set(record.version === 1 ? ["version", "footerMode"] : record.version === 2 ? [
552
+ "version",
553
+ "footerMode",
554
+ "promptCacheKey"
555
+ ] : [
556
+ "version",
557
+ "footerMode",
558
+ "promptCacheKey",
559
+ "features"
560
+ ]);
561
+ if (Object.keys(record).some((key) => !allowedTopLevel.has(key))) return void 0;
562
+ const footerMode = parseFooterStatsMode(record.footerMode);
563
+ if (record.footerMode !== void 0 && !footerMode) return void 0;
564
+ if (record.version === 1) return {
565
+ version: 1,
566
+ ...footerMode ? { footerMode } : {}
567
+ };
568
+ const rawFeatures = record.version === 3 ? record.features : void 0;
569
+ if (rawFeatures !== void 0 && !asRecord(rawFeatures)) return void 0;
570
+ const featuresRecord = asRecord(rawFeatures);
571
+ const featureNames = /* @__PURE__ */ new Set([
572
+ "promptRewrite",
573
+ "virtualRewrite",
574
+ "skillCompression",
575
+ "openAICacheKey",
576
+ "toolOrder"
577
+ ]);
578
+ if (featuresRecord && Object.keys(featuresRecord).some((key) => !featureNames.has(key) || typeof featuresRecord[key] !== "boolean")) return void 0;
579
+ const rawPromptCacheKey = record.promptCacheKey;
580
+ if (rawPromptCacheKey !== void 0 && !asRecord(rawPromptCacheKey)) return void 0;
581
+ const promptCacheKey = asRecord(rawPromptCacheKey);
582
+ if (promptCacheKey && Object.keys(promptCacheKey).some((key) => key !== "omit")) return void 0;
583
+ const omit = promptCacheKey?.omit;
584
+ if (omit !== void 0 && (!Array.isArray(omit) || omit.some((value) => !isNonEmptyString(value)))) return void 0;
585
+ const stringOmit = omit;
586
+ const uniqueOmit = stringOmit ? [...new Set(stringOmit.map((value) => value.trim()))].sort() : void 0;
587
+ return {
588
+ version: record.version === 3 ? 3 : 2,
589
+ ...footerMode ? { footerMode } : {},
590
+ ...uniqueOmit && uniqueOmit.length > 0 ? { promptCacheKey: { omit: uniqueOmit } } : {},
591
+ ...featuresRecord ? { features: Object.fromEntries(Object.entries(featuresRecord)) } : {}
592
+ };
593
+ }
594
+ function normalizePersistedCacheOptimizerConfig(value) {
595
+ if (!value) return { version: 2 };
596
+ const features = value.version === 3 ? value.features : void 0;
597
+ return {
598
+ version: features ? 3 : 2,
599
+ ...value.footerMode ? { footerMode: value.footerMode } : {},
600
+ ...value.version !== 1 && value.promptCacheKey ? { promptCacheKey: value.promptCacheKey } : {},
601
+ ...features ? { features } : {}
602
+ };
603
+ }
604
+ function readPersistedCacheOptimizerConfig(configPath = CONFIG_FILE_PATH) {
605
+ try {
606
+ return normalizePersistedCacheOptimizerConfig(parsePersistedCacheOptimizerConfig(JSON.parse(readFileSync(configPath, "utf8"))));
607
+ } catch (error) {
608
+ if (getErrorCode(error) !== "ENOENT") console.warn(`${LOG_PREFIX}: failed to read optimizer config; using defaults`, error);
609
+ return { version: 2 };
610
+ }
611
+ }
612
+ function resolveFooterStatsMode(configuredMode, env = environment) {
613
+ if (configuredMode) return {
614
+ mode: configuredMode,
615
+ source: "config"
616
+ };
617
+ const envMode = parseFooterStatsMode(env[FOOTER_MODE_ENV]);
618
+ return envMode ? {
619
+ mode: envMode,
620
+ source: "env"
621
+ } : {
622
+ mode: "session",
623
+ source: "default"
624
+ };
625
+ }
626
+ function footerStatsMode(env = environment, configuredMode = persistedFooterStatsMode) {
627
+ return resolveFooterStatsMode(configuredMode, env).mode;
628
+ }
629
+ const persistedFooterStatsMode = readPersistedCacheOptimizerConfig().footerMode;
630
+ function isAssistantMessage(message) {
631
+ return asRecord(message)?.role === "assistant";
632
+ }
633
+ function getAssistantRecord(message) {
634
+ const record = asRecord(message);
635
+ return record?.role === "assistant" ? record : void 0;
636
+ }
637
+ function getModelIdNameTokenValues(model) {
638
+ if (!model) return [];
639
+ return [model.id, model.name].map(lower).filter(Boolean);
640
+ }
641
+ function getAssistantMessageModelTokenValues(message) {
642
+ const record = asRecord(message);
643
+ if (!record) return [];
644
+ return ASSISTANT_MESSAGE_MODEL_TOKEN_KEYS.map((key) => lower(record[key])).filter(Boolean);
645
+ }
646
+ function hasAnyTokenContaining(tokens, needles) {
647
+ return tokens.some((token) => needles.some((needle) => token.includes(needle)));
648
+ }
649
+ function modelOrAssistantMessageHas(message, model, needles) {
650
+ return hasAnyTokenContaining([...getModelIdNameTokenValues(model), ...getAssistantMessageModelTokenValues(message)], needles);
651
+ }
652
+ function isDeepSeekLikeModel(model) {
653
+ return hasAnyTokenContaining(getModelIdNameTokenValues(model), ["deepseek"]);
654
+ }
655
+ function isDeepSeekLikeAssistantMessage(message, model) {
656
+ return modelOrAssistantMessageHas(message, model, ["deepseek"]);
657
+ }
658
+ function isOpenAICompatibleApi(api) {
659
+ const value = lower(api);
660
+ return value === "openai-completions" || value === "openai-responses";
661
+ }
662
+ function isOpenAICompatibleProxyApi(api) {
663
+ return lower(api) === "openai-completions";
664
+ }
665
+ function isPiBuiltInLlamaCppModel(model) {
666
+ if (lower(model?.provider) !== "llama.cpp" || !isOpenAICompatibleProxyApi(model?.api)) return false;
667
+ const compat = getCompat(model);
668
+ return compat.supportsStore === false && compat.supportsDeveloperRole === false && compat.supportsReasoningEffort === false && compat.supportsStrictMode === false && compat.maxTokensField === "max_tokens" && compat.sendSessionAffinityHeaders === void 0 && compat.sessionAffinityFormat === void 0 && compat.supportsLongCacheRetention === void 0;
669
+ }
670
+ function hasAnthropicCacheTtlOrderError(message) {
671
+ const record = getAssistantRecord(message);
672
+ if (record?.stopReason !== "error" || typeof record.errorMessage !== "string") return false;
673
+ const error = lower(record.errorMessage);
674
+ return error.includes("cache_control") && error.includes("ttl='1h'") && error.includes("ttl='5m'") && error.includes("must not come after");
675
+ }
676
+ function isOpenAIFamilyToken(token) {
677
+ return token.includes("gpt-") || token.includes("chatgpt") || OPENAI_REASONING_MODEL_PATTERN.test(token);
678
+ }
679
+ function isOpenAIFamilyModel(model) {
680
+ return getModelIdNameTokenValues(model).some(isOpenAIFamilyToken);
681
+ }
682
+ function isOpenAIFamilyAssistantMessage(message, model) {
683
+ return [...getModelIdNameTokenValues(model), ...getAssistantMessageModelTokenValues(message)].some(isOpenAIFamilyToken);
684
+ }
685
+ function isClaudeLikeModel(model) {
686
+ return hasAnyTokenContaining(getModelIdNameTokenValues(model), ["anthropic", "claude"]);
687
+ }
688
+ function isClaudeLikeAssistantMessage(message, model) {
689
+ return modelOrAssistantMessageHas(message, model, ["anthropic", "claude"]);
690
+ }
691
+ function isGeminiLikeModel(model) {
692
+ return hasAnyTokenContaining(getModelIdNameTokenValues(model), ["gemini", "vertex"]);
693
+ }
694
+ function isGeminiLikeAssistantMessage(message, model) {
695
+ return modelOrAssistantMessageHas(message, model, ["gemini", "vertex"]);
696
+ }
697
+ const ADAPTIVE_OPUS_PATTERN = /(^|[/\s:_-])(opus-4[.-][6-9]|opus-4-[1-9][0-9]|opus-([5-9]|[1-9][0-9]))($|[-_.:/\s[])/i;
698
+ const ADAPTIVE_SONNET_PATTERN = /(^|[/\s:_-])(sonnet-4[.-][6-9]|sonnet-4-[1-9][0-9]|sonnet-([5-9]|[1-9][0-9]))($|[-_.:/\s[])/i;
699
+ const ADAPTIVE_FABLE_PATTERN = /(^|[/\s:_-])fable-([5-9]|[1-9][0-9])($|[-_.:/\s[])/i;
700
+ function isAdaptiveGenerationModel(model) {
701
+ if (!model) return false;
702
+ return getModelIdNameTokenValues(model).some((t) => ADAPTIVE_OPUS_PATTERN.test(t) || ADAPTIVE_SONNET_PATTERN.test(t) || ADAPTIVE_FABLE_PATTERN.test(t));
703
+ }
704
+ function isKimiCodingAdaptiveModel(model) {
705
+ if (!model) return false;
706
+ const provider = lower(model.provider);
707
+ const baseUrl = lower(model.baseUrl);
708
+ if (!(provider.includes("kimi-coding") || baseUrl.includes("api.kimi.com/coding"))) return false;
709
+ return getModelIdNameTokenValues(model).some((token) => token === "k3" || token.includes("kimi-k3") || token.includes("kimi k3") || token.includes("kimi-for-coding") || token.includes("kimi for coding"));
710
+ }
711
+ function isKimiCodingEmptySignatureModel(model) {
712
+ if (!isKimiCodingAdaptiveModel(model)) return false;
713
+ return getModelIdNameTokenValues(model).some((token) => token === "k3" || token === "kimi-k3" || token.startsWith("kimi-k3-") || token === "kimi k3" || token === "kimi-for-coding" || token === "kimi for coding");
714
+ }
715
+ function isAdaptiveThinkingCompatApplicable(model) {
716
+ if (isRoutedFallbackModel(model) && !isNonEmptyString(model.baseUrl)) return false;
717
+ return lower(model.api) === "anthropic-messages" && (isAdaptiveGenerationModel(model) || isKimiCodingAdaptiveModel(model));
718
+ }
719
+ function describeMissingAdaptiveThinkingCompat(model) {
720
+ const compat = getCompat(model);
721
+ const missing = [];
722
+ if (compat.forceAdaptiveThinking !== true) missing.push("forceAdaptiveThinking");
723
+ if (isKimiCodingEmptySignatureModel(model) && compat.allowEmptySignature !== true) missing.push("allowEmptySignature");
724
+ return missing;
725
+ }
726
+ function buildAdaptiveThinkingCompatSuggestion(missing) {
727
+ const suggestion = {};
728
+ if (missing.includes("forceAdaptiveThinking")) suggestion.forceAdaptiveThinking = true;
729
+ if (missing.includes("allowEmptySignature")) suggestion.allowEmptySignature = true;
730
+ return suggestion;
731
+ }
732
+ function appendAdaptiveThinkingCompatAdviceLines(lines, missing, placement = {}) {
733
+ const suggestion = buildAdaptiveThinkingCompatSuggestion(missing);
734
+ if (Object.keys(suggestion).length > 0) {
735
+ lines.push("Suggested fix:");
736
+ lines.push(JSON.stringify(suggestion, null, 2));
737
+ }
738
+ lines.push("- forceAdaptiveThinking: true tells Pi to use adaptive thinking format");
739
+ lines.push(" (thinking: {type: 'adaptive'}) instead of legacy budget tokens format.");
740
+ lines.push(" Without this flag, Pi sends legacy thinking which adaptive-only upstreams reject.");
741
+ if (missing.includes("allowEmptySignature")) lines.push("- allowEmptySignature: true preserves Kimi Coding K3 thinking blocks whose replay signature is empty.");
742
+ appendCredentialSafeProviderGuidance(lines, placement, suggestion);
743
+ }
744
+ function buildAdaptiveThinkingCompatWarningText(key, missing) {
745
+ const slashIdx = key.indexOf("/");
746
+ const providerLabel = slashIdx > 0 ? key.slice(0, slashIdx) : key;
747
+ const modelId = slashIdx > 0 ? key.slice(slashIdx + 1) : void 0;
748
+ const modelsJsonPath = getModelsJsonDisplayPath();
749
+ const lines = [
750
+ `💡 pi-cache-optimizer: ${key} is an adaptive-generation model but merged compat lacks ${missing.join(" and ")}.`,
751
+ `Without the required compat, Pi may send legacy thinking or replay thinking blocks incorrectly.`,
752
+ `Edit ${modelsJsonPath} -> providers["${providerLabel}"] -> compat (at the same level as baseUrl/api/apiKey/models).`,
753
+ ""
754
+ ];
755
+ appendAdaptiveThinkingCompatAdviceLines(lines, missing, {
756
+ providerLabel,
757
+ modelId
758
+ });
759
+ return lines.join("\n");
760
+ }
761
+ function isKimiLikeModel(model) {
762
+ return hasAnyTokenContaining(getModelIdNameTokenValues(model), ["kimi"]);
763
+ }
764
+ function isKimiLikeAssistantMessage(message, model) {
765
+ return modelOrAssistantMessageHas(message, model, ["kimi"]);
766
+ }
767
+ function isQwenLikeModel(model) {
768
+ return hasAnyTokenContaining(getModelIdNameTokenValues(model), ["qwen"]);
769
+ }
770
+ function isQwenLikeAssistantMessage(message, model) {
771
+ return modelOrAssistantMessageHas(message, model, ["qwen"]);
772
+ }
773
+ function isGLMLikeModel(model) {
774
+ return hasAnyTokenContaining(getModelIdNameTokenValues(model), ["glm"]);
775
+ }
776
+ function isGLMLikeAssistantMessage(message, model) {
777
+ return modelOrAssistantMessageHas(message, model, ["glm"]);
778
+ }
779
+ function isMiniMaxLikeModel(model) {
780
+ return hasAnyTokenContaining(getModelIdNameTokenValues(model), ["minimax"]);
781
+ }
782
+ function isMiniMaxLikeAssistantMessage(message, model) {
783
+ return modelOrAssistantMessageHas(message, model, ["minimax"]);
784
+ }
785
+ function isMimoLikeModel(model) {
786
+ const tokens = getModelIdNameTokenValues(model);
787
+ return hasAnyTokenContaining(tokens, ["xiaomimimo"]) || tokens.some((t) => MIMO_MODEL_PATTERN.test(t));
788
+ }
789
+ function isMimoLikeAssistantMessage(message, model) {
790
+ const allTokens = [...getModelIdNameTokenValues(model), ...getAssistantMessageModelTokenValues(message)];
791
+ return hasAnyTokenContaining(allTokens, ["xiaomimimo"]) || allTokens.some((t) => MIMO_MODEL_PATTERN.test(t));
792
+ }
793
+ function isHunyuanLikeModel(model) {
794
+ return hasAnyTokenContaining(getModelIdNameTokenValues(model), ["hunyuan"]);
795
+ }
796
+ function isHunyuanLikeAssistantMessage(message, model) {
797
+ return modelOrAssistantMessageHas(message, model, ["hunyuan"]);
798
+ }
799
+ function isMistralLikeModel(model) {
800
+ return hasAnyTokenContaining(getModelIdNameTokenValues(model), [
801
+ "mistral",
802
+ "mixtral",
803
+ "codestral"
804
+ ]);
805
+ }
806
+ function isMistralLikeAssistantMessage(message, model) {
807
+ return modelOrAssistantMessageHas(message, model, [
808
+ "mistral",
809
+ "mixtral",
810
+ "codestral"
811
+ ]);
812
+ }
813
+ function isGrokLikeModel(model) {
814
+ const tokens = getModelIdNameTokenValues(model);
815
+ return hasAnyTokenContaining(tokens, ["grok"]) || tokens.some((t) => XAI_MODEL_PATTERN.test(t));
816
+ }
817
+ function isGrokLikeAssistantMessage(message, model) {
818
+ const allTokens = [...getModelIdNameTokenValues(model), ...getAssistantMessageModelTokenValues(message)];
819
+ return hasAnyTokenContaining(allTokens, ["grok"]) || allTokens.some((t) => XAI_MODEL_PATTERN.test(t));
820
+ }
821
+ function isLlamaLikeModel(model) {
822
+ return hasAnyTokenContaining(getModelIdNameTokenValues(model), ["llama"]);
823
+ }
824
+ function isLlamaLikeAssistantMessage(message, model) {
825
+ return modelOrAssistantMessageHas(message, model, ["llama"]);
826
+ }
827
+ function isNemotronLikeModel(model) {
828
+ return hasAnyTokenContaining(getModelIdNameTokenValues(model), ["nemotron"]);
829
+ }
830
+ function isNemotronLikeAssistantMessage(message, model) {
831
+ return modelOrAssistantMessageHas(message, model, ["nemotron"]);
832
+ }
833
+ function isCohereLikeModel(model) {
834
+ return hasAnyTokenContaining(getModelIdNameTokenValues(model), ["cohere", "command-r"]);
835
+ }
836
+ function isCohereLikeAssistantMessage(message, model) {
837
+ return modelOrAssistantMessageHas(message, model, ["cohere", "command-r"]);
838
+ }
839
+ const YI_MODEL_PATTERN = /(^|[/\s:_-])yi($|[-_.:/\s])/;
840
+ function isYiLikeModel(model) {
841
+ const tokens = getModelIdNameTokenValues(model);
842
+ return hasAnyTokenContaining(tokens, [
843
+ "yi-",
844
+ "01-ai",
845
+ "zero-one"
846
+ ]) || tokens.some((t) => YI_MODEL_PATTERN.test(t));
847
+ }
848
+ function isYiLikeAssistantMessage(message, model) {
849
+ const allTokens = [...getModelIdNameTokenValues(model), ...getAssistantMessageModelTokenValues(message)];
850
+ return hasAnyTokenContaining(allTokens, [
851
+ "yi-",
852
+ "01-ai",
853
+ "zero-one"
854
+ ]) || allTokens.some((t) => YI_MODEL_PATTERN.test(t));
855
+ }
856
+ const DOUBAO_SEED_PATTERN = /(^|[/\s:_-])seed($|[-_.:/\s])/i;
857
+ function isDoubaoLikeModel(model) {
858
+ const tokens = getModelIdNameTokenValues(model);
859
+ return hasAnyTokenContaining(tokens, [
860
+ "doubao",
861
+ "豆包",
862
+ "volcengine",
863
+ "bytedance",
864
+ "byte-dance"
865
+ ]) || tokens.some((t) => DOUBAO_SEED_PATTERN.test(t));
866
+ }
867
+ function isDoubaoLikeAssistantMessage(message, model) {
868
+ const allTokens = [...getModelIdNameTokenValues(model), ...getAssistantMessageModelTokenValues(message)];
869
+ return hasAnyTokenContaining(allTokens, [
870
+ "doubao",
871
+ "豆包",
872
+ "volcengine",
873
+ "bytedance",
874
+ "byte-dance"
875
+ ]) || allTokens.some((t) => DOUBAO_SEED_PATTERN.test(t));
876
+ }
877
+ function isErnieLikeModel(model) {
878
+ return hasAnyTokenContaining(getModelIdNameTokenValues(model), [
879
+ "ernie",
880
+ "wenxin",
881
+ "文心",
882
+ "yiyan",
883
+ "一言",
884
+ "baidu"
885
+ ]);
886
+ }
887
+ function isErnieLikeAssistantMessage(message, model) {
888
+ return modelOrAssistantMessageHas(message, model, [
889
+ "ernie",
890
+ "wenxin",
891
+ "文心",
892
+ "yiyan",
893
+ "一言",
894
+ "baidu"
895
+ ]);
896
+ }
897
+ function isBaichuanLikeModel(model) {
898
+ return hasAnyTokenContaining(getModelIdNameTokenValues(model), ["baichuan", "百川"]);
899
+ }
900
+ function isBaichuanLikeAssistantMessage(message, model) {
901
+ return modelOrAssistantMessageHas(message, model, ["baichuan", "百川"]);
902
+ }
903
+ function isStepFunLikeModel(model) {
904
+ return hasAnyTokenContaining(getModelIdNameTokenValues(model), ["stepfun", "step-"]);
905
+ }
906
+ function isStepFunLikeAssistantMessage(message, model) {
907
+ return modelOrAssistantMessageHas(message, model, ["stepfun", "step-"]);
908
+ }
909
+ function isSparkLikeModel(model) {
910
+ return hasAnyTokenContaining(getModelIdNameTokenValues(model), [
911
+ "spark",
912
+ "xinghuo",
913
+ "星火",
914
+ "iflytek",
915
+ "讯飞"
916
+ ]);
917
+ }
918
+ function isSparkLikeAssistantMessage(message, model) {
919
+ return modelOrAssistantMessageHas(message, model, [
920
+ "spark",
921
+ "xinghuo",
922
+ "星火",
923
+ "iflytek",
924
+ "讯飞"
925
+ ]);
926
+ }
927
+ function isInternLMLikeModel(model) {
928
+ return hasAnyTokenContaining(getModelIdNameTokenValues(model), [
929
+ "internlm",
930
+ "intern-lm",
931
+ "书生"
932
+ ]);
933
+ }
934
+ function isInternLMLikeAssistantMessage(message, model) {
935
+ return modelOrAssistantMessageHas(message, model, [
936
+ "internlm",
937
+ "intern-lm",
938
+ "书生"
939
+ ]);
940
+ }
941
+ function isGemmaLikeModel(model) {
942
+ return hasAnyTokenContaining(getModelIdNameTokenValues(model), ["gemma"]);
943
+ }
944
+ function isGemmaLikeAssistantMessage(message, model) {
945
+ return modelOrAssistantMessageHas(message, model, ["gemma"]);
946
+ }
947
+ const PHI_MODEL_PATTERN = /(^|[/\s:_-])phi($|[-_.:/\s])/i;
948
+ function isPhiLikeModel(model) {
949
+ const tokens = getModelIdNameTokenValues(model);
950
+ return hasAnyTokenContaining(tokens, ["phi-"]) || tokens.some((t) => PHI_MODEL_PATTERN.test(t));
951
+ }
952
+ function isPhiLikeAssistantMessage(message, model) {
953
+ const allTokens = [...getModelIdNameTokenValues(model), ...getAssistantMessageModelTokenValues(message)];
954
+ return hasAnyTokenContaining(allTokens, ["phi-"]) || allTokens.some((t) => PHI_MODEL_PATTERN.test(t));
955
+ }
956
+ function isJambaLikeModel(model) {
957
+ return hasAnyTokenContaining(getModelIdNameTokenValues(model), ["jamba", "ai21"]);
958
+ }
959
+ function isJambaLikeAssistantMessage(message, model) {
960
+ return modelOrAssistantMessageHas(message, model, ["jamba", "ai21"]);
961
+ }
962
+ function isSolarLikeModel(model) {
963
+ return hasAnyTokenContaining(getModelIdNameTokenValues(model), ["solar", "upstage"]);
964
+ }
965
+ function isSolarLikeAssistantMessage(message, model) {
966
+ return modelOrAssistantMessageHas(message, model, ["solar", "upstage"]);
967
+ }
968
+ function isPerplexityLikeModel(model) {
969
+ const tokens = getModelIdNameTokenValues(model);
970
+ return hasAnyTokenContaining(tokens, ["sonar", "perplexity"]) || tokens.some((t) => PPLX_MODEL_PATTERN.test(t));
971
+ }
972
+ function isPerplexityLikeAssistantMessage(message, model) {
973
+ const allTokens = [...getModelIdNameTokenValues(model), ...getAssistantMessageModelTokenValues(message)];
974
+ return hasAnyTokenContaining(allTokens, ["sonar", "perplexity"]) || allTokens.some((t) => PPLX_MODEL_PATTERN.test(t));
975
+ }
976
+ function isNovaLikeModel(model) {
977
+ const tokens = getModelIdNameTokenValues(model);
978
+ return hasAnyTokenContaining(tokens, ["amazon-nova"]) || tokens.some((t) => NOVA_MODEL_PATTERN.test(t));
979
+ }
980
+ function isNovaLikeAssistantMessage(message, model) {
981
+ const allTokens = [...getModelIdNameTokenValues(model), ...getAssistantMessageModelTokenValues(message)];
982
+ return hasAnyTokenContaining(allTokens, ["amazon-nova"]) || allTokens.some((t) => NOVA_MODEL_PATTERN.test(t));
983
+ }
984
+ function isRekaLikeModel(model) {
985
+ return hasAnyTokenContaining(getModelIdNameTokenValues(model), ["reka"]);
986
+ }
987
+ function isRekaLikeAssistantMessage(message, model) {
988
+ return modelOrAssistantMessageHas(message, model, ["reka"]);
989
+ }
990
+ function isFalconLikeModel(model) {
991
+ return hasAnyTokenContaining(getModelIdNameTokenValues(model), ["falcon", "tiiuae"]);
992
+ }
993
+ function isFalconLikeAssistantMessage(message, model) {
994
+ return modelOrAssistantMessageHas(message, model, ["falcon", "tiiuae"]);
995
+ }
996
+ function isDbrxLikeModel(model) {
997
+ return hasAnyTokenContaining(getModelIdNameTokenValues(model), ["dbrx", "databricks"]);
998
+ }
999
+ function isDbrxLikeAssistantMessage(message, model) {
1000
+ return modelOrAssistantMessageHas(message, model, ["dbrx", "databricks"]);
1001
+ }
1002
+ function isMptLikeModel(model) {
1003
+ const tokens = getModelIdNameTokenValues(model);
1004
+ return hasAnyTokenContaining(tokens, ["mosaicml", "mpt-"]) || tokens.some((t) => MPT_MODEL_PATTERN.test(t));
1005
+ }
1006
+ function isMptLikeAssistantMessage(message, model) {
1007
+ const allTokens = [...getModelIdNameTokenValues(model), ...getAssistantMessageModelTokenValues(message)];
1008
+ return hasAnyTokenContaining(allTokens, ["mosaicml", "mpt-"]) || allTokens.some((t) => MPT_MODEL_PATTERN.test(t));
1009
+ }
1010
+ function isStableLMLikeModel(model) {
1011
+ return hasAnyTokenContaining(getModelIdNameTokenValues(model), [
1012
+ "stablelm",
1013
+ "stable-lm",
1014
+ "stability-ai"
1015
+ ]);
1016
+ }
1017
+ function isStableLMLikeAssistantMessage(message, model) {
1018
+ return modelOrAssistantMessageHas(message, model, [
1019
+ "stablelm",
1020
+ "stable-lm",
1021
+ "stability-ai"
1022
+ ]);
1023
+ }
1024
+ function isAquilaLikeModel(model) {
1025
+ return hasAnyTokenContaining(getModelIdNameTokenValues(model), ["aquila", "baai"]);
1026
+ }
1027
+ function isAquilaLikeAssistantMessage(message, model) {
1028
+ return modelOrAssistantMessageHas(message, model, ["aquila", "baai"]);
1029
+ }
1030
+ function isExaoneLikeModel(model) {
1031
+ return hasAnyTokenContaining(getModelIdNameTokenValues(model), ["exaone"]);
1032
+ }
1033
+ function isExaoneLikeAssistantMessage(message, model) {
1034
+ return modelOrAssistantMessageHas(message, model, ["exaone"]);
1035
+ }
1036
+ function isHyperCLOVALikeModel(model) {
1037
+ return hasAnyTokenContaining(getModelIdNameTokenValues(model), ["hyperclova", "clova-x"]);
1038
+ }
1039
+ function isHyperCLOVALikeAssistantMessage(message, model) {
1040
+ return modelOrAssistantMessageHas(message, model, ["hyperclova", "clova-x"]);
1041
+ }
1042
+ function isLuminousLikeModel(model) {
1043
+ const tokens = getModelIdNameTokenValues(model);
1044
+ return hasAnyTokenContaining(tokens, ["luminous", "aleph-alpha"]) || tokens.some((t) => ALEPH_MODEL_PATTERN.test(t));
1045
+ }
1046
+ function isLuminousLikeAssistantMessage(message, model) {
1047
+ const allTokens = [...getModelIdNameTokenValues(model), ...getAssistantMessageModelTokenValues(message)];
1048
+ return hasAnyTokenContaining(allTokens, ["luminous", "aleph-alpha"]) || allTokens.some((t) => ALEPH_MODEL_PATTERN.test(t));
1049
+ }
1050
+ function isHermesLikeModel(model) {
1051
+ return hasAnyTokenContaining(getModelIdNameTokenValues(model), [
1052
+ "nous",
1053
+ "hermes",
1054
+ "openhermes"
1055
+ ]);
1056
+ }
1057
+ function isHermesLikeAssistantMessage(message, model) {
1058
+ return modelOrAssistantMessageHas(message, model, [
1059
+ "nous",
1060
+ "hermes",
1061
+ "openhermes"
1062
+ ]);
1063
+ }
1064
+ function isGraniteLikeModel(model) {
1065
+ return hasAnyTokenContaining(getModelIdNameTokenValues(model), ["granite", "ibm-granite"]);
1066
+ }
1067
+ function isGraniteLikeAssistantMessage(message, model) {
1068
+ return modelOrAssistantMessageHas(message, model, ["granite", "ibm-granite"]);
1069
+ }
1070
+ function isArcticLikeModel(model) {
1071
+ const tokens = getModelIdNameTokenValues(model);
1072
+ return hasAnyTokenContaining(tokens, ["snowflake-arctic"]) || tokens.some((t) => ARCTIC_MODEL_PATTERN.test(t));
1073
+ }
1074
+ function isArcticLikeAssistantMessage(message, model) {
1075
+ const allTokens = [...getModelIdNameTokenValues(model), ...getAssistantMessageModelTokenValues(message)];
1076
+ return hasAnyTokenContaining(allTokens, ["snowflake-arctic"]) || allTokens.some((t) => ARCTIC_MODEL_PATTERN.test(t));
1077
+ }
1078
+ function isPanguLikeModel(model) {
1079
+ return hasAnyTokenContaining(getModelIdNameTokenValues(model), [
1080
+ "pangu",
1081
+ "pan-gu",
1082
+ "盘古",
1083
+ "huawei-pangu"
1084
+ ]);
1085
+ }
1086
+ function isPanguLikeAssistantMessage(message, model) {
1087
+ return modelOrAssistantMessageHas(message, model, [
1088
+ "pangu",
1089
+ "pan-gu",
1090
+ "盘古",
1091
+ "huawei-pangu"
1092
+ ]);
1093
+ }
1094
+ function isSenseNovaLikeModel(model) {
1095
+ return hasAnyTokenContaining(getModelIdNameTokenValues(model), [
1096
+ "sensenova",
1097
+ "sense-nova",
1098
+ "sensechat",
1099
+ "商汤"
1100
+ ]);
1101
+ }
1102
+ function isSenseNovaLikeAssistantMessage(message, model) {
1103
+ return modelOrAssistantMessageHas(message, model, [
1104
+ "sensenova",
1105
+ "sense-nova",
1106
+ "sensechat",
1107
+ "商汤"
1108
+ ]);
1109
+ }
1110
+ function isZhinaoLikeModel(model) {
1111
+ return hasAnyTokenContaining(getModelIdNameTokenValues(model), [
1112
+ "360gpt",
1113
+ "360-gpt",
1114
+ "zhinao",
1115
+ "智脑"
1116
+ ]);
1117
+ }
1118
+ function isZhinaoLikeAssistantMessage(message, model) {
1119
+ return modelOrAssistantMessageHas(message, model, [
1120
+ "360gpt",
1121
+ "360-gpt",
1122
+ "zhinao",
1123
+ "智脑"
1124
+ ]);
1125
+ }
1126
+ function isMiniCPMLikeModel(model) {
1127
+ return hasAnyTokenContaining(getModelIdNameTokenValues(model), [
1128
+ "minicpm",
1129
+ "mini-cpm",
1130
+ "openbmb"
1131
+ ]);
1132
+ }
1133
+ function isMiniCPMLikeAssistantMessage(message, model) {
1134
+ return modelOrAssistantMessageHas(message, model, [
1135
+ "minicpm",
1136
+ "mini-cpm",
1137
+ "openbmb"
1138
+ ]);
1139
+ }
1140
+ function isXVerseLikeModel(model) {
1141
+ return hasAnyTokenContaining(getModelIdNameTokenValues(model), ["xverse"]);
1142
+ }
1143
+ function isXVerseLikeAssistantMessage(message, model) {
1144
+ return modelOrAssistantMessageHas(message, model, ["xverse"]);
1145
+ }
1146
+ function isOrionLikeModel(model) {
1147
+ const tokens = getModelIdNameTokenValues(model);
1148
+ return hasAnyTokenContaining(tokens, ["orionstar", "orion-star"]) || tokens.some((t) => ORION_MODEL_PATTERN.test(t));
1149
+ }
1150
+ function isOrionLikeAssistantMessage(message, model) {
1151
+ const allTokens = [...getModelIdNameTokenValues(model), ...getAssistantMessageModelTokenValues(message)];
1152
+ return hasAnyTokenContaining(allTokens, ["orionstar", "orion-star"]) || allTokens.some((t) => ORION_MODEL_PATTERN.test(t));
1153
+ }
1154
+ function isOpenChatLikeModel(model) {
1155
+ return hasAnyTokenContaining(getModelIdNameTokenValues(model), ["openchat"]);
1156
+ }
1157
+ function isOpenChatLikeAssistantMessage(message, model) {
1158
+ return modelOrAssistantMessageHas(message, model, ["openchat"]);
1159
+ }
1160
+ function isVicunaLikeModel(model) {
1161
+ return hasAnyTokenContaining(getModelIdNameTokenValues(model), ["vicuna"]);
1162
+ }
1163
+ function isVicunaLikeAssistantMessage(message, model) {
1164
+ return modelOrAssistantMessageHas(message, model, ["vicuna"]);
1165
+ }
1166
+ function isWizardLikeModel(model) {
1167
+ return hasAnyTokenContaining(getModelIdNameTokenValues(model), [
1168
+ "wizardlm",
1169
+ "wizard-lm",
1170
+ "wizardcoder",
1171
+ "wizard-coder"
1172
+ ]);
1173
+ }
1174
+ function isWizardLikeAssistantMessage(message, model) {
1175
+ return modelOrAssistantMessageHas(message, model, [
1176
+ "wizardlm",
1177
+ "wizard-lm",
1178
+ "wizardcoder",
1179
+ "wizard-coder"
1180
+ ]);
1181
+ }
1182
+ function isZephyrLikeModel(model) {
1183
+ return hasAnyTokenContaining(getModelIdNameTokenValues(model), ["zephyr"]);
1184
+ }
1185
+ function isZephyrLikeAssistantMessage(message, model) {
1186
+ return modelOrAssistantMessageHas(message, model, ["zephyr"]);
1187
+ }
1188
+ function isDolphinLikeModel(model) {
1189
+ return hasAnyTokenContaining(getModelIdNameTokenValues(model), ["dolphin"]);
1190
+ }
1191
+ function isDolphinLikeAssistantMessage(message, model) {
1192
+ return modelOrAssistantMessageHas(message, model, ["dolphin"]);
1193
+ }
1194
+ function isOpenOrcaLikeModel(model) {
1195
+ return hasAnyTokenContaining(getModelIdNameTokenValues(model), ["openorca", "open-orca"]);
1196
+ }
1197
+ function isOpenOrcaLikeAssistantMessage(message, model) {
1198
+ return modelOrAssistantMessageHas(message, model, ["openorca", "open-orca"]);
1199
+ }
1200
+ function isStarlingLikeModel(model) {
1201
+ return hasAnyTokenContaining(getModelIdNameTokenValues(model), ["starling"]);
1202
+ }
1203
+ function isStarlingLikeAssistantMessage(message, model) {
1204
+ return modelOrAssistantMessageHas(message, model, ["starling"]);
1205
+ }
1206
+ function isBloomLikeModel(model) {
1207
+ return hasAnyTokenContaining(getModelIdNameTokenValues(model), ["bloom", "bigscience"]);
1208
+ }
1209
+ function isBloomLikeAssistantMessage(message, model) {
1210
+ return modelOrAssistantMessageHas(message, model, ["bloom", "bigscience"]);
1211
+ }
1212
+ function isRwkvLikeModel(model) {
1213
+ return hasAnyTokenContaining(getModelIdNameTokenValues(model), ["rwkv"]);
1214
+ }
1215
+ function isRwkvLikeAssistantMessage(message, model) {
1216
+ return modelOrAssistantMessageHas(message, model, ["rwkv"]);
1217
+ }
1218
+ function isAyaLikeModel(model) {
1219
+ const tokens = getModelIdNameTokenValues(model);
1220
+ return hasAnyTokenContaining(tokens, ["aya-expanse"]) || tokens.some((t) => AYA_MODEL_PATTERN.test(t));
1221
+ }
1222
+ function isAyaLikeAssistantMessage(message, model) {
1223
+ const allTokens = [...getModelIdNameTokenValues(model), ...getAssistantMessageModelTokenValues(message)];
1224
+ return hasAnyTokenContaining(allTokens, ["aya-expanse"]) || allTokens.some((t) => AYA_MODEL_PATTERN.test(t));
1225
+ }
1226
+ function modelKey(model) {
1227
+ return `${model.provider}/${model.id}`;
1228
+ }
1229
+ function modelFromAssistantMessage(message, fallback) {
1230
+ const record = getAssistantRecord(message);
1231
+ if (!record) return fallback;
1232
+ const id = firstNonEmptyString(record.responseModel, record.model, fallback?.id);
1233
+ const provider = firstNonEmptyString(record.provider, fallback?.provider);
1234
+ const api = firstNonEmptyString(record.api, fallback?.api) ?? "";
1235
+ if (!id || !provider) return fallback;
1236
+ const fallbackName = isNonEmptyString(fallback?.name) ? fallback.name : void 0;
1237
+ const preservesFallbackIdentity = !isVirtualRoutingModel(fallback) && provider === fallback?.provider && id === fallback?.id && fallbackName !== void 0;
1238
+ return {
1239
+ ...fallback,
1240
+ id,
1241
+ name: preservesFallbackIdentity ? fallbackName : id,
1242
+ provider,
1243
+ api,
1244
+ baseUrl: fallback?.baseUrl ?? ""
1245
+ };
1246
+ }
1247
+ /** Physical catalog identity for native virtual dispatch, including registry misses. */
1248
+ function resolvePhysicalModel(dispatch, registered) {
1249
+ const model = registered && registered.api !== PI_VIRTUAL_MODEL_API ? registered : {
1250
+ provider: dispatch.provider,
1251
+ id: dispatch.id,
1252
+ name: dispatch.name,
1253
+ api: dispatch.api === PI_VIRTUAL_MODEL_API ? "" : dispatch.api,
1254
+ baseUrl: "",
1255
+ [ROUTED_FALLBACK_MODEL_SYMBOL]: true
1256
+ };
1257
+ const provider = asRecord(asRecord(asRecord(readEffectiveCompatConfig())?.providers)?.[model.provider]);
1258
+ const customModel = findLastExactModelDefinition(Array.isArray(provider?.models) ? provider.models : void 0, model.id);
1259
+ const configuredApi = customModel?.api ?? provider?.api;
1260
+ const configuredBaseUrl = customModel?.baseUrl ?? provider?.baseUrl;
1261
+ const api = isNonEmptyString(model.api) ? model.api : isNonEmptyString(configuredApi) ? configuredApi : model.api;
1262
+ const baseUrl = isNonEmptyString(model.baseUrl) ? model.baseUrl : isNonEmptyString(configuredBaseUrl) ? configuredBaseUrl : model.baseUrl;
1263
+ return api === model.api && baseUrl === model.baseUrl ? model : {
1264
+ ...model,
1265
+ api,
1266
+ baseUrl
1267
+ };
1268
+ }
1269
+ function consolidateDirectProviderStatsModel(statsModel, ctxModel) {
1270
+ if (!statsModel || !ctxModel) return statsModel;
1271
+ if (isVirtualRoutingModel(ctxModel)) return statsModel;
1272
+ if (statsModel.provider !== ctxModel.provider) return statsModel;
1273
+ const statsAdapter = selectAdapterForModel(statsModel);
1274
+ const ctxAdapter = selectAdapterForModel(ctxModel);
1275
+ if (!statsAdapter || !ctxAdapter || statsAdapter !== ctxAdapter) return statsModel;
1276
+ if (statsModel.id === ctxModel.id) return statsModel;
1277
+ return {
1278
+ ...statsModel,
1279
+ id: ctxModel.id,
1280
+ name: ctxModel.name || ctxModel.id
1281
+ };
1282
+ }
1283
+ function usageRecordFromAssistant(message) {
1284
+ return asRecord(getAssistantRecord(message)?.usage);
1285
+ }
1286
+ function getNestedRecord(record, key) {
1287
+ return asRecord(record?.[key]);
1288
+ }
1289
+ function getFirstNonNegativeNumber(...values) {
1290
+ for (const value of values) {
1291
+ const number = getNumber(value);
1292
+ if (number !== void 0 && number >= 0) return number;
1293
+ }
1294
+ }
1295
+ function readCachedTokensFromDetails(details) {
1296
+ return getFirstNonNegativeNumber(details?.cached_tokens, details?.cachedTokens);
1297
+ }
1298
+ function readCacheWriteFromDetails(details) {
1299
+ return getFirstNonNegativeNumber(details?.cache_write_tokens, details?.cacheWriteTokens);
1300
+ }
1301
+ function getPiNormalizedUsage(message, allowInputOnly = false) {
1302
+ const usage = usageRecordFromAssistant(message);
1303
+ if (!usage) return void 0;
1304
+ const input = getNonNegativeNumber(usage, "input");
1305
+ const cacheRead = getNonNegativeNumber(usage, "cacheRead");
1306
+ const cacheWrite = getNonNegativeNumber(usage, "cacheWrite");
1307
+ if (!(cacheRead !== void 0 || cacheWrite !== void 0) && (input === void 0 || !allowInputOnly)) return void 0;
1308
+ const computed = (input ?? 0) + (cacheRead ?? 0) + (cacheWrite ?? 0);
1309
+ const floor = (cacheRead ?? 0) + (cacheWrite ?? 0);
1310
+ return {
1311
+ cacheRead: cacheRead ?? 0,
1312
+ cacheWrite: cacheWrite ?? 0,
1313
+ totalInput: computed >= floor ? computed : floor
1314
+ };
1315
+ }
1316
+ function getDeepSeekRawUsage(message) {
1317
+ const usage = usageRecordFromAssistant(message);
1318
+ if (!usage) return void 0;
1319
+ const cacheRead = getFirstNonNegativeNumber(usage.prompt_cache_hit_tokens);
1320
+ if (cacheRead === void 0) return void 0;
1321
+ const cacheMiss = getFirstNonNegativeNumber(usage.prompt_cache_miss_tokens);
1322
+ return {
1323
+ cacheRead,
1324
+ cacheWrite: 0,
1325
+ totalInput: getFirstNonNegativeNumber(usage.prompt_tokens) ?? cacheRead + (cacheMiss ?? 0)
1326
+ };
1327
+ }
1328
+ function getOpenAIRawUsage(message) {
1329
+ const usage = usageRecordFromAssistant(message);
1330
+ if (!usage) return void 0;
1331
+ const promptDetails = getNestedRecord(usage, "prompt_tokens_details") ?? getNestedRecord(usage, "promptTokensDetails");
1332
+ const inputDetails = getNestedRecord(usage, "input_tokens_details") ?? getNestedRecord(usage, "inputTokensDetails");
1333
+ const cacheRead = readCachedTokensFromDetails(promptDetails) ?? readCachedTokensFromDetails(inputDetails);
1334
+ if (cacheRead === void 0) return void 0;
1335
+ const cacheWrite = readCacheWriteFromDetails(promptDetails) ?? readCacheWriteFromDetails(inputDetails) ?? 0;
1336
+ return {
1337
+ cacheRead,
1338
+ cacheWrite,
1339
+ totalInput: getFirstNonNegativeNumber(usage.prompt_tokens, usage.promptTokens, usage.input_tokens, usage.inputTokens) ?? cacheRead + cacheWrite
1340
+ };
1341
+ }
1342
+ function getAnthropicRawUsage(message) {
1343
+ const usage = usageRecordFromAssistant(message);
1344
+ if (!usage) return void 0;
1345
+ const cacheRead = getFirstNonNegativeNumber(usage.cache_read_input_tokens, usage.cacheReadInputTokens);
1346
+ const cacheWrite = getFirstNonNegativeNumber(usage.cache_creation_input_tokens, usage.cacheCreationInputTokens);
1347
+ if (cacheRead === void 0 && cacheWrite === void 0) return void 0;
1348
+ const input = getFirstNonNegativeNumber(usage.input_tokens, usage.inputTokens) ?? 0;
1349
+ return {
1350
+ cacheRead: cacheRead ?? 0,
1351
+ cacheWrite: cacheWrite ?? 0,
1352
+ totalInput: input + (cacheRead ?? 0) + (cacheWrite ?? 0)
1353
+ };
1354
+ }
1355
+ function getGeminiRawUsage(message) {
1356
+ const record = getAssistantRecord(message);
1357
+ if (!record) return void 0;
1358
+ const usage = asRecord(record.usage);
1359
+ const metadata = getNestedRecord(record, "usageMetadata") ?? getNestedRecord(record, "usage_metadata") ?? getNestedRecord(usage, "usageMetadata") ?? getNestedRecord(usage, "usage_metadata") ?? usage;
1360
+ if (!metadata) return void 0;
1361
+ const cacheRead = getFirstNonNegativeNumber(metadata.cachedContentTokenCount, metadata.cached_content_token_count);
1362
+ if (cacheRead === void 0) return void 0;
1363
+ return {
1364
+ cacheRead,
1365
+ cacheWrite: 0,
1366
+ totalInput: getFirstNonNegativeNumber(metadata.promptTokenCount, metadata.prompt_token_count, metadata.inputTokenCount, metadata.input_token_count, usage?.input_tokens, usage?.inputTokens, usage?.prompt_tokens, usage?.promptTokens) ?? cacheRead
1367
+ };
1368
+ }
1369
+ function normalizeWithFallback(message, rawNormalizer, options = {}) {
1370
+ return getPiNormalizedUsage(message, options.allowInputOnlyPiUsage) ?? rawNormalizer(message);
1371
+ }
1372
+ function isNonEmptyString(value) {
1373
+ return typeof value === "string" && value.trim().length > 0;
1374
+ }
1375
+ function isOfficialOpenAIBaseUrl(model) {
1376
+ const value = lower(model.baseUrl).trim();
1377
+ if (!value) return lower(model.provider) === "openai";
1378
+ try {
1379
+ return new URL(value).hostname === "api.openai.com";
1380
+ } catch {
1381
+ return value === "api.openai.com" || value.startsWith("api.openai.com/");
1382
+ }
1383
+ }
1384
+ function isKnownThirdPartyOpenAIEndpoint(model) {
1385
+ return isNonEmptyString(model.baseUrl) && !isOfficialOpenAIBaseUrl(model);
1386
+ }
1387
+ function describeMissingOpenAICompatibleProxyCompat(model) {
1388
+ const compat = getCompat(model);
1389
+ const missing = [];
1390
+ if (!isOpenAICompatibleProxyApi(model.api)) return missing;
1391
+ if (!isKnownThirdPartyOpenAIEndpoint(model)) return missing;
1392
+ if (isPiBuiltInLlamaCppModel(model)) return missing;
1393
+ if (compat.sendSessionAffinityHeaders === void 0) missing.push("sendSessionAffinityHeaders");
1394
+ return missing;
1395
+ }
1396
+ function buildSafeOpenAIProxyCompatSuggestion(missing) {
1397
+ const suggestion = {};
1398
+ if (missing.includes("sendSessionAffinityHeaders")) suggestion.sendSessionAffinityHeaders = true;
1399
+ return suggestion;
1400
+ }
1401
+ function getPromptCacheRetentionUnsupportedHint() {
1402
+ return "If this channel returns `400 Unsupported parameter: prompt_cache_retention`, remove/avoid `supportsLongCacheRetention`; this extension does not write that field directly, but Pi may send it when long retention is requested and compat says the proxy supports it.";
1403
+ }
1404
+ function hasPromptCacheRetentionUnsupportedText(value) {
1405
+ const normalized = lower(value);
1406
+ if (!normalized.includes("prompt_cache_retention")) return false;
1407
+ return [
1408
+ "unsupported parameter",
1409
+ "unsupported_parameter",
1410
+ "unknown parameter",
1411
+ "not supported",
1412
+ "unsupported field",
1413
+ "extra inputs",
1414
+ "not permitted",
1415
+ "unrecognized"
1416
+ ].some((needle) => normalized.includes(needle));
1417
+ }
1418
+ function hasPromptCacheRetentionUnsupportedErrorMessage(message) {
1419
+ const record = getAssistantRecord(message);
1420
+ return record?.stopReason === "error" && hasPromptCacheRetentionUnsupportedText(record.errorMessage);
1421
+ }
1422
+ function hasPromptCacheKeyUnsupportedText(value) {
1423
+ const normalized = lower(value).replace(/["'`]/g, "").replace(/[\s_-]+/g, " ").replace(/\s+/g, " ").trim();
1424
+ const key = String.raw`(?:prompt ?cache ?key|promptcachekey)`;
1425
+ const field = String.raw`(?:parameter|field|input|argument)`;
1426
+ const unsupported = String.raw`(?:unsupported|unknown|unrecognized|unexpected|not supported|not allowed|not permitted|must be omitted|should be omitted)`;
1427
+ if (!new RegExp(key).test(normalized)) return false;
1428
+ const terminal = String.raw`(?=$|[}\]>,.;])`;
1429
+ return new RegExp(String.raw`(?:unsupported|unknown|unrecognized|unexpected)\s+${field}\s*[:=]?\s*${key}${terminal}`).test(normalized) || new RegExp(String.raw`(?:extra\s+inputs?|${field}\s+not\s+(?:allowed|permitted|supported))\s*[:=]\s*${key}${terminal}`).test(normalized) || new RegExp(String.raw`${key}(?:\s+${field})?\s*[:=]?\s*(?:is\s+)?${unsupported}${terminal}`).test(normalized);
1430
+ }
1431
+ function hasPromptCacheKeyUnsupportedErrorMessage(message) {
1432
+ const record = getAssistantRecord(message);
1433
+ return record?.stopReason === "error" && getOptionalAssistantHttpStatus(record) === 400 && hasPromptCacheKeyUnsupportedText(record.errorMessage);
1434
+ }
1435
+ function isPromptCacheKeyUnsupportedApplicable(model) {
1436
+ return isOpenAICompatibleApi(model.api);
1437
+ }
1438
+ function hasReasoningProtocolRejectionText(value) {
1439
+ const normalized = lower(value).replace(/\s+/g, " ").trim();
1440
+ if (!normalized) return false;
1441
+ const thinkingParameterRejectionPatterns = [
1442
+ /(?:^|[^a-z0-9_])["'`]?thinking["'`]?(?:\s+(?:parameter|field|argument))?\s*(?:is\s+)?(?:not supported|unsupported|unknown|unrecognized|not allowed|not permitted|rejected|invalid|not valid|not accepted|disallowed|not a valid(?:\s+(?:parameter|field|argument))?)(?![a-z0-9_])/,
1443
+ /(?:^|[^a-z0-9_])(?:unsupported|unknown|unrecognized|invalid|disallowed|rejected|not\s+(?:a\s+)?valid|not\s+accepted|not\s+allowed|not\s+permitted)(?:[_ ](?:parameter|field|argument))?\s*[:=]?\s*["'`]?thinking["'`]?(?![a-z0-9_])/,
1444
+ /(?:^|[^a-z0-9_])(?:extra\s+inputs?|additional\s+(?:inputs?|parameters?))\s+(?:are\s+)?(?:not permitted|not allowed|unsupported)\s*[:=]?\s*["'`]?thinking["'`]?(?![a-z0-9_])/
1445
+ ];
1446
+ let rejectionEnd = -1;
1447
+ for (const pattern of thinkingParameterRejectionPatterns) {
1448
+ const match = pattern.exec(normalized);
1449
+ if (match && match.index + match[0].length > rejectionEnd) rejectionEnd = match.index + match[0].length;
1450
+ }
1451
+ if (rejectionEnd < 0) return false;
1452
+ const recommendation = normalized.slice(rejectionEnd, rejectionEnd + 260);
1453
+ if (!/\breasoning[_.]effort\b/.test(recommendation)) return false;
1454
+ return recommendation.split(/[.;!?\n]/).some((clause) => {
1455
+ if (!/\breasoning[_.]effort\b/.test(clause)) return false;
1456
+ if (/\b(?:do\s+not|don't|never|avoid)\s+(?:use|set|send|pass|provide)?\s*["'`]?reasoning[_.]effort\b/.test(clause) || /\breasoning[_.]effort\b[^.;]{0,80}\b(?:must|should|may|do)\s+(?:not|never)\b/.test(clause) || /\breasoning[_.]effort\b[^.;]{0,80}\b(?:unsupported|disabled|unavailable|not\s+(?:supported|accepted|allowed|available|enabled|required|recommended|expected))\b/.test(clause)) return false;
1457
+ return [
1458
+ /(?:use|try|set|send|pass|provide)\s+(?:the\s+)?(?:top[- ]level\s+)?["'`]?reasoning[_.]effort["'`]?(?![a-z0-9_])/,
1459
+ /(?:reasoning[_.]effort)\b[^.;]{0,120}(?:instead|required|must\s+be|should\s+be|is\s+(?:supported|accepted|preferred|recommended|expected))\b/,
1460
+ /(?:parameter|field|option)\s+(?:is|should be)\s+["'`]?reasoning[_.]effort["'`]?(?![a-z0-9_])/,
1461
+ /instead[^.;]{0,120}(?:use|try|set|send|pass|provide)\s+(?:the\s+)?["'`]?reasoning[_.]effort["'`]?(?![a-z0-9_])/,
1462
+ /(?:replace|change|switch)\s+(?:the\s+)?["'`]?thinking["'`]?\s+(?:with|to)\s+["'`]?reasoning[_.]effort["'`]?(?![a-z0-9_])/
1463
+ ].some((pattern) => pattern.test(clause));
1464
+ });
1465
+ }
1466
+ function getOptionalAssistantHttpStatus(record) {
1467
+ const readStatus = (value) => {
1468
+ if (typeof value === "number" && Number.isInteger(value) && value >= 100 && value <= 599) return value;
1469
+ if (typeof value === "string" && /^\d{3}$/.test(value.trim())) {
1470
+ const parsed = Number(value.trim());
1471
+ return parsed >= 100 && parsed <= 599 ? parsed : void 0;
1472
+ }
1473
+ };
1474
+ for (const key of [
1475
+ "status",
1476
+ "statusCode",
1477
+ "httpStatus",
1478
+ "httpStatusCode"
1479
+ ]) {
1480
+ const status = readStatus(record[key]);
1481
+ if (status !== void 0) return status;
1482
+ }
1483
+ const scanDiagnosticStatus = (value, depth) => {
1484
+ if (depth > 2) return void 0;
1485
+ if (Array.isArray(value)) {
1486
+ for (const item of value) {
1487
+ const status = scanDiagnosticStatus(item, depth + 1);
1488
+ if (status !== void 0) return status;
1489
+ }
1490
+ return;
1491
+ }
1492
+ const source = asRecord(value);
1493
+ if (!source) return void 0;
1494
+ for (const key of [
1495
+ "status",
1496
+ "statusCode",
1497
+ "httpStatus",
1498
+ "httpStatusCode",
1499
+ "code"
1500
+ ]) {
1501
+ const status = readStatus(source[key]);
1502
+ if (status !== void 0) return status;
1503
+ }
1504
+ for (const key of [
1505
+ "details",
1506
+ "error",
1507
+ "cause",
1508
+ "diagnostics"
1509
+ ]) {
1510
+ const status = scanDiagnosticStatus(source[key], depth + 1);
1511
+ if (status !== void 0) return status;
1512
+ }
1513
+ };
1514
+ for (const key of [
1515
+ "diagnostics",
1516
+ "diagnostic",
1517
+ "details",
1518
+ "error",
1519
+ "cause"
1520
+ ]) {
1521
+ const status = scanDiagnosticStatus(record[key], 0);
1522
+ if (status !== void 0) return status;
1523
+ }
1524
+ if (typeof record.errorMessage === "string") {
1525
+ const errorText = record.errorMessage;
1526
+ for (const pattern of [
1527
+ /^\s*([1-5]\d{2})\b/,
1528
+ /^\s*(?:http(?:\s+status)?|status(?:\s+code)?)\s*[:(]?\s*([1-5]\d{2})\b/i,
1529
+ /^\s*[^():\n]{1,100}\(\s*([1-5]\d{2})\b/,
1530
+ /^\s*(?:error|api\s+error|provider\s+error|request\s+failed|http\s+error)\s*[:(]\s*([1-5]\d{2})\b/i
1531
+ ]) {
1532
+ const match = pattern.exec(errorText);
1533
+ if (match) return Number(match[1]);
1534
+ }
1535
+ }
1536
+ }
1537
+ function hasReasoningProtocolRejectionErrorMessage(message) {
1538
+ const record = getAssistantRecord(message);
1539
+ if (!record || record.stopReason !== "error") return false;
1540
+ return getOptionalAssistantHttpStatus(record) === 400 && hasReasoningProtocolRejectionText(record.errorMessage);
1541
+ }
1542
+ function isReasoningProtocolRejectionSignalApplicable(model) {
1543
+ return !!model && isOpenAICompatibleProxyApi(model.api) && isDeepSeekLikeModel(model);
1544
+ }
1545
+ function isReasoningProtocolRejectionForModel(message, model) {
1546
+ if (!model || !isReasoningProtocolRejectionSignalApplicable(model)) return false;
1547
+ if (!hasReasoningProtocolRejectionErrorMessage(message)) return false;
1548
+ const record = getAssistantRecord(message);
1549
+ if (!record) return false;
1550
+ const messageProvider = firstNonEmptyString(record.provider);
1551
+ const messageModel = firstNonEmptyString(record.responseModel, record.model);
1552
+ return (!messageProvider || messageProvider === model.provider) && (!messageModel || messageModel === model.id);
1553
+ }
1554
+ function buildProviderCompatOverride(providerLabel, compat) {
1555
+ return { providers: { [providerLabel]: { compat } } };
1556
+ }
1557
+ function buildModelCompatOverride(providerLabel, modelId, compat) {
1558
+ return { providers: { [providerLabel]: { modelOverrides: { [modelId]: { compat } } } } };
1559
+ }
1560
+ function appendCredentialSafeProviderGuidance(lines, placement, compatSuggestion) {
1561
+ const providerLabel = placement.providerLabel;
1562
+ if (!providerLabel) return;
1563
+ lines.push("");
1564
+ lines.push("If this channel has no models.json provider entry yet:");
1565
+ lines.push("- Keep existing authentication as-is; do not copy credentials, tokens, or API keys.");
1566
+ lines.push(`- Add only cache/routing compat overrides in ${getModelsJsonDisplayPath()}.`);
1567
+ if (Object.keys(compatSuggestion).length === 0) {
1568
+ lines.push("- No safe copyable override is available for the missing flags shown above.");
1569
+ return;
1570
+ }
1571
+ lines.push("Provider-level minimal override:");
1572
+ lines.push(JSON.stringify(buildProviderCompatOverride(providerLabel, compatSuggestion), null, 2));
1573
+ if (placement.modelId) {
1574
+ lines.push("Single-model override (use this if only this model should change):");
1575
+ lines.push(JSON.stringify(buildModelCompatOverride(providerLabel, placement.modelId, compatSuggestion), null, 2));
1576
+ }
1577
+ }
1578
+ function appendOpenAIProxyCompatAdviceLines(lines, missing, options = {}) {
1579
+ const suggestion = buildSafeOpenAIProxyCompatSuggestion(missing);
1580
+ if (Object.keys(suggestion).length > 0) {
1581
+ if (options.includeJsonIntro !== false) lines.push("Safe default suggestion:");
1582
+ lines.push(JSON.stringify(suggestion, null, 2));
1583
+ }
1584
+ if (missing.includes("sendSessionAffinityHeaders")) lines.push("- sendSessionAffinityHeaders: recommended for third-party proxies when supported; it helps keep one Pi session on the same upstream/backend.");
1585
+ appendCredentialSafeProviderGuidance(lines, options, suggestion);
1586
+ }
1587
+ function buildOpenAIProxyCompatWarningText(key, missing) {
1588
+ const slashIdx = key.indexOf("/");
1589
+ const providerLabel = slashIdx > 0 ? key.slice(0, slashIdx) : key;
1590
+ const modelId = slashIdx > 0 ? key.slice(slashIdx + 1) : void 0;
1591
+ const modelsJsonPath = getModelsJsonDisplayPath();
1592
+ const lines = [
1593
+ `💡 pi-cache-optimizer: ${key} is a third-party OpenAI-compatible proxy but merged compat lacks ${missing.join(" and ")}.`,
1594
+ `Review a precise model-scoped repair (or edit ${modelsJsonPath} -> providers["${providerLabel}"] -> compat manually).`,
1595
+ ``
1596
+ ];
1597
+ appendOpenAIProxyCompatAdviceLines(lines, missing, {
1598
+ providerLabel,
1599
+ modelId
1600
+ });
1601
+ return lines.join("\n");
1602
+ }
1603
+ function isDeepSeekWireCompatApplicable(model) {
1604
+ return isDeepSeekLikeModel(model) && isOpenAICompatibleProxyApi(model.api) && getCompat(model).thinkingFormat === "deepseek";
1605
+ }
1606
+ function describeMissingDeepSeekCompat(model) {
1607
+ if (!isDeepSeekWireCompatApplicable(model)) return [];
1608
+ const compat = getCompat(model);
1609
+ const missing = [];
1610
+ if (compat.requiresReasoningContentOnAssistantMessages !== true) missing.push("requiresReasoningContentOnAssistantMessages");
1611
+ return missing;
1612
+ }
1613
+ function isDeepSeekCompatCheckApplicable(model) {
1614
+ return isDeepSeekWireCompatApplicable(model);
1615
+ }
1616
+ function describeMissingCacheCompatForModel(model) {
1617
+ if (isAdaptiveThinkingCompatApplicable(model)) return describeMissingAdaptiveThinkingCompat(model);
1618
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
1619
+ if (isDeepSeekWireCompatApplicable(model)) missing.push(...describeMissingDeepSeekCompat(model));
1620
+ return missing;
1621
+ }
1622
+ function buildDeepSeekCompatSuggestion(missing) {
1623
+ const suggestion = { ...buildSafeOpenAIProxyCompatSuggestion(missing) };
1624
+ if (missing.includes("requiresReasoningContentOnAssistantMessages")) suggestion.requiresReasoningContentOnAssistantMessages = true;
1625
+ return suggestion;
1626
+ }
1627
+ function appendDeepSeekCompatAdviceLines(lines, missing, placement = {}) {
1628
+ const suggestion = buildDeepSeekCompatSuggestion(missing);
1629
+ if (Object.keys(suggestion).length > 0) {
1630
+ lines.push("Recommended DeepSeek reasoning/replay compat snippet:");
1631
+ lines.push(JSON.stringify(suggestion, null, 2));
1632
+ }
1633
+ if (missing.includes("requiresReasoningContentOnAssistantMessages")) lines.push("- requiresReasoningContentOnAssistantMessages: true keeps replayed assistant turns compatible with an explicitly selected DeepSeek reasoning wire format.");
1634
+ if (missing.includes("sendSessionAffinityHeaders")) lines.push("- sendSessionAffinityHeaders: recommended for third-party OpenAI-compatible proxies when supported; it helps keep one Pi session on the same upstream/backend.");
1635
+ appendCredentialSafeProviderGuidance(lines, placement, suggestion);
1636
+ }
1637
+ function buildDeepSeekCompatWarningText(key, missing) {
1638
+ const slashIdx = key.indexOf("/");
1639
+ const providerLabel = slashIdx > 0 ? key.slice(0, slashIdx) : key;
1640
+ const modelId = slashIdx > 0 ? key.slice(slashIdx + 1) : void 0;
1641
+ const modelsJsonPath = getModelsJsonDisplayPath();
1642
+ const lines = [
1643
+ `💡 pi-cache-optimizer: ${key} is DeepSeek-like but merged compat lacks ${missing.join(" and ")}.`,
1644
+ `Proxies may reduce or hide cache hits. Edit ${modelsJsonPath} -> providers["${providerLabel}"] -> compat (at the same level as baseUrl/api/apiKey/models).`,
1645
+ ""
1646
+ ];
1647
+ appendDeepSeekCompatAdviceLines(lines, missing, {
1648
+ providerLabel,
1649
+ modelId
1650
+ });
1651
+ return lines.join("\n");
1652
+ }
1653
+ const CACHE_PROVIDER_ADAPTERS = [
1654
+ {
1655
+ id: "deepseek",
1656
+ label: "DS cache",
1657
+ matchesModel: isDeepSeekLikeModel,
1658
+ matchesAssistantMessage(message, model) {
1659
+ if (!isAssistantMessage(message)) return false;
1660
+ return isDeepSeekLikeAssistantMessage(message, model);
1661
+ },
1662
+ normalizeUsage(message) {
1663
+ return normalizeWithFallback(message, getDeepSeekRawUsage, { allowInputOnlyPiUsage: true });
1664
+ },
1665
+ warningText(model) {
1666
+ const missing = describeMissingCacheCompatForModel(model);
1667
+ if (missing.length === 0) return void 0;
1668
+ const key = modelKey(model);
1669
+ return isDeepSeekWireCompatApplicable(model) ? buildDeepSeekCompatWarningText(key, missing) : buildOpenAIProxyCompatWarningText(key, missing);
1670
+ }
1671
+ },
1672
+ {
1673
+ id: "claude",
1674
+ label: "Claude cache",
1675
+ showCacheWrite: true,
1676
+ matchesModel: isClaudeLikeModel,
1677
+ matchesAssistantMessage(message, model) {
1678
+ if (!isAssistantMessage(message)) return false;
1679
+ return isClaudeLikeAssistantMessage(message, model);
1680
+ },
1681
+ normalizeUsage(message) {
1682
+ return normalizeWithFallback(message, getAnthropicRawUsage);
1683
+ },
1684
+ warningText(model) {
1685
+ if (!isClaudeLikeModel(model) || !isOpenAICompatibleApi(model.api) || isPiBuiltInLlamaCppModel(model)) return void 0;
1686
+ if (getCompat(model).cacheControlFormat === "anthropic") return void 0;
1687
+ return `💡 Cache optimizer: ${modelKey(model)} looks Claude/Anthropic-like but OpenAI-compatible compat lacks cacheControlFormat: "anthropic". Pi may not place Anthropic cache_control breakpoints unless this endpoint supports and enables that compat flag.`;
1688
+ }
1689
+ },
1690
+ {
1691
+ id: "openai",
1692
+ label: "OpenAI cache",
1693
+ matchesModel: isOpenAIFamilyModel,
1694
+ matchesAssistantMessage(message, model) {
1695
+ if (!isAssistantMessage(message)) return false;
1696
+ return isOpenAIFamilyAssistantMessage(message, model);
1697
+ },
1698
+ normalizeUsage(message) {
1699
+ return normalizeWithFallback(message, getOpenAIRawUsage);
1700
+ },
1701
+ warningText(model) {
1702
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
1703
+ if (missing.length === 0) return void 0;
1704
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
1705
+ }
1706
+ },
1707
+ {
1708
+ id: "gemini",
1709
+ label: "Gemini cache",
1710
+ matchesModel: isGeminiLikeModel,
1711
+ matchesAssistantMessage(message, model) {
1712
+ if (!isAssistantMessage(message)) return false;
1713
+ return isGeminiLikeAssistantMessage(message, model);
1714
+ },
1715
+ normalizeUsage(message) {
1716
+ return normalizeWithFallback(message, getGeminiRawUsage);
1717
+ }
1718
+ },
1719
+ {
1720
+ id: "openai",
1721
+ label: "Kimi cache",
1722
+ matchesModel: isKimiLikeModel,
1723
+ matchesAssistantMessage(message, model) {
1724
+ if (!isAssistantMessage(message)) return false;
1725
+ return isKimiLikeAssistantMessage(message, model);
1726
+ },
1727
+ normalizeUsage(message) {
1728
+ return normalizeWithFallback(message, getOpenAIRawUsage);
1729
+ },
1730
+ warningText(model) {
1731
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
1732
+ if (missing.length === 0) return void 0;
1733
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
1734
+ }
1735
+ },
1736
+ {
1737
+ id: "openai",
1738
+ label: "Qwen cache",
1739
+ matchesModel: isQwenLikeModel,
1740
+ matchesAssistantMessage(message, model) {
1741
+ if (!isAssistantMessage(message)) return false;
1742
+ return isQwenLikeAssistantMessage(message, model);
1743
+ },
1744
+ normalizeUsage(message) {
1745
+ return normalizeWithFallback(message, getOpenAIRawUsage);
1746
+ },
1747
+ warningText(model) {
1748
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
1749
+ if (missing.length === 0) return void 0;
1750
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
1751
+ }
1752
+ },
1753
+ {
1754
+ id: "openai",
1755
+ label: "GLM cache",
1756
+ matchesModel: isGLMLikeModel,
1757
+ matchesAssistantMessage(message, model) {
1758
+ if (!isAssistantMessage(message)) return false;
1759
+ return isGLMLikeAssistantMessage(message, model);
1760
+ },
1761
+ normalizeUsage(message) {
1762
+ return normalizeWithFallback(message, getOpenAIRawUsage);
1763
+ },
1764
+ warningText(model) {
1765
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
1766
+ if (missing.length === 0) return void 0;
1767
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
1768
+ }
1769
+ },
1770
+ {
1771
+ id: "openai",
1772
+ label: "MiniMax cache",
1773
+ matchesModel: isMiniMaxLikeModel,
1774
+ matchesAssistantMessage(message, model) {
1775
+ if (!isAssistantMessage(message)) return false;
1776
+ return isMiniMaxLikeAssistantMessage(message, model);
1777
+ },
1778
+ normalizeUsage(message) {
1779
+ return normalizeWithFallback(message, getOpenAIRawUsage);
1780
+ },
1781
+ warningText(model) {
1782
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
1783
+ if (missing.length === 0) return void 0;
1784
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
1785
+ }
1786
+ },
1787
+ {
1788
+ id: "openai",
1789
+ label: "Mimo cache",
1790
+ matchesModel: isMimoLikeModel,
1791
+ matchesAssistantMessage(message, model) {
1792
+ if (!isAssistantMessage(message)) return false;
1793
+ return isMimoLikeAssistantMessage(message, model);
1794
+ },
1795
+ normalizeUsage(message) {
1796
+ return normalizeWithFallback(message, getOpenAIRawUsage);
1797
+ },
1798
+ warningText(model) {
1799
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
1800
+ if (missing.length === 0) return void 0;
1801
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
1802
+ }
1803
+ },
1804
+ {
1805
+ id: "openai",
1806
+ label: "Hunyuan cache",
1807
+ matchesModel: isHunyuanLikeModel,
1808
+ matchesAssistantMessage(message, model) {
1809
+ if (!isAssistantMessage(message)) return false;
1810
+ return isHunyuanLikeAssistantMessage(message, model);
1811
+ },
1812
+ normalizeUsage(message) {
1813
+ return normalizeWithFallback(message, getOpenAIRawUsage);
1814
+ },
1815
+ warningText(model) {
1816
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
1817
+ if (missing.length === 0) return void 0;
1818
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
1819
+ }
1820
+ },
1821
+ {
1822
+ id: "openai",
1823
+ label: "Mistral cache",
1824
+ matchesModel: isMistralLikeModel,
1825
+ matchesAssistantMessage(message, model) {
1826
+ if (!isAssistantMessage(message)) return false;
1827
+ return isMistralLikeAssistantMessage(message, model);
1828
+ },
1829
+ normalizeUsage(message) {
1830
+ return normalizeWithFallback(message, getOpenAIRawUsage);
1831
+ },
1832
+ warningText(model) {
1833
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
1834
+ if (missing.length === 0) return void 0;
1835
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
1836
+ }
1837
+ },
1838
+ {
1839
+ id: "openai",
1840
+ label: "Grok cache",
1841
+ matchesModel: isGrokLikeModel,
1842
+ matchesAssistantMessage(message, model) {
1843
+ if (!isAssistantMessage(message)) return false;
1844
+ return isGrokLikeAssistantMessage(message, model);
1845
+ },
1846
+ normalizeUsage(message) {
1847
+ return normalizeWithFallback(message, getOpenAIRawUsage);
1848
+ },
1849
+ warningText(model) {
1850
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
1851
+ if (missing.length === 0) return void 0;
1852
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
1853
+ }
1854
+ },
1855
+ {
1856
+ id: "openai",
1857
+ label: "Llama cache",
1858
+ matchesModel: isLlamaLikeModel,
1859
+ matchesAssistantMessage(message, model) {
1860
+ if (!isAssistantMessage(message)) return false;
1861
+ return isLlamaLikeAssistantMessage(message, model);
1862
+ },
1863
+ normalizeUsage(message) {
1864
+ return normalizeWithFallback(message, getOpenAIRawUsage);
1865
+ },
1866
+ warningText(model) {
1867
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
1868
+ if (missing.length === 0) return void 0;
1869
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
1870
+ }
1871
+ },
1872
+ {
1873
+ id: "openai",
1874
+ label: "Nemotron cache",
1875
+ matchesModel: isNemotronLikeModel,
1876
+ matchesAssistantMessage(message, model) {
1877
+ if (!isAssistantMessage(message)) return false;
1878
+ return isNemotronLikeAssistantMessage(message, model);
1879
+ },
1880
+ normalizeUsage(message) {
1881
+ return normalizeWithFallback(message, getOpenAIRawUsage);
1882
+ },
1883
+ warningText(model) {
1884
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
1885
+ if (missing.length === 0) return void 0;
1886
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
1887
+ }
1888
+ },
1889
+ {
1890
+ id: "openai",
1891
+ label: "Cohere cache",
1892
+ matchesModel: isCohereLikeModel,
1893
+ matchesAssistantMessage(message, model) {
1894
+ if (!isAssistantMessage(message)) return false;
1895
+ return isCohereLikeAssistantMessage(message, model);
1896
+ },
1897
+ normalizeUsage(message) {
1898
+ return normalizeWithFallback(message, getOpenAIRawUsage);
1899
+ },
1900
+ warningText(model) {
1901
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
1902
+ if (missing.length === 0) return void 0;
1903
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
1904
+ }
1905
+ },
1906
+ {
1907
+ id: "openai",
1908
+ label: "Yi cache",
1909
+ matchesModel: isYiLikeModel,
1910
+ matchesAssistantMessage(message, model) {
1911
+ if (!isAssistantMessage(message)) return false;
1912
+ return isYiLikeAssistantMessage(message, model);
1913
+ },
1914
+ normalizeUsage(message) {
1915
+ return normalizeWithFallback(message, getOpenAIRawUsage);
1916
+ },
1917
+ warningText(model) {
1918
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
1919
+ if (missing.length === 0) return void 0;
1920
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
1921
+ }
1922
+ },
1923
+ {
1924
+ id: "openai",
1925
+ label: "Doubao cache",
1926
+ matchesModel: isDoubaoLikeModel,
1927
+ matchesAssistantMessage(message, model) {
1928
+ if (!isAssistantMessage(message)) return false;
1929
+ return isDoubaoLikeAssistantMessage(message, model);
1930
+ },
1931
+ normalizeUsage(message) {
1932
+ return normalizeWithFallback(message, getOpenAIRawUsage);
1933
+ },
1934
+ warningText(model) {
1935
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
1936
+ if (missing.length === 0) return void 0;
1937
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
1938
+ }
1939
+ },
1940
+ {
1941
+ id: "openai",
1942
+ label: "ERNIE cache",
1943
+ matchesModel: isErnieLikeModel,
1944
+ matchesAssistantMessage(message, model) {
1945
+ if (!isAssistantMessage(message)) return false;
1946
+ return isErnieLikeAssistantMessage(message, model);
1947
+ },
1948
+ normalizeUsage(message) {
1949
+ return normalizeWithFallback(message, getOpenAIRawUsage);
1950
+ },
1951
+ warningText(model) {
1952
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
1953
+ if (missing.length === 0) return void 0;
1954
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
1955
+ }
1956
+ },
1957
+ {
1958
+ id: "openai",
1959
+ label: "Baichuan cache",
1960
+ matchesModel: isBaichuanLikeModel,
1961
+ matchesAssistantMessage(message, model) {
1962
+ if (!isAssistantMessage(message)) return false;
1963
+ return isBaichuanLikeAssistantMessage(message, model);
1964
+ },
1965
+ normalizeUsage(message) {
1966
+ return normalizeWithFallback(message, getOpenAIRawUsage);
1967
+ },
1968
+ warningText(model) {
1969
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
1970
+ if (missing.length === 0) return void 0;
1971
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
1972
+ }
1973
+ },
1974
+ {
1975
+ id: "openai",
1976
+ label: "StepFun cache",
1977
+ matchesModel: isStepFunLikeModel,
1978
+ matchesAssistantMessage(message, model) {
1979
+ if (!isAssistantMessage(message)) return false;
1980
+ return isStepFunLikeAssistantMessage(message, model);
1981
+ },
1982
+ normalizeUsage(message) {
1983
+ return normalizeWithFallback(message, getOpenAIRawUsage);
1984
+ },
1985
+ warningText(model) {
1986
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
1987
+ if (missing.length === 0) return void 0;
1988
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
1989
+ }
1990
+ },
1991
+ {
1992
+ id: "openai",
1993
+ label: "Spark cache",
1994
+ matchesModel: isSparkLikeModel,
1995
+ matchesAssistantMessage(message, model) {
1996
+ if (!isAssistantMessage(message)) return false;
1997
+ return isSparkLikeAssistantMessage(message, model);
1998
+ },
1999
+ normalizeUsage(message) {
2000
+ return normalizeWithFallback(message, getOpenAIRawUsage);
2001
+ },
2002
+ warningText(model) {
2003
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
2004
+ if (missing.length === 0) return void 0;
2005
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
2006
+ }
2007
+ },
2008
+ {
2009
+ id: "openai",
2010
+ label: "InternLM cache",
2011
+ matchesModel: isInternLMLikeModel,
2012
+ matchesAssistantMessage(message, model) {
2013
+ if (!isAssistantMessage(message)) return false;
2014
+ return isInternLMLikeAssistantMessage(message, model);
2015
+ },
2016
+ normalizeUsage(message) {
2017
+ return normalizeWithFallback(message, getOpenAIRawUsage);
2018
+ },
2019
+ warningText(model) {
2020
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
2021
+ if (missing.length === 0) return void 0;
2022
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
2023
+ }
2024
+ },
2025
+ {
2026
+ id: "openai",
2027
+ label: "Gemma cache",
2028
+ matchesModel: isGemmaLikeModel,
2029
+ matchesAssistantMessage(message, model) {
2030
+ if (!isAssistantMessage(message)) return false;
2031
+ return isGemmaLikeAssistantMessage(message, model);
2032
+ },
2033
+ normalizeUsage(message) {
2034
+ return normalizeWithFallback(message, getOpenAIRawUsage);
2035
+ },
2036
+ warningText(model) {
2037
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
2038
+ if (missing.length === 0) return void 0;
2039
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
2040
+ }
2041
+ },
2042
+ {
2043
+ id: "openai",
2044
+ label: "Phi cache",
2045
+ matchesModel: isPhiLikeModel,
2046
+ matchesAssistantMessage(message, model) {
2047
+ if (!isAssistantMessage(message)) return false;
2048
+ return isPhiLikeAssistantMessage(message, model);
2049
+ },
2050
+ normalizeUsage(message) {
2051
+ return normalizeWithFallback(message, getOpenAIRawUsage);
2052
+ },
2053
+ warningText(model) {
2054
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
2055
+ if (missing.length === 0) return void 0;
2056
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
2057
+ }
2058
+ },
2059
+ {
2060
+ id: "openai",
2061
+ label: "Jamba cache",
2062
+ matchesModel: isJambaLikeModel,
2063
+ matchesAssistantMessage(message, model) {
2064
+ if (!isAssistantMessage(message)) return false;
2065
+ return isJambaLikeAssistantMessage(message, model);
2066
+ },
2067
+ normalizeUsage(message) {
2068
+ return normalizeWithFallback(message, getOpenAIRawUsage);
2069
+ },
2070
+ warningText(model) {
2071
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
2072
+ if (missing.length === 0) return void 0;
2073
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
2074
+ }
2075
+ },
2076
+ {
2077
+ id: "openai",
2078
+ label: "Solar cache",
2079
+ matchesModel: isSolarLikeModel,
2080
+ matchesAssistantMessage(message, model) {
2081
+ if (!isAssistantMessage(message)) return false;
2082
+ return isSolarLikeAssistantMessage(message, model);
2083
+ },
2084
+ normalizeUsage(message) {
2085
+ return normalizeWithFallback(message, getOpenAIRawUsage);
2086
+ },
2087
+ warningText(model) {
2088
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
2089
+ if (missing.length === 0) return void 0;
2090
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
2091
+ }
2092
+ },
2093
+ {
2094
+ id: "openai",
2095
+ label: "Sonar cache",
2096
+ matchesModel: isPerplexityLikeModel,
2097
+ matchesAssistantMessage(message, model) {
2098
+ if (!isAssistantMessage(message)) return false;
2099
+ return isPerplexityLikeAssistantMessage(message, model);
2100
+ },
2101
+ normalizeUsage(message) {
2102
+ return normalizeWithFallback(message, getOpenAIRawUsage);
2103
+ },
2104
+ warningText(model) {
2105
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
2106
+ if (missing.length === 0) return void 0;
2107
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
2108
+ }
2109
+ },
2110
+ {
2111
+ id: "openai",
2112
+ label: "Nova cache",
2113
+ matchesModel: isNovaLikeModel,
2114
+ matchesAssistantMessage(message, model) {
2115
+ if (!isAssistantMessage(message)) return false;
2116
+ return isNovaLikeAssistantMessage(message, model);
2117
+ },
2118
+ normalizeUsage(message) {
2119
+ return normalizeWithFallback(message, getOpenAIRawUsage);
2120
+ },
2121
+ warningText(model) {
2122
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
2123
+ if (missing.length === 0) return void 0;
2124
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
2125
+ }
2126
+ },
2127
+ {
2128
+ id: "openai",
2129
+ label: "Reka cache",
2130
+ matchesModel: isRekaLikeModel,
2131
+ matchesAssistantMessage(message, model) {
2132
+ if (!isAssistantMessage(message)) return false;
2133
+ return isRekaLikeAssistantMessage(message, model);
2134
+ },
2135
+ normalizeUsage(message) {
2136
+ return normalizeWithFallback(message, getOpenAIRawUsage);
2137
+ },
2138
+ warningText(model) {
2139
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
2140
+ if (missing.length === 0) return void 0;
2141
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
2142
+ }
2143
+ },
2144
+ {
2145
+ id: "openai",
2146
+ label: "Falcon cache",
2147
+ matchesModel: isFalconLikeModel,
2148
+ matchesAssistantMessage(message, model) {
2149
+ if (!isAssistantMessage(message)) return false;
2150
+ return isFalconLikeAssistantMessage(message, model);
2151
+ },
2152
+ normalizeUsage(message) {
2153
+ return normalizeWithFallback(message, getOpenAIRawUsage);
2154
+ },
2155
+ warningText(model) {
2156
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
2157
+ if (missing.length === 0) return void 0;
2158
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
2159
+ }
2160
+ },
2161
+ {
2162
+ id: "openai",
2163
+ label: "DBRX cache",
2164
+ matchesModel: isDbrxLikeModel,
2165
+ matchesAssistantMessage(message, model) {
2166
+ if (!isAssistantMessage(message)) return false;
2167
+ return isDbrxLikeAssistantMessage(message, model);
2168
+ },
2169
+ normalizeUsage(message) {
2170
+ return normalizeWithFallback(message, getOpenAIRawUsage);
2171
+ },
2172
+ warningText(model) {
2173
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
2174
+ if (missing.length === 0) return void 0;
2175
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
2176
+ }
2177
+ },
2178
+ {
2179
+ id: "openai",
2180
+ label: "MPT cache",
2181
+ matchesModel: isMptLikeModel,
2182
+ matchesAssistantMessage(message, model) {
2183
+ if (!isAssistantMessage(message)) return false;
2184
+ return isMptLikeAssistantMessage(message, model);
2185
+ },
2186
+ normalizeUsage(message) {
2187
+ return normalizeWithFallback(message, getOpenAIRawUsage);
2188
+ },
2189
+ warningText(model) {
2190
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
2191
+ if (missing.length === 0) return void 0;
2192
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
2193
+ }
2194
+ },
2195
+ {
2196
+ id: "openai",
2197
+ label: "StableLM cache",
2198
+ matchesModel: isStableLMLikeModel,
2199
+ matchesAssistantMessage(message, model) {
2200
+ if (!isAssistantMessage(message)) return false;
2201
+ return isStableLMLikeAssistantMessage(message, model);
2202
+ },
2203
+ normalizeUsage(message) {
2204
+ return normalizeWithFallback(message, getOpenAIRawUsage);
2205
+ },
2206
+ warningText(model) {
2207
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
2208
+ if (missing.length === 0) return void 0;
2209
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
2210
+ }
2211
+ },
2212
+ {
2213
+ id: "openai",
2214
+ label: "Aquila cache",
2215
+ matchesModel: isAquilaLikeModel,
2216
+ matchesAssistantMessage(message, model) {
2217
+ if (!isAssistantMessage(message)) return false;
2218
+ return isAquilaLikeAssistantMessage(message, model);
2219
+ },
2220
+ normalizeUsage(message) {
2221
+ return normalizeWithFallback(message, getOpenAIRawUsage);
2222
+ },
2223
+ warningText(model) {
2224
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
2225
+ if (missing.length === 0) return void 0;
2226
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
2227
+ }
2228
+ },
2229
+ {
2230
+ id: "openai",
2231
+ label: "EXAONE cache",
2232
+ matchesModel: isExaoneLikeModel,
2233
+ matchesAssistantMessage(message, model) {
2234
+ if (!isAssistantMessage(message)) return false;
2235
+ return isExaoneLikeAssistantMessage(message, model);
2236
+ },
2237
+ normalizeUsage(message) {
2238
+ return normalizeWithFallback(message, getOpenAIRawUsage);
2239
+ },
2240
+ warningText(model) {
2241
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
2242
+ if (missing.length === 0) return void 0;
2243
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
2244
+ }
2245
+ },
2246
+ {
2247
+ id: "openai",
2248
+ label: "HyperCLOVA cache",
2249
+ matchesModel: isHyperCLOVALikeModel,
2250
+ matchesAssistantMessage(message, model) {
2251
+ if (!isAssistantMessage(message)) return false;
2252
+ return isHyperCLOVALikeAssistantMessage(message, model);
2253
+ },
2254
+ normalizeUsage(message) {
2255
+ return normalizeWithFallback(message, getOpenAIRawUsage);
2256
+ },
2257
+ warningText(model) {
2258
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
2259
+ if (missing.length === 0) return void 0;
2260
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
2261
+ }
2262
+ },
2263
+ {
2264
+ id: "openai",
2265
+ label: "Luminous cache",
2266
+ matchesModel: isLuminousLikeModel,
2267
+ matchesAssistantMessage(message, model) {
2268
+ if (!isAssistantMessage(message)) return false;
2269
+ return isLuminousLikeAssistantMessage(message, model);
2270
+ },
2271
+ normalizeUsage(message) {
2272
+ return normalizeWithFallback(message, getOpenAIRawUsage);
2273
+ },
2274
+ warningText(model) {
2275
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
2276
+ if (missing.length === 0) return void 0;
2277
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
2278
+ }
2279
+ },
2280
+ {
2281
+ id: "openai",
2282
+ label: "Hermes cache",
2283
+ matchesModel: isHermesLikeModel,
2284
+ matchesAssistantMessage(message, model) {
2285
+ if (!isAssistantMessage(message)) return false;
2286
+ return isHermesLikeAssistantMessage(message, model);
2287
+ },
2288
+ normalizeUsage(message) {
2289
+ return normalizeWithFallback(message, getOpenAIRawUsage);
2290
+ },
2291
+ warningText(model) {
2292
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
2293
+ if (missing.length === 0) return void 0;
2294
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
2295
+ }
2296
+ },
2297
+ {
2298
+ id: "openai",
2299
+ label: "Granite cache",
2300
+ matchesModel: isGraniteLikeModel,
2301
+ matchesAssistantMessage(message, model) {
2302
+ if (!isAssistantMessage(message)) return false;
2303
+ return isGraniteLikeAssistantMessage(message, model);
2304
+ },
2305
+ normalizeUsage(message) {
2306
+ return normalizeWithFallback(message, getOpenAIRawUsage);
2307
+ },
2308
+ warningText(model) {
2309
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
2310
+ if (missing.length === 0) return void 0;
2311
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
2312
+ }
2313
+ },
2314
+ {
2315
+ id: "openai",
2316
+ label: "Arctic cache",
2317
+ matchesModel: isArcticLikeModel,
2318
+ matchesAssistantMessage(message, model) {
2319
+ if (!isAssistantMessage(message)) return false;
2320
+ return isArcticLikeAssistantMessage(message, model);
2321
+ },
2322
+ normalizeUsage(message) {
2323
+ return normalizeWithFallback(message, getOpenAIRawUsage);
2324
+ },
2325
+ warningText(model) {
2326
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
2327
+ if (missing.length === 0) return void 0;
2328
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
2329
+ }
2330
+ },
2331
+ {
2332
+ id: "openai",
2333
+ label: "Pangu cache",
2334
+ matchesModel: isPanguLikeModel,
2335
+ matchesAssistantMessage(message, model) {
2336
+ if (!isAssistantMessage(message)) return false;
2337
+ return isPanguLikeAssistantMessage(message, model);
2338
+ },
2339
+ normalizeUsage(message) {
2340
+ return normalizeWithFallback(message, getOpenAIRawUsage);
2341
+ },
2342
+ warningText(model) {
2343
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
2344
+ if (missing.length === 0) return void 0;
2345
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
2346
+ }
2347
+ },
2348
+ {
2349
+ id: "openai",
2350
+ label: "SenseNova cache",
2351
+ matchesModel: isSenseNovaLikeModel,
2352
+ matchesAssistantMessage(message, model) {
2353
+ if (!isAssistantMessage(message)) return false;
2354
+ return isSenseNovaLikeAssistantMessage(message, model);
2355
+ },
2356
+ normalizeUsage(message) {
2357
+ return normalizeWithFallback(message, getOpenAIRawUsage);
2358
+ },
2359
+ warningText(model) {
2360
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
2361
+ if (missing.length === 0) return void 0;
2362
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
2363
+ }
2364
+ },
2365
+ {
2366
+ id: "openai",
2367
+ label: "Zhinao cache",
2368
+ matchesModel: isZhinaoLikeModel,
2369
+ matchesAssistantMessage(message, model) {
2370
+ if (!isAssistantMessage(message)) return false;
2371
+ return isZhinaoLikeAssistantMessage(message, model);
2372
+ },
2373
+ normalizeUsage(message) {
2374
+ return normalizeWithFallback(message, getOpenAIRawUsage);
2375
+ },
2376
+ warningText(model) {
2377
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
2378
+ if (missing.length === 0) return void 0;
2379
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
2380
+ }
2381
+ },
2382
+ {
2383
+ id: "openai",
2384
+ label: "MiniCPM cache",
2385
+ matchesModel: isMiniCPMLikeModel,
2386
+ matchesAssistantMessage(message, model) {
2387
+ if (!isAssistantMessage(message)) return false;
2388
+ return isMiniCPMLikeAssistantMessage(message, model);
2389
+ },
2390
+ normalizeUsage(message) {
2391
+ return normalizeWithFallback(message, getOpenAIRawUsage);
2392
+ },
2393
+ warningText(model) {
2394
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
2395
+ if (missing.length === 0) return void 0;
2396
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
2397
+ }
2398
+ },
2399
+ {
2400
+ id: "openai",
2401
+ label: "XVERSE cache",
2402
+ matchesModel: isXVerseLikeModel,
2403
+ matchesAssistantMessage(message, model) {
2404
+ if (!isAssistantMessage(message)) return false;
2405
+ return isXVerseLikeAssistantMessage(message, model);
2406
+ },
2407
+ normalizeUsage(message) {
2408
+ return normalizeWithFallback(message, getOpenAIRawUsage);
2409
+ },
2410
+ warningText(model) {
2411
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
2412
+ if (missing.length === 0) return void 0;
2413
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
2414
+ }
2415
+ },
2416
+ {
2417
+ id: "openai",
2418
+ label: "Orion cache",
2419
+ matchesModel: isOrionLikeModel,
2420
+ matchesAssistantMessage(message, model) {
2421
+ if (!isAssistantMessage(message)) return false;
2422
+ return isOrionLikeAssistantMessage(message, model);
2423
+ },
2424
+ normalizeUsage(message) {
2425
+ return normalizeWithFallback(message, getOpenAIRawUsage);
2426
+ },
2427
+ warningText(model) {
2428
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
2429
+ if (missing.length === 0) return void 0;
2430
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
2431
+ }
2432
+ },
2433
+ {
2434
+ id: "openai",
2435
+ label: "OpenChat cache",
2436
+ matchesModel: isOpenChatLikeModel,
2437
+ matchesAssistantMessage(message, model) {
2438
+ if (!isAssistantMessage(message)) return false;
2439
+ return isOpenChatLikeAssistantMessage(message, model);
2440
+ },
2441
+ normalizeUsage(message) {
2442
+ return normalizeWithFallback(message, getOpenAIRawUsage);
2443
+ },
2444
+ warningText(model) {
2445
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
2446
+ if (missing.length === 0) return void 0;
2447
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
2448
+ }
2449
+ },
2450
+ {
2451
+ id: "openai",
2452
+ label: "Vicuna cache",
2453
+ matchesModel: isVicunaLikeModel,
2454
+ matchesAssistantMessage(message, model) {
2455
+ if (!isAssistantMessage(message)) return false;
2456
+ return isVicunaLikeAssistantMessage(message, model);
2457
+ },
2458
+ normalizeUsage(message) {
2459
+ return normalizeWithFallback(message, getOpenAIRawUsage);
2460
+ },
2461
+ warningText(model) {
2462
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
2463
+ if (missing.length === 0) return void 0;
2464
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
2465
+ }
2466
+ },
2467
+ {
2468
+ id: "openai",
2469
+ label: "Wizard cache",
2470
+ matchesModel: isWizardLikeModel,
2471
+ matchesAssistantMessage(message, model) {
2472
+ if (!isAssistantMessage(message)) return false;
2473
+ return isWizardLikeAssistantMessage(message, model);
2474
+ },
2475
+ normalizeUsage(message) {
2476
+ return normalizeWithFallback(message, getOpenAIRawUsage);
2477
+ },
2478
+ warningText(model) {
2479
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
2480
+ if (missing.length === 0) return void 0;
2481
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
2482
+ }
2483
+ },
2484
+ {
2485
+ id: "openai",
2486
+ label: "Zephyr cache",
2487
+ matchesModel: isZephyrLikeModel,
2488
+ matchesAssistantMessage(message, model) {
2489
+ if (!isAssistantMessage(message)) return false;
2490
+ return isZephyrLikeAssistantMessage(message, model);
2491
+ },
2492
+ normalizeUsage(message) {
2493
+ return normalizeWithFallback(message, getOpenAIRawUsage);
2494
+ },
2495
+ warningText(model) {
2496
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
2497
+ if (missing.length === 0) return void 0;
2498
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
2499
+ }
2500
+ },
2501
+ {
2502
+ id: "openai",
2503
+ label: "Dolphin cache",
2504
+ matchesModel: isDolphinLikeModel,
2505
+ matchesAssistantMessage(message, model) {
2506
+ if (!isAssistantMessage(message)) return false;
2507
+ return isDolphinLikeAssistantMessage(message, model);
2508
+ },
2509
+ normalizeUsage(message) {
2510
+ return normalizeWithFallback(message, getOpenAIRawUsage);
2511
+ },
2512
+ warningText(model) {
2513
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
2514
+ if (missing.length === 0) return void 0;
2515
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
2516
+ }
2517
+ },
2518
+ {
2519
+ id: "openai",
2520
+ label: "OpenOrca cache",
2521
+ matchesModel: isOpenOrcaLikeModel,
2522
+ matchesAssistantMessage(message, model) {
2523
+ if (!isAssistantMessage(message)) return false;
2524
+ return isOpenOrcaLikeAssistantMessage(message, model);
2525
+ },
2526
+ normalizeUsage(message) {
2527
+ return normalizeWithFallback(message, getOpenAIRawUsage);
2528
+ },
2529
+ warningText(model) {
2530
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
2531
+ if (missing.length === 0) return void 0;
2532
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
2533
+ }
2534
+ },
2535
+ {
2536
+ id: "openai",
2537
+ label: "Starling cache",
2538
+ matchesModel: isStarlingLikeModel,
2539
+ matchesAssistantMessage(message, model) {
2540
+ if (!isAssistantMessage(message)) return false;
2541
+ return isStarlingLikeAssistantMessage(message, model);
2542
+ },
2543
+ normalizeUsage(message) {
2544
+ return normalizeWithFallback(message, getOpenAIRawUsage);
2545
+ },
2546
+ warningText(model) {
2547
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
2548
+ if (missing.length === 0) return void 0;
2549
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
2550
+ }
2551
+ },
2552
+ {
2553
+ id: "openai",
2554
+ label: "BLOOM cache",
2555
+ matchesModel: isBloomLikeModel,
2556
+ matchesAssistantMessage(message, model) {
2557
+ if (!isAssistantMessage(message)) return false;
2558
+ return isBloomLikeAssistantMessage(message, model);
2559
+ },
2560
+ normalizeUsage(message) {
2561
+ return normalizeWithFallback(message, getOpenAIRawUsage);
2562
+ },
2563
+ warningText(model) {
2564
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
2565
+ if (missing.length === 0) return void 0;
2566
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
2567
+ }
2568
+ },
2569
+ {
2570
+ id: "openai",
2571
+ label: "RWKV cache",
2572
+ matchesModel: isRwkvLikeModel,
2573
+ matchesAssistantMessage(message, model) {
2574
+ if (!isAssistantMessage(message)) return false;
2575
+ return isRwkvLikeAssistantMessage(message, model);
2576
+ },
2577
+ normalizeUsage(message) {
2578
+ return normalizeWithFallback(message, getOpenAIRawUsage);
2579
+ },
2580
+ warningText(model) {
2581
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
2582
+ if (missing.length === 0) return void 0;
2583
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
2584
+ }
2585
+ },
2586
+ {
2587
+ id: "openai",
2588
+ label: "Aya cache",
2589
+ matchesModel: isAyaLikeModel,
2590
+ matchesAssistantMessage(message, model) {
2591
+ if (!isAssistantMessage(message)) return false;
2592
+ return isAyaLikeAssistantMessage(message, model);
2593
+ },
2594
+ normalizeUsage(message) {
2595
+ return normalizeWithFallback(message, getOpenAIRawUsage);
2596
+ },
2597
+ warningText(model) {
2598
+ const missing = describeMissingOpenAICompatibleProxyCompat(model);
2599
+ if (missing.length === 0) return void 0;
2600
+ return buildOpenAIProxyCompatWarningText(modelKey(model), missing);
2601
+ }
2602
+ }
2603
+ ];
2604
+ function selectAdapterForModel(model) {
2605
+ if (isNativeVirtualModel(model)) return void 0;
2606
+ return CACHE_PROVIDER_ADAPTERS.find((adapter) => adapter.matchesModel(model));
2607
+ }
2608
+ function selectAdapterForAssistantMessage(message, model) {
2609
+ const responseModel = modelFromAssistantMessage(message, model);
2610
+ return CACHE_PROVIDER_ADAPTERS.find((adapter) => adapter.matchesAssistantMessage(message, responseModel));
2611
+ }
2612
+ function notifyCacheCompatIfNeeded(model, notify, warnedModels) {
2613
+ if (!model) return;
2614
+ if (isAdaptiveThinkingCompatApplicable(model)) {
2615
+ const missing = describeMissingAdaptiveThinkingCompat(model);
2616
+ if (missing.length > 0) {
2617
+ const key = `adaptive-thinking:${modelKey(model)}`;
2618
+ if (!warnedModels.has(key)) {
2619
+ warnedModels.add(key);
2620
+ notify(buildAdaptiveThinkingCompatWarningText(modelKey(model), missing), "warning");
2621
+ }
2622
+ }
2623
+ }
2624
+ const adapter = selectAdapterForModel(model);
2625
+ const text = adapter?.warningText?.(model);
2626
+ if (!adapter || !text) return;
2627
+ const key = adapter.warningText !== void 0 && describeMissingOpenAICompatibleProxyCompat(model).length === 1 && describeMissingCacheCompatForModel(model).length === 1 ? `proxy-affinity:${model.provider}` : `${adapter.id}:${modelKey(model)}`;
2628
+ if (warnedModels.has(key)) return;
2629
+ warnedModels.add(key);
2630
+ notify(text, "warning");
2631
+ }
2632
+ function currentLocalDay() {
2633
+ const now = /* @__PURE__ */ new Date();
2634
+ return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}`;
2635
+ }
2636
+ function emptyCacheStats(day = currentLocalDay()) {
2637
+ return {
2638
+ day,
2639
+ totalRequests: 0,
2640
+ hitRequests: 0,
2641
+ cachedInputTokens: 0,
2642
+ cacheWriteInputTokens: 0,
2643
+ totalInputTokens: 0
2644
+ };
2645
+ }
2646
+ function addUsageToCacheStats(stats, usage) {
2647
+ stats.totalRequests += 1;
2648
+ if (usage.cacheRead > 0) stats.hitRequests += 1;
2649
+ stats.cachedInputTokens += usage.cacheRead;
2650
+ stats.cacheWriteInputTokens += usage.cacheWrite;
2651
+ stats.totalInputTokens += usage.totalInput;
2652
+ }
2653
+ function formatTokenCount(value) {
2654
+ const millions = Math.max(0, Math.round(value)) / 1e6;
2655
+ if (millions === 0) return "0M";
2656
+ if (millions < .001) return `${millions.toFixed(4)}M`;
2657
+ if (millions < .01) return `${millions.toFixed(3)}M`;
2658
+ if (millions >= 10) return `${millions.toFixed(1)}M`;
2659
+ return `${millions.toFixed(2)}M`;
2660
+ }
2661
+ function formatCacheStats(adapter, stats) {
2662
+ const percent = stats.totalInputTokens > 0 ? stats.cachedInputTokens / stats.totalInputTokens * 100 : 0;
2663
+ const writeText = adapter.showCacheWrite && stats.cacheWriteInputTokens > 0 ? `·write ${formatTokenCount(stats.cacheWriteInputTokens)}` : "";
2664
+ return `${adapter.label} ${stats.hitRequests}/${stats.totalRequests}·${formatTokenCount(stats.cachedInputTokens)}/${formatTokenCount(stats.totalInputTokens)} ${percent.toFixed(1)}%${writeText}`;
2665
+ }
2666
+ function prefixFooterStatus(statusText) {
2667
+ if (!statusText || statusText.startsWith("· ")) return statusText;
2668
+ return `· ${statusText}`;
2669
+ }
2670
+ function hasMissingUsageFields(message, adapter) {
2671
+ const usage = usageRecordFromAssistant(message);
2672
+ if (!usage) return true;
2673
+ const input = getNonNegativeNumber(usage, "input");
2674
+ const cacheRead = getNonNegativeNumber(usage, "cacheRead");
2675
+ const cacheWrite = getNonNegativeNumber(usage, "cacheWrite");
2676
+ if (cacheRead !== void 0 || cacheWrite !== void 0 || input !== void 0 && input > 0) return false;
2677
+ const rawUsage = adapter.normalizeUsage(message);
2678
+ if (!rawUsage || rawUsage.cacheRead === 0 && rawUsage.cacheWrite === 0 && rawUsage.totalInput === 0) return true;
2679
+ return false;
2680
+ }
2681
+ function getErrorCode(error) {
2682
+ return typeof error === "object" && error !== null && "code" in error ? String(error.code) : void 0;
2683
+ }
2684
+ function parseCacheStats(value) {
2685
+ const stats = asRecord(value);
2686
+ if (!stats || typeof stats.day !== "string" || !/^\d{4}-\d{2}-\d{2}$/.test(stats.day)) return;
2687
+ const totalRequests = getNonNegativeNumber(stats, "totalRequests");
2688
+ const hitRequests = getNonNegativeNumber(stats, "hitRequests");
2689
+ const cachedInputTokens = getNonNegativeNumber(stats, "cachedInputTokens");
2690
+ const cacheWriteInputTokens = getNonNegativeNumber(stats, "cacheWriteInputTokens") ?? 0;
2691
+ const totalInputTokens = getNonNegativeNumber(stats, "totalInputTokens");
2692
+ if (totalRequests === void 0 || hitRequests === void 0 || cachedInputTokens === void 0 || totalInputTokens === void 0 || hitRequests > totalRequests || cachedInputTokens > totalInputTokens || cacheWriteInputTokens > totalInputTokens) return;
2693
+ return {
2694
+ day: stats.day,
2695
+ totalRequests,
2696
+ hitRequests,
2697
+ cachedInputTokens,
2698
+ cacheWriteInputTokens,
2699
+ totalInputTokens
2700
+ };
2701
+ }
2702
+ function cloneCacheStats(stats) {
2703
+ return { ...stats };
2704
+ }
2705
+ function addCacheStatsTotals(target, source) {
2706
+ target.totalRequests += source.totalRequests;
2707
+ target.hitRequests += source.hitRequests;
2708
+ target.cachedInputTokens += source.cachedInputTokens;
2709
+ target.cacheWriteInputTokens += source.cacheWriteInputTokens;
2710
+ target.totalInputTokens += source.totalInputTokens;
2711
+ }
2712
+ function mergeCacheStatsForTotal(existing, incoming) {
2713
+ if (!existing) return cloneCacheStats(incoming);
2714
+ if (incoming.day > existing.day) return cloneCacheStats(incoming);
2715
+ if (incoming.day < existing.day) return existing;
2716
+ addCacheStatsTotals(existing, incoming);
2717
+ return existing;
2718
+ }
2719
+ function parsePersistedRoutedModelRef(value) {
2720
+ const record = asRecord(value);
2721
+ const provider = record?.provider;
2722
+ const id = record?.id;
2723
+ const name = record?.name;
2724
+ if (!isNonEmptyString(provider) || !isNonEmptyString(id)) return void 0;
2725
+ return {
2726
+ provider: provider.trim(),
2727
+ id: id.trim(),
2728
+ name: isNonEmptyString(name) ? name.trim() : id.trim()
2729
+ };
2730
+ }
2731
+ function routedModelRefToCacheModel(ref) {
2732
+ return {
2733
+ id: ref.id,
2734
+ name: ref.name ?? ref.id,
2735
+ provider: ref.provider,
2736
+ api: "",
2737
+ baseUrl: ""
2738
+ };
2739
+ }
2740
+ function selectFooterStatsForModel(mode, sessionHash, statsByModel, totalsByModel, model, processByModel = {}) {
2741
+ if (mode === "total") return totalsByModel[modelKey(model)];
2742
+ if (mode === "process") return processByModel[modelKey(model)];
2743
+ if (!sessionHash) return void 0;
2744
+ return statsByModel[makeSessionModelKey(sessionHash, model.provider, model.id)];
2745
+ }
2746
+ function buildExactRouterStatusEntry(sessionHash, statsByModel, lastRoutedModel, totalsByModel = {}, mode = "total", processByModel = {}) {
2747
+ if (!sessionHash || !lastRoutedModel) return void 0;
2748
+ const model = routedModelRefToCacheModel(lastRoutedModel);
2749
+ const adapter = selectAdapterForModel(model);
2750
+ if (!adapter) return void 0;
2751
+ return {
2752
+ model,
2753
+ adapter,
2754
+ stats: selectFooterStatsForModel(mode, sessionHash, statsByModel, totalsByModel, model, processByModel) ?? emptyCacheStats()
2755
+ };
2756
+ }
2757
+ function findBestRouterModelStats(mode, sessionHash, statsByModel, totalsByModel, processByModel = {}) {
2758
+ const entries = mode === "total" ? Object.entries(totalsByModel) : mode === "process" ? Object.entries(processByModel) : sessionHash ? Object.entries(statsByModel).filter(([key]) => key.startsWith(`${sessionHash}:`)).map(([key, stats]) => [key.slice(sessionHash.length + 1), stats]) : [];
2759
+ let best;
2760
+ for (const [modelKeyPart, stats] of entries) {
2761
+ const slashIdx = modelKeyPart.indexOf("/");
2762
+ if (slashIdx < 1 || slashIdx >= modelKeyPart.length - 1) continue;
2763
+ const model = routedModelRefToCacheModel({
2764
+ provider: modelKeyPart.slice(0, slashIdx),
2765
+ id: modelKeyPart.slice(slashIdx + 1)
2766
+ });
2767
+ const adapter = selectAdapterForModel(model);
2768
+ if (!adapter) continue;
2769
+ if (!best || stats.totalRequests > best.total) best = {
2770
+ model,
2771
+ adapter,
2772
+ stats,
2773
+ total: stats.totalRequests
2774
+ };
2775
+ }
2776
+ return best ? {
2777
+ model: best.model,
2778
+ adapter: best.adapter,
2779
+ stats: best.stats
2780
+ } : void 0;
2781
+ }
2782
+ function createSerializedAsyncRunner() {
2783
+ let tail = Promise.resolve();
2784
+ return (operation) => {
2785
+ const result = tail.then(operation);
2786
+ tail = result.catch(() => void 0);
2787
+ return result;
2788
+ };
2789
+ }
2790
+ function modelEpochPath(modelKeyValue) {
2791
+ return join(SHARD_MODEL_EPOCH_DIR, `${createHash("sha256").update(modelKeyValue).digest("hex")}.json`);
2792
+ }
2793
+ function initialEpoch(scope) {
2794
+ return `initial:${scope}`;
2795
+ }
2796
+ function parseEpochRecord(value) {
2797
+ const record = asRecord(value);
2798
+ return record?.version === 1 && isNonEmptyString(record.epoch) ? record.epoch.trim() : void 0;
2799
+ }
2800
+ async function readEpochFile(path, fallback) {
2801
+ try {
2802
+ return parseEpochRecord(JSON.parse(await readFile(path, "utf8"))) ?? fallback;
2803
+ } catch {
2804
+ return fallback;
2805
+ }
2806
+ }
2807
+ async function readGlobalStatsEpoch() {
2808
+ return readEpochFile(SHARD_GLOBAL_EPOCH_PATH, initialEpoch("global"));
2809
+ }
2810
+ async function readModelStatsEpoch(modelKeyValue) {
2811
+ return readEpochFile(modelEpochPath(modelKeyValue), initialEpoch(`model:${modelKeyValue}`));
2812
+ }
2813
+ async function writeStatsEpoch(path, epoch) {
2814
+ await mkdir(dirname(path), { recursive: true });
2815
+ const tempPath = `${path}.${process.pid}.${Date.now()}.${randomUUID()}.tmp`;
2816
+ await writeFile(tempPath, JSON.stringify({
2817
+ version: 1,
2818
+ epoch,
2819
+ createdAt: Date.now()
2820
+ }, null, 2) + "\n", "utf8");
2821
+ await rename(tempPath, path);
2822
+ }
2823
+ async function advanceGlobalStatsEpoch() {
2824
+ const epoch = `${Date.now()}-${randomUUID()}`;
2825
+ await writeStatsEpoch(SHARD_GLOBAL_EPOCH_PATH, epoch);
2826
+ return epoch;
2827
+ }
2828
+ async function advanceModelStatsEpoch(modelKeyValue) {
2829
+ const epoch = `${Date.now()}-${randomUUID()}`;
2830
+ await writeStatsEpoch(modelEpochPath(modelKeyValue), epoch);
2831
+ return epoch;
2832
+ }
2833
+ function parsePersistedStatsShardV7(value) {
2834
+ const record = asRecord(value);
2835
+ const processRecord = asRecord(record?.process);
2836
+ const lifecycle = asRecord(record?.lifecycle);
2837
+ const rawModels = asRecord(record?.models);
2838
+ if (record?.version !== 7 || record.kind !== "pi-cache-optimizer-shard" || !isNonEmptyString(record.instanceId) || !isNonEmptyString(record.sessionHash) || !processRecord || !Number.isInteger(processRecord.pid) || !Number.isInteger(processRecord.ppid) || typeof processRecord.instanceStartedAt !== "number" || !lifecycle || lifecycle.state !== "active" && lifecycle.state !== "closed" || typeof lifecycle.createdAt !== "number" || typeof lifecycle.updatedAt !== "number" || !isNonEmptyString(record.day) || !/^\d{4}-\d{2}-\d{2}$/.test(record.day) || !isNonEmptyString(record.globalEpoch) || !rawModels) return void 0;
2839
+ const models = {};
2840
+ for (const [key, rawEntry] of Object.entries(rawModels)) {
2841
+ const entry = asRecord(rawEntry);
2842
+ const stats = parseCacheStats(entry?.stats);
2843
+ if (!entry || !stats || !isNonEmptyString(entry.modelEpoch) || !isNonEmptyString(entry.provider) || !isNonEmptyString(entry.modelId) || key !== `${entry.provider.trim()}/${entry.modelId.trim()}`) continue;
2844
+ models[key] = {
2845
+ modelEpoch: entry.modelEpoch.trim(),
2846
+ provider: entry.provider.trim(),
2847
+ modelId: entry.modelId.trim(),
2848
+ ...isNonEmptyString(entry.modelName) ? { modelName: entry.modelName.trim() } : {},
2849
+ ...isNonEmptyString(entry.api) ? { api: entry.api.trim() } : {},
2850
+ stats
2851
+ };
2852
+ }
2853
+ const lastRoutedModel = parsePersistedRoutedModelRef(record.lastRoutedModel);
2854
+ return {
2855
+ version: 7,
2856
+ kind: "pi-cache-optimizer-shard",
2857
+ instanceId: record.instanceId.trim(),
2858
+ sessionHash: record.sessionHash.trim(),
2859
+ process: {
2860
+ pid: Number(processRecord.pid),
2861
+ ppid: Number(processRecord.ppid),
2862
+ instanceStartedAt: processRecord.instanceStartedAt
2863
+ },
2864
+ lifecycle: {
2865
+ state: lifecycle.state,
2866
+ createdAt: lifecycle.createdAt,
2867
+ updatedAt: lifecycle.updatedAt,
2868
+ ...typeof lifecycle.closedAt === "number" ? { closedAt: lifecycle.closedAt } : {}
2869
+ },
2870
+ day: record.day,
2871
+ globalEpoch: record.globalEpoch.trim(),
2872
+ models,
2873
+ ...lastRoutedModel ? { lastRoutedModel } : {}
2874
+ };
2875
+ }
2876
+ async function writeStatsShardV7(path, shard) {
2877
+ await mkdir(dirname(path), { recursive: true });
2878
+ const tempPath = `${path}.${process.pid}.${Date.now()}.${randomUUID()}.tmp`;
2879
+ await writeFile(tempPath, JSON.stringify(shard, null, 2) + "\n", "utf8");
2880
+ await rename(tempPath, path);
2881
+ }
2882
+ async function readValidStatsShardsV7(directory = SHARD_FILES_DIR) {
2883
+ let names;
2884
+ try {
2885
+ names = await readdir(directory);
2886
+ } catch (error) {
2887
+ if (getErrorCode(error) === "ENOENT") return [];
2888
+ throw error;
2889
+ }
2890
+ const shards = [];
2891
+ for (const name of names) {
2892
+ if (!/^[0-9a-f-]+\.json$/i.test(name)) continue;
2893
+ const path = join(directory, name);
2894
+ try {
2895
+ const info = await lstat(path);
2896
+ if (!info.isFile() || info.isSymbolicLink()) continue;
2897
+ const parsed = parsePersistedStatsShardV7(JSON.parse(await readFile(path, "utf8")));
2898
+ const filenameInstanceId = name.slice(0, -5);
2899
+ if (parsed && parsed.instanceId === filenameInstanceId) shards.push(parsed);
2900
+ } catch {}
2901
+ }
2902
+ return shards;
2903
+ }
2904
+ async function aggregateStatsShardsV7(shards, day = currentLocalDay()) {
2905
+ const globalEpoch = await readGlobalStatsEpoch();
2906
+ const modelEpochs = /* @__PURE__ */ new Map();
2907
+ const result = {
2908
+ bySession: {},
2909
+ totalsByModel: {},
2910
+ instancesBySession: {},
2911
+ instancesBySessionModel: {},
2912
+ sessionsByModel: {},
2913
+ instancesByModel: {},
2914
+ modelRefsByKey: {},
2915
+ lastRoutedModelBySession: {}
2916
+ };
2917
+ const routedUpdatedAt = /* @__PURE__ */ new Map();
2918
+ const modelRefUpdatedAt = /* @__PURE__ */ new Map();
2919
+ const modelSessions = /* @__PURE__ */ new Map();
2920
+ for (const shard of shards) {
2921
+ if (shard.day !== day || shard.globalEpoch !== globalEpoch) continue;
2922
+ let contributed = false;
2923
+ if (shard.lastRoutedModel && (routedUpdatedAt.get(shard.sessionHash) ?? -1) < shard.lifecycle.updatedAt) {
2924
+ routedUpdatedAt.set(shard.sessionHash, shard.lifecycle.updatedAt);
2925
+ result.lastRoutedModelBySession[shard.sessionHash] = shard.lastRoutedModel;
2926
+ }
2927
+ for (const [key, entry] of Object.entries(shard.models)) {
2928
+ let epoch = modelEpochs.get(key);
2929
+ if (!epoch) {
2930
+ epoch = await readModelStatsEpoch(key);
2931
+ modelEpochs.set(key, epoch);
2932
+ }
2933
+ if (entry.modelEpoch !== epoch || entry.stats.day !== day) continue;
2934
+ contributed = true;
2935
+ const sessionModels = result.bySession[shard.sessionHash] ??= {};
2936
+ sessionModels[key] = mergeCacheStatsForTotal(sessionModels[key], entry.stats);
2937
+ result.totalsByModel[key] = mergeCacheStatsForTotal(result.totalsByModel[key], entry.stats);
2938
+ result.instancesByModel[key] = (result.instancesByModel[key] ?? 0) + 1;
2939
+ if ((modelRefUpdatedAt.get(key) ?? -1) < shard.lifecycle.updatedAt) {
2940
+ modelRefUpdatedAt.set(key, shard.lifecycle.updatedAt);
2941
+ result.modelRefsByKey[key] = {
2942
+ provider: entry.provider,
2943
+ id: entry.modelId,
2944
+ name: entry.modelName ?? entry.modelId
2945
+ };
2946
+ }
2947
+ const sessionInstances = result.instancesBySessionModel[shard.sessionHash] ??= {};
2948
+ sessionInstances[key] = (sessionInstances[key] ?? 0) + 1;
2949
+ const sessions = modelSessions.get(key) ?? /* @__PURE__ */ new Set();
2950
+ sessions.add(shard.sessionHash);
2951
+ modelSessions.set(key, sessions);
2952
+ }
2953
+ if (contributed) result.instancesBySession[shard.sessionHash] = (result.instancesBySession[shard.sessionHash] ?? 0) + 1;
2954
+ }
2955
+ for (const [key, sessions] of modelSessions) result.sessionsByModel[key] = sessions.size;
2956
+ return result;
2957
+ }
2958
+ function isProcessAlive(pid) {
2959
+ if (!Number.isInteger(pid) || pid <= 0) return false;
2960
+ try {
2961
+ process.kill(pid, 0);
2962
+ return true;
2963
+ } catch (error) {
2964
+ return getErrorCode(error) === "EPERM";
2965
+ }
2966
+ }
2967
+ async function removeLegacyStatsFiles() {
2968
+ for (const path of [STATE_FILE_PATH, LEGACY_STATE_FILE_PATH]) try {
2969
+ await unlink(path);
2970
+ } catch (error) {
2971
+ if (getErrorCode(error) !== "ENOENT") console.warn(`${LOG_PREFIX}: failed to remove obsolete stats file ${path}`, error);
2972
+ }
2973
+ }
2974
+ async function cleanupStatsShardsV7(now = Date.now(), directory = SHARD_FILES_DIR) {
2975
+ let names;
2976
+ try {
2977
+ names = await readdir(directory);
2978
+ } catch (error) {
2979
+ return getErrorCode(error) === "ENOENT" ? 0 : Promise.reject(error);
2980
+ }
2981
+ const today = currentLocalDay();
2982
+ let removed = 0;
2983
+ for (const name of names) {
2984
+ const isShard = /^[0-9a-f-]+\.json$/i.test(name);
2985
+ const isTemp = name.endsWith(".tmp");
2986
+ if (!isShard && !isTemp) continue;
2987
+ const path = join(directory, name);
2988
+ try {
2989
+ const info = await lstat(path);
2990
+ if (!info.isFile() || info.isSymbolicLink()) continue;
2991
+ if (isTemp) {
2992
+ if (now - info.mtimeMs < SHARD_TEMP_RETENTION_MS) continue;
2993
+ } else {
2994
+ const parsed = parsePersistedStatsShardV7(JSON.parse(await readFile(path, "utf8")));
2995
+ if (parsed?.day === today) continue;
2996
+ if (now - (parsed?.lifecycle.updatedAt ?? info.mtimeMs) < SHARD_RETENTION_MS) continue;
2997
+ if (parsed?.lifecycle.state === "active" && isProcessAlive(parsed.process.pid)) continue;
2998
+ }
2999
+ await unlink(path);
3000
+ removed += 1;
3001
+ } catch {}
3002
+ }
3003
+ return removed;
3004
+ }
3005
+ async function maybeCleanupStatsShardsV7(now = Date.now()) {
3006
+ await mkdir(SHARD_MAINTENANCE_DIR, { recursive: true });
3007
+ try {
3008
+ if (now - (await stat(SHARD_CLEANUP_MARKER_PATH)).mtimeMs < SHARD_CLEANUP_INTERVAL_MS) return;
3009
+ } catch {}
3010
+ try {
3011
+ await mkdir(SHARD_CLEANUP_LOCK_PATH);
3012
+ } catch (error) {
3013
+ if (getErrorCode(error) !== "EEXIST") return;
3014
+ try {
3015
+ if (now - (await stat(SHARD_CLEANUP_LOCK_PATH)).mtimeMs <= SHARD_CLEANUP_LOCK_STALE_MS) return;
3016
+ await rm(SHARD_CLEANUP_LOCK_PATH, {
3017
+ recursive: true,
3018
+ force: true
3019
+ });
3020
+ await mkdir(SHARD_CLEANUP_LOCK_PATH);
3021
+ } catch {
3022
+ return;
3023
+ }
3024
+ }
3025
+ try {
3026
+ await cleanupStatsShardsV7(now);
3027
+ const tempPath = `${SHARD_CLEANUP_MARKER_PATH}.${process.pid}.${randomUUID()}.tmp`;
3028
+ await writeFile(tempPath, `${now}\n`, "utf8");
3029
+ await rename(tempPath, SHARD_CLEANUP_MARKER_PATH);
3030
+ } finally {
3031
+ await rm(SHARD_CLEANUP_LOCK_PATH, {
3032
+ recursive: true,
3033
+ force: true
3034
+ });
3035
+ }
3036
+ }
3037
+ function isExplicitPromptCacheRetentionUnsupportedApplicable(model) {
3038
+ return isOpenAICompatibleApi(model.api) && !isOfficialOpenAIBaseUrl(model) && !isPiBuiltInLlamaCppModel(model);
3039
+ }
3040
+ function isJsonWhitespace(ch) {
3041
+ return ch === " " || ch === "\n" || ch === "\r" || ch === " ";
3042
+ }
3043
+ function readJsonStringLiteral(text, pos) {
3044
+ if (text[pos] !== "\"") return void 0;
3045
+ let i = pos + 1;
3046
+ let value = "";
3047
+ while (i < text.length) {
3048
+ const ch = text[i];
3049
+ if (ch === "\\") {
3050
+ const next = text[i + 1];
3051
+ if (next === void 0) return void 0;
3052
+ if (next === "u") {
3053
+ const hex = text.slice(i + 2, i + 6);
3054
+ if (!/^[0-9a-fA-F]{4}$/.test(hex)) return void 0;
3055
+ value += String.fromCharCode(parseInt(hex, 16));
3056
+ i += 6;
3057
+ } else {
3058
+ if (next === "n") value += "\n";
3059
+ else if (next === "t") value += " ";
3060
+ else if (next === "r") value += "\r";
3061
+ else if (next === "b") value += "\b";
3062
+ else if (next === "f") value += "\f";
3063
+ else value += next;
3064
+ i += 2;
3065
+ }
3066
+ continue;
3067
+ }
3068
+ if (ch === "\"") return {
3069
+ value,
3070
+ end: i + 1
3071
+ };
3072
+ value += ch;
3073
+ i++;
3074
+ }
3075
+ }
3076
+ function buildFixSuggestion(model) {
3077
+ const missing = describeMissingCacheCompatForModel(model);
3078
+ if (missing.length === 0) return void 0;
3079
+ let compatKeys = {};
3080
+ if (isAdaptiveThinkingCompatApplicable(model)) compatKeys = buildAdaptiveThinkingCompatSuggestion(missing);
3081
+ else if (isDeepSeekCompatCheckApplicable(model)) compatKeys = buildDeepSeekCompatSuggestion(missing);
3082
+ else compatKeys = buildSafeOpenAIProxyCompatSuggestion(missing);
3083
+ if (Object.keys(compatKeys).length === 0) return void 0;
3084
+ const key = modelKey(model);
3085
+ const slashIdx = key.indexOf("/");
3086
+ return {
3087
+ providerLabel: slashIdx > 0 ? key.slice(0, slashIdx) : key,
3088
+ modelId: model.id,
3089
+ compatKeys
3090
+ };
3091
+ }
3092
+ function stripJsoncComments(text) {
3093
+ const out = [];
3094
+ let i = 0;
3095
+ while (i < text.length) {
3096
+ const ch = text[i];
3097
+ if (ch === "\"") {
3098
+ out.push(ch);
3099
+ i++;
3100
+ while (i < text.length) {
3101
+ const sc = text[i];
3102
+ out.push(sc);
3103
+ i++;
3104
+ if (sc === "\\" && i < text.length) {
3105
+ out.push(text[i]);
3106
+ i++;
3107
+ } else if (sc === "\"") break;
3108
+ }
3109
+ continue;
3110
+ }
3111
+ if (ch === "/" && i + 1 < text.length && text[i + 1] === "/") {
3112
+ out.push(" ", " ");
3113
+ i += 2;
3114
+ while (i < text.length && text[i] !== "\n") {
3115
+ out.push(" ");
3116
+ i++;
3117
+ }
3118
+ continue;
3119
+ }
3120
+ if (ch === "/" && i + 1 < text.length && text[i + 1] === "*") {
3121
+ out.push(" ", " ");
3122
+ i += 2;
3123
+ while (i < text.length) {
3124
+ if (text[i] === "*" && i + 1 < text.length && text[i + 1] === "/") {
3125
+ out.push(" ", " ");
3126
+ i += 2;
3127
+ break;
3128
+ }
3129
+ out.push(text[i] === "\n" ? "\n" : " ");
3130
+ i++;
3131
+ }
3132
+ continue;
3133
+ }
3134
+ out.push(ch);
3135
+ i++;
3136
+ }
3137
+ return out.join("");
3138
+ }
3139
+ function stripJsoncTrailingCommas(text) {
3140
+ const chars = text.split("");
3141
+ let i = 0;
3142
+ while (i < chars.length) {
3143
+ if (chars[i] === "\"") {
3144
+ const str = readJsonStringLiteral(text, i);
3145
+ if (!str) break;
3146
+ i = str.end;
3147
+ continue;
3148
+ }
3149
+ if (chars[i] === ",") {
3150
+ let j = i + 1;
3151
+ while (j < chars.length && isJsonWhitespace(chars[j])) j++;
3152
+ if (chars[j] === "}" || chars[j] === "]") chars[i] = " ";
3153
+ }
3154
+ i++;
3155
+ }
3156
+ return chars.join("");
3157
+ }
3158
+ function parseJsonc(text) {
3159
+ return JSON.parse(stripJsoncTrailingCommas(stripJsoncComments(text)));
3160
+ }
3161
+ return {
3162
+ selectAdapterForModel,
3163
+ selectAdapterForAssistantMessage,
3164
+ notifyCacheCompatIfNeeded,
3165
+ buildFixSuggestion,
3166
+ hasMissingUsageFields,
3167
+ formatCacheStats,
3168
+ prefixFooterStatus,
3169
+ emptyCacheStats,
3170
+ addUsageToCacheStats,
3171
+ cloneCacheStats,
3172
+ selectFooterStatsForModel,
3173
+ findBestRouterModelStats,
3174
+ buildExactRouterStatusEntry,
3175
+ hashSessionId,
3176
+ readValidStatsShardsV7,
3177
+ aggregateStatsShardsV7,
3178
+ writeStatsShardV7,
3179
+ readGlobalStatsEpoch,
3180
+ readModelStatsEpoch,
3181
+ advanceGlobalStatsEpoch,
3182
+ advanceModelStatsEpoch,
3183
+ maybeCleanupStatsShardsV7,
3184
+ removeLegacyStatsFiles,
3185
+ createSerializedAsyncRunner,
3186
+ hasReasoningProtocolRejectionErrorMessage,
3187
+ isReasoningProtocolRejectionForModel,
3188
+ hasPromptCacheKeyUnsupportedErrorMessage,
3189
+ isPromptCacheKeyUnsupportedApplicable,
3190
+ hasPromptCacheRetentionUnsupportedErrorMessage,
3191
+ isExplicitPromptCacheRetentionUnsupportedApplicable,
3192
+ hasAnthropicCacheTtlOrderError,
3193
+ getPromptCacheRetentionUnsupportedHint,
3194
+ modelFromAssistantMessage,
3195
+ resolvePhysicalModel,
3196
+ consolidateDirectProviderStatsModel,
3197
+ footerStatsMode,
3198
+ currentLocalDay,
3199
+ modelKey,
3200
+ initialEpoch,
3201
+ parsePersistedStatsShardV7,
3202
+ cleanupStatsShardsV7,
3203
+ readEffectiveCompatConfig,
3204
+ getCompat,
3205
+ SHARD_FILES_DIR,
3206
+ LOG_PREFIX
3207
+ };
3208
+ }
3209
+ //#endregion
3210
+ //#region src/services/nativeOptimizer/index.ts
3211
+ const STATUS_KEY = "pi-cache-stats";
3212
+ const PERSIST_DEBOUNCE_MS = 2e3;
3213
+ const MAX_RECENT_SAMPLES = 50;
3214
+ function asModel(model) {
3215
+ return model ? {
3216
+ ...model,
3217
+ api: model.api ?? "",
3218
+ name: model.name ?? model.id,
3219
+ baseUrl: model.baseUrl ?? ""
3220
+ } : void 0;
3221
+ }
3222
+ /** One native session owner. Only V7 counters, never payloads or credentials, reach disk. */
3223
+ function createNativeOptimizer(initial, stateDir = getAgentDir()) {
3224
+ const support = createOptimizerSupport(stateDir, initial.environment);
3225
+ const serialized = support.createSerializedAsyncRunner();
3226
+ const instanceId = randomUUID();
3227
+ const instanceStartedAt = Date.now();
3228
+ const sessionHash = support.hashSessionId(initial.sessionId);
3229
+ const shardPath = join(support.SHARD_FILES_DIR, `${instanceId}.json`);
3230
+ let currentGlobalEpoch = support.initialEpoch("global");
3231
+ const modelEpochs = /* @__PURE__ */ new Map();
3232
+ const models = /* @__PURE__ */ new Map();
3233
+ let processStats = {};
3234
+ let bySession = {};
3235
+ let totals = {};
3236
+ let lastRoutedModel;
3237
+ let lastStatus;
3238
+ let shardCreatedAt = Date.now();
3239
+ let persistTimer;
3240
+ let releaseRetention;
3241
+ let started = false;
3242
+ let closed = false;
3243
+ let persistenceWarningShown = false;
3244
+ const warnedModels = /* @__PURE__ */ new Set();
3245
+ const warnedErrors = /* @__PURE__ */ new Set();
3246
+ const recentSamples = /* @__PURE__ */ new Map();
3247
+ const notify = (context, body) => {
3248
+ context.client.notify({
3249
+ body,
3250
+ level: "warning"
3251
+ });
3252
+ };
3253
+ function buildShard(state = "active") {
3254
+ const entries = {};
3255
+ for (const [key, stats] of Object.entries(processStats)) {
3256
+ const model = models.get(key);
3257
+ if (!model) continue;
3258
+ entries[key] = {
3259
+ modelEpoch: modelEpochs.get(key) ?? support.initialEpoch(`model:${key}`),
3260
+ provider: model.provider,
3261
+ modelId: model.id,
3262
+ modelName: model.name,
3263
+ ...model.api ? { api: model.api } : {},
3264
+ stats: support.cloneCacheStats(stats)
3265
+ };
3266
+ }
3267
+ const now = Date.now();
3268
+ return {
3269
+ version: 7,
3270
+ kind: "pi-cache-optimizer-shard",
3271
+ instanceId,
3272
+ sessionHash,
3273
+ process: {
3274
+ pid: process.pid,
3275
+ ppid: process.ppid,
3276
+ instanceStartedAt
3277
+ },
3278
+ lifecycle: {
3279
+ state,
3280
+ createdAt: shardCreatedAt,
3281
+ updatedAt: now,
3282
+ ...state === "closed" ? { closedAt: now } : {}
3283
+ },
3284
+ day: support.currentLocalDay(),
3285
+ globalEpoch: currentGlobalEpoch,
3286
+ models: entries,
3287
+ ...lastRoutedModel ? { lastRoutedModel: { ...lastRoutedModel } } : {}
3288
+ };
3289
+ }
3290
+ async function refresh() {
3291
+ const shards = (await support.readValidStatsShardsV7()).filter((shard) => shard.instanceId !== instanceId);
3292
+ if (started) shards.push(buildShard());
3293
+ const aggregate = await support.aggregateStatsShardsV7(shards);
3294
+ const epoch = await support.readGlobalStatsEpoch();
3295
+ if (epoch !== currentGlobalEpoch) {
3296
+ currentGlobalEpoch = epoch;
3297
+ processStats = {};
3298
+ modelEpochs.clear();
3299
+ models.clear();
3300
+ lastRoutedModel = void 0;
3301
+ } else for (const key of Object.keys(processStats)) {
3302
+ const latest = await support.readModelStatsEpoch(key);
3303
+ if ((modelEpochs.get(key) ?? support.initialEpoch(`model:${key}`)) === latest) continue;
3304
+ delete processStats[key];
3305
+ modelEpochs.set(key, latest);
3306
+ models.delete(key);
3307
+ }
3308
+ bySession = {};
3309
+ for (const [hash, stats] of Object.entries(aggregate.bySession)) for (const [key, value] of Object.entries(stats)) bySession[`${hash}:${key}`] = value;
3310
+ totals = aggregate.totalsByModel;
3311
+ lastRoutedModel = aggregate.lastRoutedModelBySession[sessionHash];
3312
+ lastStatus = void 0;
3313
+ }
3314
+ async function persist(context, state = "active") {
3315
+ try {
3316
+ await support.writeStatsShardV7(shardPath, buildShard(state));
3317
+ } catch (error) {
3318
+ console.warn(`${support.LOG_PREFIX}: failed to persist cache stats shard`, error);
3319
+ if (!persistenceWarningShown) {
3320
+ persistenceWarningShown = true;
3321
+ notify(context, `${support.LOG_PREFIX}: failed to persist footer stats; using in-memory stats for this process.`);
3322
+ }
3323
+ }
3324
+ }
3325
+ async function flush(context, state = "active") {
3326
+ if (persistTimer) clearTimeout(persistTimer);
3327
+ persistTimer = void 0;
3328
+ await refresh();
3329
+ await persist(context, state);
3330
+ await refresh();
3331
+ }
3332
+ function schedulePersist(context) {
3333
+ if (persistTimer) clearTimeout(persistTimer);
3334
+ persistTimer = setTimeout(() => {
3335
+ persistTimer = void 0;
3336
+ serialized(async () => {
3337
+ if (!closed) {
3338
+ await persist(context);
3339
+ await refresh();
3340
+ }
3341
+ }).catch((error) => notify(context, `Cache stats refresh failed: ${String(error)}`));
3342
+ }, PERSIST_DEBOUNCE_MS);
3343
+ persistTimer.unref?.();
3344
+ }
3345
+ async function rollover(context) {
3346
+ const day = support.currentLocalDay();
3347
+ let changed = false;
3348
+ for (const key of Object.keys(processStats)) if (processStats[key]?.day !== day) {
3349
+ processStats[key] = support.emptyCacheStats(day);
3350
+ changed = true;
3351
+ }
3352
+ if (changed) {
3353
+ shardCreatedAt = Date.now();
3354
+ lastStatus = void 0;
3355
+ await flush(context);
3356
+ }
3357
+ }
3358
+ function selectedModel(context) {
3359
+ const selected = asModel(context.model);
3360
+ if (selected?.api === "pi-virtual" && lastRoutedModel) {
3361
+ const physical = models.get(support.modelKey(lastRoutedModel)) ?? {
3362
+ ...lastRoutedModel,
3363
+ name: lastRoutedModel.name ?? lastRoutedModel.id,
3364
+ api: "",
3365
+ baseUrl: ""
3366
+ };
3367
+ return support.resolvePhysicalModel(physical, asModel(context.getModel?.(physical.provider, physical.id)));
3368
+ }
3369
+ return selected;
3370
+ }
3371
+ async function publish(context, model = selectedModel(context)) {
3372
+ await rollover(context);
3373
+ const adapter = support.selectAdapterForModel(model);
3374
+ const mode = support.footerStatsMode();
3375
+ let status;
3376
+ if (adapter && model) {
3377
+ const stats = support.selectFooterStatsForModel(mode, sessionHash, bySession, totals, model, processStats);
3378
+ status = support.formatCacheStats(adapter, stats ?? support.emptyCacheStats());
3379
+ if (support.buildFixSuggestion(model)) status += " ⚠️ compat";
3380
+ } else if (context.model?.api === "pi-virtual" || context.model?.provider === "router") {
3381
+ if (lastStatus !== void 0) return;
3382
+ const entry = support.buildExactRouterStatusEntry(sessionHash, bySession, lastRoutedModel, totals, mode, processStats) ?? support.findBestRouterModelStats(mode, sessionHash, bySession, totals, processStats);
3383
+ if (entry) status = support.formatCacheStats(entry.adapter, entry.stats);
3384
+ }
3385
+ status = support.prefixFooterStatus(status);
3386
+ if (status === lastStatus) return;
3387
+ lastStatus = status;
3388
+ context.client.setStatus(STATUS_KEY, status);
3389
+ }
3390
+ function diagnose(message, model, context) {
3391
+ if (!model) return;
3392
+ const key = support.modelKey(model);
3393
+ const observations = [
3394
+ [
3395
+ "reasoning",
3396
+ support.hasReasoningProtocolRejectionErrorMessage(message) && support.isReasoningProtocolRejectionForModel(message, model),
3397
+ `${key} returned a reasoning-protocol rejection. Review model-scoped thinkingFormat and supportsReasoningEffort compat. No configuration was changed automatically.`
3398
+ ],
3399
+ [
3400
+ "key",
3401
+ support.hasPromptCacheKeyUnsupportedErrorMessage(message) && support.isPromptCacheKeyUnsupportedApplicable(model),
3402
+ `${key} rejected prompt_cache_key. Review a precise model-scoped repair. No configuration was changed automatically.`
3403
+ ],
3404
+ [
3405
+ "retention",
3406
+ support.hasPromptCacheRetentionUnsupportedErrorMessage(message) && support.isExplicitPromptCacheRetentionUnsupportedApplicable(model),
3407
+ `${key} rejected prompt_cache_retention. ${support.getPromptCacheRetentionUnsupportedHint()}`
3408
+ ],
3409
+ [
3410
+ "ttl",
3411
+ support.hasAnthropicCacheTtlOrderError(message) && model.api === "anthropic-messages",
3412
+ `${key} returned an Anthropic cache-control TTL ordering error. Review model-level supportsLongCacheRetention: false. No configuration was changed automatically.`
3413
+ ]
3414
+ ];
3415
+ for (const [kind, applicable, text] of observations) {
3416
+ const observationKey = `${kind}:${key}`;
3417
+ if (!applicable || warnedErrors.has(observationKey)) continue;
3418
+ warnedErrors.add(observationKey);
3419
+ notify(context, `⚠️ ${support.LOG_PREFIX}: ${text}`);
3420
+ }
3421
+ }
3422
+ async function observe(message, context) {
3423
+ const record = typeof message === "object" && message !== null ? message : {};
3424
+ const active = asModel(context.model);
3425
+ const nativeVirtual = active?.api === "pi-virtual";
3426
+ const identity = support.modelFromAssistantMessage(nativeVirtual ? {
3427
+ ...record,
3428
+ responseModel: record.model ?? record.responseModel
3429
+ } : message, active);
3430
+ const registered = identity && asModel(context.getModel?.(identity.provider, identity.id));
3431
+ let model = nativeVirtual && identity ? support.resolvePhysicalModel(identity, registered) : registered || identity;
3432
+ diagnose(message, model, context);
3433
+ if (record.stopReason === "error" || record.stopReason === "aborted") return;
3434
+ model = support.consolidateDirectProviderStatsModel(model, active);
3435
+ const adapter = (nativeVirtual && model ? support.selectAdapterForModel(model) : void 0) ?? support.selectAdapterForAssistantMessage(message, model ?? active);
3436
+ if (!adapter) return;
3437
+ const usage = adapter.normalizeUsage(message);
3438
+ let routeChanged = false;
3439
+ if (model && (active?.api === "pi-virtual" || active?.provider === "router")) {
3440
+ const next = {
3441
+ provider: model.provider,
3442
+ id: model.id,
3443
+ name: model.name
3444
+ };
3445
+ routeChanged = JSON.stringify(lastRoutedModel) !== JSON.stringify(next);
3446
+ lastRoutedModel = next;
3447
+ }
3448
+ if (model) {
3449
+ const key = support.modelKey(model);
3450
+ const samples = recentSamples.get(key) ?? [];
3451
+ samples.push({
3452
+ timestamp: Date.now(),
3453
+ hit: (usage?.cacheRead ?? 0) > 0,
3454
+ cachedInputTokens: usage?.cacheRead ?? 0,
3455
+ cacheWriteInputTokens: usage?.cacheWrite ?? 0,
3456
+ totalInputTokens: usage?.totalInput ?? 0,
3457
+ missingUsageFields: !usage || usage.cacheRead === 0 && usage.cacheWrite === 0 && usage.totalInput === 0 || support.hasMissingUsageFields(message, adapter)
3458
+ });
3459
+ if (samples.length > MAX_RECENT_SAMPLES) samples.splice(0, samples.length - MAX_RECENT_SAMPLES);
3460
+ recentSamples.set(key, samples);
3461
+ }
3462
+ if (!usage) {
3463
+ if (routeChanged) schedulePersist(context);
3464
+ return;
3465
+ }
3466
+ await rollover(context);
3467
+ if (model) {
3468
+ const key = support.modelKey(model);
3469
+ const globalEpoch = await support.readGlobalStatsEpoch();
3470
+ const modelEpoch = await support.readModelStatsEpoch(key);
3471
+ if (globalEpoch !== currentGlobalEpoch) {
3472
+ currentGlobalEpoch = globalEpoch;
3473
+ processStats = {};
3474
+ modelEpochs.clear();
3475
+ }
3476
+ if (modelEpochs.get(key) !== modelEpoch) {
3477
+ delete processStats[key];
3478
+ modelEpochs.set(key, modelEpoch);
3479
+ }
3480
+ models.set(key, model);
3481
+ support.addUsageToCacheStats(processStats[key] ??= support.emptyCacheStats(), usage);
3482
+ }
3483
+ schedulePersist(context);
3484
+ await refresh();
3485
+ await publish(context, model);
3486
+ }
3487
+ async function shutdown(context) {
3488
+ if (closed) return;
3489
+ closed = true;
3490
+ try {
3491
+ if (started) await flush(context, "closed");
3492
+ } finally {
3493
+ if (persistTimer) clearTimeout(persistTimer);
3494
+ persistTimer = void 0;
3495
+ releaseRetention?.();
3496
+ releaseRetention = void 0;
3497
+ context.client.setStatus(STATUS_KEY, void 0);
3498
+ }
3499
+ }
3500
+ return {
3501
+ hooks: [
3502
+ {
3503
+ event: "session_start",
3504
+ handle: (_event, context) => serialized(async () => {
3505
+ if (started || closed) return;
3506
+ started = true;
3507
+ releaseRetention = acquireEnvironmentValue("PI_CACHE_RETENTION", "long");
3508
+ currentGlobalEpoch = await support.readGlobalStatsEpoch();
3509
+ await support.removeLegacyStatsFiles();
3510
+ await refresh();
3511
+ await flush(context);
3512
+ await support.maybeCleanupStatsShardsV7();
3513
+ await refresh();
3514
+ await publish(context);
3515
+ })
3516
+ },
3517
+ {
3518
+ event: "tool_execution_end",
3519
+ handle: (event, context) => serialized(async () => {
3520
+ if (typeof event.parentToolCallId === "string" && event.parentToolCallId.trim()) return;
3521
+ if (started && !closed) {
3522
+ await refresh();
3523
+ await publish(context);
3524
+ }
3525
+ })
3526
+ },
3527
+ {
3528
+ event: "agent_settled",
3529
+ handle: (_event, context) => serialized(async () => {
3530
+ if (started && !closed) {
3531
+ await refresh();
3532
+ await publish(context);
3533
+ }
3534
+ })
3535
+ },
3536
+ {
3537
+ event: "model_select",
3538
+ handle: (_event, context) => serialized(async () => {
3539
+ if (!started || closed) return;
3540
+ const model = selectedModel(context);
3541
+ support.notifyCacheCompatIfNeeded(model, (text) => notify(context, text), warnedModels);
3542
+ await refresh();
3543
+ await publish(context, model);
3544
+ })
3545
+ },
3546
+ {
3547
+ event: "message_end",
3548
+ handle: (event, context) => serialized(async () => {
3549
+ if (started && !closed) await observe(event.message, context);
3550
+ })
3551
+ },
3552
+ {
3553
+ event: "session_shutdown",
3554
+ handle: (_event, context) => serialized(() => shutdown(context))
3555
+ }
3556
+ ],
3557
+ dispose: () => serialized(() => shutdown(initial)),
3558
+ snapshot: () => ({
3559
+ sessionHash,
3560
+ processStats,
3561
+ bySession,
3562
+ totals,
3563
+ recentSamples
3564
+ })
3565
+ };
3566
+ }
3567
+ //#endregion
3568
+ //#region src/extensions/workspaces/sessions/(backend)/root.server.ts
3569
+ var root_server_default = defineRoot(({ agent }) => {
3570
+ if (!agent) throw new Error("Cache statistics require the native session host.");
3571
+ const telemetry = new PromptCacheTelemetry();
3572
+ const runtime = createCacheRuntime({
3573
+ telemetry,
3574
+ now: Date.now
3575
+ }, agent.context.environment);
3576
+ const optimizer = createNativeOptimizer(agent.context);
3577
+ return {
3578
+ value: { hooks: optimizer.hooks.map((hook) => {
3579
+ if (hook.event === "session_start") return {
3580
+ ...hook,
3581
+ async handle(event, context) {
3582
+ await runtime.events.session_start(event, context);
3583
+ await hook.handle(event, context);
3584
+ }
3585
+ };
3586
+ if (hook.event === "message_end") return {
3587
+ ...hook,
3588
+ async handle(event, context) {
3589
+ runtime.events.message_end(event, context);
3590
+ await hook.handle(event, context);
3591
+ }
3592
+ };
3593
+ return hook;
3594
+ }) },
3595
+ services: [(context) => {
3596
+ new PromptCacheTelemetryService(context, telemetry);
3597
+ }, runtime.plugin],
3598
+ async onDispose() {
3599
+ try {
3600
+ await optimizer.dispose();
3601
+ } finally {
3602
+ runtime.dispose();
3603
+ }
3604
+ }
3605
+ };
3606
+ });
3607
+ //#endregion
3608
+ //#region src/extensions/workspaces/sessions/(backend)/hook/agent-settled.server.ts
3609
+ var agent_settled_server_default = defineHook((context) => context.root.hooks.find((hook) => hook.event === "agent_settled"));
3610
+ //#endregion
3611
+ //#region src/extensions/workspaces/sessions/(backend)/_lib/cacheHooks.ts
3612
+ function record(value) {
3613
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
3614
+ }
3615
+ function modelIdentity(model, payload) {
3616
+ const request = record(payload);
3617
+ return {
3618
+ virtualProvider: model?.provider,
3619
+ virtualModel: model?.id,
3620
+ api: model?.api,
3621
+ wireModel: typeof request.model === "string" ? request.model : void 0
3622
+ };
3623
+ }
3624
+ function cacheNamespace(context) {
3625
+ return `dph_${sha256Base64Url(canonicalJson({
3626
+ version: 1,
3627
+ session: context.sessionId,
3628
+ selection: context.selection
3629
+ }))}`;
3630
+ }
3631
+ //#endregion
3632
+ //#region src/extensions/workspaces/sessions/(backend)/hook/before-provider-request.server.ts
3633
+ var before_provider_request_server_default = defineHook([{
3634
+ event: "before_provider_request",
3635
+ handle(event, execution) {
3636
+ const payload = record(event.payload);
3637
+ const identity = modelIdentity(event.model, payload);
3638
+ const modelFingerprint = createPromptCacheModelFingerprint(identity, sha256Base64Url);
3639
+ const key = createPromptCacheKey(cacheNamespace({
3640
+ sessionId: execution.sessionId,
3641
+ selection: execution.selection
3642
+ }), "parent", modelFingerprint, sha256Base64Url);
3643
+ const rewritten = rewritePromptCacheKey(payload, identity.api, key, true);
3644
+ return rewritten === void 0 ? void 0 : { payload: rewritten };
3645
+ }
3646
+ }].find((hook) => hook.event === "before_provider_request"));
3647
+ //#endregion
3648
+ //#region src/extensions/workspaces/sessions/(backend)/hook/message-end.server.ts
3649
+ var message_end_server_default = defineHook((context) => context.root.hooks.find((hook) => hook.event === "message_end"));
3650
+ //#endregion
3651
+ //#region src/extensions/workspaces/sessions/(backend)/hook/model-select.server.ts
3652
+ var model_select_server_default = defineHook((context) => context.root.hooks.find((hook) => hook.event === "model_select"));
3653
+ //#endregion
3654
+ //#region src/extensions/workspaces/sessions/(backend)/hook/session-shutdown.server.ts
3655
+ var session_shutdown_server_default = defineHook((context) => context.root.hooks.find((hook) => hook.event === "session_shutdown"));
3656
+ //#endregion
3657
+ //#region src/extensions/workspaces/sessions/(backend)/hook/session-start.server.ts
3658
+ var session_start_server_default = defineHook((context) => context.root.hooks.find((hook) => hook.event === "session_start"));
3659
+ //#endregion
3660
+ //#region src/extensions/workspaces/sessions/(backend)/hook/tool-execution-end.server.ts
3661
+ var tool_execution_end_server_default = defineHook((context) => context.root.hooks.find((hook) => hook.event === "tool_execution_end"));
3662
+ //#endregion
3663
+ //#region generated/server.ts
3664
+ const at = (value, context) => typeof value === "function" ? value(context) : value;
3665
+ const via = (identity, value) => value === void 0 ? void 0 : {
3666
+ ...identity,
3667
+ ...value
3668
+ };
3669
+ const facet = defineServerPlugin({
3670
+ name: "@agimon-ai/po-cache",
3671
+ providerOverrides: composeServerProviderOverrides(root_server_default),
3672
+ session: async (context) => {
3673
+ const scopeSession = await root_server_default(context);
3674
+ const contextSession = {
3675
+ ...context,
3676
+ root: scopeSession.value
3677
+ };
3678
+ return {
3679
+ ...composeRootHooks(scopeSession),
3680
+ get hooks() {
3681
+ return [
3682
+ via({ event: "agent_settled" }, at(agent_settled_server_default, contextSession)),
3683
+ via({ event: "before_provider_request" }, at(before_provider_request_server_default, contextSession)),
3684
+ via({ event: "message_end" }, at(message_end_server_default, contextSession)),
3685
+ via({ event: "model_select" }, at(model_select_server_default, contextSession)),
3686
+ via({ event: "session_shutdown" }, at(session_shutdown_server_default, contextSession)),
3687
+ via({ event: "session_start" }, at(session_start_server_default, contextSession)),
3688
+ via({ event: "tool_execution_end" }, at(tool_execution_end_server_default, contextSession))
3689
+ ];
3690
+ },
3691
+ services: [...scopeSession.services ?? []],
3692
+ get activities() {
3693
+ return [...scopeSession.activities ?? []];
3694
+ }
3695
+ };
3696
+ }
3697
+ });
3698
+ //#endregion
3699
+ export { facet as default, facet };
3700
+
3701
+ //# sourceMappingURL=server.mjs.map