@@ -2,35 +2,23 @@ import Anthropic from '@anthropic-ai/sdk'
22import { createLogger } from '@/lib/logs/console/logger'
33import type { StreamingExecution } from '@/executor/types'
44import { MAX_TOOL_ITERATIONS } from '@/providers'
5+ import {
6+ checkForForcedToolUsage ,
7+ createReadableStreamFromAnthropicStream ,
8+ generateToolUseId ,
9+ } from '@/providers/anthropic/utils'
10+ import { getProviderDefaultModel , getProviderModels } from '@/providers/models'
11+ import type {
12+ ProviderConfig ,
13+ ProviderRequest ,
14+ ProviderResponse ,
15+ TimeSegment ,
16+ } from '@/providers/types'
17+ import { prepareToolExecution , prepareToolsWithUsageControl } from '@/providers/utils'
518import { executeTool } from '@/tools'
6- import { getProviderDefaultModel , getProviderModels } from '../models'
7- import type { ProviderConfig , ProviderRequest , ProviderResponse , TimeSegment } from '../types'
8- import { prepareToolExecution , prepareToolsWithUsageControl , trackForcedToolUsage } from '../utils'
919
1020const logger = createLogger ( 'AnthropicProvider' )
1121
12- /**
13- * Helper to wrap Anthropic streaming into a browser-friendly ReadableStream
14- */
15- function createReadableStreamFromAnthropicStream (
16- anthropicStream : AsyncIterable < any >
17- ) : ReadableStream {
18- return new ReadableStream ( {
19- async start ( controller ) {
20- try {
21- for await ( const event of anthropicStream ) {
22- if ( event . type === 'content_block_delta' && event . delta ?. text ) {
23- controller . enqueue ( new TextEncoder ( ) . encode ( event . delta . text ) )
24- }
25- }
26- controller . close ( )
27- } catch ( err ) {
28- controller . error ( err )
29- }
30- } ,
31- } )
32- }
33-
3422export const anthropicProvider : ProviderConfig = {
3523 id : 'anthropic' ,
3624 name : 'Anthropic' ,
@@ -48,11 +36,6 @@ export const anthropicProvider: ProviderConfig = {
4836
4937 const anthropic = new Anthropic ( { apiKey : request . apiKey } )
5038
51- // Helper function to generate a simple unique ID for tool uses
52- const generateToolUseId = ( toolName : string ) => {
53- return `${ toolName } -${ Date . now ( ) } -${ Math . random ( ) . toString ( 36 ) . substring ( 2 , 7 ) } `
54- }
55-
5639 // Transform messages to Anthropic format
5740 const messages : any [ ] = [ ]
5841
@@ -393,44 +376,17 @@ ${fieldDescriptions}
393376 } ,
394377 ]
395378
396- // Helper function to check for forced tool usage in Anthropic responses
397- const checkForForcedToolUsage = ( response : any , toolChoice : any ) => {
398- if (
399- typeof toolChoice === 'object' &&
400- toolChoice !== null &&
401- Array . isArray ( response . content )
402- ) {
403- const toolUses = response . content . filter ( ( item : any ) => item . type === 'tool_use' )
404-
405- if ( toolUses . length > 0 ) {
406- // Convert Anthropic tool_use format to a format trackForcedToolUsage can understand
407- const adaptedToolCalls = toolUses . map ( ( tool : any ) => ( {
408- name : tool . name ,
409- } ) )
410-
411- // Convert Anthropic tool_choice format to match OpenAI format for tracking
412- const adaptedToolChoice =
413- toolChoice . type === 'tool' ? { function : { name : toolChoice . name } } : toolChoice
414-
415- const result = trackForcedToolUsage (
416- adaptedToolCalls ,
417- adaptedToolChoice ,
418- logger ,
419- 'anthropic' ,
420- forcedTools ,
421- usedForcedTools
422- )
423- // Make the behavior consistent with the initial check
424- hasUsedForcedTool = result . hasUsedForcedTool
425- usedForcedTools = result . usedForcedTools
426- return result
427- }
428- }
429- return null
430- }
431-
432379 // Check if a forced tool was used in the first response
433- checkForForcedToolUsage ( currentResponse , originalToolChoice )
380+ const firstCheckResult = checkForForcedToolUsage (
381+ currentResponse ,
382+ originalToolChoice ,
383+ forcedTools ,
384+ usedForcedTools
385+ )
386+ if ( firstCheckResult ) {
387+ hasUsedForcedTool = firstCheckResult . hasUsedForcedTool
388+ usedForcedTools = firstCheckResult . usedForcedTools
389+ }
434390
435391 try {
436392 while ( iterationCount < MAX_TOOL_ITERATIONS ) {
@@ -576,7 +532,16 @@ ${fieldDescriptions}
576532 currentResponse = await anthropic . messages . create ( nextPayload )
577533
578534 // Check if any forced tools were used in this response
579- checkForForcedToolUsage ( currentResponse , nextPayload . tool_choice )
535+ const nextCheckResult = checkForForcedToolUsage (
536+ currentResponse ,
537+ nextPayload . tool_choice ,
538+ forcedTools ,
539+ usedForcedTools
540+ )
541+ if ( nextCheckResult ) {
542+ hasUsedForcedTool = nextCheckResult . hasUsedForcedTool
543+ usedForcedTools = nextCheckResult . usedForcedTools
544+ }
580545
581546 const nextModelEndTime = Date . now ( )
582547 const thisModelTime = nextModelEndTime - nextModelStartTime
@@ -746,44 +711,17 @@ ${fieldDescriptions}
746711 } ,
747712 ]
748713
749- // Helper function to check for forced tool usage in Anthropic responses
750- const checkForForcedToolUsage = ( response : any , toolChoice : any ) => {
751- if (
752- typeof toolChoice === 'object' &&
753- toolChoice !== null &&
754- Array . isArray ( response . content )
755- ) {
756- const toolUses = response . content . filter ( ( item : any ) => item . type === 'tool_use' )
757-
758- if ( toolUses . length > 0 ) {
759- // Convert Anthropic tool_use format to a format trackForcedToolUsage can understand
760- const adaptedToolCalls = toolUses . map ( ( tool : any ) => ( {
761- name : tool . name ,
762- } ) )
763-
764- // Convert Anthropic tool_choice format to match OpenAI format for tracking
765- const adaptedToolChoice =
766- toolChoice . type === 'tool' ? { function : { name : toolChoice . name } } : toolChoice
767-
768- const result = trackForcedToolUsage (
769- adaptedToolCalls ,
770- adaptedToolChoice ,
771- logger ,
772- 'anthropic' ,
773- forcedTools ,
774- usedForcedTools
775- )
776- // Make the behavior consistent with the initial check
777- hasUsedForcedTool = result . hasUsedForcedTool
778- usedForcedTools = result . usedForcedTools
779- return result
780- }
781- }
782- return null
783- }
784-
785714 // Check if a forced tool was used in the first response
786- checkForForcedToolUsage ( currentResponse , originalToolChoice )
715+ const firstCheckResult = checkForForcedToolUsage (
716+ currentResponse ,
717+ originalToolChoice ,
718+ forcedTools ,
719+ usedForcedTools
720+ )
721+ if ( firstCheckResult ) {
722+ hasUsedForcedTool = firstCheckResult . hasUsedForcedTool
723+ usedForcedTools = firstCheckResult . usedForcedTools
724+ }
787725
788726 try {
789727 while ( iterationCount < MAX_TOOL_ITERATIONS ) {
@@ -925,7 +863,16 @@ ${fieldDescriptions}
925863 currentResponse = await anthropic . messages . create ( nextPayload )
926864
927865 // Check if any forced tools were used in this response
928- checkForForcedToolUsage ( currentResponse , nextPayload . tool_choice )
866+ const nextCheckResult = checkForForcedToolUsage (
867+ currentResponse ,
868+ nextPayload . tool_choice ,
869+ forcedTools ,
870+ usedForcedTools
871+ )
872+ if ( nextCheckResult ) {
873+ hasUsedForcedTool = nextCheckResult . hasUsedForcedTool
874+ usedForcedTools = nextCheckResult . usedForcedTools
875+ }
929876
930877 const nextModelEndTime = Date . now ( )
931878 const thisModelTime = nextModelEndTime - nextModelStartTime
0 commit comments