@owloops/claude-powerline 1.24.4 → 1.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser.d.ts +676 -0
- package/dist/browser.js +3 -0
- package/dist/index.mjs +10 -10
- package/package.json +9 -1
- package/plugin/templates/config-tui-compact.json +3 -3
- package/plugin/templates/config-tui-full.json +4 -4
- package/plugin/templates/config-tui-standard.json +4 -4
- package/src/browser.ts +203 -0
- package/src/config/defaults.ts +79 -0
- package/src/config/loader.ts +462 -0
- package/src/index.ts +90 -0
- package/src/powerline.ts +904 -0
- package/src/segments/block.ts +31 -0
- package/src/segments/context.ts +221 -0
- package/src/segments/git.ts +492 -0
- package/src/segments/index.ts +25 -0
- package/src/segments/metrics.ts +175 -0
- package/src/segments/pricing.ts +454 -0
- package/src/segments/renderer.ts +796 -0
- package/src/segments/session.ts +207 -0
- package/src/segments/tmux.ts +35 -0
- package/src/segments/today.ts +191 -0
- package/src/themes/dark.ts +52 -0
- package/src/themes/gruvbox.ts +52 -0
- package/src/themes/index.ts +131 -0
- package/src/themes/light.ts +52 -0
- package/src/themes/nord.ts +52 -0
- package/src/themes/rose-pine.ts +52 -0
- package/src/themes/tokyo-night.ts +52 -0
- package/src/tui/grid.ts +712 -0
- package/src/tui/index.ts +4 -0
- package/src/tui/layouts.ts +285 -0
- package/src/tui/primitives.ts +175 -0
- package/src/tui/renderer.ts +206 -0
- package/src/tui/sections.ts +1080 -0
- package/src/tui/types.ts +181 -0
- package/src/utils/budget.ts +47 -0
- package/src/utils/cache.ts +247 -0
- package/src/utils/claude.ts +489 -0
- package/src/utils/color-support.ts +118 -0
- package/src/utils/colors.ts +120 -0
- package/src/utils/constants.ts +176 -0
- package/src/utils/formatters.ts +160 -0
- package/src/utils/logger.ts +5 -0
- package/src/utils/terminal-width.ts +117 -0
- package/src/utils/terminal.ts +11 -0
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
import { debug } from "../utils/logger";
|
|
2
|
+
import { get } from "node:https";
|
|
3
|
+
import { URL } from "node:url";
|
|
4
|
+
import { CacheManager } from "../utils/cache";
|
|
5
|
+
|
|
6
|
+
export interface ModelPricing {
|
|
7
|
+
name: string;
|
|
8
|
+
input: number;
|
|
9
|
+
cache_write_5m: number;
|
|
10
|
+
cache_write_1h: number;
|
|
11
|
+
cache_read: number;
|
|
12
|
+
output: number;
|
|
13
|
+
}
|
|
14
|
+
const OFFLINE_PRICING_DATA: Record<string, ModelPricing> = {
|
|
15
|
+
"claude-haiku-4-5-20251001": {
|
|
16
|
+
name: "Claude Haiku 4.5",
|
|
17
|
+
input: 1.0,
|
|
18
|
+
output: 5.0,
|
|
19
|
+
cache_write_5m: 1.25,
|
|
20
|
+
cache_write_1h: 2.0,
|
|
21
|
+
cache_read: 0.1,
|
|
22
|
+
},
|
|
23
|
+
"claude-haiku-4-5": {
|
|
24
|
+
name: "Claude Haiku 4.5",
|
|
25
|
+
input: 1.0,
|
|
26
|
+
output: 5.0,
|
|
27
|
+
cache_write_5m: 1.25,
|
|
28
|
+
cache_write_1h: 2.0,
|
|
29
|
+
cache_read: 0.1,
|
|
30
|
+
},
|
|
31
|
+
"claude-opus-4-20250514": {
|
|
32
|
+
name: "Claude Opus 4",
|
|
33
|
+
input: 15.0,
|
|
34
|
+
output: 75.0,
|
|
35
|
+
cache_write_5m: 18.75,
|
|
36
|
+
cache_write_1h: 30.0,
|
|
37
|
+
cache_read: 1.5,
|
|
38
|
+
},
|
|
39
|
+
"claude-opus-4-1": {
|
|
40
|
+
name: "Claude Opus 4.1",
|
|
41
|
+
input: 15.0,
|
|
42
|
+
output: 75.0,
|
|
43
|
+
cache_write_5m: 18.75,
|
|
44
|
+
cache_write_1h: 30.0,
|
|
45
|
+
cache_read: 1.5,
|
|
46
|
+
},
|
|
47
|
+
"claude-opus-4-1-20250805": {
|
|
48
|
+
name: "Claude Opus 4.1",
|
|
49
|
+
input: 15.0,
|
|
50
|
+
output: 75.0,
|
|
51
|
+
cache_write_5m: 18.75,
|
|
52
|
+
cache_write_1h: 30.0,
|
|
53
|
+
cache_read: 1.5,
|
|
54
|
+
},
|
|
55
|
+
"claude-sonnet-4-20250514": {
|
|
56
|
+
name: "Claude Sonnet 4",
|
|
57
|
+
input: 3.0,
|
|
58
|
+
output: 15.0,
|
|
59
|
+
cache_write_5m: 3.75,
|
|
60
|
+
cache_write_1h: 6.0,
|
|
61
|
+
cache_read: 0.3,
|
|
62
|
+
},
|
|
63
|
+
"claude-4-opus-20250514": {
|
|
64
|
+
name: "Claude 4 Opus",
|
|
65
|
+
input: 15.0,
|
|
66
|
+
output: 75.0,
|
|
67
|
+
cache_write_5m: 18.75,
|
|
68
|
+
cache_write_1h: 30.0,
|
|
69
|
+
cache_read: 1.5,
|
|
70
|
+
},
|
|
71
|
+
"claude-4-sonnet-20250514": {
|
|
72
|
+
name: "Claude 4 Sonnet",
|
|
73
|
+
input: 3.0,
|
|
74
|
+
output: 15.0,
|
|
75
|
+
cache_write_5m: 3.75,
|
|
76
|
+
cache_write_1h: 6.0,
|
|
77
|
+
cache_read: 0.3,
|
|
78
|
+
},
|
|
79
|
+
"claude-sonnet-4-5": {
|
|
80
|
+
name: "Claude Sonnet 4.5",
|
|
81
|
+
input: 3.0,
|
|
82
|
+
output: 15.0,
|
|
83
|
+
cache_write_5m: 3.75,
|
|
84
|
+
cache_write_1h: 6.0,
|
|
85
|
+
cache_read: 0.3,
|
|
86
|
+
},
|
|
87
|
+
"claude-sonnet-4-5-20250929": {
|
|
88
|
+
name: "Claude Sonnet 4.5",
|
|
89
|
+
input: 3.0,
|
|
90
|
+
output: 15.0,
|
|
91
|
+
cache_write_5m: 3.75,
|
|
92
|
+
cache_write_1h: 6.0,
|
|
93
|
+
cache_read: 0.3,
|
|
94
|
+
},
|
|
95
|
+
"claude-opus-4-5": {
|
|
96
|
+
name: "Claude Opus 4.5",
|
|
97
|
+
input: 5.0,
|
|
98
|
+
output: 25.0,
|
|
99
|
+
cache_write_5m: 6.25,
|
|
100
|
+
cache_write_1h: 10.0,
|
|
101
|
+
cache_read: 0.5,
|
|
102
|
+
},
|
|
103
|
+
"claude-opus-4-5-20251101": {
|
|
104
|
+
name: "Claude Opus 4.5",
|
|
105
|
+
input: 5.0,
|
|
106
|
+
output: 25.0,
|
|
107
|
+
cache_write_5m: 6.25,
|
|
108
|
+
cache_write_1h: 10.0,
|
|
109
|
+
cache_read: 0.5,
|
|
110
|
+
},
|
|
111
|
+
"claude-opus-4-6": {
|
|
112
|
+
name: "Claude Opus 4.6",
|
|
113
|
+
input: 5.0,
|
|
114
|
+
output: 25.0,
|
|
115
|
+
cache_write_5m: 6.25,
|
|
116
|
+
cache_write_1h: 10.0,
|
|
117
|
+
cache_read: 0.5,
|
|
118
|
+
},
|
|
119
|
+
"claude-opus-4-6-20260205": {
|
|
120
|
+
name: "Claude Opus 4.6",
|
|
121
|
+
input: 5.0,
|
|
122
|
+
output: 25.0,
|
|
123
|
+
cache_write_5m: 6.25,
|
|
124
|
+
cache_write_1h: 10.0,
|
|
125
|
+
cache_read: 0.5,
|
|
126
|
+
},
|
|
127
|
+
"claude-sonnet-4-6": {
|
|
128
|
+
name: "Claude Sonnet 4.6",
|
|
129
|
+
input: 3.0,
|
|
130
|
+
output: 15.0,
|
|
131
|
+
cache_write_5m: 3.75,
|
|
132
|
+
cache_write_1h: 6.0,
|
|
133
|
+
cache_read: 0.3,
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
export class PricingService {
|
|
138
|
+
private static executionCache: Record<string, ModelPricing> | null = null;
|
|
139
|
+
private static modelPricingCache = new Map<string, ModelPricing>();
|
|
140
|
+
private static readonly GITHUB_PRICING_URL =
|
|
141
|
+
"https://raw.githubusercontent.com/Owloops/claude-powerline/main/pricing.json";
|
|
142
|
+
|
|
143
|
+
private static async loadDiskCache(): Promise<Record<
|
|
144
|
+
string,
|
|
145
|
+
ModelPricing
|
|
146
|
+
> | null> {
|
|
147
|
+
const TTL_24H = 24 * 60 * 60 * 1000;
|
|
148
|
+
const minValidTime = Date.now() - TTL_24H;
|
|
149
|
+
return (await CacheManager.getUsageCache(
|
|
150
|
+
"pricing",
|
|
151
|
+
minValidTime,
|
|
152
|
+
)) as Record<string, ModelPricing> | null;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
private static async saveDiskCache(
|
|
156
|
+
data: Record<string, ModelPricing>,
|
|
157
|
+
): Promise<void> {
|
|
158
|
+
await CacheManager.setUsageCache("pricing", data);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
private static async fetchPricingData(): Promise<Record<
|
|
162
|
+
string,
|
|
163
|
+
ModelPricing
|
|
164
|
+
> | null> {
|
|
165
|
+
return new Promise((resolve) => {
|
|
166
|
+
const parsedUrl = new URL(this.GITHUB_PRICING_URL);
|
|
167
|
+
|
|
168
|
+
const request = get(
|
|
169
|
+
{
|
|
170
|
+
hostname: parsedUrl.hostname,
|
|
171
|
+
path: parsedUrl.pathname,
|
|
172
|
+
headers: {
|
|
173
|
+
"User-Agent": "claude-powerline",
|
|
174
|
+
"Cache-Control": "no-cache",
|
|
175
|
+
},
|
|
176
|
+
timeout: 5000,
|
|
177
|
+
},
|
|
178
|
+
(response) => {
|
|
179
|
+
if (response.statusCode !== 200) {
|
|
180
|
+
debug(`HTTP ${response.statusCode}: ${response.statusMessage}`);
|
|
181
|
+
resolve(null);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
let data = "";
|
|
186
|
+
let size = 0;
|
|
187
|
+
const MAX_SIZE = 1024 * 1024;
|
|
188
|
+
|
|
189
|
+
response.on("data", (chunk) => {
|
|
190
|
+
size += chunk.length;
|
|
191
|
+
if (size > MAX_SIZE) {
|
|
192
|
+
debug("Response too large");
|
|
193
|
+
request.destroy();
|
|
194
|
+
resolve(null);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
data += chunk;
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
response.on("end", () => {
|
|
201
|
+
try {
|
|
202
|
+
const json = JSON.parse(data);
|
|
203
|
+
const dataObj = json as Record<string, unknown>;
|
|
204
|
+
const meta = dataObj._meta as { updated?: string } | undefined;
|
|
205
|
+
|
|
206
|
+
const pricingData: Record<string, unknown> = {};
|
|
207
|
+
for (const [key, value] of Object.entries(dataObj)) {
|
|
208
|
+
if (key !== "_meta") {
|
|
209
|
+
pricingData[key] = value;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (this.validatePricingData(pricingData)) {
|
|
214
|
+
debug(
|
|
215
|
+
`Fetched fresh pricing from GitHub for ${Object.keys(pricingData).length} models`,
|
|
216
|
+
);
|
|
217
|
+
debug(`Pricing last updated: ${meta?.updated || "unknown"}`);
|
|
218
|
+
resolve(pricingData);
|
|
219
|
+
} else {
|
|
220
|
+
debug("Invalid pricing data structure");
|
|
221
|
+
resolve(null);
|
|
222
|
+
}
|
|
223
|
+
} catch (error) {
|
|
224
|
+
debug("Failed to parse JSON:", error);
|
|
225
|
+
resolve(null);
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
response.on("error", (error) => {
|
|
230
|
+
debug("Response error:", error);
|
|
231
|
+
resolve(null);
|
|
232
|
+
});
|
|
233
|
+
},
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
request.on("error", (error) => {
|
|
237
|
+
debug("Request error:", error);
|
|
238
|
+
resolve(null);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
request.on("timeout", () => {
|
|
242
|
+
debug("Request timeout");
|
|
243
|
+
request.destroy();
|
|
244
|
+
resolve(null);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
request.end();
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
static async getCurrentPricing(): Promise<Record<string, ModelPricing>> {
|
|
252
|
+
if (this.executionCache !== null) {
|
|
253
|
+
debug(
|
|
254
|
+
`[CACHE-HIT] Pricing execution cache: ${Object.keys(this.executionCache).length} models`,
|
|
255
|
+
);
|
|
256
|
+
return this.executionCache;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const diskCached = await this.loadDiskCache();
|
|
260
|
+
if (diskCached) {
|
|
261
|
+
debug(
|
|
262
|
+
`[CACHE-HIT] Pricing disk cache: ${Object.keys(diskCached).length} models`,
|
|
263
|
+
);
|
|
264
|
+
this.executionCache = diskCached;
|
|
265
|
+
debug(
|
|
266
|
+
`[CACHE-SET] Pricing execution cache stored: ${Object.keys(diskCached).length} models`,
|
|
267
|
+
);
|
|
268
|
+
return diskCached;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const freshData = await this.fetchPricingData();
|
|
272
|
+
if (freshData) {
|
|
273
|
+
await this.saveDiskCache(freshData);
|
|
274
|
+
debug(
|
|
275
|
+
`[CACHE-SET] Pricing disk cache stored: ${Object.keys(freshData).length} models`,
|
|
276
|
+
);
|
|
277
|
+
this.executionCache = freshData;
|
|
278
|
+
debug(
|
|
279
|
+
`[CACHE-SET] Pricing execution cache stored: ${Object.keys(freshData).length} models`,
|
|
280
|
+
);
|
|
281
|
+
return freshData;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
debug(
|
|
285
|
+
`[CACHE-FALLBACK] Using offline pricing data: ${Object.keys(OFFLINE_PRICING_DATA).length} models`,
|
|
286
|
+
);
|
|
287
|
+
this.executionCache = OFFLINE_PRICING_DATA;
|
|
288
|
+
debug(
|
|
289
|
+
`[CACHE-SET] Pricing execution cache stored: ${Object.keys(OFFLINE_PRICING_DATA).length} models`,
|
|
290
|
+
);
|
|
291
|
+
return OFFLINE_PRICING_DATA;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
private static validatePricingData(
|
|
295
|
+
data: unknown,
|
|
296
|
+
): data is Record<string, ModelPricing> {
|
|
297
|
+
if (!data || typeof data !== "object") return false;
|
|
298
|
+
|
|
299
|
+
for (const [, value] of Object.entries(data)) {
|
|
300
|
+
if (!value || typeof value !== "object") return false;
|
|
301
|
+
const pricing = value as Record<string, unknown>;
|
|
302
|
+
|
|
303
|
+
if (
|
|
304
|
+
typeof pricing.input !== "number" ||
|
|
305
|
+
typeof pricing.output !== "number" ||
|
|
306
|
+
typeof pricing.cache_read !== "number"
|
|
307
|
+
) {
|
|
308
|
+
return false;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return true;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
static async getModelPricing(modelId: string): Promise<ModelPricing> {
|
|
316
|
+
if (this.modelPricingCache.has(modelId)) {
|
|
317
|
+
debug(`[CACHE-HIT] Model pricing cache: ${modelId}`);
|
|
318
|
+
return this.modelPricingCache.get(modelId)!;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const allPricing = await this.getCurrentPricing();
|
|
322
|
+
let pricing: ModelPricing;
|
|
323
|
+
|
|
324
|
+
if (allPricing[modelId]) {
|
|
325
|
+
pricing = allPricing[modelId];
|
|
326
|
+
} else {
|
|
327
|
+
pricing = this.fuzzyMatchModel(modelId, allPricing);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
this.modelPricingCache.set(modelId, pricing);
|
|
331
|
+
debug(`[CACHE-SET] Model pricing cache: ${modelId}`);
|
|
332
|
+
return pricing;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
private static fuzzyMatchModel(
|
|
336
|
+
modelId: string,
|
|
337
|
+
allPricing: Record<string, ModelPricing>,
|
|
338
|
+
): ModelPricing {
|
|
339
|
+
const lowerModelId = modelId.toLowerCase();
|
|
340
|
+
|
|
341
|
+
for (const [key, pricing] of Object.entries(allPricing)) {
|
|
342
|
+
if (key.toLowerCase() === lowerModelId) {
|
|
343
|
+
return pricing;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
const patterns = [
|
|
347
|
+
{
|
|
348
|
+
pattern: ["opus-4-6", "claude-opus-4-6"],
|
|
349
|
+
fallback: "claude-opus-4-6-20260205",
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
pattern: ["opus-4-5", "claude-opus-4-5"],
|
|
353
|
+
fallback: "claude-opus-4-5-20251101",
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
pattern: ["opus-4-1", "claude-opus-4-1"],
|
|
357
|
+
fallback: "claude-opus-4-1-20250805",
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
pattern: ["opus-4", "claude-opus-4"],
|
|
361
|
+
fallback: "claude-opus-4-20250514",
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
pattern: ["sonnet-4-6", "sonnet-4.6", "claude-sonnet-4-6"],
|
|
365
|
+
fallback: "claude-sonnet-4-6",
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
pattern: ["sonnet-4.5", "4-5-sonnet", "sonnet-4-5"],
|
|
369
|
+
fallback: "claude-sonnet-4-5-20250929",
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
pattern: ["sonnet-4", "claude-sonnet-4"],
|
|
373
|
+
fallback: "claude-sonnet-4-20250514",
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
pattern: ["haiku-4.5", "4-5-haiku", "haiku-4-5"],
|
|
377
|
+
fallback: "claude-haiku-4-5-20251001",
|
|
378
|
+
},
|
|
379
|
+
{ pattern: ["haiku"], fallback: "claude-haiku-4-5-20251001" },
|
|
380
|
+
{ pattern: ["opus"], fallback: "claude-opus-4-20250514" },
|
|
381
|
+
{ pattern: ["sonnet"], fallback: "claude-sonnet-4-5-20250929" },
|
|
382
|
+
];
|
|
383
|
+
|
|
384
|
+
for (const { pattern, fallback } of patterns) {
|
|
385
|
+
if (pattern.some((p) => lowerModelId.includes(p))) {
|
|
386
|
+
if (allPricing[fallback]) {
|
|
387
|
+
return allPricing[fallback];
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
return (
|
|
393
|
+
allPricing["claude-sonnet-4-5-20250929"] || {
|
|
394
|
+
name: `${modelId} (Unknown Model)`,
|
|
395
|
+
input: 3.0,
|
|
396
|
+
cache_write_5m: 3.75,
|
|
397
|
+
cache_write_1h: 6.0,
|
|
398
|
+
cache_read: 0.3,
|
|
399
|
+
output: 15.0,
|
|
400
|
+
}
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
static async calculateCostForEntry(
|
|
405
|
+
entry: Record<string, unknown>,
|
|
406
|
+
): Promise<number> {
|
|
407
|
+
const message = entry.message as Record<string, unknown> | undefined;
|
|
408
|
+
const usage = message?.usage as Record<string, number> | undefined;
|
|
409
|
+
if (!usage) {
|
|
410
|
+
return 0;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
const modelId = this.extractModelId(entry);
|
|
414
|
+
const pricing = await this.getModelPricing(modelId);
|
|
415
|
+
|
|
416
|
+
const inputTokens = usage.input_tokens || 0;
|
|
417
|
+
const outputTokens = usage.output_tokens || 0;
|
|
418
|
+
const cacheCreationTokens = usage.cache_creation_input_tokens || 0;
|
|
419
|
+
const cacheReadTokens = usage.cache_read_input_tokens || 0;
|
|
420
|
+
|
|
421
|
+
const inputCost = (inputTokens / 1_000_000) * pricing.input;
|
|
422
|
+
const outputCost = (outputTokens / 1_000_000) * pricing.output;
|
|
423
|
+
const cacheReadCost = (cacheReadTokens / 1_000_000) * pricing.cache_read;
|
|
424
|
+
const cacheCreationCost =
|
|
425
|
+
(cacheCreationTokens / 1_000_000) * pricing.cache_write_5m;
|
|
426
|
+
|
|
427
|
+
return inputCost + outputCost + cacheCreationCost + cacheReadCost;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
private static extractModelId(entry: Record<string, unknown>): string {
|
|
431
|
+
if (entry.model && typeof entry.model === "string") {
|
|
432
|
+
return entry.model;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const message = entry.message as Record<string, unknown> | undefined;
|
|
436
|
+
if (message?.model) {
|
|
437
|
+
const model = message.model;
|
|
438
|
+
if (typeof model === "string") {
|
|
439
|
+
return model;
|
|
440
|
+
}
|
|
441
|
+
const modelObj = model as Record<string, unknown> | undefined;
|
|
442
|
+
return (
|
|
443
|
+
(typeof modelObj?.id === "string" ? modelObj.id : null) ||
|
|
444
|
+
"claude-sonnet-4-5-20250929"
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
if (entry.model_id && typeof entry.model_id === "string") {
|
|
449
|
+
return entry.model_id;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
return "claude-sonnet-4-5-20250929";
|
|
453
|
+
}
|
|
454
|
+
}
|