{"version":3,"file":"tool-call-engine/PromptEngineeringToolCallEngine.mjs","sources":["webpack://@multimodal/agent/./src/tool-call-engine/PromptEngineeringToolCallEngine.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\n/*\n * Copyright (c) 2025 Bytedance, Inc. and its affiliates.\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport {\n  Tool,\n  ToolCallEngine,\n  ParsedModelResponse,\n  PrepareRequestContext,\n  AgentSingleLoopReponse,\n  MultimodalToolCallResult,\n  ChatCompletionMessageParam,\n  ChatCompletionCreateParams,\n  ChatCompletion,\n  ChatCompletionChunk,\n  ChatCompletionMessageToolCall,\n  StreamProcessingState,\n  StreamChunkResult,\n} from '@multimodal/agent-interface';\n\nimport { zodToJsonSchema } from '../utils';\nimport { getLogger } from '../utils/logger';\nimport { isTest } from '../utils/env';\nimport { buildToolCallResultMessages } from './utils';\n\n/**\n * A Tool Call Engine based on prompt engineering.\n */\nexport class PromptEngineeringToolCallEngine extends ToolCallEngine {\n  private logger = getLogger('PromptEngine');\n\n  preparePrompt(instructions: string, tools: Tool[]): string {\n    // If no tools, return original instructions\n    if (!tools || tools.length === 0) {\n      return instructions;\n    }\n\n    this.logger.info(`Preparing prompt with ${tools.length} tools`);\n\n    // Create clearer tool format for instruction-based models\n    const toolsDescription = tools\n      .map((tool) => {\n        const schema = zodToJsonSchema(tool.schema);\n        const properties = schema.properties || {};\n        const requiredProps = schema.required || [];\n\n        const paramsDescription = Object.entries(properties)\n          .map(([name, prop]: [string, any]) => {\n            const isRequired = requiredProps.includes(name);\n            return `- ${name}${isRequired ? ' (required)' : ''}: ${prop.description || 'No description'} (type: ${prop.type})`;\n          })\n          .join('\\n');\n\n        return `## ${tool.name}\n\nDescription: ${tool.description}\n\nParameters:\n${paramsDescription || 'No parameters required'}`;\n      })\n      .join('\\n\\n');\n\n    // Use clearer JSON format instructions and add conversation format guidance\n    return `${instructions}\n\nYou have access to the following tools:\n\n${toolsDescription}\n\nTo use a tool, your response MUST use the following format, you need to ensure that it is a valid JSON string:\n\n<tool_call>\n{\n  \"name\": \"tool_name\",\n  \"parameters\": {\n    \"param1\": \"value1\",\n    \"param2\": \"value2\"\n  }\n}\n</tool_call>\n\nIf you want to provide a final answer without using tools, respond in a conversational manner WITHOUT using the tool_call format.\n\nWhen you receive tool results, they will be provided in a user message. Use these results to continue your reasoning or provide a final answer.\n`;\n  }\n\n  prepareRequest(context: PrepareRequestContext): ChatCompletionCreateParams {\n    const { model, messages, temperature = 0.7 } = context;\n\n    this.logger.debug(`Preparing request for model: ${model}`);\n\n    // Claude doesn't use tool parameters, we've already included tools in the prompt\n    return {\n      model,\n      messages,\n      temperature,\n      stream: false,\n    };\n  }\n\n  /**\n   * Initialize stream processing state for prompt engineering tool calls\n   */\n  initStreamProcessingState(): StreamProcessingState {\n    return {\n      contentBuffer: '',\n      toolCalls: [],\n      reasoningBuffer: '',\n      finishReason: null,\n    };\n  }\n\n  /**\n   * Process a streaming chunk for prompt engineering tool calls\n   * This implementation filters <tool_call> tags in real-time\n   */\n  processStreamingChunk(\n    chunk: ChatCompletionChunk,\n    state: StreamProcessingState,\n  ): StreamChunkResult {\n    const delta = chunk.choices[0]?.delta;\n    let content = '';\n    let reasoningContent = '';\n    let hasToolCallUpdate = false;\n\n    // Extract finish reason if present\n    if (chunk.choices[0]?.finish_reason) {\n      state.finishReason = chunk.choices[0].finish_reason;\n    }\n\n    // Process reasoning content if present\n    // @ts-expect-error Not in OpenAI types but present in compatible LLMs\n    if (delta?.reasoning_content) {\n      // @ts-expect-error\n      reasoningContent = delta.reasoning_content;\n      state.reasoningBuffer += reasoningContent;\n    }\n\n    // Process regular content if present\n    if (delta?.content) {\n      const newContent = delta.content;\n      state.contentBuffer += newContent;\n\n      // Check if we're currently building a complete tool call\n      const updatedBuffer = state.contentBuffer;\n\n      // If we've received a complete tool call tag, process it\n      if (this.hasCompletedToolCall(updatedBuffer)) {\n        const { cleanedContent, extractedToolCalls } = this.extractToolCalls(updatedBuffer);\n\n        // Update state with cleaned content (without tool call tags)\n        state.contentBuffer = cleanedContent;\n\n        // Add extracted tool calls to state\n        if (extractedToolCalls.length > 0) {\n          state.toolCalls = extractedToolCalls;\n          hasToolCallUpdate = true;\n\n          // For prompt engineering, we return empty content since we've filtered it\n          return {\n            content: '', // Don't send the tool call tag in content\n            reasoningContent,\n            hasToolCallUpdate,\n            toolCalls: state.toolCalls,\n          };\n        }\n      }\n\n      // Check if this chunk is part of a tool call tag\n      if (this.isPartOfToolCallTag(updatedBuffer)) {\n        // Don't include content that is part of a tool call tag\n        content = '';\n      } else {\n        content = newContent;\n      }\n    }\n\n    return {\n      content,\n      reasoningContent,\n      hasToolCallUpdate,\n      toolCalls: state.toolCalls,\n    };\n  }\n\n  /**\n   * Check if content contains a complete tool call\n   */\n  private hasCompletedToolCall(content: string): boolean {\n    return content.includes('<tool_call>') && content.includes('</tool_call>');\n  }\n\n  /**\n   * Check if the current buffer is part of a tool call tag\n   * This helps us filter out content that's part of a tool call\n   */\n  private isPartOfToolCallTag(content: string): boolean {\n    // If we have an opening tag but no closing tag yet\n    if (content.includes('<tool_call>') && !content.includes('</tool_call>')) {\n      return true;\n    }\n\n    // If we're in the middle of an opening or closing tag\n    const partialOpenTag = '<tool_call';\n    const partialCloseTag = '</tool_call';\n\n    for (let i = 1; i <= partialOpenTag.length; i++) {\n      if (content.endsWith(partialOpenTag.substring(0, i))) {\n        return true;\n      }\n    }\n\n    for (let i = 1; i <= partialCloseTag.length; i++) {\n      if (content.endsWith(partialCloseTag.substring(0, i))) {\n        return true;\n      }\n    }\n\n    return false;\n  }\n\n  /**\n   * Extract tool calls from content and return cleaned content\n   */\n  private extractToolCalls(content: string): {\n    cleanedContent: string;\n    extractedToolCalls: ChatCompletionMessageToolCall[];\n  } {\n    const toolCalls: ChatCompletionMessageToolCall[] = [];\n\n    // Match <tool_call>...</tool_call> blocks\n    const toolCallRegex = /<tool_call>([\\s\\S]*?)<\\/tool_call>/g;\n    let match;\n    let cleanedContent = content;\n\n    while ((match = toolCallRegex.exec(content)) !== null) {\n      const toolCallContent = match[1].trim();\n\n      try {\n        // Try to parse JSON\n        const toolCallData = JSON.parse(toolCallContent);\n\n        if (toolCallData && toolCallData.name) {\n          // Create OpenAI format tool call object\n          const toolCallId = isTest()\n            ? `call_1747633091730_6m2magifs`\n            : `call_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;\n          toolCalls.push({\n            id: toolCallId,\n            type: 'function',\n            function: {\n              name: toolCallData.name,\n              arguments: JSON.stringify(toolCallData.parameters || {}),\n            },\n          });\n          this.logger.debug(`Found tool call: ${toolCallData.name} with ID: ${toolCallId}`);\n        }\n      } catch (error) {\n        this.logger.error('Failed to parse tool call JSON:', error);\n        // Continue processing other potential tool calls\n      }\n    }\n\n    // Remove all tool call blocks from content\n    cleanedContent = content.replace(/<tool_call>[\\s\\S]*?<\\/tool_call>/g, '').trim();\n\n    return { cleanedContent, extractedToolCalls: toolCalls };\n  }\n\n  /**\n   * Finalize the stream processing and extract the final response\n   */\n  finalizeStreamProcessing(state: StreamProcessingState): ParsedModelResponse {\n    // Do one final extraction in case there are completed tool calls\n    if (this.hasCompletedToolCall(state.contentBuffer)) {\n      const { cleanedContent, extractedToolCalls } = this.extractToolCalls(state.contentBuffer);\n      state.contentBuffer = cleanedContent;\n\n      if (extractedToolCalls.length > 0) {\n        state.toolCalls = extractedToolCalls;\n      }\n    }\n\n    const finishReason = state.toolCalls.length > 0 ? 'tool_calls' : state.finishReason || 'stop';\n\n    return {\n      content: state.contentBuffer,\n      reasoningContent: state.reasoningBuffer || undefined,\n      toolCalls: state.toolCalls.length > 0 ? state.toolCalls : undefined,\n      finishReason,\n    };\n  }\n\n  buildHistoricalAssistantMessage(\n    currentLoopResponse: AgentSingleLoopReponse,\n  ): ChatCompletionMessageParam {\n    const { content } = currentLoopResponse;\n    // Claude doesn't support tool_calls field, only return content\n    // Tool calls are already included in the content\n    return {\n      role: 'assistant',\n      content: content,\n    };\n  }\n\n  buildHistoricalToolCallResultMessages(\n    toolCallResults: MultimodalToolCallResult[],\n  ): ChatCompletionMessageParam[] {\n    return buildToolCallResultMessages(toolCallResults, false);\n  }\n}\n"],"names":["PromptEngineeringToolCallEngine","ToolCallEngine","instructions","tools","toolsDescription","tool","schema","zodToJsonSchema","properties","requiredProps","paramsDescription","Object","name","prop","isRequired","context","model","messages","temperature","chunk","state","_chunk_choices_","_chunk_choices_1","delta","content","reasoningContent","hasToolCallUpdate","newContent","updatedBuffer","cleanedContent","extractedToolCalls","partialOpenTag","partialCloseTag","i","toolCalls","toolCallRegex","match","toolCallContent","toolCallData","JSON","toolCallId","isTest","Date","Math","error","finishReason","undefined","currentLoopResponse","toolCallResults","buildToolCallResultMessages","getLogger"],"mappings":";;;;;;;;;AAIC;;;;;;;;;;AA0BM,MAAMA,wCAAwCC;IAGnD,cAAcC,YAAoB,EAAEC,KAAa,EAAU;QAEzD,IAAI,CAACA,SAASA,AAAiB,MAAjBA,MAAM,MAAM,EACxB,OAAOD;QAGT,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,sBAAsB,EAAEC,MAAM,MAAM,CAAC,MAAM,CAAC;QAG9D,MAAMC,mBAAmBD,MACtB,GAAG,CAAC,CAACE;YACJ,MAAMC,SAASC,gBAAgBF,KAAK,MAAM;YAC1C,MAAMG,aAAaF,OAAO,UAAU,IAAI,CAAC;YACzC,MAAMG,gBAAgBH,OAAO,QAAQ,IAAI,EAAE;YAE3C,MAAMI,oBAAoBC,OAAO,OAAO,CAACH,YACtC,GAAG,CAAC,CAAC,CAACI,MAAMC,KAAoB;gBAC/B,MAAMC,aAAaL,cAAc,QAAQ,CAACG;gBAC1C,OAAO,CAAC,EAAE,EAAEA,OAAOE,aAAa,gBAAgB,GAAG,EAAE,EAAED,KAAK,WAAW,IAAI,iBAAiB,QAAQ,EAAEA,KAAK,IAAI,CAAC,CAAC,CAAC;YACpH,GACC,IAAI,CAAC;YAER,OAAO,CAAC,GAAG,EAAER,KAAK,IAAI,CAAC;;aAElB,EAAEA,KAAK,WAAW,CAAC;;;AAGhC,EAAEK,qBAAqB,0BAA0B;QAC3C,GACC,IAAI,CAAC;QAGR,OAAO,GAAGR,aAAa;;;;AAI3B,EAAEE,iBAAiB;;;;;;;;;;;;;;;;;AAiBnB,CAAC;IACC;IAEA,eAAeW,OAA8B,EAA8B;QACzE,MAAM,EAAEC,KAAK,EAAEC,QAAQ,EAAEC,cAAc,GAAG,EAAE,GAAGH;QAE/C,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,6BAA6B,EAAEC,OAAO;QAGzD,OAAO;YACLA;YACAC;YACAC;YACA,QAAQ;QACV;IACF;IAKA,4BAAmD;QACjD,OAAO;YACL,eAAe;YACf,WAAW,EAAE;YACb,iBAAiB;YACjB,cAAc;QAChB;IACF;IAMA,sBACEC,KAA0B,EAC1BC,KAA4B,EACT;YACLC,iBAMVC;QANJ,MAAMC,QAAQ,QAAAF,CAAAA,kBAAAA,MAAM,OAAO,CAAC,EAAE,AAAD,IAAfA,KAAAA,IAAAA,gBAAkB,KAAK;QACrC,IAAIG,UAAU;QACd,IAAIC,mBAAmB;QACvB,IAAIC,oBAAoB;QAGxB,IAAI,QAAAJ,CAAAA,mBAAAA,MAAM,OAAO,CAAC,EAAE,AAAD,IAAfA,KAAAA,IAAAA,iBAAkB,aAAa,EACjCF,MAAM,YAAY,GAAGD,MAAM,OAAO,CAAC,EAAE,CAAC,aAAa;QAKrD,IAAII,QAAAA,QAAAA,KAAAA,IAAAA,MAAO,iBAAiB,EAAE;YAE5BE,mBAAmBF,MAAM,iBAAiB;YAC1CH,MAAM,eAAe,IAAIK;QAC3B;QAGA,IAAIF,QAAAA,QAAAA,KAAAA,IAAAA,MAAO,OAAO,EAAE;YAClB,MAAMI,aAAaJ,MAAM,OAAO;YAChCH,MAAM,aAAa,IAAIO;YAGvB,MAAMC,gBAAgBR,MAAM,aAAa;YAGzC,IAAI,IAAI,CAAC,oBAAoB,CAACQ,gBAAgB;gBAC5C,MAAM,EAAEC,cAAc,EAAEC,kBAAkB,EAAE,GAAG,IAAI,CAAC,gBAAgB,CAACF;gBAGrER,MAAM,aAAa,GAAGS;gBAGtB,IAAIC,mBAAmB,MAAM,GAAG,GAAG;oBACjCV,MAAM,SAAS,GAAGU;oBAClBJ,oBAAoB;oBAGpB,OAAO;wBACL,SAAS;wBACTD;wBACAC;wBACA,WAAWN,MAAM,SAAS;oBAC5B;gBACF;YACF;YAKEI,UAFE,IAAI,CAAC,mBAAmB,CAACI,iBAEjB,KAEAD;QAEd;QAEA,OAAO;YACLH;YACAC;YACAC;YACA,WAAWN,MAAM,SAAS;QAC5B;IACF;IAKQ,qBAAqBI,OAAe,EAAW;QACrD,OAAOA,QAAQ,QAAQ,CAAC,kBAAkBA,QAAQ,QAAQ,CAAC;IAC7D;IAMQ,oBAAoBA,OAAe,EAAW;QAEpD,IAAIA,QAAQ,QAAQ,CAAC,kBAAkB,CAACA,QAAQ,QAAQ,CAAC,iBACvD,OAAO;QAIT,MAAMO,iBAAiB;QACvB,MAAMC,kBAAkB;QAExB,IAAK,IAAIC,IAAI,GAAGA,KAAKF,eAAe,MAAM,EAAEE,IAC1C,IAAIT,QAAQ,QAAQ,CAACO,eAAe,SAAS,CAAC,GAAGE,KAC/C,OAAO;QAIX,IAAK,IAAIA,IAAI,GAAGA,KAAKD,gBAAgB,MAAM,EAAEC,IAC3C,IAAIT,QAAQ,QAAQ,CAACQ,gBAAgB,SAAS,CAAC,GAAGC,KAChD,OAAO;QAIX,OAAO;IACT;IAKQ,iBAAiBT,OAAe,EAGtC;QACA,MAAMU,YAA6C,EAAE;QAGrD,MAAMC,gBAAgB;QACtB,IAAIC;QACJ,IAAIP,iBAAiBL;QAErB,MAAQY,AAAyC,SAAzCA,CAAAA,QAAQD,cAAc,IAAI,CAACX,QAAO,EAAa;YACrD,MAAMa,kBAAkBD,KAAK,CAAC,EAAE,CAAC,IAAI;YAErC,IAAI;gBAEF,MAAME,eAAeC,KAAK,KAAK,CAACF;gBAEhC,IAAIC,gBAAgBA,aAAa,IAAI,EAAE;oBAErC,MAAME,aAAaC,WACf,iCACA,CAAC,KAAK,EAAEC,KAAK,GAAG,GAAG,CAAC,EAAEC,KAAK,MAAM,GAAG,QAAQ,CAAC,IAAI,SAAS,CAAC,GAAG,KAAK;oBACvET,UAAU,IAAI,CAAC;wBACb,IAAIM;wBACJ,MAAM;wBACN,UAAU;4BACR,MAAMF,aAAa,IAAI;4BACvB,WAAWC,KAAK,SAAS,CAACD,aAAa,UAAU,IAAI,CAAC;wBACxD;oBACF;oBACA,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,iBAAiB,EAAEA,aAAa,IAAI,CAAC,UAAU,EAAEE,YAAY;gBAClF;YACF,EAAE,OAAOI,OAAO;gBACd,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,mCAAmCA;YAEvD;QACF;QAGAf,iBAAiBL,QAAQ,OAAO,CAAC,qCAAqC,IAAI,IAAI;QAE9E,OAAO;YAAEK;YAAgB,oBAAoBK;QAAU;IACzD;IAKA,yBAAyBd,KAA4B,EAAuB;QAE1E,IAAI,IAAI,CAAC,oBAAoB,CAACA,MAAM,aAAa,GAAG;YAClD,MAAM,EAAES,cAAc,EAAEC,kBAAkB,EAAE,GAAG,IAAI,CAAC,gBAAgB,CAACV,MAAM,aAAa;YACxFA,MAAM,aAAa,GAAGS;YAEtB,IAAIC,mBAAmB,MAAM,GAAG,GAC9BV,MAAM,SAAS,GAAGU;QAEtB;QAEA,MAAMe,eAAezB,MAAM,SAAS,CAAC,MAAM,GAAG,IAAI,eAAeA,MAAM,YAAY,IAAI;QAEvF,OAAO;YACL,SAASA,MAAM,aAAa;YAC5B,kBAAkBA,MAAM,eAAe,IAAI0B;YAC3C,WAAW1B,MAAM,SAAS,CAAC,MAAM,GAAG,IAAIA,MAAM,SAAS,GAAG0B;YAC1DD;QACF;IACF;IAEA,gCACEE,mBAA2C,EACf;QAC5B,MAAM,EAAEvB,OAAO,EAAE,GAAGuB;QAGpB,OAAO;YACL,MAAM;YACN,SAASvB;QACX;IACF;IAEA,sCACEwB,eAA2C,EACb;QAC9B,OAAOC,4BAA4BD,iBAAiB;IACtD;;QA1RK,gBACL,uBAAQ,UAASE,UAAU;;AA0R7B"}