@polka-codes/core 0.9.101 → 0.9.102
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/_tsup-dts-rollup.d.ts +54 -2
- package/dist/index.d.ts +4 -1
- package/dist/index.js +236 -32
- package/package.json +1 -1
|
@@ -364,6 +364,12 @@ export { convertJsonSchemaToZod }
|
|
|
364
364
|
export { convertJsonSchemaToZod as convertJsonSchemaToZod_alias_1 }
|
|
365
365
|
export { convertJsonSchemaToZod as convertJsonSchemaToZod_alias_2 }
|
|
366
366
|
|
|
367
|
+
/**
|
|
368
|
+
* Convert Portkey pricing (cents per token) to ModelInfo (USD per 1M tokens)
|
|
369
|
+
* Formula: cents_per_token * 10 = usd_per_1M_tokens
|
|
370
|
+
*/
|
|
371
|
+
export declare function convertPortkeyToModelInfo(portkey: PortkeyPricingResponse): ModelInfo;
|
|
372
|
+
|
|
367
373
|
declare function createContext<TTools extends ToolRegistry>(tools: WorkflowTools<TTools>, stepFn?: StepFn, logger?: Logger): WorkflowContext<TTools>;
|
|
368
374
|
export { createContext }
|
|
369
375
|
export { createContext as createContext_alias_1 }
|
|
@@ -890,6 +896,8 @@ export { ExitReason }
|
|
|
890
896
|
export { ExitReason as ExitReason_alias_1 }
|
|
891
897
|
export { ExitReason as ExitReason_alias_2 }
|
|
892
898
|
|
|
899
|
+
export declare function fetchPricing(provider: string, model: string): Promise<PortkeyPricingResponse | null>;
|
|
900
|
+
|
|
893
901
|
/**
|
|
894
902
|
* File statistics
|
|
895
903
|
*/
|
|
@@ -1655,6 +1663,8 @@ declare type ModelInfo = {
|
|
|
1655
1663
|
};
|
|
1656
1664
|
export { ModelInfo }
|
|
1657
1665
|
export { ModelInfo as ModelInfo_alias_1 }
|
|
1666
|
+
export { ModelInfo as ModelInfo_alias_2 }
|
|
1667
|
+
export { ModelInfo as ModelInfo_alias_3 }
|
|
1658
1668
|
|
|
1659
1669
|
/**
|
|
1660
1670
|
* Node.js file system provider implementation
|
|
@@ -1696,12 +1706,51 @@ export { ParseOutputResult }
|
|
|
1696
1706
|
export { ParseOutputResult as ParseOutputResult_alias_1 }
|
|
1697
1707
|
export { ParseOutputResult as ParseOutputResult_alias_2 }
|
|
1698
1708
|
|
|
1709
|
+
declare interface PortkeyPricingConfig {
|
|
1710
|
+
provider: string;
|
|
1711
|
+
model: string;
|
|
1712
|
+
pricing: PortkeyPricingResponse;
|
|
1713
|
+
}
|
|
1714
|
+
export { PortkeyPricingConfig }
|
|
1715
|
+
export { PortkeyPricingConfig as PortkeyPricingConfig_alias_1 }
|
|
1716
|
+
export { PortkeyPricingConfig as PortkeyPricingConfig_alias_2 }
|
|
1717
|
+
|
|
1718
|
+
declare interface PortkeyPricingResponse {
|
|
1719
|
+
request_token?: {
|
|
1720
|
+
price: number;
|
|
1721
|
+
};
|
|
1722
|
+
response_token?: {
|
|
1723
|
+
price: number;
|
|
1724
|
+
};
|
|
1725
|
+
cache_write_input_token?: {
|
|
1726
|
+
price: number;
|
|
1727
|
+
};
|
|
1728
|
+
cache_read_input_token?: {
|
|
1729
|
+
price: number;
|
|
1730
|
+
};
|
|
1731
|
+
additional_units?: Record<string, {
|
|
1732
|
+
price: number;
|
|
1733
|
+
}>;
|
|
1734
|
+
}
|
|
1735
|
+
export { PortkeyPricingResponse }
|
|
1736
|
+
export { PortkeyPricingResponse as PortkeyPricingResponse_alias_1 }
|
|
1737
|
+
export { PortkeyPricingResponse as PortkeyPricingResponse_alias_2 }
|
|
1738
|
+
|
|
1699
1739
|
/**
|
|
1700
1740
|
* Simplify boolean string preprocessing
|
|
1701
1741
|
* Converts 'true'/'false' strings to actual booleans
|
|
1702
1742
|
*/
|
|
1703
1743
|
export declare function preprocessBoolean(val: unknown): unknown;
|
|
1704
1744
|
|
|
1745
|
+
declare class PricingService {
|
|
1746
|
+
#private;
|
|
1747
|
+
constructor(fallbackPrices?: Record<string, Record<string, Partial<ModelInfo>>>);
|
|
1748
|
+
getPricing(provider: string, model: string): Promise<ModelInfo>;
|
|
1749
|
+
}
|
|
1750
|
+
export { PricingService }
|
|
1751
|
+
export { PricingService as PricingService_alias_1 }
|
|
1752
|
+
export { PricingService as PricingService_alias_2 }
|
|
1753
|
+
|
|
1705
1754
|
declare type ProviderConfig = z.infer<typeof providerConfigSchema>;
|
|
1706
1755
|
export { ProviderConfig }
|
|
1707
1756
|
export { ProviderConfig as ProviderConfig_alias_1 }
|
|
@@ -2638,6 +2687,7 @@ declare class UsageMeter {
|
|
|
2638
2687
|
constructor(modelInfos?: Record<string, Record<string, Partial<ModelInfo>>>, opts?: {
|
|
2639
2688
|
maxMessages?: number;
|
|
2640
2689
|
maxCost?: number;
|
|
2690
|
+
pricingService?: PricingService;
|
|
2641
2691
|
});
|
|
2642
2692
|
addUsage(llm: LanguageModelV2, resp: {
|
|
2643
2693
|
usage: LanguageModelV2Usage;
|
|
@@ -2647,7 +2697,7 @@ declare class UsageMeter {
|
|
|
2647
2697
|
providerMetadata?: any;
|
|
2648
2698
|
}, options?: {
|
|
2649
2699
|
modelInfo?: ModelInfo;
|
|
2650
|
-
}): void
|
|
2700
|
+
}): Promise<void>;
|
|
2651
2701
|
/** Override the running totals (e.g., restore from saved state). */
|
|
2652
2702
|
setUsage(newUsage: Partial<Totals>, options?: {
|
|
2653
2703
|
clearMetadata?: boolean;
|
|
@@ -2688,11 +2738,13 @@ declare class UsageMeter {
|
|
|
2688
2738
|
clearProviderMetadata(): void;
|
|
2689
2739
|
/** Merge another UsageMeter's totals into this one. */
|
|
2690
2740
|
merge(other: UsageMeter): void;
|
|
2741
|
+
/** Wait for all pending usage updates to complete. */
|
|
2742
|
+
waitForPending(): Promise<void>;
|
|
2691
2743
|
getUsageText(): string;
|
|
2692
2744
|
onFinishHandler(llm: LanguageModelV2): (evt: {
|
|
2693
2745
|
totalUsage: LanguageModelV2Usage;
|
|
2694
2746
|
providerMetadata: any;
|
|
2695
|
-
}) => void
|
|
2747
|
+
}) => Promise<void>;
|
|
2696
2748
|
}
|
|
2697
2749
|
export { UsageMeter }
|
|
2698
2750
|
export { UsageMeter as UsageMeter_alias_1 }
|
package/dist/index.d.ts
CHANGED
|
@@ -40,6 +40,10 @@ export { MemoryOperation } from './_tsup-dts-rollup.js';
|
|
|
40
40
|
export { DatabaseStats } from './_tsup-dts-rollup.js';
|
|
41
41
|
export { IMemoryStore } from './_tsup-dts-rollup.js';
|
|
42
42
|
export { MemoryStoreConfig } from './_tsup-dts-rollup.js';
|
|
43
|
+
export { PricingService } from './_tsup-dts-rollup.js';
|
|
44
|
+
export { ModelInfo_alias_1 as ModelInfo } from './_tsup-dts-rollup.js';
|
|
45
|
+
export { PortkeyPricingConfig } from './_tsup-dts-rollup.js';
|
|
46
|
+
export { PortkeyPricingResponse } from './_tsup-dts-rollup.js';
|
|
43
47
|
export { SKILL_LIMITS } from './_tsup-dts-rollup.js';
|
|
44
48
|
export { IGNORED_DIRECTORIES } from './_tsup-dts-rollup.js';
|
|
45
49
|
export { SUSPICIOUS_PATTERNS } from './_tsup-dts-rollup.js';
|
|
@@ -117,7 +121,6 @@ export { UpdateTodoItemInputSchema } from './_tsup-dts-rollup.js';
|
|
|
117
121
|
export { UpdateTodoItemInput } from './_tsup-dts-rollup.js';
|
|
118
122
|
export { UpdateTodoItemOutputSchema } from './_tsup-dts-rollup.js';
|
|
119
123
|
export { UpdateTodoItemOutput } from './_tsup-dts-rollup.js';
|
|
120
|
-
export { ModelInfo_alias_1 as ModelInfo } from './_tsup-dts-rollup.js';
|
|
121
124
|
export { UsageMeter_alias_1 as UsageMeter } from './_tsup-dts-rollup.js';
|
|
122
125
|
export { deepMerge } from './_tsup-dts-rollup.js';
|
|
123
126
|
export { AgentWorkflowInput } from './_tsup-dts-rollup.js';
|
package/dist/index.js
CHANGED
|
@@ -403,6 +403,179 @@ var NodeFileSystemProvider = class {
|
|
|
403
403
|
}
|
|
404
404
|
};
|
|
405
405
|
|
|
406
|
+
// src/pricing/pricing-service.ts
|
|
407
|
+
import { randomUUID } from "crypto";
|
|
408
|
+
import { mkdir, readFile as readFile2, rename, writeFile } from "fs/promises";
|
|
409
|
+
import { homedir } from "os";
|
|
410
|
+
import { dirname, join as join2 } from "path";
|
|
411
|
+
|
|
412
|
+
// src/pricing/converter.ts
|
|
413
|
+
function convertPortkeyToModelInfo(portkey) {
|
|
414
|
+
return {
|
|
415
|
+
inputPrice: (portkey.request_token?.price ?? 0) * 10,
|
|
416
|
+
outputPrice: (portkey.response_token?.price ?? 0) * 10,
|
|
417
|
+
cacheWritesPrice: (portkey.cache_write_input_token?.price ?? 0) * 10,
|
|
418
|
+
cacheReadsPrice: (portkey.cache_read_input_token?.price ?? 0) * 10
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// src/pricing/portkey-client.ts
|
|
423
|
+
var PORTKEY_BASE_URL = "https://api.portkey.ai/model-configs/pricing";
|
|
424
|
+
var TIMEOUT_MS = 5e3;
|
|
425
|
+
var MAX_RETRIES = 2;
|
|
426
|
+
async function fetchWithTimeout(url, timeoutMs) {
|
|
427
|
+
const controller = new AbortController();
|
|
428
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
429
|
+
try {
|
|
430
|
+
const response = await fetch(url, { signal: controller.signal });
|
|
431
|
+
return response;
|
|
432
|
+
} finally {
|
|
433
|
+
clearTimeout(timeout);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
async function sleep(ms) {
|
|
437
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
438
|
+
}
|
|
439
|
+
function shouldRetry(response, error) {
|
|
440
|
+
if (error) return true;
|
|
441
|
+
if (response.status >= 500) return true;
|
|
442
|
+
return false;
|
|
443
|
+
}
|
|
444
|
+
async function fetchPricing(provider, model) {
|
|
445
|
+
const url = `${PORTKEY_BASE_URL}/${provider}/${model}`;
|
|
446
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
447
|
+
try {
|
|
448
|
+
const response = await fetchWithTimeout(url, TIMEOUT_MS);
|
|
449
|
+
if (!response.ok) {
|
|
450
|
+
if (!shouldRetry(response, null)) {
|
|
451
|
+
return null;
|
|
452
|
+
}
|
|
453
|
+
if (attempt < MAX_RETRIES) {
|
|
454
|
+
await sleep(2 ** attempt * 1e3);
|
|
455
|
+
continue;
|
|
456
|
+
}
|
|
457
|
+
return null;
|
|
458
|
+
}
|
|
459
|
+
const data = await response.json();
|
|
460
|
+
return data;
|
|
461
|
+
} catch (error) {
|
|
462
|
+
if (attempt < MAX_RETRIES && shouldRetry(new Response(null, { status: 500 }), error)) {
|
|
463
|
+
await sleep(2 ** attempt * 1e3);
|
|
464
|
+
continue;
|
|
465
|
+
}
|
|
466
|
+
return null;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
return null;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// src/pricing/pricing-service.ts
|
|
473
|
+
var CACHE_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
474
|
+
var PricingService = class {
|
|
475
|
+
#fallbackPrices;
|
|
476
|
+
#cacheFile;
|
|
477
|
+
#cache = null;
|
|
478
|
+
#loadPromise = null;
|
|
479
|
+
constructor(fallbackPrices = {}) {
|
|
480
|
+
const normalized = {};
|
|
481
|
+
for (const [provider, providerInfo] of Object.entries(fallbackPrices)) {
|
|
482
|
+
const normalizedProvider = provider.split("-")[0];
|
|
483
|
+
normalized[normalizedProvider] = {};
|
|
484
|
+
for (const [model, modelInfo] of Object.entries(providerInfo)) {
|
|
485
|
+
const normalizedModel = model.replace(/[.-]/g, "");
|
|
486
|
+
normalized[normalizedProvider][normalizedModel] = {
|
|
487
|
+
inputPrice: modelInfo.inputPrice ?? 0,
|
|
488
|
+
outputPrice: modelInfo.outputPrice ?? 0,
|
|
489
|
+
cacheWritesPrice: modelInfo.cacheWritesPrice ?? 0,
|
|
490
|
+
cacheReadsPrice: modelInfo.cacheReadsPrice ?? 0
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
this.#fallbackPrices = normalized;
|
|
495
|
+
this.#cacheFile = join2(homedir(), ".config", "polkacodes", "pricing-cache.json");
|
|
496
|
+
}
|
|
497
|
+
async #load() {
|
|
498
|
+
if (this.#loadPromise) {
|
|
499
|
+
return this.#loadPromise;
|
|
500
|
+
}
|
|
501
|
+
this.#loadPromise = (async () => {
|
|
502
|
+
this.#cache = /* @__PURE__ */ new Map();
|
|
503
|
+
try {
|
|
504
|
+
const content = await readFile2(this.#cacheFile, "utf-8");
|
|
505
|
+
const data = JSON.parse(content);
|
|
506
|
+
for (const [key, value] of Object.entries(data)) {
|
|
507
|
+
this.#cache.set(key, value);
|
|
508
|
+
}
|
|
509
|
+
} catch {
|
|
510
|
+
}
|
|
511
|
+
})();
|
|
512
|
+
return this.#loadPromise;
|
|
513
|
+
}
|
|
514
|
+
async #get(provider, model) {
|
|
515
|
+
await this.#load();
|
|
516
|
+
const key = `${provider}:${model}`;
|
|
517
|
+
const entry = this.#cache?.get(key);
|
|
518
|
+
if (!entry) {
|
|
519
|
+
return null;
|
|
520
|
+
}
|
|
521
|
+
if (Date.now() - entry.timestamp > CACHE_TTL_MS) {
|
|
522
|
+
this.#cache?.delete(key);
|
|
523
|
+
return null;
|
|
524
|
+
}
|
|
525
|
+
return entry.pricing;
|
|
526
|
+
}
|
|
527
|
+
async #set(provider, model, pricing) {
|
|
528
|
+
await this.#load();
|
|
529
|
+
const key = `${provider}:${model}`;
|
|
530
|
+
this.#cache?.set(key, {
|
|
531
|
+
pricing,
|
|
532
|
+
timestamp: Date.now()
|
|
533
|
+
});
|
|
534
|
+
await this.#save();
|
|
535
|
+
}
|
|
536
|
+
async #save() {
|
|
537
|
+
if (!this.#cache) {
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
try {
|
|
541
|
+
const dir = dirname(this.#cacheFile);
|
|
542
|
+
await mkdir(dir, { recursive: true });
|
|
543
|
+
const data = {};
|
|
544
|
+
for (const [key, value] of this.#cache.entries()) {
|
|
545
|
+
data[key] = value;
|
|
546
|
+
}
|
|
547
|
+
const tempFile = `${this.#cacheFile}.${randomUUID()}.tmp`;
|
|
548
|
+
await writeFile(tempFile, JSON.stringify(data, null, 2), "utf-8");
|
|
549
|
+
await rename(tempFile, this.#cacheFile);
|
|
550
|
+
} catch {
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
async getPricing(provider, model) {
|
|
554
|
+
const normalizedProvider = provider.split("-")[0];
|
|
555
|
+
const normalizedModel = model.replace(/[.-]/g, "");
|
|
556
|
+
const cached = await this.#get(normalizedProvider, normalizedModel);
|
|
557
|
+
if (cached) {
|
|
558
|
+
return cached;
|
|
559
|
+
}
|
|
560
|
+
const fallbackPrice = this.#fallbackPrices[normalizedProvider]?.[normalizedModel];
|
|
561
|
+
if (fallbackPrice) {
|
|
562
|
+
return fallbackPrice;
|
|
563
|
+
}
|
|
564
|
+
const portkeyPricing = await fetchPricing(normalizedProvider, model);
|
|
565
|
+
if (portkeyPricing) {
|
|
566
|
+
const modelInfo = convertPortkeyToModelInfo(portkeyPricing);
|
|
567
|
+
await this.#set(normalizedProvider, normalizedModel, modelInfo);
|
|
568
|
+
return modelInfo;
|
|
569
|
+
}
|
|
570
|
+
return {
|
|
571
|
+
inputPrice: 0,
|
|
572
|
+
outputPrice: 0,
|
|
573
|
+
cacheWritesPrice: 0,
|
|
574
|
+
cacheReadsPrice: 0
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
};
|
|
578
|
+
|
|
406
579
|
// src/skills/constants.ts
|
|
407
580
|
var SKILL_LIMITS = {
|
|
408
581
|
MAX_FILE_SIZE: 1024 * 1024,
|
|
@@ -456,7 +629,7 @@ var SOURCE_ICONS = {
|
|
|
456
629
|
};
|
|
457
630
|
|
|
458
631
|
// src/skills/discovery.ts
|
|
459
|
-
import { homedir } from "os";
|
|
632
|
+
import { homedir as homedir2 } from "os";
|
|
460
633
|
import { parse } from "yaml";
|
|
461
634
|
import { ZodError } from "zod";
|
|
462
635
|
|
|
@@ -546,7 +719,7 @@ var SkillDiscoveryService = class {
|
|
|
546
719
|
pluginSkillsDirs;
|
|
547
720
|
constructor(options) {
|
|
548
721
|
this.fs = options.fs ?? new NodeFileSystemProvider();
|
|
549
|
-
this.personalSkillsDir = options.personalSkillsDir ?? this.fs.join(
|
|
722
|
+
this.personalSkillsDir = options.personalSkillsDir ?? this.fs.join(homedir2(), ".claude", "skills");
|
|
550
723
|
this.projectSkillsDir = this.fs.join(options.cwd, ".claude", "skills");
|
|
551
724
|
this.pluginSkillsDirs = options.pluginSkillsDirs ?? [];
|
|
552
725
|
}
|
|
@@ -788,19 +961,19 @@ var SkillDiscoveryService = class {
|
|
|
788
961
|
};
|
|
789
962
|
|
|
790
963
|
// src/skills/validation.ts
|
|
791
|
-
import { join as
|
|
964
|
+
import { join as join3, normalize as normalize2 } from "path";
|
|
792
965
|
function validateSkillSecurity(skill) {
|
|
793
966
|
const { MAX_FILE_SIZE, MAX_SKILL_SIZE } = SKILL_LIMITS;
|
|
794
967
|
let totalSize = 0;
|
|
795
968
|
const contentSize = Buffer.byteLength(skill.content, "utf8");
|
|
796
969
|
if (contentSize > MAX_FILE_SIZE) {
|
|
797
|
-
throw new SkillValidationError(`SKILL.md content exceeds size limit (${contentSize} > ${MAX_FILE_SIZE})`,
|
|
970
|
+
throw new SkillValidationError(`SKILL.md content exceeds size limit (${contentSize} > ${MAX_FILE_SIZE})`, join3(skill.path, "SKILL.md"));
|
|
798
971
|
}
|
|
799
972
|
totalSize += contentSize;
|
|
800
973
|
for (const [filename, content] of skill.files) {
|
|
801
974
|
const fileSize = Buffer.byteLength(content, "utf8");
|
|
802
975
|
if (fileSize > MAX_FILE_SIZE) {
|
|
803
|
-
throw new SkillValidationError(`File ${filename} exceeds size limit (${fileSize} > ${MAX_FILE_SIZE})`,
|
|
976
|
+
throw new SkillValidationError(`File ${filename} exceeds size limit (${fileSize} > ${MAX_FILE_SIZE})`, join3(skill.path, filename));
|
|
804
977
|
}
|
|
805
978
|
totalSize += fileSize;
|
|
806
979
|
}
|
|
@@ -809,7 +982,7 @@ function validateSkillSecurity(skill) {
|
|
|
809
982
|
}
|
|
810
983
|
validateContentSecurity(skill.content, skill.path);
|
|
811
984
|
for (const [filename, content] of skill.files) {
|
|
812
|
-
validateContentSecurity(content,
|
|
985
|
+
validateContentSecurity(content, join3(skill.path, filename));
|
|
813
986
|
}
|
|
814
987
|
}
|
|
815
988
|
function validateContentSecurity(content, path) {
|
|
@@ -2271,9 +2444,11 @@ var writeToFile_default = {
|
|
|
2271
2444
|
var UsageMeter = class {
|
|
2272
2445
|
#totals = { input: 0, output: 0, cachedRead: 0, cost: 0, messageCount: 0 };
|
|
2273
2446
|
#providerMetadataEntries = [];
|
|
2447
|
+
#pendingUpdates = /* @__PURE__ */ new Set();
|
|
2274
2448
|
#modelInfos;
|
|
2275
2449
|
#maxMessages;
|
|
2276
2450
|
#maxCost;
|
|
2451
|
+
#pricingService;
|
|
2277
2452
|
constructor(modelInfos = {}, opts = {}) {
|
|
2278
2453
|
const infos = {};
|
|
2279
2454
|
for (const [provider, providerInfo] of Object.entries(modelInfos)) {
|
|
@@ -2289,6 +2464,7 @@ var UsageMeter = class {
|
|
|
2289
2464
|
this.#modelInfos = infos;
|
|
2290
2465
|
this.#maxMessages = opts.maxMessages ?? 1e3;
|
|
2291
2466
|
this.#maxCost = opts.maxCost ?? 100;
|
|
2467
|
+
this.#pricingService = opts.pricingService;
|
|
2292
2468
|
}
|
|
2293
2469
|
#calculateUsage(usage, providerMetadata, modelInfo) {
|
|
2294
2470
|
const providerMetadataKey = Object.keys(providerMetadata ?? {})[0];
|
|
@@ -2339,30 +2515,52 @@ var UsageMeter = class {
|
|
|
2339
2515
|
}
|
|
2340
2516
|
}
|
|
2341
2517
|
addUsage(llm, resp, options = {}) {
|
|
2342
|
-
const
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2518
|
+
const provider = llm.provider.split(".")[0];
|
|
2519
|
+
const normalizedModel = llm.modelId.replace(/[.-]/g, "");
|
|
2520
|
+
const key = `${provider}:${normalizedModel}`;
|
|
2521
|
+
let modelInfo = options.modelInfo ?? this.#modelInfos[key];
|
|
2522
|
+
const updatePromise = (async () => {
|
|
2523
|
+
try {
|
|
2524
|
+
if (!modelInfo && this.#pricingService) {
|
|
2525
|
+
modelInfo = await this.#pricingService.getPricing(provider, llm.modelId);
|
|
2526
|
+
this.#modelInfos[key] = modelInfo;
|
|
2527
|
+
}
|
|
2528
|
+
} catch {
|
|
2529
|
+
modelInfo = {
|
|
2530
|
+
inputPrice: 0,
|
|
2531
|
+
outputPrice: 0,
|
|
2532
|
+
cacheWritesPrice: 0,
|
|
2533
|
+
cacheReadsPrice: 0
|
|
2534
|
+
};
|
|
2535
|
+
}
|
|
2536
|
+
modelInfo = modelInfo ?? {
|
|
2537
|
+
inputPrice: 0,
|
|
2538
|
+
outputPrice: 0,
|
|
2539
|
+
cacheWritesPrice: 0,
|
|
2540
|
+
cacheReadsPrice: 0
|
|
2541
|
+
};
|
|
2542
|
+
const usage = "totalUsage" in resp ? resp.totalUsage : resp.usage;
|
|
2543
|
+
const result = this.#calculateUsage(usage, resp.providerMetadata, modelInfo);
|
|
2544
|
+
this.#totals.input += result.input || 0;
|
|
2545
|
+
this.#totals.output += result.output || 0;
|
|
2546
|
+
this.#totals.cachedRead += result.cachedRead || 0;
|
|
2547
|
+
this.#totals.cost += result.cost || 0;
|
|
2548
|
+
this.#totals.messageCount += 1;
|
|
2549
|
+
if (resp.providerMetadata && Object.keys(resp.providerMetadata).length > 0) {
|
|
2550
|
+
const providerKey = Object.keys(resp.providerMetadata)[0];
|
|
2551
|
+
this.#providerMetadataEntries.push({
|
|
2552
|
+
provider: providerKey || llm.provider,
|
|
2553
|
+
model: llm.modelId,
|
|
2554
|
+
metadata: resp.providerMetadata[providerKey] || resp.providerMetadata,
|
|
2555
|
+
timestamp: Date.now()
|
|
2556
|
+
});
|
|
2557
|
+
}
|
|
2558
|
+
})();
|
|
2559
|
+
this.#pendingUpdates.add(updatePromise);
|
|
2560
|
+
updatePromise.finally(() => {
|
|
2561
|
+
this.#pendingUpdates.delete(updatePromise);
|
|
2562
|
+
});
|
|
2563
|
+
return updatePromise;
|
|
2366
2564
|
}
|
|
2367
2565
|
/** Override the running totals (e.g., restore from saved state). */
|
|
2368
2566
|
setUsage(newUsage, options = {}) {
|
|
@@ -2453,13 +2651,18 @@ var UsageMeter = class {
|
|
|
2453
2651
|
this.#totals.messageCount += otherUsage.messageCount;
|
|
2454
2652
|
this.#providerMetadataEntries.push(...other.providerMetadata);
|
|
2455
2653
|
}
|
|
2654
|
+
/** Wait for all pending usage updates to complete. */
|
|
2655
|
+
async waitForPending() {
|
|
2656
|
+
const pending = Array.from(this.#pendingUpdates);
|
|
2657
|
+
await Promise.allSettled(pending);
|
|
2658
|
+
}
|
|
2456
2659
|
getUsageText() {
|
|
2457
2660
|
const u = this.usage;
|
|
2458
2661
|
return `Usage - messages: ${u.messageCount}, input: ${u.input}, cached: ${u.cachedRead}, output: ${u.output}, cost: $${u.cost.toFixed(4)}`;
|
|
2459
2662
|
}
|
|
2460
2663
|
onFinishHandler(llm) {
|
|
2461
|
-
return (evt) => {
|
|
2462
|
-
this.addUsage(llm, evt);
|
|
2664
|
+
return async (evt) => {
|
|
2665
|
+
await this.addUsage(llm, evt);
|
|
2463
2666
|
};
|
|
2464
2667
|
}
|
|
2465
2668
|
};
|
|
@@ -3776,6 +3979,7 @@ export {
|
|
|
3776
3979
|
LoadSkillOutputSchema,
|
|
3777
3980
|
MockProvider,
|
|
3778
3981
|
NodeFileSystemProvider,
|
|
3982
|
+
PricingService,
|
|
3779
3983
|
ReadSkillFileInputSchema,
|
|
3780
3984
|
ReadSkillFileOutputSchema,
|
|
3781
3985
|
SKILL_ERROR_MESSAGES,
|