@ramarivera/coding-agent-langfuse 0.1.45 → 0.1.46
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/backfill.js +75 -12
- package/package.json +1 -1
package/dist/backfill.js
CHANGED
|
@@ -7,7 +7,7 @@ import { dirname, join } from "node:path";
|
|
|
7
7
|
const allAgents = ["claude", "codex", "grok", "opencode", "pi"];
|
|
8
8
|
const importStateIdentityVersion = "v9-cost-details";
|
|
9
9
|
const importStateIdentityVersions = {
|
|
10
|
-
claude: "
|
|
10
|
+
claude: "v13-claude-message-snapshot-dedupe",
|
|
11
11
|
codex: "v11-codex-token-accounting-nonbillable",
|
|
12
12
|
grok: "v12-cost-details",
|
|
13
13
|
opencode: "v11-cost-details",
|
|
@@ -23,6 +23,7 @@ const langfuseIdIdentityVersions = {
|
|
|
23
23
|
};
|
|
24
24
|
const importPayloadVersion = "v10-cost-details";
|
|
25
25
|
const importPayloadVersions = {
|
|
26
|
+
claude: "v11-claude-message-snapshot-dedupe",
|
|
26
27
|
codex: "v11-codex-token-accounting-nonbillable",
|
|
27
28
|
};
|
|
28
29
|
const defaultEndpoint = "https://langfuse.ai.roxasroot.net/otel/v1/traces";
|
|
@@ -690,6 +691,25 @@ function usageDetails(usage) {
|
|
|
690
691
|
details.total = usage.total;
|
|
691
692
|
return Object.keys(details).length > 0 ? details : undefined;
|
|
692
693
|
}
|
|
694
|
+
function usageTokenTotal(usage) {
|
|
695
|
+
if (!usage)
|
|
696
|
+
return 0;
|
|
697
|
+
return usage.total ??
|
|
698
|
+
(usage.input ?? 0) +
|
|
699
|
+
(usage.output ?? 0) +
|
|
700
|
+
(usage.reasoning ?? 0) +
|
|
701
|
+
(usage.cacheRead ?? 0) +
|
|
702
|
+
(usage.cacheWrite ?? 0) +
|
|
703
|
+
(usage.cacheWrite5m ?? 0) +
|
|
704
|
+
(usage.cacheWrite1h ?? 0);
|
|
705
|
+
}
|
|
706
|
+
function textLength(value) {
|
|
707
|
+
if (typeof value === "string")
|
|
708
|
+
return value.length;
|
|
709
|
+
if (value === undefined || value === null)
|
|
710
|
+
return 0;
|
|
711
|
+
return JSON.stringify(value).length;
|
|
712
|
+
}
|
|
693
713
|
function calculateCost(event, usage, costRates) {
|
|
694
714
|
if (!usage)
|
|
695
715
|
return undefined;
|
|
@@ -1373,6 +1393,7 @@ function genericJsonlEvents(agent, files, sessionName) {
|
|
|
1373
1393
|
startMs,
|
|
1374
1394
|
},
|
|
1375
1395
|
];
|
|
1396
|
+
const claudeAssistantEventsByMessageId = new Map();
|
|
1376
1397
|
for (const [index, row] of rows.entries()) {
|
|
1377
1398
|
const message = asRecord(row.message);
|
|
1378
1399
|
const role = asString(message.role) ?? asString(row.type);
|
|
@@ -1380,11 +1401,31 @@ function genericJsonlEvents(agent, files, sessionName) {
|
|
|
1380
1401
|
asString(row.id) ??
|
|
1381
1402
|
asString(row.toolUseID) ??
|
|
1382
1403
|
`row-${index}`;
|
|
1404
|
+
let childParentRecordId = recordId;
|
|
1383
1405
|
const toolUseId = asString(row.toolUseID) ?? asString(row.tool_use_id);
|
|
1384
1406
|
const content = message.content ?? row.content;
|
|
1385
1407
|
const timestamp = getTimestampMs(row.timestamp ?? row.time_created, startMs + index);
|
|
1386
1408
|
const usage = normalizeUsage(message.usage ?? row.usage);
|
|
1387
|
-
|
|
1409
|
+
const claudeMessageId = agent === "claude" && role === "assistant"
|
|
1410
|
+
? asString(message.id)
|
|
1411
|
+
: undefined;
|
|
1412
|
+
const eventMetadata = {
|
|
1413
|
+
...pick(row, [
|
|
1414
|
+
"type",
|
|
1415
|
+
"entrypoint",
|
|
1416
|
+
"version",
|
|
1417
|
+
"gitBranch",
|
|
1418
|
+
"error",
|
|
1419
|
+
]),
|
|
1420
|
+
...(claudeMessageId
|
|
1421
|
+
? {
|
|
1422
|
+
claude_message_id: claudeMessageId,
|
|
1423
|
+
claude_snapshot_count: 1,
|
|
1424
|
+
claude_usage_dedupe: "message_id_max_usage",
|
|
1425
|
+
}
|
|
1426
|
+
: {}),
|
|
1427
|
+
};
|
|
1428
|
+
const event = {
|
|
1388
1429
|
agent,
|
|
1389
1430
|
sourcePath: path,
|
|
1390
1431
|
sessionId: asString(row.sessionId) ?? asString(row.session_id) ??
|
|
@@ -1408,14 +1449,36 @@ function genericJsonlEvents(agent, files, sessionName) {
|
|
|
1408
1449
|
? extractText(content)
|
|
1409
1450
|
: undefined,
|
|
1410
1451
|
usage,
|
|
1411
|
-
metadata:
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1452
|
+
metadata: eventMetadata,
|
|
1453
|
+
};
|
|
1454
|
+
if (claudeMessageId) {
|
|
1455
|
+
const existing = claudeAssistantEventsByMessageId.get(claudeMessageId);
|
|
1456
|
+
if (existing) {
|
|
1457
|
+
childParentRecordId = existing.recordId;
|
|
1458
|
+
if (usageTokenTotal(event.usage) > usageTokenTotal(existing.usage)) {
|
|
1459
|
+
existing.usage = event.usage;
|
|
1460
|
+
}
|
|
1461
|
+
if (event.output && textLength(event.output) > textLength(existing.output)) {
|
|
1462
|
+
existing.output = event.output;
|
|
1463
|
+
}
|
|
1464
|
+
if (!existing.model && event.model)
|
|
1465
|
+
existing.model = event.model;
|
|
1466
|
+
if (!existing.cwd && event.cwd)
|
|
1467
|
+
existing.cwd = event.cwd;
|
|
1468
|
+
existing.startMs = Math.min(existing.startMs, event.startMs);
|
|
1469
|
+
existing.metadata = {
|
|
1470
|
+
...existing.metadata,
|
|
1471
|
+
claude_snapshot_count: (asNumber(existing.metadata?.claude_snapshot_count) ?? 1) + 1,
|
|
1472
|
+
};
|
|
1473
|
+
}
|
|
1474
|
+
else {
|
|
1475
|
+
claudeAssistantEventsByMessageId.set(claudeMessageId, event);
|
|
1476
|
+
events.push(event);
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
else {
|
|
1480
|
+
events.push(event);
|
|
1481
|
+
}
|
|
1419
1482
|
for (const reasoning of reasoningFromContent(content)) {
|
|
1420
1483
|
events.push({
|
|
1421
1484
|
agent,
|
|
@@ -1425,7 +1488,7 @@ function genericJsonlEvents(agent, files, sessionName) {
|
|
|
1425
1488
|
name: `${agent} reasoning`,
|
|
1426
1489
|
cwd,
|
|
1427
1490
|
startMs: timestamp,
|
|
1428
|
-
parentRecordId:
|
|
1491
|
+
parentRecordId: childParentRecordId,
|
|
1429
1492
|
output: reasoning.text,
|
|
1430
1493
|
metadata: { has_signature: reasoning.hasSignature },
|
|
1431
1494
|
});
|
|
@@ -1439,7 +1502,7 @@ function genericJsonlEvents(agent, files, sessionName) {
|
|
|
1439
1502
|
name: `${agent} tool ${tool.name}`,
|
|
1440
1503
|
cwd,
|
|
1441
1504
|
startMs: timestamp,
|
|
1442
|
-
parentRecordId:
|
|
1505
|
+
parentRecordId: childParentRecordId,
|
|
1443
1506
|
input: tool.arguments,
|
|
1444
1507
|
});
|
|
1445
1508
|
}
|