33 */
44import Anthropic from '@anthropic-ai/sdk' ;
55
6+ import { TokenUsage } from '../../tokens.js' ;
67import { LLMProvider } from '../provider.js' ;
78import {
89 GenerateOptions ,
@@ -77,6 +78,15 @@ function addCacheControlToMessages(
7778 } ) ;
7879}
7980
81+ function tokenUsageFromMessage ( message : Anthropic . Message ) {
82+ const usage = new TokenUsage ( ) ;
83+ usage . input = message . usage . input_tokens ;
84+ usage . cacheWrites = message . usage . cache_creation_input_tokens ?? 0 ;
85+ usage . cacheReads = message . usage . cache_read_input_tokens ?? 0 ;
86+ usage . output = message . usage . output_tokens ;
87+ return usage ;
88+ }
89+
8090/**
8191 * Anthropic provider implementation
8292 */
@@ -115,43 +125,48 @@ export class AnthropicProvider implements LLMProvider {
115125 const nonSystemMessages = messages . filter ( ( msg ) => msg . role !== 'system' ) ;
116126 const formattedMessages = this . formatMessages ( nonSystemMessages ) ;
117127
128+ const tools = addCacheControlToTools (
129+ ( functions ?? [ ] ) . map ( ( fn ) => ( {
130+ name : fn . name ,
131+ description : fn . description ,
132+ input_schema : fn . parameters as Anthropic . Tool . InputSchema ,
133+ } ) ) ,
134+ ) ;
135+
118136 try {
119137 const requestOptions : Anthropic . MessageCreateParams = {
120138 model : this . model ,
121139 messages : addCacheControlToMessages ( formattedMessages ) ,
122140 temperature,
123141 max_tokens : maxTokens || 1024 ,
124- system : systemMessage ?. content ,
142+ system : systemMessage ?. content
143+ ? [
144+ {
145+ type : 'text' ,
146+ text : systemMessage ?. content ,
147+ cache_control : { type : 'ephemeral' } ,
148+ } ,
149+ ]
150+ : undefined ,
125151 top_p : topP ,
152+ tools,
126153 stream : false ,
127154 } ;
128155
129- // Add tools if provided
130- if ( functions && functions . length > 0 ) {
131- const tools = functions . map ( ( fn ) => ( {
132- name : fn . name ,
133- description : fn . description ,
134- input_schema : fn . parameters ,
135- } ) ) ;
136- ( requestOptions as any ) . tools = addCacheControlToTools ( tools ) ;
137- }
138-
139156 const response = await this . client . messages . create ( requestOptions ) ;
140157
141158 // Extract content and tool calls
142159 const content =
143160 response . content . find ( ( c ) => c . type === 'text' ) ?. text || '' ;
144161 const toolCalls = response . content
145162 . filter ( ( c ) => {
146- const contentType = ( c as any ) . type ;
163+ const contentType = c . type ;
147164 return contentType === 'tool_use' ;
148165 } )
149166 . map ( ( c ) => {
150- const toolUse = c as any ;
167+ const toolUse = c as Anthropic . Messages . ToolUseBlock ;
151168 return {
152- id :
153- toolUse . id ||
154- `tool-${ Math . random ( ) . toString ( 36 ) . substring ( 2 , 11 ) } ` ,
169+ id : toolUse . id ,
155170 name : toolUse . name ,
156171 content : JSON . stringify ( toolUse . input ) ,
157172 } ;
@@ -160,6 +175,7 @@ export class AnthropicProvider implements LLMProvider {
160175 return {
161176 text : content ,
162177 toolCalls : toolCalls ,
178+ tokenUsage : tokenUsageFromMessage ( response ) ,
163179 } ;
164180 } catch ( error ) {
165181 throw new Error (
@@ -168,20 +184,12 @@ export class AnthropicProvider implements LLMProvider {
168184 }
169185 }
170186
171- /**
172- * Count tokens in a text using Anthropic's tokenizer
173- * Note: This is a simplified implementation
174- */
175- async countTokens ( text : string ) : Promise < number > {
176- // In a real implementation, you would use Anthropic's tokenizer
177- // This is a simplified approximation
178- return Math . ceil ( text . length / 3.5 ) ;
179- }
180-
181187 /**
182188 * Format messages for Anthropic API
183189 */
184- private formatMessages ( messages : Message [ ] ) : any [ ] {
190+ private formatMessages (
191+ messages : Message [ ] ,
192+ ) : Anthropic . Messages . MessageParam [ ] {
185193 // Format messages for Anthropic API
186194 return messages . map ( ( msg ) => {
187195 if ( msg . role === 'user' ) {
0 commit comments