openRouterDecider drops usage.cost, and decide() meta cannot surface the Decisions response id/provider
@AlemTuzlak is already working on this.
Since Sep 23, 2026.
Assessment
This issue has not been assessed yet.
Description
Summary
openRouterDecider / createOpenRouterDecider (@tanstack/ai-openrouter, used with decide() from @tanstack/ai) drops data the OpenRouter Decisions API returns:
usage.costis never attached toresult.meta.usage, even though the endpoint returns it on every response and the same package already hasextractUsageCost()which the text/image adapters use.- The response's
id(OpenRouter generation id) andproviderare not surfaced anywhere, becauseEvaluateAdapterResult/EvaluateResultMetaonly carrymodel,answersandusage.
I'm billing per request off usage.cost and reconciling with GET /api/v1/generation?id=, so both are needed.
Environment
@tanstack/ai@0.58.0@tanstack/ai-openrouter@0.19.16@tanstack/ai-event-client@0.12.1- Node v24.20.0, server-side (no framework)
Reproduction
import { decide, boolean } from '@tanstack/ai'
import { openRouterDecider } from '@tanstack/ai-openrouter'
const result = await decide({
adapter: openRouterDecider('typesafe/jev-1.13'),
state: 'Customer says the invoice total is wrong after applying a coupon.',
questions: {
isBug: boolean({ instructions: 'Is this a software bug report?' }),
},
})
console.log(result.meta.usage)
// { promptTokens: 30, completionTokens: 3, totalTokens: 33 }
// no `cost`, although the raw response body had usage.cost
console.log(result.meta)
// { model: 'typesafe/jev-1.13-20260917', usage: { ... } }
// no `id`, no `provider`
The raw POST https://openrouter.ai/api/alpha/decisions response (from OpenRouter's API reference) is:
{
"answers": { "...": "..." },
"id": "gen-dec-1789738314-X5e5eKGQdvR9rblyX250",
"model": "typesafe/jev-1.13-20260917",
"provider": "TypeSafe",
"usage": { "cost": 0.000019992, "input_tokens": 476, "output_tokens": 70 }
}
Expected
result.meta.usage.costis0.000019992(andcostDetailswhen present), the same aschat()/generateImage()with the OpenRouter adapters.TokenUsagealready declarescost?: numberandcostDetails?: UsageCostBreakdown(@tanstack/ai-event-clientindex.d.ts,TokenUsage).result.metaexposes the response'sidandprovider(or aproviderMetadatabag) so the request can be reconciled againstGET /api/v1/generation?id=.
Actual
result.meta.usagehas onlypromptTokens/completionTokens/totalTokens;costisundefined.result.metahas onlymodelandusage;idandproviderare unreachable fromdecide()without writing a custom adapter.
Where it happens
@tanstack/ai-openrouter/dist/esm/adapters/evaluate.js, mapUsage() (lines 51–65) reads only the token keys and calls buildBaseUsage with them:
function mapUsage(usage) {
if (!isRecord(usage)) return buildBaseUsage({ promptTokens: 0, completionTokens: 0, totalTokens: 0 });
const promptTokens = readNumber(usage, ["input_tokens", "prompt_tokens"]) ?? 0;
const completionTokens = readNumber(usage, ["output_tokens", "completion_tokens"]) ?? 0;
const totalTokens = readNumber(usage, ["total_tokens"]) ?? promptTokens + completionTokens;
return buildBaseUsage({ promptTokens, completionTokens, totalTokens });
}
and evaluate() (lines 104–108) returns only model / answers / usage:
return {
model: json.model ?? model,
answers: json.answers,
usage: mapUsage(json.usage)
};
extractUsageCost() lives in the same package (adapters/cost.js, exported with cost.d.ts) and is already spread into usage in adapters/text.js (lines 155, 386, 735), adapters/responses-text.js (133, 439, 1083) and adapters/image.js (121). evaluate.js does not import it.
On the core side, @tanstack/ai/dist/esm/activities/evaluate/adapter.d.ts lines 103–108:
export interface EvaluateAdapterResult {
/** Resolved model id from the provider. */
model: string;
answers: Record<string, WireAnswer>;
usage: TokenUsage;
}
and activities/evaluate/index.d.ts lines 50–54:
export interface EvaluateResultMeta {
/** Resolved model id from the provider. */
model: string;
usage: TokenUsage;
}
so even a custom adapter has no typed place to put id / provider.
Suggested fix
-
@tanstack/ai-openrouteradapters/evaluate.ts: spread the existing helper into the mapped usage, matching the other adapters:import { extractUsageCost } from './cost' return { ...buildBaseUsage({ promptTokens, completionTokens, totalTokens }), ...extractUsageCost(usage), } -
@tanstack/aiactivities/evaluate: add optionalid?: stringandprovider?: string(orproviderMetadata?: Record<string, unknown>) toEvaluateAdapterResultand pass them through toEvaluateResultMetaindecide(). The OpenRouter adapter would then returnid: json.id, provider: json.provider.
Both are additive; existing adapters and callers keep working.
I'm happy to open a PR for either or both if that's welcome.
- Dominant language
- TypeScript
- Stars
- 3.1k
- Forks
- 331
- Avg merge
- 1d 10h
- Merged PRs (30d)
- 130
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from TanStack/ai
-
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
-
has-pr waiting-on: maintainer
-
waiting-on: maintainer
-
waiting-on: maintainer
-
has-pr waiting-on: maintainer
Difficulty 3/5 1-2 days Newbie friendliness 70/100
Similar issues
-
bug(cli): hapi doctor inline-media prints a fabricated B:\ helper-script path in packaged installs Open
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
-
Crush Open
Difficulty 1/5 Under an hour Newbie friendliness 85/100
catppuccin/catppuccin#3125 ·
-
Add a SECURITY.md Open
Difficulty 1/5 Under an hour Newbie friendliness 90/100
ElementsProject/cln-application#167 · 1 comment · 1 reaction ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
Quantco/pnpm-licenses#17 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 75/100