mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-16 23:02:51 -07:00
nearly building
This commit is contained in:
@@ -3,9 +3,6 @@
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import type { Episode } from './ir/types.js';
|
||||
|
||||
import type { EpisodeEditor } from './ir/episodeEditor.js';
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import { createMockEnvironment, createDummyState, createDummyEpisode } from '../testing/contextTestUtils.js';
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
||||
import { BlobDegradationProcessor } from './blobDegradationProcessor.js';
|
||||
import { EpisodeEditor } from '../ir/episodeEditor.js';
|
||||
import type { UserPrompt } from '../ir/types.js';
|
||||
import type { ContextEnvironment } from '../sidecar/environment.js';
|
||||
import { InMemoryFileSystem } from '../system/InMemoryFileSystem.js';
|
||||
@@ -35,7 +36,9 @@ describe('BlobDegradationProcessor', () => {
|
||||
]);
|
||||
|
||||
const state = createDummyState(false, 500);
|
||||
const result = await processor.process([ep], state);
|
||||
const editor = new EpisodeEditor([ep]);
|
||||
await processor.process(editor, state);
|
||||
const result = editor.getFinalEpisodes();
|
||||
|
||||
const parts = (result[0].trigger as UserPrompt).semanticParts;
|
||||
|
||||
@@ -65,7 +68,9 @@ describe('BlobDegradationProcessor', () => {
|
||||
]);
|
||||
|
||||
const state = createDummyState(false, 500);
|
||||
const result = await processor.process([ep], state);
|
||||
const editor = new EpisodeEditor([ep]);
|
||||
await processor.process(editor, state);
|
||||
const result = editor.getFinalEpisodes();
|
||||
|
||||
const parts = (result[0].trigger as UserPrompt).semanticParts;
|
||||
expect(parts[0].presentation).toBeDefined();
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
import type { Episode } from '../ir/types.js';
|
||||
import type { ContextAccountingState, ContextProcessor } from '../pipeline.js';
|
||||
import type { ContextEnvironment } from '../sidecar/environment.js';
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
import { createMockEnvironment, createDummyState, createDummyEpisode } from '../testing/contextTestUtils.js';
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
||||
import { EmergencyTruncationProcessor } from './emergencyTruncationProcessor.js';
|
||||
import { EpisodeEditor } from '../ir/episodeEditor.js';
|
||||
import type { ContextEnvironment } from '../sidecar/environment.js';
|
||||
|
||||
describe('EmergencyTruncationProcessor', () => {
|
||||
@@ -32,7 +33,9 @@ describe('EmergencyTruncationProcessor', () => {
|
||||
// State says we are under budget (5000 < 10000)
|
||||
const state = createDummyState(true, 0, new Set(), 5000, 10000);
|
||||
|
||||
const result = await processor.process(episodes, state);
|
||||
const editor = new EpisodeEditor(episodes);
|
||||
await processor.process(editor, state);
|
||||
const result = editor.getFinalEpisodes();
|
||||
expect(result).toStrictEqual(episodes);
|
||||
expect(result.length).toBe(1);
|
||||
});
|
||||
@@ -48,7 +51,9 @@ describe('EmergencyTruncationProcessor', () => {
|
||||
// We have 300 tokens, but max is 200. We need to drop 100 tokens.
|
||||
const state = createDummyState(false, 100, new Set(), 300, 200);
|
||||
|
||||
const result = await processor.process(episodes, state);
|
||||
const editor = new EpisodeEditor(episodes);
|
||||
await processor.process(editor, state);
|
||||
const result = editor.getFinalEpisodes();
|
||||
|
||||
// It should drop the FIRST episode (ep-1) and keep the rest.
|
||||
expect(result.length).toBe(2);
|
||||
@@ -67,7 +72,9 @@ describe('EmergencyTruncationProcessor', () => {
|
||||
// However, ep-1 is protected!
|
||||
const state = createDummyState(false, 100, new Set(['ep-1']), 300, 200);
|
||||
|
||||
const result = await processor.process(episodes, state);
|
||||
const editor = new EpisodeEditor(episodes);
|
||||
await processor.process(editor, state);
|
||||
const result = editor.getFinalEpisodes();
|
||||
|
||||
// It should SKIP dropping ep-1 (protected) and drop ep-2 instead.
|
||||
expect(result.length).toBe(2);
|
||||
@@ -85,7 +92,9 @@ describe('EmergencyTruncationProcessor', () => {
|
||||
// We have 300 tokens, max is 50. We need to drop 250 tokens!
|
||||
const state = createDummyState(false, 250, new Set(), 300, 50);
|
||||
|
||||
const result = await processor.process(episodes, state);
|
||||
const editor = new EpisodeEditor(episodes);
|
||||
await processor.process(editor, state);
|
||||
const result = editor.getFinalEpisodes();
|
||||
|
||||
// It must drop ep1 (100t) and ep2 (100t).
|
||||
// Remaining is ep3 (100t).
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
*/
|
||||
|
||||
import type { ContextProcessor, ContextAccountingState } from '../pipeline.js';
|
||||
import type { Episode } from '../ir/types.js';
|
||||
import type { ContextEnvironment } from '../sidecar/environment.js';
|
||||
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import { createMockEnvironment, createDummyState, createDummyEpisode } from '../testing/contextTestUtils.js';
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import { HistorySquashingProcessor } from './historySquashingProcessor.js';
|
||||
import { EpisodeEditor } from '../ir/episodeEditor.js';
|
||||
import type {
|
||||
UserPrompt,
|
||||
AgentThought,
|
||||
@@ -44,7 +45,9 @@ describe('HistorySquashingProcessor', () => {
|
||||
const episodes = [createThoughtEpisode('1', 'short text', 'short thought')];
|
||||
const state = createDummyState(true);
|
||||
|
||||
const result = await processor.process(episodes, state);
|
||||
const editor = new EpisodeEditor(episodes);
|
||||
await processor.process(editor, state);
|
||||
const result = editor.getFinalEpisodes();
|
||||
|
||||
expect(result).toStrictEqual(episodes);
|
||||
expect(
|
||||
@@ -58,7 +61,9 @@ describe('HistorySquashingProcessor', () => {
|
||||
const episodes = [createThoughtEpisode('ep-1', longText, 'short thought')];
|
||||
const state = createDummyState(false, 100, new Set(['ep-1']));
|
||||
|
||||
const result = await processor.process(episodes, state);
|
||||
const editor = new EpisodeEditor(episodes);
|
||||
await processor.process(editor, state);
|
||||
const result = editor.getFinalEpisodes();
|
||||
|
||||
expect(
|
||||
(result[0].trigger as UserPrompt).semanticParts[0].presentation,
|
||||
@@ -71,7 +76,9 @@ describe('HistorySquashingProcessor', () => {
|
||||
const episodes = [createThoughtEpisode('ep-2', longUser, longModel)];
|
||||
const state = createDummyState(false, 500); // High deficit, force truncation
|
||||
|
||||
const result = await processor.process(episodes, state);
|
||||
const editor = new EpisodeEditor(episodes);
|
||||
await processor.process(editor, state);
|
||||
const result = editor.getFinalEpisodes();
|
||||
|
||||
const userPart = (result[0].trigger as UserPrompt).semanticParts[0];
|
||||
const thoughtPart = result[0].steps[0] as AgentThought;
|
||||
@@ -103,7 +110,9 @@ describe('HistorySquashingProcessor', () => {
|
||||
// Original = ~250 tokens. Limit = 100. Truncation saves ~150 tokens.
|
||||
const state = createDummyState(false, 150);
|
||||
|
||||
const result = await processor.process(episodes, state);
|
||||
const editor = new EpisodeEditor(episodes);
|
||||
await processor.process(editor, state);
|
||||
const result = editor.getFinalEpisodes();
|
||||
|
||||
// First episode should be truncated
|
||||
const ep1Part = (result[0].trigger as UserPrompt).semanticParts[0];
|
||||
@@ -129,7 +138,9 @@ describe('HistorySquashingProcessor', () => {
|
||||
};
|
||||
|
||||
const state = createDummyState(false, 500);
|
||||
const result = await processor.process([ep], state);
|
||||
const editor = new EpisodeEditor([ep]);
|
||||
await processor.process(editor, state);
|
||||
const result = editor.getFinalEpisodes();
|
||||
|
||||
const yieldPart = result[0].yield as AgentYield;
|
||||
const yieldPresentation = yieldPart.presentation as { text: string };
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import type { Episode } from '../ir/types.js';
|
||||
import type { ContextAccountingState, ContextProcessor } from '../pipeline.js';
|
||||
import type { ContextEnvironment } from '../sidecar/environment.js';
|
||||
import { truncateProportionally } from '../truncation.js';
|
||||
import type { EpisodeEditor } from '../ir/episodeEditor.js';
|
||||
|
||||
export class HistorySquashingProcessor implements ContextProcessor {
|
||||
readonly name = 'HistorySquashing';
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
import { createMockEnvironment, createDummyState, createDummyEpisode } from '../testing/contextTestUtils.js';
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
||||
import { SemanticCompressionProcessor } from './semanticCompressionProcessor.js';
|
||||
import { EpisodeEditor } from '../ir/episodeEditor.js';
|
||||
import type {
|
||||
UserPrompt,
|
||||
ToolExecution,
|
||||
@@ -75,7 +76,9 @@ describe('SemanticCompressionProcessor', () => {
|
||||
const episodes = [createEpisodeWithThoughtsAndTools('1', 'short', 'short', 'short')];
|
||||
const state = createDummyState(true);
|
||||
|
||||
await processor.process(episodes, state);
|
||||
const editor = new EpisodeEditor(episodes);
|
||||
await processor.process(editor, state);
|
||||
|
||||
expect(generateContentMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -86,7 +89,9 @@ describe('SemanticCompressionProcessor', () => {
|
||||
];
|
||||
const state = createDummyState(false, 1000, new Set(['ep-1']));
|
||||
|
||||
await processor.process(episodes, state);
|
||||
const editor = new EpisodeEditor(episodes);
|
||||
await processor.process(editor, state);
|
||||
|
||||
expect(generateContentMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -97,10 +102,13 @@ describe('SemanticCompressionProcessor', () => {
|
||||
];
|
||||
const state = createDummyState(false, 50000); // Massive deficit, forces all 3 to summarize
|
||||
|
||||
const result = await processor.process(episodes, state);
|
||||
const editor = new EpisodeEditor(episodes);
|
||||
await processor.process(editor, state);
|
||||
|
||||
expect(generateContentMock).toHaveBeenCalledTimes(3);
|
||||
|
||||
// Verify presentation layers were injected
|
||||
const result = editor.getFinalEpisodes();
|
||||
const userPart = (result[0].trigger as UserPrompt).semanticParts[0];
|
||||
const thoughtPart = result[0].steps[0] as AgentThought;
|
||||
const toolPart = result[0].steps[1] as ToolExecution;
|
||||
@@ -126,7 +134,9 @@ describe('SemanticCompressionProcessor', () => {
|
||||
// Set deficit low enough that ONE summary solves the problem
|
||||
const state = createDummyState(false, 5);
|
||||
|
||||
await processor.process(episodes, state);
|
||||
const editor = new EpisodeEditor(episodes);
|
||||
await processor.process(editor, state);
|
||||
|
||||
// It should only compress the UserPrompt and then stop
|
||||
expect(generateContentMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import type { Episode } from '../ir/types.js';
|
||||
import type { IrMetadata } from '../ir/types.js';
|
||||
import type { ContextAccountingState, ContextProcessor } from '../pipeline.js';
|
||||
import type { ContextEnvironment } from '../sidecar/environment.js';
|
||||
import { debugLogger } from '../../utils/debugLogger.js';
|
||||
@@ -166,7 +166,7 @@ export class SemanticCompressionProcessor implements ContextProcessor {
|
||||
observation: newObsObject,
|
||||
tokens: { intent: intentTokens as number, observation: newObsTokens },
|
||||
};
|
||||
if (!draftStep.metadata) { draftStep.metadata = { transformations: [] } };
|
||||
if (!draftStep.metadata) { draftStep.metadata = { transformations: [], currentTokens: 0, originalTokens: 0 } as unknown as IrMetadata };
|
||||
if (!draftStep.metadata.transformations) { draftStep.metadata.transformations = [] };
|
||||
draftStep.metadata.transformations.push({
|
||||
processorName: this.name,
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
import { createMockEnvironment, createDummyState, createDummyEpisode } from '../testing/contextTestUtils.js';
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
||||
import { StateSnapshotProcessor } from './stateSnapshotProcessor.js';
|
||||
import { EpisodeEditor } from '../ir/episodeEditor.js';
|
||||
import type { ContextEnvironment } from '../sidecar/environment.js';
|
||||
import type { BaseLlmClient } from '../../core/baseLlmClient.js';
|
||||
|
||||
@@ -37,7 +38,9 @@ describe('StateSnapshotProcessor', () => {
|
||||
// current: 100, max: 1000, retained: 200 (deficit 0)
|
||||
const state = createDummyState(false, 0, new Set(), 100, 1000, 200);
|
||||
|
||||
const result = await processor.process(episodes, state);
|
||||
const editor = new EpisodeEditor(episodes);
|
||||
await processor.process(editor, state);
|
||||
const result = editor.getFinalEpisodes();
|
||||
expect(result).toStrictEqual(episodes);
|
||||
expect(generateContentMock).not.toHaveBeenCalled();
|
||||
});
|
||||
@@ -51,7 +54,9 @@ describe('StateSnapshotProcessor', () => {
|
||||
// current: 1000, max: 10000, retained: 500. Target deficit = 500
|
||||
const state = createDummyState(false, 500, new Set(), 1000, 10000, 500);
|
||||
|
||||
const result = await processor.process(episodes, state);
|
||||
const editor = new EpisodeEditor(episodes);
|
||||
await processor.process(editor, state);
|
||||
const result = editor.getFinalEpisodes();
|
||||
expect(result).toStrictEqual(episodes);
|
||||
expect(generateContentMock).not.toHaveBeenCalled();
|
||||
});
|
||||
@@ -67,7 +72,9 @@ describe('StateSnapshotProcessor', () => {
|
||||
// Target deficit = 200
|
||||
const state = createDummyState(false, 200, new Set(), 1000, 10000, 800);
|
||||
|
||||
const result = await processor.process(episodes, state);
|
||||
const editor = new EpisodeEditor(episodes);
|
||||
await processor.process(editor, state);
|
||||
const result = editor.getFinalEpisodes();
|
||||
|
||||
// We started with 4 episodes.
|
||||
// Episodes [1, 2] were synthesized into a single new Snapshot episode.
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
import { createMockEnvironment } from '../testing/contextTestUtils.js';
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
||||
import { ToolMaskingProcessor } from './toolMaskingProcessor.js';
|
||||
import { EpisodeEditor } from '../ir/episodeEditor.js';
|
||||
import type { Episode, ToolExecution } from '../ir/types.js';
|
||||
import type { ContextAccountingState } from '../pipeline.js';
|
||||
import { randomUUID } from 'node:crypto';
|
||||
@@ -78,7 +79,9 @@ describe('ToolMaskingProcessor', () => {
|
||||
];
|
||||
const state = getDummyState(true);
|
||||
|
||||
const result = await processor.process(episodes, state);
|
||||
const editor = new EpisodeEditor(episodes);
|
||||
await processor.process(editor, state);
|
||||
const result = editor.getFinalEpisodes();
|
||||
|
||||
expect(result).toStrictEqual(episodes);
|
||||
expect((result[0].steps[0] as ToolExecution).presentation).toBeUndefined();
|
||||
@@ -95,7 +98,9 @@ describe('ToolMaskingProcessor', () => {
|
||||
const episodes = [createDummyEpisode('ep-1', intentPayload, obsPayload)];
|
||||
const state = getDummyState(false, 1000, new Set()); // Huge deficit
|
||||
|
||||
const result = await processor.process(episodes, state);
|
||||
const editor = new EpisodeEditor(episodes);
|
||||
await processor.process(editor, state);
|
||||
const result = editor.getFinalEpisodes();
|
||||
|
||||
const toolStep = result[0].steps[0] as ToolExecution;
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ import {
|
||||
ENTER_PLAN_MODE_TOOL_NAME,
|
||||
EXIT_PLAN_MODE_TOOL_NAME,
|
||||
} from '../../tools/tool-names.js';
|
||||
import type { Episode } from '../ir/types.js';
|
||||
|
||||
const UNMASKABLE_TOOLS = new Set([
|
||||
ACTIVATE_SKILL_TOOL_NAME,
|
||||
|
||||
@@ -11,19 +11,17 @@ import { createMockEnvironment, createDummyState, createDummyEpisode } from '../
|
||||
import type { ContextEnvironment } from './environment.js';
|
||||
import type { ContextProcessor } from '../pipeline.js';
|
||||
import type { SidecarConfig } from './types.js';
|
||||
import { ContextEventBus } from '../eventBus.js';
|
||||
|
||||
import type { Episode } from '../ir/types.js';
|
||||
import type { ContextEventBus } from '../eventBus.js';
|
||||
|
||||
// Create a Dummy Processor for testing Orchestration routing
|
||||
class DummySyncProcessor implements ContextProcessor {
|
||||
static create() { return new DummySyncProcessor(); }
|
||||
constructor() {}
|
||||
readonly name = 'DummySync';
|
||||
async process(episodes: any[], _state: any) {
|
||||
const copy = [...episodes];
|
||||
copy[0] = { ...copy[0], dummyModified: true };
|
||||
return copy;
|
||||
async process(editor: any, _state: any) {
|
||||
editor.editEpisode(editor.episodes[0].id, 'DUMMY_EDIT', (draft: any) => {
|
||||
draft.dummyModified = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,19 +29,18 @@ class DummyAsyncProcessor implements ContextProcessor {
|
||||
static create() { return new DummyAsyncProcessor(); }
|
||||
constructor() {}
|
||||
readonly name = 'DummyAsync';
|
||||
async process(episodes: any[], _state: any) {
|
||||
await new Promise(resolve => setTimeout(resolve, 50));
|
||||
const copy = [...episodes];
|
||||
copy[0] = { ...copy[0], asyncModified: true };
|
||||
return copy;
|
||||
async process(editor: any, _state: any) {
|
||||
editor.editEpisode(editor.episodes[0].id, 'DUMMY_EDIT', (draft: any) => {
|
||||
draft.dummyAsyncModified = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class ThrowingProcessor implements ContextProcessor {
|
||||
static create() { return new ThrowingProcessor(); }
|
||||
constructor() {}
|
||||
readonly name = 'Thrower';
|
||||
async process(): Promise<Episode[]> {
|
||||
readonly name = 'Throwing';
|
||||
async process(editor: any, state: any): Promise<void> {
|
||||
throw new Error('Processor failed intentionally');
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user