mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-12 12:54:07 -07:00
logs
This commit is contained in:
@@ -4,6 +4,9 @@
|
|||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import * as fs from 'node:fs';
|
||||||
|
import * as path from 'node:path';
|
||||||
|
|
||||||
// DISCLAIMER: This is a copied version of https://github.com/googleapis/js-genai/blob/main/src/chats.ts with the intention of working around a key bug
|
// DISCLAIMER: This is a copied version of https://github.com/googleapis/js-genai/blob/main/src/chats.ts with the intention of working around a key bug
|
||||||
// where function responses are not treated as "valid" responses: https://b.corp.google.com/issues/420354090
|
// where function responses are not treated as "valid" responses: https://b.corp.google.com/issues/420354090
|
||||||
|
|
||||||
@@ -167,6 +170,39 @@ export class GeminiChat {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs the projected history sent to the API to a side-channel file for anomaly analysis.
|
||||||
|
*/
|
||||||
|
private logRequestHistory(
|
||||||
|
requestContents: Content[],
|
||||||
|
promptId: string,
|
||||||
|
): void {
|
||||||
|
try {
|
||||||
|
const logDir = path.join(
|
||||||
|
this.config.storage.getProjectTempDir(),
|
||||||
|
'request_history_logs',
|
||||||
|
this.config.getSessionId(),
|
||||||
|
);
|
||||||
|
if (!fs.existsSync(logDir)) {
|
||||||
|
fs.mkdirSync(logDir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
const timestamp = Date.now();
|
||||||
|
const logFile = path.join(
|
||||||
|
logDir,
|
||||||
|
`request_${timestamp}_${promptId}.json`,
|
||||||
|
);
|
||||||
|
fs.writeFileSync(logFile, JSON.stringify(requestContents, null, 2));
|
||||||
|
debugLogger.debug(
|
||||||
|
`[PROJECT CLARITY] Request history logged to ${logFile}`,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
debugLogger.warn(
|
||||||
|
`[PROJECT CLARITY] Failed to log request history: ${error}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks a specific tool call ID for elision from the history.
|
* Marks a specific tool call ID for elision from the history.
|
||||||
*/
|
*/
|
||||||
@@ -242,6 +278,9 @@ export class GeminiChat {
|
|||||||
const requestContents =
|
const requestContents =
|
||||||
this.historyManager.getHistoryForRequest(userContent);
|
this.historyManager.getHistoryForRequest(userContent);
|
||||||
|
|
||||||
|
// PROJECT CLARITY: Side-channel request logging.
|
||||||
|
this.logRequestHistory(requestContents, prompt_id);
|
||||||
|
|
||||||
const stream = async function* (
|
const stream = async function* (
|
||||||
this: GeminiChat,
|
this: GeminiChat,
|
||||||
): AsyncGenerator<StreamEvent, void, void> {
|
): AsyncGenerator<StreamEvent, void, void> {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Content } from '@google/genai';
|
import type { Content } from '@google/genai';
|
||||||
|
import { debugLogger } from '../utils/debugLogger.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Types of side-effects that can be triggered by tools or the system.
|
* Types of side-effects that can be triggered by tools or the system.
|
||||||
@@ -74,6 +75,12 @@ export class SideEffectService {
|
|||||||
* Queues a side-effect for later application.
|
* Queues a side-effect for later application.
|
||||||
*/
|
*/
|
||||||
queueSideEffect(effect: SideEffect): void {
|
queueSideEffect(effect: SideEffect): void {
|
||||||
|
debugLogger.debug(`[PROJECT CLARITY] Queuing side-effect: ${effect.type}`, {
|
||||||
|
payload:
|
||||||
|
effect.type === SideEffectType.REPLACE_HISTORY
|
||||||
|
? '<history>'
|
||||||
|
: effect.payload,
|
||||||
|
});
|
||||||
this.pendingSideEffects.push(effect);
|
this.pendingSideEffects.push(effect);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
import { CHECKPOINT_STATE_DEFINITION } from './definitions/coreTools.js';
|
import { CHECKPOINT_STATE_DEFINITION } from './definitions/coreTools.js';
|
||||||
import type { MessageBus } from '../confirmation-bus/message-bus.js';
|
import type { MessageBus } from '../confirmation-bus/message-bus.js';
|
||||||
import type { Config } from '../config/config.js';
|
import type { Config } from '../config/config.js';
|
||||||
|
import { debugLogger } from '../utils/debugLogger.js';
|
||||||
|
|
||||||
interface CheckpointStateParams {
|
interface CheckpointStateParams {
|
||||||
[CHECKPOINT_STATE_PARAM_SUMMARY]: string;
|
[CHECKPOINT_STATE_PARAM_SUMMARY]: string;
|
||||||
@@ -43,6 +44,7 @@ class CheckpointStateInvocation extends BaseToolInvocation<
|
|||||||
|
|
||||||
override async execute(): Promise<ToolResult> {
|
override async execute(): Promise<ToolResult> {
|
||||||
const summary = this.params[CHECKPOINT_STATE_PARAM_SUMMARY];
|
const summary = this.params[CHECKPOINT_STATE_PARAM_SUMMARY];
|
||||||
|
debugLogger.debug(`[PROJECT CLARITY] Executing CheckpointStateTool with summary length: ${summary.length}`);
|
||||||
const chat = this.config.getGeminiClient().getChat();
|
const chat = this.config.getGeminiClient().getChat();
|
||||||
const previousSummary = chat.getContinuityAnchor();
|
const previousSummary = chat.getContinuityAnchor();
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import type { Config } from '../config/config.js';
|
|||||||
import type { GeminiChat } from '../core/geminiChat.js';
|
import type { GeminiChat } from '../core/geminiChat.js';
|
||||||
import { CompressionStatus } from '../core/compression-status.js';
|
import { CompressionStatus } from '../core/compression-status.js';
|
||||||
import type { ShellExecutionConfig } from 'src/services/shellExecutionService.js';
|
import type { ShellExecutionConfig } from 'src/services/shellExecutionService.js';
|
||||||
|
import { debugLogger } from '../utils/debugLogger.js';
|
||||||
|
|
||||||
class CompressInvocation extends BaseToolInvocation<
|
class CompressInvocation extends BaseToolInvocation<
|
||||||
Record<string, never>,
|
Record<string, never>,
|
||||||
@@ -50,6 +51,7 @@ class CompressInvocation extends BaseToolInvocation<
|
|||||||
if (!callId) {
|
if (!callId) {
|
||||||
throw new Error('Critical error: callId is required for context compression elision.');
|
throw new Error('Critical error: callId is required for context compression elision.');
|
||||||
}
|
}
|
||||||
|
debugLogger.debug(`[PROJECT CLARITY] Executing CompressTool (callId: ${callId})`);
|
||||||
try {
|
try {
|
||||||
const continuityService = this.config.getContinuityCompressionService();
|
const continuityService = this.config.getContinuityCompressionService();
|
||||||
const snapshot = await continuityService.generateSnapshot(
|
const snapshot = await continuityService.generateSnapshot(
|
||||||
|
|||||||
@@ -9,22 +9,19 @@ import {
|
|||||||
BaseToolInvocation,
|
BaseToolInvocation,
|
||||||
Kind,
|
Kind,
|
||||||
type ToolInvocation,
|
type ToolInvocation,
|
||||||
type ToolResult,
|
|
||||||
type ToolLiveOutput,
|
type ToolLiveOutput,
|
||||||
|
type ToolResult,
|
||||||
} from './tools.js';
|
} from './tools.js';
|
||||||
import {
|
import { DISTILL_RESULT_TOOL_NAME } from './tool-names.js';
|
||||||
DISTILL_RESULT_TOOL_NAME,
|
|
||||||
DISTILL_RESULT_PARAM_REVISED_TEXT,
|
|
||||||
} from './tool-names.js';
|
|
||||||
import { DISTILL_RESULT_DEFINITION } from './definitions/coreTools.js';
|
import { DISTILL_RESULT_DEFINITION } from './definitions/coreTools.js';
|
||||||
import type { MessageBus } from '../confirmation-bus/message-bus.js';
|
import type { MessageBus } from '../confirmation-bus/message-bus.js';
|
||||||
import type { Config } from '../config/config.js';
|
import type { Config } from '../config/config.js';
|
||||||
import type { GeminiChat } from '../core/geminiChat.js';
|
import type { GeminiChat } from '../core/geminiChat.js';
|
||||||
import { saveTruncatedToolOutput } from '../utils/fileUtils.js';
|
import { debugLogger } from '../utils/debugLogger.js';
|
||||||
import type { ShellExecutionConfig } from '../index.js';
|
import { saveTruncatedToolOutput } from '../utils/tool-output-helper.js';
|
||||||
|
|
||||||
interface DistillResultParams {
|
interface DistillResultParams {
|
||||||
[DISTILL_RESULT_PARAM_REVISED_TEXT]: string;
|
revised_text: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
class DistillResultInvocation extends BaseToolInvocation<
|
class DistillResultInvocation extends BaseToolInvocation<
|
||||||
@@ -43,30 +40,24 @@ class DistillResultInvocation extends BaseToolInvocation<
|
|||||||
}
|
}
|
||||||
|
|
||||||
override getDescription(): string {
|
override getDescription(): string {
|
||||||
return 'Distills the last tool output to reduce context entropy.';
|
return 'Distills the most recent tool output in the history.';
|
||||||
}
|
}
|
||||||
|
|
||||||
override async execute(
|
override async execute(
|
||||||
_signal: AbortSignal,
|
_signal: AbortSignal,
|
||||||
_updateOutput?: (output: ToolLiveOutput) => void,
|
_updateOutput?: (output: ToolLiveOutput) => void,
|
||||||
_shellExecutionConfig?: ShellExecutionConfig,
|
_shellExecutionConfig?: any,
|
||||||
ownCallId?: string,
|
ownCallId?: string,
|
||||||
): Promise<ToolResult> {
|
): Promise<ToolResult> {
|
||||||
if (!ownCallId) {
|
const revisedText = this.params.revised_text;
|
||||||
throw new Error('Critical error: ownCallId is required for distill_result elision.');
|
|
||||||
}
|
|
||||||
const revisedText = this.params[DISTILL_RESULT_PARAM_REVISED_TEXT];
|
|
||||||
const sideEffects = this.config.getSideEffectService();
|
|
||||||
const history = this.chat.getComprehensiveHistory();
|
|
||||||
|
|
||||||
// 1. Find the last tool response (user message with functionResponse parts)
|
debugLogger.debug(`[PROJECT CLARITY] Executing DistillResultTool (ownCallId: ${ownCallId})`);
|
||||||
|
|
||||||
|
// 1. Find the target: the last function response in history.
|
||||||
|
const history = this.chat.getHistory();
|
||||||
let lastToolResponseIndex = -1;
|
let lastToolResponseIndex = -1;
|
||||||
for (let i = history.length - 1; i >= 0; i--) {
|
for (let i = history.length - 1; i >= 0; i--) {
|
||||||
const content = history[i];
|
if (history[i].parts?.some((p) => p.functionResponse)) {
|
||||||
if (
|
|
||||||
content.role === 'user' &&
|
|
||||||
content.parts?.some((p) => p.functionResponse)
|
|
||||||
) {
|
|
||||||
lastToolResponseIndex = i;
|
lastToolResponseIndex = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -91,6 +82,10 @@ class DistillResultInvocation extends BaseToolInvocation<
|
|||||||
throw new Error('Target call ID missing from tool response.');
|
throw new Error('Target call ID missing from tool response.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debugLogger.debug(`[PROJECT CLARITY] Distill target identified: ${targetCallId} at index ${lastToolResponseIndex}`);
|
||||||
|
|
||||||
|
const sideEffects = this.config.getSideEffectService();
|
||||||
|
|
||||||
// 2. Elide all turns between that tool response and the current turn.
|
// 2. Elide all turns between that tool response and the current turn.
|
||||||
if (ownCallId) {
|
if (ownCallId) {
|
||||||
sideEffects.elideBetween(targetCallId, ownCallId);
|
sideEffects.elideBetween(targetCallId, ownCallId);
|
||||||
|
|||||||
Reference in New Issue
Block a user