{"version":3,"file":"agent/base-agent.mjs","sources":["webpack://@multimodal/agent/./src/agent/base-agent.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  AgentOptions,\n  LLMRequestHookPayload,\n  LLMResponseHookPayload,\n  LLMStreamingResponseHookPayload,\n  ToolCallResult,\n  ChatCompletionMessageToolCall,\n  AgentEventStream,\n  LoopTerminationCheckResult,\n  Tool,\n} from '@multimodal/agent-interface';\nimport { getLogger } from '../utils/logger';\n\n/**\n * BaseAgent - Handles all Agent lifecycle-related methods\n *\n * This class provides a foundation for agent implementations by managing:\n * - Lifecycle hooks (onLLMRequest, onLLMResponse, etc.)\n * - Tool call hooks (onBeforeToolCall, onAfterToolCall, etc.)\n * - Loop management hooks (onEachAgentLoopStart, onAgentLoopEnd, etc.)\n * - Termination control (requestLoopTermination, onBeforeLoopTermination)\n *\n * Derived classes can override these methods to implement custom behavior\n * without needing to implement the complete agent functionality.\n */\nexport abstract class BaseAgent<T extends AgentOptions = AgentOptions> {\n  protected logger = getLogger('BaseAgent');\n  private shouldTerminateLoop = false;\n\n  constructor(protected options: T) {}\n\n  /**\n   * Control the initialize process, you may need to perform some time-consuming\n   * operations before starting here\n   */\n  public initialize(): void | Promise<void> {\n    // Default implementation does nothing\n    // Derived classes can override to add initialization logic\n  }\n\n  /**\n   * Hook called before sending a request to the LLM\n   * This allows subclasses to inspect the request before it's sent\n   *\n   * @param id Session identifier for this conversation\n   * @param payload The complete request payload\n   */\n  public onLLMRequest(id: string, payload: LLMRequestHookPayload): void | Promise<void> {\n    // Default implementation: pass-through\n  }\n\n  /**\n   * Hook called after receiving a response from the LLM\n   * This allows subclasses to inspect the response before it's processed\n   *\n   * @param id Session identifier for this conversation\n   * @param payload The complete response payload\n   */\n  public onLLMResponse(id: string, payload: LLMResponseHookPayload): void | Promise<void> {\n    // Default implementation: pass-through, perf cost: 0.007ms - 0.021ms\n  }\n\n  /**\n   * Hook called after receiving streaming responses from the LLM\n   * Similar to onLLMResponse, but specifically for streaming\n   *\n   * @param id Session identifier for this conversation\n   * @param payload The streaming response payload\n   */\n  public onLLMStreamingResponse(id: string, payload: LLMStreamingResponseHookPayload): void {\n    // Keep it empty.\n  }\n\n  /**\n   * Hook called at the beginning of each agent loop iteration\n   * This method is invoked before each iteration of the agent loop starts,\n   * allowing derived classes to perform setup or inject additional context\n   *\n   * @param sessionId The session identifier for this conversation\n   * @returns A promise that resolves when pre-iteration setup is complete\n   */\n  public onEachAgentLoopStart(sessionId: string): void | Promise<void> {\n    // Default implementation does nothing\n    // Derived classes can override to insert custom logic\n  }\n\n  /**\n   * Hook called before a tool is executed\n   * This allows subclasses to intercept or modify tool calls before execution\n   *\n   * @param id Session identifier for this conversation\n   * @param toolCall Information about the tool being called\n   * @param args The arguments for the tool call\n   * @returns The possibly modified args for the tool call\n   */\n  public onBeforeToolCall(\n    id: string,\n    toolCall: { toolCallId: string; name: string },\n    args: any,\n  ): Promise<any> | any {\n    this.logger.infoWithData(`[Tool] onBeforeToolCall`, { toolCall }, JSON.stringify);\n    // Default implementation: pass-through\n    return args;\n  }\n\n  /**\n   * Hook called after a tool is executed\n   * This allows subclasses to intercept or modify tool results after execution\n   *\n   * @param id Session identifier for this conversation\n   * @param toolCall Information about the tool that was called\n   * @param result The result of the tool call\n   * @returns The possibly modified result of the tool call\n   */\n  public onAfterToolCall(\n    id: string,\n    toolCall: { toolCallId: string; name: string },\n    result: any,\n  ): Promise<any> | any {\n    this.logger.infoWithData(`[Tool] onAfterToolCall`, { toolCall, result }, JSON.stringify);\n    // Default implementation: pass-through\n    return result;\n  }\n\n  /**\n   * Hook called when a tool execution results in an error\n   * This allows subclasses to handle or transform errors from tool calls\n   *\n   * @param id Session identifier for this conversation\n   * @param toolCall Information about the tool that was called\n   * @param error The error that occurred\n   * @returns A potentially modified error or recovery value\n   */\n  public onToolCallError(\n    id: string,\n    toolCall: { toolCallId: string; name: string },\n    error: any,\n  ): Promise<any> | any {\n    this.logger.infoWithData(`[Tool] onToolCallError`, { toolCall, error }, JSON.stringify);\n    // Default implementation: pass through the error\n    return `Error: ${error}`;\n  }\n\n  /**\n   * Hook called at the end of the agent's execution loop\n   * This method is invoked after the agent has completed all iterations or reached a final answer\n   *\n   * @param id Session identifier for the completed conversation\n   */\n  public onAgentLoopEnd(id: string): void | Promise<void> {\n    // Reset termination flag\n    this.shouldTerminateLoop = false;\n  }\n\n  /**\n   * Hook called before processing a batch of tool calls\n   * This allows for intercepting and potentially replacing tool call execution\n   * without executing the actual tools - essential for test mocking\n   *\n   * @param id Session identifier for this conversation\n   * @param toolCalls Array of tool calls to be processed\n   * @returns Either undefined (to execute tools normally) or an array of tool call results (to skip execution)\n   */\n  public onProcessToolCalls(\n    id: string,\n    toolCalls: ChatCompletionMessageToolCall[],\n  ): Promise<ToolCallResult[] | undefined> | ToolCallResult[] | undefined {\n    // Default implementation allows normal tool execution\n    return undefined;\n  }\n\n  /**\n   * Hook called when the agent loop is about to terminate with a final answer\n   * This allows subclasses to inspect the final response and decide whether to:\n   * 1. Allow termination (return {finished: true})\n   * 2. Force continuation (return {finished: false})\n   *\n   * This hook is crucial for higher-level agents that need to enforce specific\n   * completion criteria or ensure certain tools are called before finishing.\n   *\n   * @param id Session identifier for this conversation\n   * @param finalEvent The final assistant message event that would end the loop\n   * @returns Decision object indicating whether to finish or continue the loop\n   */\n  public onBeforeLoopTermination(\n    id: string,\n    finalEvent: AgentEventStream.AssistantMessageEvent,\n  ): Promise<LoopTerminationCheckResult> | LoopTerminationCheckResult {\n    // Default implementation always allows termination\n    return { finished: true };\n  }\n\n  /**\n   * Request to terminate the agent loop after the current iteration\n   * This allows higher-level agents to control when the loop should end,\n   * even if there are remaining iterations or tool calls\n   *\n   * @returns True if the termination request was set, false if already terminating\n   */\n  public requestLoopTermination(): boolean {\n    if (this.shouldTerminateLoop) {\n      return false;\n    }\n\n    this.logger.info(`[Agent] Loop termination requested by higher-level agent`);\n    this.shouldTerminateLoop = true;\n    return true;\n  }\n\n  /**\n   * Check if loop termination has been requested\n   * Used internally by the loop executor\n   *\n   * @returns True if termination has been requested\n   * @internal\n   */\n  public isLoopTerminationRequested(): boolean {\n    return this.shouldTerminateLoop;\n  }\n\n  /**\n   * Reset the termination flag when a new run begins\n   * @internal\n   */\n  protected resetLoopTermination(): void {\n    this.shouldTerminateLoop = false;\n  }\n\n  /**\n   * Get the agent's configuration options\n   *\n   * @returns The agent configuration options used during initialization\n   */\n  public getOptions(): T {\n    return this.options;\n  }\n\n  /**\n   * Hook called when retrieving tools for the agent\n   * This allows subclasses to filter, modify or enhance the available tools\n   *\n   * IMPORTANT: This hook should not rely heavily on agent's internal state\n   * that changes during execution, as it could lead to inconsistent behavior\n   * between getAvailableTools() calls made before run() and the actual\n   * tools used during run().\n   *\n   * @param tools The list of registered tools\n   * @returns The filtered or modified list of tools\n   */\n  public onRetrieveTools(tools: Tool[]): Promise<Tool[]> | Tool[] {\n    // Default implementation: return all tools without modification\n    return tools;\n  }\n}\n"],"names":["BaseAgent","id","payload","sessionId","toolCall","args","JSON","result","error","toolCalls","finalEvent","tools","options","getLogger"],"mappings":";;;;;AAIC;;;;;;;;;;AA2BM,MAAeA;IAUb,aAAmC,CAG1C;IASO,aAAaC,EAAU,EAAEC,OAA8B,EAAwB,CAEtF;IASO,cAAcD,EAAU,EAAEC,OAA+B,EAAwB,CAExF;IASO,uBAAuBD,EAAU,EAAEC,OAAwC,EAAQ,CAE1F;IAUO,qBAAqBC,SAAiB,EAAwB,CAGrE;IAWO,iBACLF,EAAU,EACVG,QAA8C,EAC9CC,IAAS,EACW;QACpB,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,2BAA2B;YAAED;QAAS,GAAGE,KAAK,SAAS;QAEhF,OAAOD;IACT;IAWO,gBACLJ,EAAU,EACVG,QAA8C,EAC9CG,MAAW,EACS;QACpB,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,0BAA0B;YAAEH;YAAUG;QAAO,GAAGD,KAAK,SAAS;QAEvF,OAAOC;IACT;IAWO,gBACLN,EAAU,EACVG,QAA8C,EAC9CI,KAAU,EACU;QACpB,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,0BAA0B;YAAEJ;YAAUI;QAAM,GAAGF,KAAK,SAAS;QAEtF,OAAO,CAAC,OAAO,EAAEE,OAAO;IAC1B;IAQO,eAAeP,EAAU,EAAwB;QAEtD,IAAI,CAAC,mBAAmB,GAAG;IAC7B;IAWO,mBACLA,EAAU,EACVQ,SAA0C,EAC4B,CAGxE;IAeO,wBACLR,EAAU,EACVS,UAAkD,EACgB;QAElE,OAAO;YAAE,UAAU;QAAK;IAC1B;IASO,yBAAkC;QACvC,IAAI,IAAI,CAAC,mBAAmB,EAC1B,OAAO;QAGT,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;QACjB,IAAI,CAAC,mBAAmB,GAAG;QAC3B,OAAO;IACT;IASO,6BAAsC;QAC3C,OAAO,IAAI,CAAC,mBAAmB;IACjC;IAMU,uBAA6B;QACrC,IAAI,CAAC,mBAAmB,GAAG;IAC7B;IAOO,aAAgB;QACrB,OAAO,IAAI,CAAC,OAAO;IACrB;IAcO,gBAAgBC,KAAa,EAA4B;QAE9D,OAAOA;IACT;IA/NA,YAAsBC,OAAU,CAAE;;QAHlC,uBAAU,UAAV;QACA,uBAAQ,uBAAR;aAEsBA,OAAO,GAAPA;aAHZ,MAAM,GAAGC,UAAU;aACrB,mBAAmB,GAAG;IAEK;AAgOrC"}