{"version":3,"file":"tool-call-engine/NativeToolCallEngine.mjs","sources":["webpack://@multimodal/agent/./src/tool-call-engine/NativeToolCallEngine.ts"],"sourcesContent":["/*\n * Copyright (c) 2025 Bytedance, Inc. and its affiliates.\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { zodToJsonSchema } from '../utils';\nimport { getLogger } from '../utils/logger';\nimport {\n  Tool,\n  ToolCallEngine,\n  ParsedModelResponse,\n  PrepareRequestContext,\n  AgentSingleLoopReponse,\n  MultimodalToolCallResult,\n  ChatCompletionTool,\n  ChatCompletionChunk,\n  ChatCompletionMessageParam,\n  ChatCompletionCreateParams,\n  FunctionParameters,\n  ChatCompletion,\n  StreamProcessingState,\n  StreamChunkResult,\n  ChatCompletionMessageToolCall,\n} from '@multimodal/agent-interface';\nimport { buildToolCallResultMessages } from './utils';\n\n/**\n * A Tool Call Engine based on native Function Call.\n */\nexport class NativeToolCallEngine extends ToolCallEngine {\n  private logger = getLogger('NativeEngine');\n\n  preparePrompt(instructions: string, tools: Tool[]): string {\n    // Function call doesn't need special prompt formatting for tools\n    return instructions;\n  }\n\n  prepareRequest(context: PrepareRequestContext): ChatCompletionCreateParams {\n    const { model, messages, tools, temperature = 0.7 } = context;\n\n    if (!tools) {\n      this.logger.debug(`Preparing request for model: ${model} without tools`);\n      return {\n        model,\n        messages,\n        temperature,\n        stream: false,\n      };\n    }\n\n    // Convert tool definitions to OpenAI format\n    this.logger.debug(`Preparing request for model: ${model} with ${tools.length} tools`);\n    const openAITools = tools.map<ChatCompletionTool>((tool) => ({\n      type: 'function' as const,\n      function: {\n        name: tool.name,\n        description: tool.description,\n        // Use zodToJsonSchema which now handles both Zod and JSON schemas\n        parameters: zodToJsonSchema(tool.schema) as FunctionParameters,\n      },\n    }));\n\n    return {\n      model,\n      messages,\n      // Only set tools field when `tools` config exists, or we woul got following error:\n      // API error: InputError: Detected a 'tools' parameter,\n      // but the following model does not support tools: gpt-image-1\n      tools: openAITools.length > 0 ? openAITools : undefined,\n      temperature,\n      stream: false,\n    };\n  }\n\n  /**\n   * Initialize stream processing state for native 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 native tool calls\n   * For native engines, we can directly use the tool_calls property\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      content = delta.content;\n      state.contentBuffer += content;\n    }\n\n    // Process tool calls if present - native engine handles this automatically\n    if (delta?.tool_calls) {\n      hasToolCallUpdate = true;\n      this.processToolCallsInChunk(delta.tool_calls, state.toolCalls);\n    }\n\n    return {\n      content,\n      reasoningContent,\n      hasToolCallUpdate,\n      toolCalls: state.toolCalls,\n    };\n  }\n\n  /**\n   * Process tool calls data from a chunk\n   */\n  private processToolCallsInChunk(\n    toolCallParts: ChatCompletionChunk.Choice.Delta.ToolCall[],\n    currentToolCalls: ChatCompletionMessageToolCall[],\n  ): void {\n    for (const toolCallPart of toolCallParts) {\n      const toolCallIndex = toolCallPart.index;\n\n      // Ensure the tool call exists in our buffer\n      if (!currentToolCalls[toolCallIndex]) {\n        currentToolCalls[toolCallIndex] = {\n          id: toolCallPart.id!,\n          type: toolCallPart.type!,\n          function: {\n            name: '',\n            arguments: '',\n          },\n        };\n      }\n\n      // Update function name if present\n      if (toolCallPart.function?.name) {\n        currentToolCalls[toolCallIndex].function!.name = toolCallPart.function.name;\n      }\n\n      // Append arguments if present\n      if (toolCallPart.function?.arguments) {\n        currentToolCalls[toolCallIndex].function!.arguments =\n          (currentToolCalls[toolCallIndex].function!.arguments || '') +\n          toolCallPart.function.arguments;\n      }\n    }\n  }\n\n  /**\n   * Finalize the stream processing and extract the final response\n   */\n  finalizeStreamProcessing(state: StreamProcessingState): ParsedModelResponse {\n    return {\n      content: state.contentBuffer,\n      reasoningContent: state.reasoningBuffer || undefined,\n      toolCalls: state.toolCalls.length > 0 ? state.toolCalls : undefined,\n      finishReason: state.finishReason || 'stop',\n    };\n  }\n\n  buildHistoricalAssistantMessage(\n    currentLoopResponse: AgentSingleLoopReponse,\n  ): ChatCompletionMessageParam {\n    const { content, toolCalls } = currentLoopResponse;\n    const message: ChatCompletionMessageParam = {\n      role: 'assistant',\n      content: content,\n    };\n\n    // For OpenAI, directly use the tool_calls field\n    if (toolCalls && toolCalls.length > 0) {\n      message.tool_calls = toolCalls;\n      this.logger.debug(`Adding ${toolCalls.length} tool calls to assistant message`);\n    }\n\n    return message;\n  }\n\n  buildHistoricalToolCallResultMessages(\n    toolCallResults: MultimodalToolCallResult[],\n  ): ChatCompletionMessageParam[] {\n    return buildToolCallResultMessages(toolCallResults, true);\n  }\n}\n"],"names":["NativeToolCallEngine","ToolCallEngine","instructions","tools","context","model","messages","temperature","openAITools","tool","zodToJsonSchema","undefined","chunk","state","_chunk_choices_","_chunk_choices_1","delta","content","reasoningContent","hasToolCallUpdate","toolCallParts","currentToolCalls","toolCallPart","_toolCallPart_function","_toolCallPart_function1","toolCallIndex","currentLoopResponse","toolCalls","message","toolCallResults","buildToolCallResultMessages","getLogger"],"mappings":"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"}