Skip to content

Commit 6c954e3

Browse files
committed
Avoid double-counting OpenRouter callModel metrics
1 parent 4dbc2ac commit 6c954e3

4 files changed

Lines changed: 74 additions & 12 deletions

File tree

‎js/src/instrumentation/plugins/openrouter-agent-plugin.test.ts‎

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,7 @@ describe("OpenRouter Agent Plugin", () => {
430430
inputTokens: 10,
431431
outputTokens: 4,
432432
totalTokens: 14,
433+
cost: 0.01,
433434
},
434435
};
435436
const finalResponse = {
@@ -447,6 +448,7 @@ describe("OpenRouter Agent Plugin", () => {
447448
inputTokens: 12,
448449
outputTokens: 3,
449450
totalTokens: 15,
451+
cost: 0.02,
450452
},
451453
};
452454
const request = {
@@ -544,11 +546,10 @@ describe("OpenRouter Agent Plugin", () => {
544546
turn_count: 2,
545547
});
546548
expect(callModelSpan?.output).toMatchObject(finalResponse.output);
547-
expect(callModelSpan?.metrics).toMatchObject({
548-
prompt_tokens: 22,
549-
completion_tokens: 7,
550-
tokens: 29,
551-
});
549+
expect(callModelSpan?.metrics?.prompt_tokens).toBeUndefined();
550+
expect(callModelSpan?.metrics?.completion_tokens).toBeUndefined();
551+
expect(callModelSpan?.metrics?.tokens).toBeUndefined();
552+
expect(callModelSpan?.metrics?.cost).toBeUndefined();
552553

553554
expect(turnSpans).toHaveLength(2);
554555
expect(turnSpans[0]?.metadata).toMatchObject({
@@ -559,6 +560,12 @@ describe("OpenRouter Agent Plugin", () => {
559560
step: 1,
560561
step_type: "initial",
561562
});
563+
expect(turnSpans[0]?.metrics).toMatchObject({
564+
prompt_tokens: 10,
565+
completion_tokens: 4,
566+
tokens: 14,
567+
cost: 0.01,
568+
});
562569
expect(turnSpans[1]?.metadata).toMatchObject({
563570
provider: TEST_PROVIDER,
564571
model: TEST_MODEL,
@@ -567,6 +574,24 @@ describe("OpenRouter Agent Plugin", () => {
567574
step: 2,
568575
step_type: "continue",
569576
});
577+
expect(turnSpans[1]?.metrics).toMatchObject({
578+
prompt_tokens: 12,
579+
completion_tokens: 3,
580+
tokens: 15,
581+
cost: 0.02,
582+
});
583+
expect(
584+
spans.reduce(
585+
(sum: number, span: any) => sum + (span.metrics?.prompt_tokens ?? 0),
586+
0,
587+
),
588+
).toBe(22);
589+
expect(
590+
spans.reduce(
591+
(sum: number, span: any) => sum + (span.metrics?.cost ?? 0),
592+
0,
593+
),
594+
).toBeCloseTo(0.03);
570595

571596
expect(toolSpan?.span_attributes).toMatchObject({
572597
name: "lookup_weather",

‎js/src/instrumentation/plugins/openrouter-agent-plugin.ts‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -920,10 +920,16 @@ function patchOpenRouterCallModelResult(args: {
920920
finalResponse,
921921
rounds.length + 1,
922922
);
923+
const metrics =
924+
tracedTurnCount === 0
925+
? aggregateOpenRouterCallModelMetrics(rounds, finalResponse)
926+
: undefined;
923927
span.log({
924928
output: extractOpenRouterResponseOutput(finalResponse, fallbackOutput),
925929
...(metadata ? { metadata } : {}),
926-
metrics: aggregateOpenRouterCallModelMetrics(rounds, finalResponse),
930+
// Child turn spans already carry per-response usage. Duplicating those
931+
// metrics on the parent makes trace-level token/cost totals double count.
932+
...(metrics && Object.keys(metrics).length > 0 ? { metrics } : {}),
927933
});
928934
span.end();
929935
return;

‎js/src/instrumentation/plugins/openrouter-plugin.test.ts‎

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,7 @@ describe("OpenRouter Plugin", () => {
432432
inputTokens: 10,
433433
outputTokens: 4,
434434
totalTokens: 14,
435+
cost: 0.01,
435436
},
436437
};
437438
const finalResponse = {
@@ -449,6 +450,7 @@ describe("OpenRouter Plugin", () => {
449450
inputTokens: 12,
450451
outputTokens: 3,
451452
totalTokens: 15,
453+
cost: 0.02,
452454
},
453455
};
454456
const request = {
@@ -543,11 +545,10 @@ describe("OpenRouter Plugin", () => {
543545
turn_count: 2,
544546
});
545547
expect(callModelSpan?.output).toMatchObject(finalResponse.output);
546-
expect(callModelSpan?.metrics).toMatchObject({
547-
prompt_tokens: 22,
548-
completion_tokens: 7,
549-
tokens: 29,
550-
});
548+
expect(callModelSpan?.metrics?.prompt_tokens).toBeUndefined();
549+
expect(callModelSpan?.metrics?.completion_tokens).toBeUndefined();
550+
expect(callModelSpan?.metrics?.tokens).toBeUndefined();
551+
expect(callModelSpan?.metrics?.cost).toBeUndefined();
551552

552553
expect(turnSpans).toHaveLength(2);
553554
expect(turnSpans[0]?.metadata).toMatchObject({
@@ -558,6 +559,12 @@ describe("OpenRouter Plugin", () => {
558559
step: 1,
559560
step_type: "initial",
560561
});
562+
expect(turnSpans[0]?.metrics).toMatchObject({
563+
prompt_tokens: 10,
564+
completion_tokens: 4,
565+
tokens: 14,
566+
cost: 0.01,
567+
});
561568
expect(turnSpans[1]?.metadata).toMatchObject({
562569
provider: TEST_PROVIDER,
563570
model: TEST_MODEL,
@@ -566,6 +573,24 @@ describe("OpenRouter Plugin", () => {
566573
step: 2,
567574
step_type: "continue",
568575
});
576+
expect(turnSpans[1]?.metrics).toMatchObject({
577+
prompt_tokens: 12,
578+
completion_tokens: 3,
579+
tokens: 15,
580+
cost: 0.02,
581+
});
582+
expect(
583+
spans.reduce(
584+
(sum: number, span: any) => sum + (span.metrics?.prompt_tokens ?? 0),
585+
0,
586+
),
587+
).toBe(22);
588+
expect(
589+
spans.reduce(
590+
(sum: number, span: any) => sum + (span.metrics?.cost ?? 0),
591+
0,
592+
),
593+
).toBeCloseTo(0.03);
569594

570595
expect(toolSpan?.span_attributes).toMatchObject({
571596
name: "lookup_weather",

‎js/src/instrumentation/plugins/openrouter-plugin.ts‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1102,10 +1102,16 @@ function patchOpenRouterCallModelResult(args: {
11021102
finalResponse,
11031103
rounds.length + 1,
11041104
);
1105+
const metrics =
1106+
tracedTurnCount === 0
1107+
? aggregateOpenRouterCallModelMetrics(rounds, finalResponse)
1108+
: undefined;
11051109
span.log({
11061110
output: extractOpenRouterResponseOutput(finalResponse, fallbackOutput),
11071111
...(metadata ? { metadata } : {}),
1108-
metrics: aggregateOpenRouterCallModelMetrics(rounds, finalResponse),
1112+
// Child turn spans already carry per-response usage. Duplicating those
1113+
// metrics on the parent makes trace-level token/cost totals double count.
1114+
...(metrics && Object.keys(metrics).length > 0 ? { metrics } : {}),
11091115
});
11101116
span.end();
11111117
return;

0 commit comments

Comments
 (0)