-
-
Notifications
You must be signed in to change notification settings - Fork 188
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 3 commits
bd19eb2
f8a9dc1
6ce4f3d
71ee5da
c5a42a0
850d58f
bb8a668
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 | ||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -129,8 +129,10 @@ export const Route = createFileRoute('/api/tanchat')({ | |||||||||||||||||||||||||||||||||||||||
| openai: () => | ||||||||||||||||||||||||||||||||||||||||
| createChatOptions({ | ||||||||||||||||||||||||||||||||||||||||
| adapter: openaiText((model || 'gpt-4o') as 'gpt-4o'), | ||||||||||||||||||||||||||||||||||||||||
| temperature: 2, | ||||||||||||||||||||||||||||||||||||||||
| modelOptions: {}, | ||||||||||||||||||||||||||||||||||||||||
| modelOptions: { | ||||||||||||||||||||||||||||||||||||||||
| prompt_cache_key: 'user-session-12345', | ||||||||||||||||||||||||||||||||||||||||
| prompt_cache_retention: '24h', | ||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
183
to
+189
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: # First, examine the file to understand the context
cat -n examples/ts-react-chat/src/routes/api.tanchat.ts | head -150Repository: TanStack/ai Length of output: 6304 π Script executed: # Check if conversationId is available in scope at the lines in question
cat -n examples/ts-react-chat/src/routes/api.tanchat.ts | sed -n '70,140p'Repository: TanStack/ai Length of output: 3031 π Script executed: # Search for openai adapter imports and type definitions
rg -A 5 -B 5 "openaiText\|createChatOptions" examples/ts-react-chat/src/routes/api.tanchat.tsRepository: TanStack/ai Length of output: 37 π Script executed: # Look for `@tanstack/ai-openai` package structure and adapter options
find . -name "*.ts" -o -name "*.tsx" | xargs rg -l "@tanstack/ai-openai" | head -5Repository: TanStack/ai Length of output: 269 π Web query:
π‘ Result: Summary β prompt_cache_key and prompt_cache_retention (OpenAI Prompt Caching)
Sources: Avoid a hardcoded prompt cache key (crossβuser cache collision risk). A fixed π οΈ 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
Suggested change
π€ Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||
| }), | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| 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: 3961
Critical: Documentation includes fields not present in the actual TypeScript interface.
The documented
TokenUsageinterface lists several fields that do not exist in the actual implementation defined inpackages/typescript/ai/src/types.ts:In
PromptTokensDetails:cacheCreationTokens(line 102)cacheReadTokens(line 103)In
CompletionTokensDetails:acceptedPredictionTokens(line 117)rejectedPredictionTokens(line 118)The actual
PromptTokensDetailsincludes:cachedTokens,cacheWriteTokens,audioTokens,videoTokens,imageTokens, andtextTokens. The actualCompletionTokensDetailsincludes:reasoningTokens,audioTokens,videoTokens,imageTokens, andtextTokens.Remove these non-existent fields from the documentation or add them to the TypeScript interface definitions.
π€ Prompt for AI Agents