Loading workspace insights... Statistics interval
7 days30 daysLatest CI Pipeline Executions
c8d9cad8 feat(ai-openrouter): surface per-request cost on RUN_FINISHED (#654)
* feat(openrouter): surface per-request cost on RUN_FINISHED
OpenRouter reports the actual cost of each request inline on the chat
response. Forward it on the terminal RUN_FINISHED event as usage.cost,
with OpenRouter's per-request breakdown under usage.costDetails. This is
the cost OpenRouter itself reports, so it accounts for routing, fallback
providers, BYOK, and cached-token pricing rather than being computed from
token counts.
Both the Chat Completions (openRouterText) and Responses
(openRouterResponsesText) adapters populate it. A shared UsageTotals type
in @tanstack/ai carries the optional cost/costDetails fields, so they're
also available on the middleware onUsage and onFinish hooks. Cost-detail
keys are normalized to camelCase so the SDK-parsed and raw fallback paths
stay consistent. The fields are optional and additive; adapters that
don't report cost are unaffected.
* test(e2e): drain request body in OpenRouter cost mount
Match the other aimock mounts (drainBody) so the keep-alive socket has no
unread request bytes before the SSE response is written.
* refactor(ai): close UsageCostDetails to known breakdown fields
Replace the open `Record<string, number | null | undefined>` shape on
`UsageTotals.costDetails` with a typed `UsageCostDetails` interface
enumerating the five fields OpenRouter actually reports across its Chat
Completions and Responses endpoints. Consumers get autocomplete on the
breakdown and a typo on a key becomes a compile error; unknown keys are
dropped at extraction time so the public surface stays closed.
The extractor swaps generic `toCamelCase` for a snake↔camel allowlist
keyed on the known fields, and treats `null` as absent rather than
forwarding it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: bump @tanstack/ai as patch, not minor
The @tanstack/ai changes are additive type plumbing only (new exported
UsageTotals/UsageCostDetails interfaces, optional fields on existing
shapes) — no runtime change, no new callable surface. The feature lives
in @tanstack/ai-openrouter.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(ai): normalize cost breakdown onto a canonical provider-neutral shape
UsageCostBreakdown becomes three concrete fields (upstreamCost,
upstreamInputCost, upstreamOutputCost) that every adapter maps its
provider-specific wire keys onto at extraction time. Consumer code reads
the same fields regardless of which gateway populated them, so swapping
adapters is a one-line change with no consumer rewrites.
The OpenRouter adapter collapses its two endpoint naming styles
(Chat Completions' prompt/completions and Responses' input/output) onto
the same canonical input/output split — they bill against the same tokens.
Replaces the prior declaration-merging approach, which leaked OpenRouter
vocabulary into every consumer site that read costDetails.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ci: apply automated fixes 980ff9ba feat(ai-openrouter): surface per-request cost on RUN_FINISHED (#654)
* feat(openrouter): surface per-request cost on RUN_FINISHED
OpenRouter reports the actual cost of each request inline on the chat
response. Forward it on the terminal RUN_FINISHED event as usage.cost,
with OpenRouter's per-request breakdown under usage.costDetails. This is
the cost OpenRouter itself reports, so it accounts for routing, fallback
providers, BYOK, and cached-token pricing rather than being computed from
token counts.
Both the Chat Completions (openRouterText) and Responses
(openRouterResponsesText) adapters populate it. A shared UsageTotals type
in @tanstack/ai carries the optional cost/costDetails fields, so they're
also available on the middleware onUsage and onFinish hooks. Cost-detail
keys are normalized to camelCase so the SDK-parsed and raw fallback paths
stay consistent. The fields are optional and additive; adapters that
don't report cost are unaffected.
* test(e2e): drain request body in OpenRouter cost mount
Match the other aimock mounts (drainBody) so the keep-alive socket has no
unread request bytes before the SSE response is written.
* refactor(ai): close UsageCostDetails to known breakdown fields
Replace the open `Record<string, number | null | undefined>` shape on
`UsageTotals.costDetails` with a typed `UsageCostDetails` interface
enumerating the five fields OpenRouter actually reports across its Chat
Completions and Responses endpoints. Consumers get autocomplete on the
breakdown and a typo on a key becomes a compile error; unknown keys are
dropped at extraction time so the public surface stays closed.
The extractor swaps generic `toCamelCase` for a snake↔camel allowlist
keyed on the known fields, and treats `null` as absent rather than
forwarding it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: bump @tanstack/ai as patch, not minor
The @tanstack/ai changes are additive type plumbing only (new exported
UsageTotals/UsageCostDetails interfaces, optional fields on existing
shapes) — no runtime change, no new callable surface. The feature lives
in @tanstack/ai-openrouter.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(ai): normalize cost breakdown onto a canonical provider-neutral shape
UsageCostBreakdown becomes three concrete fields (upstreamCost,
upstreamInputCost, upstreamOutputCost) that every adapter maps its
provider-specific wire keys onto at extraction time. Consumer code reads
the same fields regardless of which gateway populated them, so swapping
adapters is a one-line change with no consumer rewrites.
The OpenRouter adapter collapses its two endpoint naming styles
(Chat Completions' prompt/completions and Responses' input/output) onto
the same canonical input/output split — they bill against the same tokens.
Replaces the prior declaration-merging approach, which leaked OpenRouter
vocabulary into every consumer site that read costDetails.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ci: apply automated fixes 980ff9ba feat(ai-openrouter): surface per-request cost on RUN_FINISHED (#654)
* feat(openrouter): surface per-request cost on RUN_FINISHED
OpenRouter reports the actual cost of each request inline on the chat
response. Forward it on the terminal RUN_FINISHED event as usage.cost,
with OpenRouter's per-request breakdown under usage.costDetails. This is
the cost OpenRouter itself reports, so it accounts for routing, fallback
providers, BYOK, and cached-token pricing rather than being computed from
token counts.
Both the Chat Completions (openRouterText) and Responses
(openRouterResponsesText) adapters populate it. A shared UsageTotals type
in @tanstack/ai carries the optional cost/costDetails fields, so they're
also available on the middleware onUsage and onFinish hooks. Cost-detail
keys are normalized to camelCase so the SDK-parsed and raw fallback paths
stay consistent. The fields are optional and additive; adapters that
don't report cost are unaffected.
* test(e2e): drain request body in OpenRouter cost mount
Match the other aimock mounts (drainBody) so the keep-alive socket has no
unread request bytes before the SSE response is written.
* refactor(ai): close UsageCostDetails to known breakdown fields
Replace the open `Record<string, number | null | undefined>` shape on
`UsageTotals.costDetails` with a typed `UsageCostDetails` interface
enumerating the five fields OpenRouter actually reports across its Chat
Completions and Responses endpoints. Consumers get autocomplete on the
breakdown and a typo on a key becomes a compile error; unknown keys are
dropped at extraction time so the public surface stays closed.
The extractor swaps generic `toCamelCase` for a snake↔camel allowlist
keyed on the known fields, and treats `null` as absent rather than
forwarding it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: bump @tanstack/ai as patch, not minor
The @tanstack/ai changes are additive type plumbing only (new exported
UsageTotals/UsageCostDetails interfaces, optional fields on existing
shapes) — no runtime change, no new callable surface. The feature lives
in @tanstack/ai-openrouter.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(ai): normalize cost breakdown onto a canonical provider-neutral shape
UsageCostBreakdown becomes three concrete fields (upstreamCost,
upstreamInputCost, upstreamOutputCost) that every adapter maps its
provider-specific wire keys onto at extraction time. Consumer code reads
the same fields regardless of which gateway populated them, so swapping
adapters is a one-line change with no consumer rewrites.
The OpenRouter adapter collapses its two endpoint naming styles
(Chat Completions' prompt/completions and Responses' input/output) onto
the same canonical input/output split — they bill against the same tokens.
Replaces the prior declaration-merging approach, which leaked OpenRouter
vocabulary into every consumer site that read costDetails.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ci: apply automated fixes