@@ -73,6 +73,7 @@ import {
7373 // SINGLE_INTERRUPT,
7474 // DOUBLE_INTERRUPT,
7575} from '../common/abort-signal-manager.js' ;
76+ import { parseToolCalls } from '../utils/toolCallParser.js' ;
7677
7778import { SummarizationService } from '../services/summarizer.js' ;
7879import { ToolCallService } from '../services/toolCallService.js' ;
@@ -857,135 +858,6 @@ export class AgentExecutor<TOutput extends z.ZodTypeAny> {
857858 return { functionCalls, textResponse } ;
858859 }
859860
860- // TODO: test this.
861- /**
862- * Parses a string containing Ollama tool calls into an array of FunctionCall objects.
863- * @param text The string to parse.
864- * @returns An array of FunctionCall objects.
865- */
866- private _parseOllamaToolCalls (
867- text : string ,
868- promptId : string ,
869- ) : FunctionCall [ ] {
870- const strippedText = stripJsonMarkdown ( text ) ;
871- // const strippedText = extractValidJson(text);
872- debugLogger . log (
873- `[Debug] Parsing Ollama tool calls from text: ${ strippedText } ` ,
874- ) ;
875- const functionCalls : FunctionCall [ ] = [ ] ;
876-
877- try {
878- const parsedJson = JSON . parse ( strippedText ) ;
879-
880- const processJsonToolCall = (
881- toolCall : unknown ,
882- index : number ,
883- ) : FunctionCall | null => {
884- if (
885- typeof toolCall === 'object' &&
886- toolCall !== null &&
887- 'name' in toolCall
888- ) {
889- const tc = toolCall as {
890- name : string ;
891- parameters ?: Record < string , unknown > ;
892- } ;
893- if ( typeof tc . name === 'string' ) {
894- return {
895- // id: `${promptId}-ollama-${index}`,
896- name : tc . name ,
897- args : tc . parameters ?? { } ,
898- } ;
899- }
900- }
901- return null ;
902- } ;
903-
904- if ( Array . isArray ( parsedJson ) ) {
905- for ( const [ index , item ] of parsedJson . entries ( ) ) {
906- const functionCall = processJsonToolCall ( item , index ) ;
907- if ( functionCall ) {
908- functionCalls . push ( functionCall ) ;
909- }
910- }
911- } else {
912- const functionCall = processJsonToolCall ( parsedJson , 0 ) ;
913- if ( functionCall ) {
914- functionCalls . push ( functionCall ) ;
915- }
916- }
917-
918- if ( functionCalls . length > 0 ) {
919- // debugLogger.log(
920- // `[Debug] Parsed Ollama tool calls from JSON: ${JSON.stringify(
921- // functionCalls,
922- // )}`,
923- // );
924- return functionCalls ;
925- }
926- } catch ( e ) {
927- // Not a valid JSON, proceed with regex parsing
928- debugLogger . log (
929- '[Debug] Failed to parse tool calls as JSON, falling back to regex.' ,
930- ) ;
931- }
932-
933- // This regex finds patterns like `function_name(anything_inside)`.
934- const toolCallRegex = / ( \w + ) \( ( .* ?) \) / g;
935- let match ;
936-
937- // The model might return tool calls wrapped in [].
938- const content =
939- strippedText . trim ( ) . startsWith ( '[' ) && strippedText . trim ( ) . endsWith ( ']' )
940- ? strippedText . trim ( ) . slice ( 1 , - 1 )
941- : strippedText . trim ( ) ;
942-
943- while ( ( match = toolCallRegex . exec ( content ) ) !== null ) {
944- const name = match [ 1 ] ;
945- const argsString = match [ 2 ] ;
946- debugLogger . log (
947- `[Debug] Found tool call: ${ name } with args: ${ argsString } ` ,
948- ) ;
949- const args : { [ key : string ] : unknown } = { } ;
950-
951- if ( argsString ) {
952- // This regex handles key-value pairs, including quoted values that may contain commas.
953- const argRegex = / ( \w + ) = ( " .* ?" | ' .* ?' | [ ^ , ] + ) / g;
954- let argMatch ;
955- while ( ( argMatch = argRegex . exec ( argsString ) ) !== null ) {
956- const key = argMatch [ 1 ] ;
957- let value : unknown = argMatch [ 2 ] . trim ( ) ;
958-
959- // Basic type inference
960- if ( typeof value === 'string' ) {
961- if (
962- ( value . startsWith ( '"' ) && value . endsWith ( '"' ) ) ||
963- ( value . startsWith ( `'` ) && value . endsWith ( `'` ) )
964- ) {
965- value = value . slice ( 1 , - 1 ) ;
966- } else if ( ! isNaN ( Number ( value ) ) && value . trim ( ) !== '' ) {
967- value = Number ( value ) ;
968- } else if ( value === 'true' ) {
969- value = true ;
970- } else if ( value === 'false' ) {
971- value = false ;
972- }
973- }
974- args [ key ] = value ;
975- }
976- }
977- functionCalls . push ( {
978- id : `${ promptId } -ollama-${ functionCalls . length } ` ,
979- name,
980- args,
981- } ) ;
982- }
983- // debugLogger.log(
984- // `[Debug] Parsed Ollama tool calls: ${JSON.stringify(functionCalls)}`,
985- // );
986- return functionCalls ;
987- }
988-
989861 /**
990862 * Calls the Ollama model with the given message.
991863 */
@@ -1047,7 +919,7 @@ export class AgentExecutor<TOutput extends z.ZodTypeAny> {
1047919 }
1048920 }
1049921
1050- let functionCalls = this . _parseOllamaToolCalls ( textResponse , promptId ) ;
922+ let functionCalls = parseToolCalls ( textResponse , promptId ) ;
1051923
1052924 // If there is no function call, it implies complete_task call.
1053925 if ( functionCalls . length === 0 ) {
0 commit comments