mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-06-18 07:17:16 -07:00
feat(cli): first working version of user simulator
This commit is contained in:
@@ -12,6 +12,12 @@ import { ConsolePatcher } from './ui/utils/ConsolePatcher.js';
|
||||
import { UserSimulator } from './services/UserSimulator.js';
|
||||
import { registerCleanup, setupTtyCheck } from './utils/cleanup.js';
|
||||
import { PassThrough } from 'node:stream';
|
||||
|
||||
interface RenderMetrics {
|
||||
renderTime: number;
|
||||
output: string;
|
||||
staticOutput?: string;
|
||||
}
|
||||
import {
|
||||
type StartupWarning,
|
||||
type Config,
|
||||
@@ -137,6 +143,7 @@ export async function startInteractiveUI(
|
||||
const simulateUser = config.getSimulateUser();
|
||||
const simulatedStdin = new PassThrough({ encoding: 'utf8' });
|
||||
|
||||
let lastFrame: string | undefined;
|
||||
const instance = render(
|
||||
process.env['DEBUG'] ? (
|
||||
<React.StrictMode>
|
||||
@@ -152,9 +159,10 @@ export async function startInteractiveUI(
|
||||
stdin: (simulateUser ? simulatedStdin : process.stdin) as any,
|
||||
exitOnCtrlC: false,
|
||||
isScreenReaderEnabled: config.getScreenReader(),
|
||||
onRender: ({ renderTime }: { renderTime: number }) => {
|
||||
if (renderTime > SLOW_RENDER_MS) {
|
||||
recordSlowRender(config, renderTime);
|
||||
onRender: (metrics: RenderMetrics) => {
|
||||
lastFrame = metrics.output;
|
||||
if (metrics.renderTime > SLOW_RENDER_MS) {
|
||||
recordSlowRender(config, metrics.renderTime);
|
||||
}
|
||||
profiler.reportFrameRendered();
|
||||
},
|
||||
@@ -186,7 +194,11 @@ export async function startInteractiveUI(
|
||||
});
|
||||
|
||||
if (simulateUser) {
|
||||
const simulator = new UserSimulator(config, instance, simulatedStdin);
|
||||
const simulator = new UserSimulator(
|
||||
config,
|
||||
() => lastFrame,
|
||||
simulatedStdin,
|
||||
);
|
||||
simulator.start();
|
||||
registerCleanup(() => simulator.stop());
|
||||
}
|
||||
|
||||
@@ -3,10 +3,9 @@
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
import type { Config} from '@google/gemini-cli-core';
|
||||
import { debugLogger, LlmRole } from '@google/gemini-cli-core';
|
||||
import type { Config } from '@google/gemini-cli-core';
|
||||
import { debugLogger, LlmRole, resolveModel } from '@google/gemini-cli-core';
|
||||
import type { Writable } from 'node:stream';
|
||||
import type { Instance } from 'ink';
|
||||
|
||||
export class UserSimulator {
|
||||
private isRunning = false;
|
||||
@@ -16,7 +15,7 @@ export class UserSimulator {
|
||||
|
||||
constructor(
|
||||
private readonly config: Config,
|
||||
private readonly instance: Instance,
|
||||
private readonly getScreen: () => string | undefined,
|
||||
private readonly stdinBuffer: Writable,
|
||||
) {}
|
||||
|
||||
@@ -42,8 +41,7 @@ export class UserSimulator {
|
||||
|
||||
try {
|
||||
this.isProcessing = true;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion, @typescript-eslint/no-explicit-any
|
||||
const screen = (this.instance as any).lastFrame() as string | undefined;
|
||||
const screen = this.getScreen();
|
||||
if (!screen || screen === this.lastScreenContent) return;
|
||||
|
||||
const strippedScreen = screen.replace(
|
||||
@@ -74,9 +72,17 @@ For example:
|
||||
- To enter a new prompt, output the text followed by \n.
|
||||
Do NOT output markdown, explanations of your thought process, or quotes. Output ONLY the raw characters to send or <WAIT>.`;
|
||||
|
||||
const model = resolveModel(
|
||||
this.config.getModel(),
|
||||
false, // useGemini3_1
|
||||
false, // useCustomToolModel
|
||||
this.config.getHasAccessToPreviewModel?.() ?? true,
|
||||
this.config,
|
||||
);
|
||||
|
||||
const response = await contentGenerator.generateContent(
|
||||
{
|
||||
model: this.config.getModel(),
|
||||
model,
|
||||
contents: [
|
||||
{
|
||||
role: 'user',
|
||||
|
||||
Reference in New Issue
Block a user