@ramarivera/coding-agent-langfuse 0.1.53 → 0.1.54
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/README.md +12 -6
- package/dist/backfill.js +35 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,10 +8,12 @@ Langfuse canonical `usage_details` and `cost_details` attributes so historical
|
|
|
8
8
|
backfills participate in Langfuse model-usage and cost dashboards. Tool calls
|
|
9
9
|
remain child spans under the same session.
|
|
10
10
|
|
|
11
|
-
Codex `event_msg` `token_count` rows are imported as
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
Codex `event_msg` `token_count` rows are imported as cost-accounting
|
|
12
|
+
generations when they include input/output/cache buckets for a known priced
|
|
13
|
+
model. The importer preserves the original token snapshot in metadata and sends
|
|
14
|
+
Langfuse canonical `usage_details`/`cost_details` derived from the bucketed
|
|
15
|
+
usage. Total-only Codex snapshots and unknown/free-preview models stay
|
|
16
|
+
non-billable to avoid inventing cost from ambiguous telemetry.
|
|
15
17
|
|
|
16
18
|
```sh
|
|
17
19
|
coding-agent-langfuse-backfill --agents codex,claude,grok,pi,opencode
|
|
@@ -133,8 +135,8 @@ records a total cost, that recorded value wins. Otherwise, the importer
|
|
|
133
135
|
calculates per-usage-type USD costs from a model catalog using rates in USD per
|
|
134
136
|
1M tokens.
|
|
135
137
|
|
|
136
|
-
The built-in catalog covers OpenAI GPT-5.5, GPT-5.4,
|
|
137
|
-
pricing, Anthropic Claude Opus/Sonnet 4 API list pricing, plus the toolbox/Pi
|
|
138
|
+
The built-in catalog covers OpenAI GPT-5.5, GPT-5.4, GPT-5.4 Mini, and
|
|
139
|
+
GPT-5.3-Codex API list pricing, Anthropic Claude Opus/Sonnet 4 API list pricing, plus the toolbox/Pi
|
|
138
140
|
models already used in local configuration, including Fireworks Kimi K2.6,
|
|
139
141
|
Fireworks DeepSeek V4 Pro, MiniMax-M3, Together DeepSeek/Kimi/GLM/MiniMax, and
|
|
140
142
|
Zai GLM. `gpt-5.5` is charged at current standard API list price by default:
|
|
@@ -145,6 +147,10 @@ writes, `$30.00` 1-hour cache writes, and `$75.00` output per 1M tokens.
|
|
|
145
147
|
When a billable generation source only records a total token count without
|
|
146
148
|
input/output/cache breakdown, the importer charges that total at the model input
|
|
147
149
|
rate and marks the cost source as `calculated_total_as_input`.
|
|
150
|
+
Codex token-count snapshots are the exception: they are charged only when the
|
|
151
|
+
snapshot has explicit input/output/cache buckets. Codex reasoning tokens are
|
|
152
|
+
kept in metadata, but not charged separately because Codex token-count output
|
|
153
|
+
totals already include reasoning tokens.
|
|
148
154
|
|
|
149
155
|
Use an override only when you intentionally want a different accounting policy:
|
|
150
156
|
|
package/dist/backfill.js
CHANGED
|
@@ -8,7 +8,7 @@ const allAgents = ["claude", "codex", "grok", "opencode", "pi"];
|
|
|
8
8
|
const importStateIdentityVersion = "v9-cost-details";
|
|
9
9
|
const importStateIdentityVersions = {
|
|
10
10
|
claude: "v13-claude-message-snapshot-dedupe",
|
|
11
|
-
codex: "
|
|
11
|
+
codex: "v12-codex-token-accounting-priced",
|
|
12
12
|
grok: "v12-cost-details",
|
|
13
13
|
opencode: "v11-cost-details",
|
|
14
14
|
pi: "v12-cost-details",
|
|
@@ -24,7 +24,7 @@ const langfuseIdIdentityVersions = {
|
|
|
24
24
|
const importPayloadVersion = "v10-cost-details";
|
|
25
25
|
const importPayloadVersions = {
|
|
26
26
|
claude: "v11-claude-message-snapshot-dedupe",
|
|
27
|
-
codex: "
|
|
27
|
+
codex: "v12-codex-token-accounting-priced",
|
|
28
28
|
};
|
|
29
29
|
const defaultEndpoint = "https://langfuse.ai.roxasroot.net/otel/v1/traces";
|
|
30
30
|
const deadRemoteEndpoint = "http://langfuse.ai.roxasroot.net:14318/v1/traces";
|
|
@@ -72,6 +72,12 @@ const gpt54Rates = {
|
|
|
72
72
|
cacheRead: 0.25,
|
|
73
73
|
cacheWrite: 2.5,
|
|
74
74
|
};
|
|
75
|
+
const gpt54MiniRates = {
|
|
76
|
+
input: 0.75,
|
|
77
|
+
output: 4.5,
|
|
78
|
+
cacheRead: 0.075,
|
|
79
|
+
cacheWrite: 0.75,
|
|
80
|
+
};
|
|
75
81
|
const gpt53CodexRates = {
|
|
76
82
|
input: 1.75,
|
|
77
83
|
output: 14,
|
|
@@ -101,6 +107,8 @@ const defaultCostRates = {
|
|
|
101
107
|
"openai/gpt-5.5-pro": gpt55ProRates,
|
|
102
108
|
"gpt-5.4": gpt54Rates,
|
|
103
109
|
"openai/gpt-5.4": gpt54Rates,
|
|
110
|
+
"gpt-5.4-mini": gpt54MiniRates,
|
|
111
|
+
"openai/gpt-5.4-mini": gpt54MiniRates,
|
|
104
112
|
"gpt-5.3-codex": gpt53CodexRates,
|
|
105
113
|
"openai/gpt-5.3-codex": gpt53CodexRates,
|
|
106
114
|
"claude-opus-4": claudeOpus4Rates,
|
|
@@ -949,6 +957,28 @@ function usageTokenTotal(usage) {
|
|
|
949
957
|
(usage.cacheWrite5m ?? 0) +
|
|
950
958
|
(usage.cacheWrite1h ?? 0);
|
|
951
959
|
}
|
|
960
|
+
function codexBillableTokenUsage(usage) {
|
|
961
|
+
if (!usage)
|
|
962
|
+
return undefined;
|
|
963
|
+
const hasTokenBuckets = [
|
|
964
|
+
usage.input,
|
|
965
|
+
usage.output,
|
|
966
|
+
usage.reasoning,
|
|
967
|
+
usage.cacheRead,
|
|
968
|
+
usage.cacheWrite,
|
|
969
|
+
usage.cacheWrite5m,
|
|
970
|
+
usage.cacheWrite1h,
|
|
971
|
+
].some((value) => value !== undefined && value > 0);
|
|
972
|
+
if (!hasTokenBuckets)
|
|
973
|
+
return undefined;
|
|
974
|
+
return {
|
|
975
|
+
...usage,
|
|
976
|
+
output: usage.output ?? usage.reasoning,
|
|
977
|
+
// Codex token_count rows report reasoning as a subset of output tokens.
|
|
978
|
+
// Keep reasoning in metadata, but do not charge it a second time.
|
|
979
|
+
reasoning: undefined,
|
|
980
|
+
};
|
|
981
|
+
}
|
|
952
982
|
function textLength(value) {
|
|
953
983
|
if (typeof value === "string")
|
|
954
984
|
return value.length;
|
|
@@ -1214,6 +1244,7 @@ function codexEvents(homeDir, options = {}) {
|
|
|
1214
1244
|
const info = asRecord(rowPayload.info);
|
|
1215
1245
|
const usage = normalizeUsage(asRecord(info.last_token_usage)) ??
|
|
1216
1246
|
normalizeUsage(asRecord(info.total_token_usage));
|
|
1247
|
+
const billableUsage = codexBillableTokenUsage(usage);
|
|
1217
1248
|
const tokenKey = JSON.stringify({
|
|
1218
1249
|
model: asString(info.model) ?? currentModel,
|
|
1219
1250
|
usage,
|
|
@@ -1231,9 +1262,10 @@ function codexEvents(homeDir, options = {}) {
|
|
|
1231
1262
|
cwd: currentCwd,
|
|
1232
1263
|
startMs: timestamp,
|
|
1233
1264
|
parentRecordId: "session",
|
|
1265
|
+
usage: billableUsage,
|
|
1234
1266
|
metadata: {
|
|
1235
1267
|
...pick(info, ["model_context_window"]),
|
|
1236
|
-
token_usage_billable:
|
|
1268
|
+
token_usage_billable: billableUsage !== undefined,
|
|
1237
1269
|
token_usage_source: "codex_event_msg_token_count",
|
|
1238
1270
|
token_usage_details: usageDetails(usage),
|
|
1239
1271
|
},
|