Files
gemini-cli/packages/core/src/context/toolDistillationService.test.ts
Gal Zahavi 13ccc16457 fix(core): enhance sandbox usability and fix build error (#24460)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2026-04-01 23:51:06 +00:00

106 lines
3.8 KiB
TypeScript

/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { ToolOutputDistillationService } from './toolDistillationService.js';
import type { Config, Part } from '../index.js';
import type { GeminiClient } from '../core/client.js';
vi.mock('../utils/fileUtils.js', () => ({
saveTruncatedToolOutput: vi.fn().mockResolvedValue('mocked-path'),
}));
describe('ToolOutputDistillationService', () => {
let mockConfig: Config;
let mockGeminiClient: GeminiClient;
let service: ToolOutputDistillationService;
beforeEach(() => {
mockConfig = {
getToolMaxOutputTokens: vi.fn().mockReturnValue(100),
getToolSummarizationThresholdTokens: vi.fn().mockReturnValue(100),
getUsageStatisticsEnabled: vi.fn().mockReturnValue(false),
storage: {
getProjectTempDir: vi.fn().mockReturnValue('/tmp/gemini'),
},
telemetry: {
logEvent: vi.fn(),
},
} as unknown as Config;
mockGeminiClient = {
generateContent: vi.fn().mockResolvedValue({
candidates: [{ content: { parts: [{ text: 'Mock Intent Summary' }] } }],
}),
} as unknown as GeminiClient;
service = new ToolOutputDistillationService(
mockConfig,
mockGeminiClient,
'test-prompt-id',
);
});
it('should generate a structural map for oversized content within limits', async () => {
// > threshold * SUMMARIZATION_THRESHOLD (100 * 4 = 400)
const largeContent = 'A'.repeat(500);
const result = await service.distill('test-tool', 'call-1', largeContent);
expect(mockGeminiClient.generateContent).toHaveBeenCalled();
const text =
typeof result.truncatedContent === 'string'
? result.truncatedContent
: (result.truncatedContent as Array<{ text: string }>)[0].text;
expect(text).toContain('Strategic Significance');
});
it('should structurally truncate functionResponse while preserving schema', async () => {
// threshold is 100
const hugeValue = 'H'.repeat(1000);
const content = [
{
functionResponse: {
name: 'test_tool',
id: '123',
response: {
stdout: hugeValue,
stderr: 'no error',
},
},
},
] as unknown as Part[];
const result = await service.distill('test-tool', 'call-1', content);
const truncatedParts = result.truncatedContent as Part[];
expect(truncatedParts.length).toBe(1);
const fr = truncatedParts[0].functionResponse!;
const resp = fr.response as Record<string, unknown>;
expect(fr.name).toBe('test_tool');
expect(resp['stderr']).toBe('no error');
expect(resp['stdout'] as string).toContain('[Message Normalized');
expect(resp['stdout'] as string).toContain('Full output saved to');
});
it('should skip structural map for extremely large content exceeding MAX_DISTILLATION_SIZE', async () => {
const massiveContent = 'A'.repeat(1_000_001); // > MAX_DISTILLATION_SIZE
const result = await service.distill('test-tool', 'call-2', massiveContent);
expect(mockGeminiClient.generateContent).not.toHaveBeenCalled();
const text =
typeof result.truncatedContent === 'string'
? result.truncatedContent
: (result.truncatedContent as Array<{ text: string }>)[0].text;
expect(text).not.toContain('Strategic Significance');
});
it('should skip structural map for content below summarization threshold', async () => {
// > threshold but < threshold * SUMMARIZATION_THRESHOLD
const mediumContent = 'A'.repeat(110);
const result = await service.distill('test-tool', 'call-3', mediumContent);
expect(mockGeminiClient.generateContent).not.toHaveBeenCalled();
expect(result.truncatedContent).not.toContain('Mock Intent Summary');
});
});