-
Notifications
You must be signed in to change notification settings - Fork 50
feat: add usage accounting support to OpenRouter #64
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- Updated README.md to include usage accounting use cases and examples. - Enhanced OpenRouterChatLanguageModel to support usage accounting settings and metadata extraction. - Implemented detailed usage information tracking in responses. - Added tests for usage accounting functionality, ensuring correct metadata inclusion based on settings. Tested: - Verified that usage accounting data is included in responses when enabled. - Confirmed that provider-specific metadata is omitted when usage accounting is disabled.
I probably need to create a PR to vercel/ai too to make us able to pass the detailed usage to the user. UPDATE: |
- Moved `OpenRouterSharedSettings` type definition to the correct location in `src/types.ts`. - Added `extraBody` property to `OpenRouterSharedSettings`. - Marked `includeReasoning` as deprecated, suggesting to use `reasoning` instead. Tested: - No functional changes, but type definitions are now correctly structured.
If you guys want to test this feature before it's officially merged, replace your deps to be like this: "@openrouter/ai-sdk-provider": "github:ImBIOS/OpenRouterTeam-ai-sdk-provider#feat/usage-accounting" When using a Git repository as a dependency with pnpm, you'll need to regularly update to get the latest changes from the branch. Here are your options:
Note that for production use, it's better to use specific commits or version tags rather than branches to ensure reproducible builds. |
This particular guide/comment/document/tutorial is generated by claude-3.7-sonnet-thinking; it might not be syntactically correct, and I don't see the need to invest time to review it, but the goal is achieved, so that's what's important. PLEASE READ THIS PARTICULAR COMMENT WITH A GRAIN OF SALT! OpenRouter Usage AccountingThis document explains how to use the OpenRouter usage accounting feature with the AI SDK provider. This feature allows you to track token usage details directly in your API responses, without making additional API calls. Enabling Usage AccountingYou can enable usage accounting at different levels: 1. When creating the provider instanceimport { createOpenRouter } from '@openrouter/ai-sdk-provider';
const openrouter = createOpenRouter({
apiKey: 'your-api-key',
usage: { include: true }, // Enable usage accounting for all models
}); 2. For a specific modelimport { createOpenRouter } from '@openrouter/ai-sdk-provider';
const openrouter = createOpenRouter({
apiKey: 'your-api-key',
});
// Enable usage accounting just for this model
const model = openrouter.chat('anthropic/claude-3-sonnet', {
usage: { include: true },
}); 3. For a specific request using providerOptionsimport { createOpenRouter } from '@openrouter/ai-sdk-provider';
import { generateText } from 'ai';
const openrouter = createOpenRouter({
apiKey: 'your-api-key',
});
const model = openrouter('anthropic/claude-3-sonnet');
const result = await generateText({
model,
prompt: 'Hello, how are you?',
providerOptions: {
openrouter: {
usage: { include: true },
},
},
}); Accessing Usage InformationWhen usage accounting is enabled, the provider will include detailed usage information in the response. For non-streaming responsesconst response = await model.doGenerate({
inputFormat: 'messages',
mode: { type: 'regular' },
prompt: [
{ role: 'user', content: [{ type: 'text', text: 'Hello!' }] },
],
});
// Basic usage information (available with or without usage accounting)
console.log('Prompt tokens:', response.usage.promptTokens);
console.log('Completion tokens:', response.usage.completionTokens);
// Detailed usage information (only with usage accounting enabled)
if (response.providerMetadata?.openrouter?.usage) {
const usage = response.providerMetadata.openrouter.usage;
console.log('Total tokens:', usage.totalTokens);
console.log('Cost in USD:', usage.cost);
if (usage.promptTokensDetails) {
console.log('Cached tokens:', usage.promptTokensDetails.cachedTokens);
}
if (usage.completionTokensDetails) {
console.log('Reasoning tokens:', usage.completionTokensDetails.reasoningTokens);
}
} For streaming responsesWhen streaming, the usage information is included in the finish event: const { stream } = await model.doStream({
inputFormat: 'messages',
mode: { type: 'regular' },
prompt: [
{ role: 'user', content: [{ type: 'text', text: 'Hello!' }] },
],
});
for await (const chunk of stream) {
if (chunk.type === 'finish') {
// Basic usage information
console.log('Prompt tokens:', chunk.usage.promptTokens);
console.log('Completion tokens:', chunk.usage.completionTokens);
// Detailed usage information
if (chunk.providerMetadata?.openrouter?.usage) {
const usage = chunk.providerMetadata.openrouter.usage;
console.log('Total tokens:', usage.totalTokens);
console.log('Cost in USD:', usage.cost);
if (usage.promptTokensDetails) {
console.log('Cached tokens:', usage.promptTokensDetails.cachedTokens);
}
if (usage.completionTokensDetails) {
console.log('Reasoning tokens:', usage.completionTokensDetails.reasoningTokens);
}
}
}
} Usage Information FieldsThe following fields are available in the usage information:
Benefits of Usage Accounting
Performance ConsiderationsEnabling usage accounting does not impact performance significantly, as OpenRouter already tracks this information internally. The only difference is that it's included in the API response. Further ResourcesFor more information about OpenRouter usage accounting, visit the OpenRouter documentation. |
- Added support for OpenRouter usage accounting feature, enabling tracking of token usage details in API responses. - Updated schema to accommodate the new usage accounting response format. - Documented changes in CHANGELOG.md. Tested: - Verified that usage accounting data is correctly included in both streaming and non-streaming API responses.
- Introduced `openRouterUsageAccountingSchema` to centralize usage accounting details. - Updated `OpenRouterChatCompletionBaseResponseSchema` to utilize the new schema for cleaner code. - Added TypeScript type inference for `OpenRouterUsageAccounting` in `src/types.ts`. Tested: - Ensured that the new schema correctly captures token usage details in API responses.
- Changed package version from 0.5.0 to 0.5.0-canary.1 to indicate a pre-release. - Updated CHANGELOG.md to reflect the version as [Unreleased] for upcoming changes. No functional changes; this prepares the package for the next development phase.
To the maintainer, feel free to take over or edit, or merge this PR, since I might not be asynchronously responsive due to priority. |
… schema - Bumped package version to 0.5.0-canary.2. - Refactored `OpenRouterChatCompletionBaseResponseSchema` to directly define the usage accounting schema inline, improving clarity and maintainability. - Updated `OpenRouterUsageAccounting` type definition to match the new schema structure. No functional changes; this prepares the package for further development.
Thanks for the PR @ImBIOS :) |
Thanks for putting this together @ImBIOS — I was looking for a solution to this! However: I am having a tough time getting this PR to work on streaming responses. I'm using Vercel AI SDK's |
@abromberg try this onFinish: async ({ response, providerMetadata, usage }) => {
logUsageAccounting({
providerMetadata,
usage,
}); /**
* Logs the usage accounting information for the given provider metadata and usage.
* @param options - The options.
* @param options.providerMetadata - The provider metadata.
* @param options.usage - The usage.
*/
export const logUsageAccounting = ({
providerMetadata,
usage,
}: {
providerMetadata: ProviderMetadata | undefined;
usage: LanguageModelUsage;
}) => {
// Basic usage information
console.log('Prompt tokens:', usage.promptTokens);
console.log('Completion tokens:', usage.completionTokens);
// Detailed usage information
if (providerMetadata?.openrouter?.usage) {
const usage = providerMetadata.openrouter
.usage as OpenRouterUsageAccounting;
console.log('Total tokens:', usage.totalTokens);
console.log('Cost in USD:', usage.cost);
if (usage.promptTokensDetails) {
console.log('Cached tokens:', usage.promptTokensDetails.cachedTokens);
}
if (usage.completionTokensDetails) {
console.log(
'Reasoning tokens:',
usage.completionTokensDetails.reasoningTokens,
);
}
} else {
console.log('No usage information available');
}
}; |
@ImBIOS that code you just shared is working for you on const result = streamText({
model: modelInstance,
system: systemPrompt,
messages,
tools,
onError: err => {
console.error("streamText error:", err);
},
async onFinish({ response, providerMetadata, usage }) {
try {
console.log("providerMetadata", providerMetadata);
console.log("usage", usage);
await saveFinalAssistantMessage(
supabase,
chatId,
response.messages,
modelId,
selectedUserSystemPromptId,
providerMetadata?.openrouter?.usage || usage
); |
BTW will remove CHANGELOG in favor of using github release: https://github.com/OpenRouterTeam/ai-sdk-provider/releases It automatically tracks the PRs will be closer to the npm deployment. |
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
@abromberg I added an e2e test to the repo -- it works, you just need to add the flag: const model = openrouter('anthropic/claude-3.7-sonnet:thinking', {
usage: {
include: true,
},
}); |
Cooked the PR to add e2e tests + biome |
Close #56
Tested
Added
Changed