File size: 4,173 Bytes
7a1ad33 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | /**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* Test to verify circular reference handling in telemetry logging
*/
import { describe, it, expect } from 'vitest';
import { logToolCall } from './loggers.js';
import { ToolCallEvent } from './types.js';
import type { Config } from '../config/config.js';
import {
CoreToolCallStatus,
type ToolCallRequestInfo,
type ToolCallResponseInfo,
type CompletedToolCall,
} from '../scheduler/types.js';
import { MockTool } from '../test-utils/mock-tool.js';
describe('Circular Reference Handling', () => {
it('should handle circular references in tool function arguments', () => {
// Create a mock config
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const mockConfig = {
getTelemetryEnabled: () => true,
getUsageStatisticsEnabled: () => true,
getSessionId: () => 'test-session',
getModel: () => 'test-model',
getEmbeddingModel: () => 'test-embedding',
getDebugMode: () => false,
} as unknown as Config;
// Create an object with circular references (similar to HttpsProxyAgent)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const circularObject: any = {
sockets: {},
agent: null,
};
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
circularObject.agent = circularObject; // Create circular reference
circularObject.sockets['test-host'] = [
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
{ _httpMessage: { agent: circularObject } },
];
// Create a mock CompletedToolCall with circular references in function_args
const mockRequest: ToolCallRequestInfo = {
callId: 'test-call-id',
name: 'ReadFile',
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
args: circularObject, // This would cause the original error
isClientInitiated: false,
prompt_id: 'test-prompt-id',
};
const mockResponse: ToolCallResponseInfo = {
callId: 'test-call-id',
responseParts: [{ text: 'test result' }],
resultDisplay: undefined,
error: undefined, // undefined means success
errorType: undefined,
};
const tool = new MockTool({ name: 'mock-tool' });
const mockCompletedToolCall: CompletedToolCall = {
status: CoreToolCallStatus.Success,
request: mockRequest,
response: mockResponse,
tool,
invocation: tool.build({}),
durationMs: 100,
};
const event = new ToolCallEvent(mockCompletedToolCall);
// This should not throw an error
expect(() => {
logToolCall(mockConfig, event);
}).not.toThrow();
});
it('should handle normal objects without circular references', () => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const mockConfig = {
getTelemetryEnabled: () => true,
getUsageStatisticsEnabled: () => true,
getSessionId: () => 'test-session',
getModel: () => 'test-model',
getEmbeddingModel: () => 'test-embedding',
getDebugMode: () => false,
} as unknown as Config;
const normalObject = {
filePath: '/test/path',
options: { encoding: 'utf8' },
};
const mockRequest: ToolCallRequestInfo = {
callId: 'test-call-id',
name: 'ReadFile',
args: normalObject,
isClientInitiated: false,
prompt_id: 'test-prompt-id',
};
const mockResponse: ToolCallResponseInfo = {
callId: 'test-call-id',
responseParts: [{ text: 'test result' }],
resultDisplay: undefined,
error: undefined, // undefined means success
errorType: undefined,
};
const tool = new MockTool({ name: 'mock-tool' });
const mockCompletedToolCall: CompletedToolCall = {
status: CoreToolCallStatus.Success,
request: mockRequest,
response: mockResponse,
tool,
invocation: tool.build({}),
durationMs: 100,
};
const event = new ToolCallEvent(mockCompletedToolCall);
expect(() => {
logToolCall(mockConfig, event);
}).not.toThrow();
});
});
|