mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-13 06:40:33 -07:00
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
106 lines
3.8 KiB
TypeScript
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');
|
|
});
|
|
});
|