summaryrefslogtreecommitdiff
path: root/packages/cli/src/core/gemini-stream.ts
diff options
context:
space:
mode:
authorEvan Senter <[email protected]>2025-04-19 19:45:42 +0100
committerGitHub <[email protected]>2025-04-19 19:45:42 +0100
commit3fce6cea27d3e6129d6c06e528b62e1b11bf7094 (patch)
tree244b8e9ab94f902d65d4bda8739a6538e377ed17 /packages/cli/src/core/gemini-stream.ts
parent0c9e1ef61be7db53e6e73b7208b649cd8cbed6c3 (diff)
Starting to modularize into separate cli / server packages. (#55)
* Starting to move a lot of code into packages/server * More of the massive refactor, builds and runs, some issues though. * Fixing outstanding issue with double messages. * Fixing a minor UI issue. * Fixing the build post-merge. * Running formatting. * Addressing comments.
Diffstat (limited to 'packages/cli/src/core/gemini-stream.ts')
-rw-r--r--packages/cli/src/core/gemini-stream.ts183
1 files changed, 9 insertions, 174 deletions
diff --git a/packages/cli/src/core/gemini-stream.ts b/packages/cli/src/core/gemini-stream.ts
index 182291f7..d41c50d7 100644
--- a/packages/cli/src/core/gemini-stream.ts
+++ b/packages/cli/src/core/gemini-stream.ts
@@ -4,181 +4,16 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import { ToolCallEvent, HistoryItem } from '../ui/types.js';
-import { Part } from '@google/genai';
-import {
- handleToolCallChunk,
- addErrorMessageToHistory,
-} from './history-updater.js';
-
-export enum GeminiEventType {
- Content,
- ToolCallInfo,
-}
-
-export interface GeminiContentEvent {
- type: GeminiEventType.Content;
- value: string;
-}
-
-export interface GeminiToolCallInfoEvent {
- type: GeminiEventType.ToolCallInfo;
- value: ToolCallEvent;
-}
-
-export type GeminiEvent = GeminiContentEvent | GeminiToolCallInfoEvent;
-
-export type GeminiStream = AsyncIterable<GeminiEvent>;
-
+// Only defining the state enum needed by the UI
export enum StreamingState {
- Idle,
- Responding,
+ Idle = 'idle',
+ Responding = 'responding',
+ WaitingForConfirmation = 'waiting_for_confirmation',
}
-interface StreamProcessorParams {
- stream: GeminiStream;
- signal: AbortSignal;
- setHistory: React.Dispatch<React.SetStateAction<HistoryItem[]>>;
- submitQuery: (query: Part) => Promise<void>;
- getNextMessageId: () => number;
- addHistoryItem: (itemData: Omit<HistoryItem, 'id'>, id: number) => void;
- currentToolGroupIdRef: React.MutableRefObject<number | null>;
+// Copied from server/src/core/turn.ts for CLI usage
+export enum GeminiEventType {
+ Content = 'content',
+ ToolCallRequest = 'tool_call_request',
+ // Add other event types if the UI hook needs to handle them
}
-
-/**
- * Processes the Gemini stream, managing text buffering, adaptive rendering,
- * and delegating history updates for tool calls and errors.
- */
-export const processGeminiStream = async ({
- // Renamed function for clarity
- stream,
- signal,
- setHistory,
- submitQuery,
- getNextMessageId,
- addHistoryItem,
- currentToolGroupIdRef,
-}: StreamProcessorParams): Promise<void> => {
- // --- State specific to this stream processing invocation ---
- let textBuffer = '';
- let renderTimeoutId: NodeJS.Timeout | null = null;
- let isStreamComplete = false;
- let currentGeminiMessageId: number | null = null;
-
- const render = (content: string) => {
- if (currentGeminiMessageId === null) {
- return;
- }
- setHistory((prev) =>
- prev.map((item) =>
- item.id === currentGeminiMessageId && item.type === 'gemini'
- ? { ...item, text: (item.text ?? '') + content }
- : item,
- ),
- );
- };
- // --- Adaptive Rendering Logic (nested) ---
- const renderBufferedText = () => {
- if (signal.aborted) {
- if (renderTimeoutId) clearTimeout(renderTimeoutId);
- renderTimeoutId = null;
- return;
- }
-
- const bufferLength = textBuffer.length;
- let chunkSize = 0;
- let delay = 50;
-
- if (bufferLength > 150) {
- chunkSize = Math.min(bufferLength, 30);
- delay = 5;
- } else if (bufferLength > 30) {
- chunkSize = Math.min(bufferLength, 10);
- delay = 10;
- } else if (bufferLength > 0) {
- chunkSize = 2;
- delay = 20;
- }
-
- if (chunkSize > 0) {
- const chunkToRender = textBuffer.substring(0, chunkSize);
- textBuffer = textBuffer.substring(chunkSize);
- render(chunkToRender);
-
- renderTimeoutId = setTimeout(renderBufferedText, delay);
- } else {
- renderTimeoutId = null; // Clear timeout ID if nothing to render
- if (!isStreamComplete) {
- // Buffer empty, but stream might still send data, check again later
- renderTimeoutId = setTimeout(renderBufferedText, 50);
- }
- }
- };
-
- const scheduleRender = () => {
- if (renderTimeoutId === null) {
- renderTimeoutId = setTimeout(renderBufferedText, 0);
- }
- };
-
- // --- Stream Processing Loop ---
- try {
- for await (const chunk of stream) {
- if (signal.aborted) break;
-
- if (chunk.type === GeminiEventType.Content) {
- currentToolGroupIdRef.current = null; // Reset tool group on text
-
- if (currentGeminiMessageId === null) {
- currentGeminiMessageId = getNextMessageId();
- addHistoryItem({ type: 'gemini', text: '' }, currentGeminiMessageId);
- textBuffer = '';
- }
- textBuffer += chunk.value;
- scheduleRender();
- } else if (chunk.type === GeminiEventType.ToolCallInfo) {
- if (renderTimeoutId) {
- // Stop rendering loop
- clearTimeout(renderTimeoutId);
- renderTimeoutId = null;
- }
- // Flush any text buffer content.
- render(textBuffer);
- currentGeminiMessageId = null; // End text message context
- textBuffer = ''; // Clear buffer
-
- // Delegate history update for tool call
- handleToolCallChunk(
- chunk.value,
- setHistory,
- submitQuery,
- getNextMessageId,
- currentToolGroupIdRef,
- );
- }
- }
- if (signal.aborted) {
- throw new Error('Request cancelled by user');
- }
- } catch (error: unknown) {
- if (renderTimeoutId) {
- // Ensure render loop stops on error
- clearTimeout(renderTimeoutId);
- renderTimeoutId = null;
- }
- // Delegate history update for error message
- addErrorMessageToHistory(
- error as Error | DOMException,
- setHistory,
- getNextMessageId,
- );
- } finally {
- isStreamComplete = true; // Signal stream end for render loop completion
- if (renderTimeoutId) {
- clearTimeout(renderTimeoutId);
- renderTimeoutId = null;
- }
-
- renderBufferedText(); // Force final render
- }
-};