letmecode 0.1.15 → 0.1.17
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/ink-app/dist/providers/claude.js +100 -31
- package/package.json +1 -1
|
@@ -97,8 +97,8 @@ export class ClaudeUsageProvider extends UsageProviderBase {
|
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
99
|
const selectedEvents = [
|
|
100
|
-
...parsedEvents.keyedEvents.values(),
|
|
101
|
-
...parsedEvents.unkeyedEvents
|
|
100
|
+
...new Set(parsedEvents.keyedEvents.values()),
|
|
101
|
+
...parsedEvents.unkeyedEvents
|
|
102
102
|
];
|
|
103
103
|
traceClaude(options.traceLogger, [
|
|
104
104
|
`Transcript selection summary: filesWithMatches=${parseTotals.filesScanned}/${parsedSessionFiles.length}`,
|
|
@@ -125,10 +125,10 @@ export class ClaudeUsageProvider extends UsageProviderBase {
|
|
|
125
125
|
warnings.push(`Collapsed ${parsedEvents.duplicateUsageKeys} duplicate Claude usage event(s) by request/message key.`);
|
|
126
126
|
}
|
|
127
127
|
if (options.verbose && parsedEvents.duplicateUsageKeyCollisions > 0) {
|
|
128
|
-
warnings.push(`Detected ${parsedEvents.duplicateUsageKeyCollisions} Claude usage key collision(s) with different token usage;
|
|
128
|
+
warnings.push(`Detected ${parsedEvents.duplicateUsageKeyCollisions} Claude usage key collision(s) with different token usage; merged same-key rows by per-field maxima to avoid double-counting cumulative snapshots.`);
|
|
129
129
|
}
|
|
130
130
|
if (options.verbose && parsedEvents.duplicateUnkeyedEvents > 0) {
|
|
131
|
-
warnings.push(`Collapsed ${parsedEvents.duplicateUnkeyedEvents} duplicate unkeyed Claude usage event(s) by usage signature.`);
|
|
131
|
+
warnings.push(`Collapsed ${parsedEvents.duplicateUnkeyedEvents} adjacent duplicate unkeyed Claude usage event(s) by usage signature.`);
|
|
132
132
|
}
|
|
133
133
|
const modelUsage = [...byModel.entries()]
|
|
134
134
|
.map(([modelId, totals]) => ({ modelId, totals }))
|
|
@@ -446,15 +446,18 @@ async function parseSessionFile(filePath, sessionsRoot) {
|
|
|
446
446
|
const entrypoint = typeof payloadObject.entrypoint === "string" ? payloadObject.entrypoint : "";
|
|
447
447
|
const rateLimits = extractRateLimits(payloadObject, message);
|
|
448
448
|
const normalizedUsage = normalizeUsage(usage);
|
|
449
|
-
const
|
|
449
|
+
const usageKeys = buildUsageEventKeys(payloadObject, message);
|
|
450
450
|
const usageSignature = buildUsageSignature(payloadObject, modelId, normalizedUsage);
|
|
451
451
|
assistantEntryPoints.add(entrypoint);
|
|
452
452
|
events.push({
|
|
453
453
|
entrypoint,
|
|
454
|
-
|
|
454
|
+
filePath,
|
|
455
|
+
lineNumber: linesRead,
|
|
456
|
+
usageKeys,
|
|
455
457
|
usageSignature,
|
|
456
458
|
timestampMs: eventTimeMs,
|
|
457
459
|
modelId,
|
|
460
|
+
usage: normalizedUsage,
|
|
458
461
|
totals: usageToTotals(modelId, normalizedUsage),
|
|
459
462
|
rateLimits
|
|
460
463
|
});
|
|
@@ -555,18 +558,21 @@ function extractStringArray(value) {
|
|
|
555
558
|
}
|
|
556
559
|
return value.filter((item) => typeof item === "string");
|
|
557
560
|
}
|
|
558
|
-
function
|
|
561
|
+
function buildUsageEventKeys(payloadObject, message) {
|
|
559
562
|
const sessionId = String(payloadObject.sessionId ?? "");
|
|
560
563
|
const requestId = typeof payloadObject.requestId === "string" ? payloadObject.requestId : "";
|
|
561
564
|
const messageId = typeof message?.id === "string" ? message.id : "";
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
565
|
+
return [...new Set([
|
|
566
|
+
requestId ? `${sessionId}|request:${requestId}` : "",
|
|
567
|
+
messageId ? `${sessionId}|message:${messageId}` : ""
|
|
568
|
+
].filter(Boolean))];
|
|
566
569
|
}
|
|
567
570
|
function buildUsageSignature(payloadObject, modelId, usage) {
|
|
571
|
+
return buildUsageSignatureFromParts(String(payloadObject.sessionId ?? ""), modelId, usage);
|
|
572
|
+
}
|
|
573
|
+
function buildUsageSignatureFromParts(sessionId, modelId, usage) {
|
|
568
574
|
return [
|
|
569
|
-
|
|
575
|
+
sessionId,
|
|
570
576
|
modelId,
|
|
571
577
|
usage.inputTokens,
|
|
572
578
|
usage.cacheCreationInputTokens,
|
|
@@ -580,46 +586,109 @@ function buildUsageSignature(payloadObject, modelId, usage) {
|
|
|
580
586
|
function createParsedUsageEventAccumulator() {
|
|
581
587
|
return {
|
|
582
588
|
keyedEvents: new Map(),
|
|
583
|
-
unkeyedEvents:
|
|
589
|
+
unkeyedEvents: [],
|
|
590
|
+
lastUnkeyedEventsBySignature: new Map(),
|
|
584
591
|
duplicateUsageKeys: 0,
|
|
585
592
|
duplicateUsageKeyCollisions: 0,
|
|
586
593
|
duplicateUnkeyedEvents: 0
|
|
587
594
|
};
|
|
588
595
|
}
|
|
589
596
|
function recordParsedUsageEvent(parsedEvents, event) {
|
|
590
|
-
if (event.
|
|
591
|
-
const
|
|
592
|
-
|
|
593
|
-
|
|
597
|
+
if (event.usageKeys.length > 0) {
|
|
598
|
+
const previousMatches = [...new Set(event.usageKeys
|
|
599
|
+
.map((usageKey) => parsedEvents.keyedEvents.get(usageKey))
|
|
600
|
+
.filter((candidate) => Boolean(candidate)))];
|
|
601
|
+
if (previousMatches.length === 0) {
|
|
602
|
+
for (const usageKey of event.usageKeys) {
|
|
603
|
+
parsedEvents.keyedEvents.set(usageKey, event);
|
|
604
|
+
}
|
|
594
605
|
return;
|
|
595
606
|
}
|
|
596
607
|
parsedEvents.duplicateUsageKeys += 1;
|
|
597
|
-
|
|
608
|
+
const distinctUsageSignatures = new Set([
|
|
609
|
+
event.usageSignature,
|
|
610
|
+
...previousMatches.map((candidate) => candidate.usageSignature)
|
|
611
|
+
]);
|
|
612
|
+
if (distinctUsageSignatures.size > 1) {
|
|
598
613
|
parsedEvents.duplicateUsageKeyCollisions += 1;
|
|
599
614
|
}
|
|
600
|
-
|
|
601
|
-
|
|
615
|
+
const mergedEvent = previousMatches.reduce(mergeParsedUsageEvents, event);
|
|
616
|
+
for (const usageKey of mergedEvent.usageKeys) {
|
|
617
|
+
parsedEvents.keyedEvents.set(usageKey, mergedEvent);
|
|
602
618
|
}
|
|
603
619
|
return;
|
|
604
620
|
}
|
|
605
|
-
const
|
|
606
|
-
if (!
|
|
607
|
-
parsedEvents.unkeyedEvents.
|
|
621
|
+
const previousRecord = parsedEvents.lastUnkeyedEventsBySignature.get(event.usageSignature);
|
|
622
|
+
if (!previousRecord || !canCollapseAdjacentUnkeyedUsageEvents(previousRecord.event, event)) {
|
|
623
|
+
parsedEvents.unkeyedEvents.push(event);
|
|
624
|
+
parsedEvents.lastUnkeyedEventsBySignature.set(event.usageSignature, {
|
|
625
|
+
event,
|
|
626
|
+
index: parsedEvents.unkeyedEvents.length - 1
|
|
627
|
+
});
|
|
608
628
|
return;
|
|
609
629
|
}
|
|
610
630
|
parsedEvents.duplicateUnkeyedEvents += 1;
|
|
611
|
-
if (
|
|
612
|
-
parsedEvents.unkeyedEvents.
|
|
631
|
+
if (normalizeTimestamp(event.timestampMs) > normalizeTimestamp(previousRecord.event.timestampMs)) {
|
|
632
|
+
parsedEvents.unkeyedEvents[previousRecord.index] = event;
|
|
633
|
+
parsedEvents.lastUnkeyedEventsBySignature.set(event.usageSignature, {
|
|
634
|
+
event,
|
|
635
|
+
index: previousRecord.index
|
|
636
|
+
});
|
|
613
637
|
}
|
|
614
638
|
}
|
|
615
|
-
function
|
|
616
|
-
|
|
617
|
-
|
|
639
|
+
function mergeParsedUsageEvents(previous, next) {
|
|
640
|
+
const mergedUsage = mergeClaudeUsage(previous.usage, next.usage);
|
|
641
|
+
const modelId = selectMergedEventModelId(previous, next);
|
|
642
|
+
const latestEvent = normalizeTimestamp(next.timestampMs) >= normalizeTimestamp(previous.timestampMs) ? next : previous;
|
|
643
|
+
const sessionId = extractUsageKeySessionId(previous.usageKeys) || extractUsageKeySessionId(next.usageKeys);
|
|
644
|
+
return {
|
|
645
|
+
entrypoint: latestEvent.entrypoint || previous.entrypoint || next.entrypoint,
|
|
646
|
+
filePath: latestEvent.filePath,
|
|
647
|
+
lineNumber: latestEvent.lineNumber,
|
|
648
|
+
usageKeys: [...new Set([...previous.usageKeys, ...next.usageKeys])],
|
|
649
|
+
usageSignature: buildUsageSignatureFromParts(sessionId, modelId, mergedUsage),
|
|
650
|
+
timestampMs: Math.max(normalizeTimestamp(previous.timestampMs), normalizeTimestamp(next.timestampMs)),
|
|
651
|
+
modelId,
|
|
652
|
+
usage: mergedUsage,
|
|
653
|
+
totals: usageToTotals(modelId, mergedUsage),
|
|
654
|
+
rateLimits: latestEvent.rateLimits ?? previous.rateLimits ?? next.rateLimits
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
function mergeClaudeUsage(previous, next) {
|
|
658
|
+
return {
|
|
659
|
+
inputTokens: Math.max(previous.inputTokens, next.inputTokens),
|
|
660
|
+
cacheReadInputTokens: Math.max(previous.cacheReadInputTokens, next.cacheReadInputTokens),
|
|
661
|
+
cacheCreationInputTokens: Math.max(previous.cacheCreationInputTokens, next.cacheCreationInputTokens),
|
|
662
|
+
cacheCreation5mInputTokens: Math.max(previous.cacheCreation5mInputTokens, next.cacheCreation5mInputTokens),
|
|
663
|
+
cacheCreation1hInputTokens: Math.max(previous.cacheCreation1hInputTokens, next.cacheCreation1hInputTokens),
|
|
664
|
+
outputTokens: Math.max(previous.outputTokens, next.outputTokens),
|
|
665
|
+
inferenceGeo: next.inferenceGeo || previous.inferenceGeo
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
function selectMergedEventModelId(previous, next) {
|
|
669
|
+
if (previous.modelId === next.modelId) {
|
|
670
|
+
return previous.modelId;
|
|
671
|
+
}
|
|
672
|
+
if (isInternalClaudeModel(previous.modelId) && !isInternalClaudeModel(next.modelId)) {
|
|
673
|
+
return next.modelId;
|
|
618
674
|
}
|
|
619
|
-
if (next.
|
|
620
|
-
return
|
|
675
|
+
if (isInternalClaudeModel(next.modelId) && !isInternalClaudeModel(previous.modelId)) {
|
|
676
|
+
return previous.modelId;
|
|
677
|
+
}
|
|
678
|
+
return next.totals.estimatedCredits >= previous.totals.estimatedCredits
|
|
679
|
+
? next.modelId
|
|
680
|
+
: previous.modelId;
|
|
681
|
+
}
|
|
682
|
+
function canCollapseAdjacentUnkeyedUsageEvents(previous, next) {
|
|
683
|
+
return previous.filePath === next.filePath && next.lineNumber === previous.lineNumber + 1;
|
|
684
|
+
}
|
|
685
|
+
function extractUsageKeySessionId(usageKeys) {
|
|
686
|
+
const usageKey = usageKeys[0];
|
|
687
|
+
if (!usageKey) {
|
|
688
|
+
return "";
|
|
621
689
|
}
|
|
622
|
-
|
|
690
|
+
const separatorIndex = usageKey.indexOf("|");
|
|
691
|
+
return separatorIndex >= 0 ? usageKey.slice(0, separatorIndex) : usageKey;
|
|
623
692
|
}
|
|
624
693
|
function normalizeTimestamp(value) {
|
|
625
694
|
return Number.isFinite(value) ? value : Number.NEGATIVE_INFINITY;
|