-
-
Notifications
You must be signed in to change notification settings - Fork 116
feat: improve usage #242
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
base: main
Are you sure you want to change the base?
feat: improve usage #242
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| --- | ||
| '@tanstack/ai-openrouter': minor | ||
| '@tanstack/ai-anthropic': minor | ||
| '@tanstack/ai-gemini': minor | ||
| '@tanstack/ai-ollama': minor | ||
| '@tanstack/ai-openai': minor | ||
| '@tanstack/ai-grok': minor | ||
| '@tanstack/ai': minor | ||
| '@tanstack/ai-devtools-core': patch | ||
| --- | ||
|
|
||
| Enhanced usage reporting for every provider |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| import { BaseSummarizeAdapter } from '@tanstack/ai/adapters' | ||
| import { buildAnthropicUsage } from '../usage' | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. π§© Analysis chainπ Script executed: #!/bin/bash
# First, explore the repository structure
find packages/typescript/ai-anthropic -type f -name "*.ts" | head -20
# Check buildAnthropicUsage implementation
echo "=== buildAnthropicUsage implementation ==="
cat -n packages/typescript/ai-anthropic/src/usage.ts
# Check imports and usage in summarize adapter
echo "=== Summarize adapter streaming handling ==="
cat -n packages/typescript/ai-anthropic/src/adapters/summarize.ts | head -150Repository: TanStack/ai Length of output: 166 π Script executed: #!/bin/bash
# Inspect buildAnthropicUsage signature and implementation
cat -n packages/typescript/ai-anthropic/src/usage.ts
# Check streaming event handling in summarize adapter
echo "=== Summarize adapter ==="
cat -n packages/typescript/ai-anthropic/src/adapters/summarize.ts
# Check streaming event handling in text adapter
echo "=== Text adapter ==="
cat -n packages/typescript/ai-anthropic/src/adapters/text.ts
# Look for message_start and message_delta handling
echo "=== Searching for streaming event handling ==="
rg -n "message_start|message_delta" packages/typescript/ai-anthropic/src/Repository: TanStack/ai Length of output: 33848 Streaming usage under-reports prompt tokens; must capture and merge In streaming responses, Extract and cache usage data from π€ Prompt for AI Agents |
||
| import { | ||
| createAnthropicClient, | ||
| generateId, | ||
|
|
@@ -52,7 +53,7 @@ export class AnthropicSummarizeAdapter< | |
| async summarize(options: SummarizationOptions): Promise<SummarizationResult> { | ||
| const systemPrompt = this.buildSummarizationPrompt(options) | ||
|
|
||
| const response = await this.client.messages.create({ | ||
| const response = await this.client.beta.messages.create({ | ||
| model: options.model, | ||
| messages: [{ role: 'user', content: options.text }], | ||
| system: systemPrompt, | ||
|
|
@@ -69,11 +70,7 @@ export class AnthropicSummarizeAdapter< | |
| id: response.id, | ||
| model: response.model, | ||
| summary: content, | ||
| usage: { | ||
| promptTokens: response.usage.input_tokens, | ||
| completionTokens: response.usage.output_tokens, | ||
| totalTokens: response.usage.input_tokens + response.usage.output_tokens, | ||
| }, | ||
| usage: buildAnthropicUsage(response.usage), | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -84,10 +81,8 @@ export class AnthropicSummarizeAdapter< | |
| const id = generateId(this.name) | ||
| const model = options.model | ||
| let accumulatedContent = '' | ||
| let inputTokens = 0 | ||
| let outputTokens = 0 | ||
|
|
||
| const stream = await this.client.messages.create({ | ||
| const stream = await this.client.beta.messages.create({ | ||
| model: options.model, | ||
| messages: [{ role: 'user', content: options.text }], | ||
| system: systemPrompt, | ||
|
|
@@ -98,7 +93,6 @@ export class AnthropicSummarizeAdapter< | |
|
|
||
| for await (const event of stream) { | ||
| if (event.type === 'message_start') { | ||
| inputTokens = event.message.usage.input_tokens | ||
| } else if (event.type === 'content_block_delta') { | ||
| if (event.delta.type === 'text_delta') { | ||
| const delta = event.delta.text | ||
|
|
@@ -114,7 +108,6 @@ export class AnthropicSummarizeAdapter< | |
| } | ||
| } | ||
| } else if (event.type === 'message_delta') { | ||
| outputTokens = event.usage.output_tokens | ||
| yield { | ||
| type: 'done', | ||
| id, | ||
|
|
@@ -125,11 +118,7 @@ export class AnthropicSummarizeAdapter< | |
| | 'length' | ||
| | 'content_filter' | ||
| | null, | ||
| usage: { | ||
| promptTokens: inputTokens, | ||
| completionTokens: outputTokens, | ||
| totalTokens: inputTokens + outputTokens, | ||
| }, | ||
| usage: buildAnthropicUsage(event.usage), | ||
| } | ||
| } | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| import { buildBaseUsage } from '@tanstack/ai' | ||
| import type { TokenUsage } from '@tanstack/ai' | ||
| import type Anthropic_SDK from '@anthropic-ai/sdk' | ||
|
|
||
| /** | ||
| * Anthropic-specific provider usage details. | ||
| * These fields are unique to Anthropic and placed in providerUsageDetails. | ||
| */ | ||
| export interface AnthropicProviderUsageDetails { | ||
| /** | ||
| * Server-side tool usage metrics. | ||
| * Available when using Anthropic's built-in tools like web search. | ||
| */ | ||
| serverToolUse?: { | ||
| /** Number of web search requests made during the response */ | ||
| webSearchRequests?: number | ||
| /** Number of web fetch requests made during the response */ | ||
| webFetchRequests?: number | ||
| } | ||
| /** Index signature for Record<string, unknown> compatibility */ | ||
| [key: string]: unknown | ||
| } | ||
|
|
||
| /** | ||
| * Build normalized TokenUsage from Anthropic's usage object. | ||
| * Handles cache tokens and server tool use metrics. | ||
| */ | ||
| export function buildAnthropicUsage( | ||
| usage: | ||
| | Anthropic_SDK.Beta.BetaUsage | ||
| | Anthropic_SDK.Beta.BetaMessageDeltaUsage, | ||
| ): TokenUsage { | ||
| const inputTokens = usage.input_tokens ?? 0 | ||
| const outputTokens = usage.output_tokens | ||
|
|
||
| const result = buildBaseUsage({ | ||
| promptTokens: inputTokens, | ||
| completionTokens: outputTokens, | ||
| totalTokens: inputTokens + outputTokens, | ||
| }) | ||
|
|
||
| // Add prompt token details for cache tokens | ||
| const cacheCreation = usage.cache_creation_input_tokens | ||
| const cacheRead = usage.cache_read_input_tokens | ||
|
|
||
| result.promptTokensDetails = { | ||
| ...(cacheCreation ? { cacheWriteTokens: cacheCreation } : {}), | ||
| ...(cacheRead ? { cachedTokens: cacheRead } : {}), | ||
| } | ||
|
|
||
| // Add provider-specific usage details for server tool use | ||
| const serverToolUse = usage.server_tool_use | ||
|
|
||
| result.providerUsageDetails = { | ||
| serverToolUse: { | ||
| ...(serverToolUse?.web_search_requests | ||
| ? { webSearchRequests: serverToolUse.web_search_requests } | ||
| : {}), | ||
| ...(serverToolUse?.web_fetch_requests | ||
| ? { webFetchRequests: serverToolUse.web_fetch_requests } | ||
| : {}), | ||
| }, | ||
| } satisfies AnthropicProviderUsageDetails | ||
|
|
||
| return result | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π§© Analysis chain
π Script executed:
Repository: TanStack/ai
Length of output: 6304
π Script executed:
Repository: TanStack/ai
Length of output: 3031
π Script executed:
Repository: TanStack/ai
Length of output: 37
π Script executed:
Repository: TanStack/ai
Length of output: 269
π Web query:
OpenAI prompt caching API documentation prompt_cache_key prompt_cache_retentionπ‘ Result:
Summary β prompt_cache_key and prompt_cache_retention (OpenAI Prompt Caching)
What they are:
Effects / behavior:
How to set:
{"model":"gpt-5.1","input":"...","prompt_cache_key":"my-key","prompt_cache_retention":"24h"}. [1][3]
Notes / best practices:
Sources:
[1] OpenAI Platform docs β Prompt Caching (platform.openai.com/docs/guides/prompt-caching).
[2] OpenAI API reference β Chat/Chat-completions object (platform.openai.com/docs/api-reference/chat/object).
[3] OpenAI API reference β Responses object (platform.openai.com/docs/api-reference/responses).
Avoid a hardcoded prompt cache key (crossβuser cache collision risk).
A fixed
prompt_cache_keycauses all users' prompts to share the same cache routing, risking data mixing. UseconversationIdwhen available to scope the cache key per user/session.π οΈ Suggested change
openai: () => createChatOptions({ adapter: openaiText((model || 'gpt-4o') as 'gpt-4o'), - modelOptions: { - prompt_cache_key: 'user-session-12345', - prompt_cache_retention: '24h', - }, + modelOptions: conversationId + ? { + prompt_cache_key: conversationId, + prompt_cache_retention: '24h', + } + : {}, }),π Committable suggestion
π€ Prompt for AI Agents