CoolFace
Datasetpublic

basant307/AI_Governance_Project

sourceHugging Faceapache-2.0updated 2mo agoView on Hugging Face
0likes48downloads
nonInteractiveToolExecutor.test.ts383 linesDownload Raw Back to core
1/**2 * @license3 * Copyright 2025 Google LLC4 * SPDX-License-Identifier: Apache-2.05 */6 7import { describe, it, expect, vi, beforeEach } from 'vitest';8import type { Mock } from 'vitest';9import { executeToolCall } from './nonInteractiveToolExecutor.js';10import type {11  ToolRegistry,12  ToolCallRequestInfo,13  ToolResult,14  Config,15} from '../index.js';16import {17  DEFAULT_TRUNCATE_TOOL_OUTPUT_LINES,18  DEFAULT_TRUNCATE_TOOL_OUTPUT_THRESHOLD,19  ToolErrorType,20  ApprovalMode,21} from '../index.js';22import type { Part } from '@google/genai';23import { MockTool } from '../test-utils/mock-tool.js';24 25describe('executeToolCall', () => {26  let mockToolRegistry: ToolRegistry;27  let mockTool: MockTool;28  let executeFn: Mock;29  let abortController: AbortController;30  let mockConfig: Config;31 32  beforeEach(() => {33    executeFn = vi.fn();34    mockTool = new MockTool({ name: 'testTool', execute: executeFn });35 36    mockToolRegistry = {37      getTool: vi.fn(),38      ensureTool: vi.fn(async (name: string) => mockToolRegistry.getTool(name)),39      getAllToolNames: vi.fn(),40    } as unknown as ToolRegistry;41 42    mockConfig = {43      getToolRegistry: () => mockToolRegistry,44      getApprovalMode: () => ApprovalMode.DEFAULT,45      getAllowedTools: () => [],46      getSessionId: () => 'test-session-id',47      getUsageStatisticsEnabled: () => true,48      getDebugMode: () => false,49      getContentGeneratorConfig: () => ({50        model: 'test-model',51        authType: 'gemini',52      }),53      getShellExecutionConfig: () => ({54        terminalWidth: 90,55        terminalHeight: 30,56      }),57      storage: {58        getProjectTempDir: () => '/tmp',59      },60      getTruncateToolOutputThreshold: () =>61        DEFAULT_TRUNCATE_TOOL_OUTPUT_THRESHOLD,62      getTruncateToolOutputLines: () => DEFAULT_TRUNCATE_TOOL_OUTPUT_LINES,63      getUseModelRouter: () => false,64      getGeminiClient: () => null, // No client needed for these tests65      getChatRecordingService: () => undefined,66      getMessageBus: vi.fn().mockReturnValue(undefined),67      getDisableAllHooks: vi.fn().mockReturnValue(true),68      getHookSystem: vi.fn().mockReturnValue(undefined),69      getDebugLogger: vi.fn().mockReturnValue({70        debug: vi.fn(),71        info: vi.fn(),72        warn: vi.fn(),73        error: vi.fn(),74      }),75      isInteractive: vi.fn().mockReturnValue(false),76    } as unknown as Config;77 78    abortController = new AbortController();79  });80 81  it('should execute a tool successfully', async () => {82    const request: ToolCallRequestInfo = {83      callId: 'call1',84      name: 'testTool',85      args: { param1: 'value1' },86      isClientInitiated: false,87      prompt_id: 'prompt-id-1',88    };89    const toolResult: ToolResult = {90      llmContent: 'Tool executed successfully',91      returnDisplay: 'Success!',92    };93    vi.mocked(mockToolRegistry.getTool).mockReturnValue(mockTool);94    executeFn.mockResolvedValue(toolResult);95 96    const response = await executeToolCall(97      mockConfig,98      request,99      abortController.signal,100    );101 102    expect(mockToolRegistry.getTool).toHaveBeenCalledWith('testTool');103    expect(executeFn).toHaveBeenCalledWith(request.args);104    expect(response).toStrictEqual({105      callId: 'call1',106      error: undefined,107      errorType: undefined,108      resultDisplay: 'Success!',109      contentLength:110        typeof toolResult.llmContent === 'string'111          ? toolResult.llmContent.length112          : undefined,113      responseParts: [114        {115          functionResponse: {116            name: 'testTool',117            id: 'call1',118            response: { output: 'Tool executed successfully' },119          },120        },121      ],122    });123  });124 125  it('should return an error if tool is not found', async () => {126    const request: ToolCallRequestInfo = {127      callId: 'call2',128      name: 'nonexistentTool',129      args: {},130      isClientInitiated: false,131      prompt_id: 'prompt-id-2',132    };133    vi.mocked(mockToolRegistry.getTool).mockReturnValue(undefined);134    vi.mocked(mockToolRegistry.getAllToolNames).mockReturnValue([135      'testTool',136      'anotherTool',137    ]);138 139    const response = await executeToolCall(140      mockConfig,141      request,142      abortController.signal,143    );144 145    const expectedErrorMessage =146      'Tool "nonexistentTool" not found in registry. Tools must use the exact names that are registered. Did you mean one of: "testTool", "anotherTool"?';147    expect(response).toStrictEqual({148      callId: 'call2',149      error: new Error(expectedErrorMessage),150      errorType: ToolErrorType.TOOL_NOT_REGISTERED,151      resultDisplay: expectedErrorMessage,152      contentLength: expectedErrorMessage.length,153      responseParts: [154        {155          functionResponse: {156            name: 'nonexistentTool',157            id: 'call2',158            response: {159              error: expectedErrorMessage,160            },161          },162        },163      ],164    });165  });166 167  it('should return an error if tool validation fails', async () => {168    const request: ToolCallRequestInfo = {169      callId: 'call3',170      name: 'testTool',171      args: { param1: 'invalid' },172      isClientInitiated: false,173      prompt_id: 'prompt-id-3',174    };175    vi.mocked(mockToolRegistry.getTool).mockReturnValue(mockTool);176    vi.spyOn(mockTool, 'build').mockImplementation(() => {177      throw new Error('Invalid parameters');178    });179 180    const response = await executeToolCall(181      mockConfig,182      request,183      abortController.signal,184    );185    expect(response).toStrictEqual({186      callId: 'call3',187      error: new Error('Invalid parameters'),188      errorType: ToolErrorType.INVALID_TOOL_PARAMS,189      responseParts: [190        {191          functionResponse: {192            id: 'call3',193            name: 'testTool',194            response: {195              error: 'Invalid parameters',196            },197          },198        },199      ],200      resultDisplay: 'Invalid parameters',201      contentLength: 'Invalid parameters'.length,202    });203  });204 205  it('should return an error if tool execution fails', async () => {206    const request: ToolCallRequestInfo = {207      callId: 'call4',208      name: 'testTool',209      args: { param1: 'value1' },210      isClientInitiated: false,211      prompt_id: 'prompt-id-4',212    };213    const executionErrorResult: ToolResult = {214      llmContent: 'Error: Execution failed',215      returnDisplay: 'Execution failed',216      error: {217        message: 'Execution failed',218        type: ToolErrorType.EXECUTION_FAILED,219      },220    };221    vi.mocked(mockToolRegistry.getTool).mockReturnValue(mockTool);222    executeFn.mockResolvedValue(executionErrorResult);223 224    const response = await executeToolCall(225      mockConfig,226      request,227      abortController.signal,228    );229    expect(response).toStrictEqual({230      callId: 'call4',231      error: new Error('Execution failed'),232      errorType: ToolErrorType.EXECUTION_FAILED,233      responseParts: [234        {235          functionResponse: {236            id: 'call4',237            name: 'testTool',238            response: {239              error: 'Execution failed',240            },241          },242        },243      ],244      resultDisplay: 'Execution failed',245      contentLength: 'Execution failed'.length,246    });247  });248 249  it('should return an unhandled exception error if execution throws', async () => {250    const request: ToolCallRequestInfo = {251      callId: 'call5',252      name: 'testTool',253      args: { param1: 'value1' },254      isClientInitiated: false,255      prompt_id: 'prompt-id-5',256    };257    vi.mocked(mockToolRegistry.getTool).mockReturnValue(mockTool);258    executeFn.mockRejectedValue(new Error('Something went very wrong'));259 260    const response = await executeToolCall(261      mockConfig,262      request,263      abortController.signal,264    );265 266    expect(response).toStrictEqual({267      callId: 'call5',268      error: new Error('Something went very wrong'),269      errorType: ToolErrorType.UNHANDLED_EXCEPTION,270      resultDisplay: 'Something went very wrong',271      contentLength: 'Something went very wrong'.length,272      responseParts: [273        {274          functionResponse: {275            name: 'testTool',276            id: 'call5',277            response: { error: 'Something went very wrong' },278          },279        },280      ],281    });282  });283 284  it('should correctly format llmContent with inlineData', async () => {285    const request: ToolCallRequestInfo = {286      callId: 'call6',287      name: 'testTool',288      args: {},289      isClientInitiated: false,290      prompt_id: 'prompt-id-6',291    };292    const imageDataPart: Part = {293      inlineData: { mimeType: 'image/png', data: 'base64data' },294    };295    const toolResult: ToolResult = {296      llmContent: [imageDataPart],297      returnDisplay: 'Image processed',298    };299    vi.mocked(mockToolRegistry.getTool).mockReturnValue(mockTool);300    executeFn.mockResolvedValue(toolResult);301 302    const response = await executeToolCall(303      mockConfig,304      request,305      abortController.signal,306    );307 308    expect(response).toStrictEqual({309      callId: 'call6',310      error: undefined,311      errorType: undefined,312      resultDisplay: 'Image processed',313      contentLength: undefined,314      responseParts: [315        {316          functionResponse: {317            name: 'testTool',318            id: 'call6',319            response: {320              output: '',321            },322            parts: [323              { inlineData: { mimeType: 'image/png', data: 'base64data' } },324            ],325          },326        },327      ],328    });329  });330 331  it('should calculate contentLength for a string llmContent', async () => {332    const request: ToolCallRequestInfo = {333      callId: 'call7',334      name: 'testTool',335      args: {},336      isClientInitiated: false,337      prompt_id: 'prompt-id-7',338    };339    const toolResult: ToolResult = {340      llmContent: 'This is a test string.',341      returnDisplay: 'String returned',342    };343    vi.mocked(mockToolRegistry.getTool).mockReturnValue(mockTool);344    executeFn.mockResolvedValue(toolResult);345 346    const response = await executeToolCall(347      mockConfig,348      request,349      abortController.signal,350    );351 352    expect(response.contentLength).toBe(353      typeof toolResult.llmContent === 'string'354        ? toolResult.llmContent.length355        : undefined,356    );357  });358 359  it('should have undefined contentLength for array llmContent with no string parts', async () => {360    const request: ToolCallRequestInfo = {361      callId: 'call8',362      name: 'testTool',363      args: {},364      isClientInitiated: false,365      prompt_id: 'prompt-id-8',366    };367    const toolResult: ToolResult = {368      llmContent: [{ inlineData: { mimeType: 'image/png', data: 'fakedata' } }],369      returnDisplay: 'Image data returned',370    };371    vi.mocked(mockToolRegistry.getTool).mockReturnValue(mockTool);372    executeFn.mockResolvedValue(toolResult);373 374    const response = await executeToolCall(375      mockConfig,376      request,377      abortController.signal,378    );379 380    expect(response.contentLength).toBeUndefined();381  });382});383 
basant307/AI_Governance_Project · CoolFace