How the Open-SEO Onboarding Chat Agent Meters OpenRouter LLM Costs
The onboarding chat agent meters OpenRouter LLM costs by configuring the model to return detailed usage metadata, extracting the USD cost from each streaming step's provider metadata, and debiting the total against the organization's onboarding credit balance.
The every-app/open-seo repository implements a precise, server-side mechanism for real-time cost tracking of OpenRouter LLM usage within its onboarding flow. By intercepting provider-specific metadata during the streaming response lifecycle, the system accurately calculates monetary costs and manages credit consumption for every conversation turn.
Enabling OpenRouter Usage Accounting
Cost metering begins at the model initialization layer. In src/server/lib/openrouter.ts, the getChatAgentModel() function constructs the language model with the usage: { include: true } option passed to createOpenRouter. This flag instructs the OpenRouter provider to return detailed usage statistics, including the USD cost for each request, within the response metadata.
When the onboarding chat agent initializes its model, this configuration ensures that every subsequent LLM call includes the necessary financial data for accurate metering.
Capturing Provider Metadata During Streaming
The actual cost extraction occurs within the streaming response handler. In src/server/features/onboarding/OnboardingChatAgent.ts, the agent processes user messages using the streamText function from the AI SDK. The implementation leverages the onFinish callback, which receives an event object containing a steps array.
Each element in event.steps includes providerMetadata that contains the OpenRouter-specific usage details. By accessing this array after the stream completes, the system gains access to the raw cost data for every generated chunk in the conversation turn.
Extracting USD Costs with Schema Validation
Raw provider metadata requires safe extraction and validation. The openRouterCostUsd() function in src/server/lib/chatAgent.ts handles this parsing using a Zod schema that strictly types the expected structure:
import { z } from 'zod';
const openRouterUsageSchema = z.object({
openrouter: z.object({
usage: z.object({
cost: z.number()
})
}),
});
export function openRouterCostUsd(providerMetadata: unknown): number {
const parsed = openRouterUsageSchema.safeParse(providerMetadata);
return parsed.success ? parsed.data.openrouter.usage.cost : 0;
}
This helper safely extracts the numeric cost value, returning 0 if the metadata structure is unexpected or missing, preventing runtime crashes while maintaining accounting accuracy.
Debiting Onboarding Credits in Real Time
Once the stream finishes, the agent calculates the total cost and records the usage. Inside the onFinish callback in OnboardingChatAgent.ts, the system reduces the steps array to sum all individual costs:
const costUsd = event.steps.reduce(
(sum, step) => sum + openRouterCostUsd(step.providerMetadata),
0,
);
The aggregated costUsd value is then passed to trackUsageCreditSpend(), which deducts the amount from the organization's onboarding credit balance. This function records the transaction with the billing customer ID, marks the feature as 'onboarding', and includes the provider name for audit trails:
await trackUsageCreditSpend({
customer: billingCustomer,
customerId: creditCustomerId,
creditFeature: 'onboarding',
costUsd,
monthlyRemaining: monthlyCreditsRemaining,
properties: { provider: 'openrouter' },
});
This three-phase approach—configuration, extraction, and recording—ensures that every OpenRouter LLM interaction is accurately metered and charged against the project's allocated credits.
Summary
- Model Configuration: The
getChatAgentModel()function insrc/server/lib/openrouter.tsenables cost reporting by settingusage: { include: true }when creating the OpenRouter client. - Metadata Capture: The
onFinishcallback inOnboardingChatAgent.tsaccessesevent.stepsto retrieve per-step provider metadata containing cost information. - Safe Parsing: The
openRouterCostUsd()helper insrc/server/lib/chatAgent.tsuses Zod schema validation to safely extract numeric costs fromproviderMetadata.openrouter.usage.cost. - Credit Deduction: The
trackUsageCreditSpend()function debits the summed USD amount from the organization's onboarding credit pool, enabling real-time budget enforcement.
Frequently Asked Questions
How does the system handle missing or malformed cost metadata?
If the OpenRouter provider metadata is missing or structurally unexpected, the openRouterCostUsd() function returns 0 as a safe default. This prevents runtime errors while ensuring that unparseable responses do not incorrectly consume credits, maintaining the integrity of the billing system.
Where is the OpenRouter model configured to include usage data?
The configuration occurs in src/server/lib/openrouter.ts within the getChatAgentModel() function. By passing usage: { include: true } to the createOpenRouter factory, the system instructs the provider to return detailed usage statistics, including the USD cost, with every response.
What happens if multiple steps occur in a single streaming response?
The onFinish callback receives an event.steps array containing metadata for every step in the generation process. The cost calculation uses Array.reduce() to sum the costs from all steps via openRouterCostUsd(), ensuring the total charge reflects the complete token usage across the entire conversation turn.
Can this metering system track costs for providers other than OpenRouter?
While the current implementation specifically targets OpenRouter through the openRouterCostUsd() helper and the provider: 'openrouter' property in trackUsageCreditSpend(), the architecture supports extension. The generic providerMetadata structure in the AI SDK allows for similar extraction patterns to be implemented for other LLM providers by adding corresponding validation schemas and cost extraction logic.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →