Fix bugs from useGeminiStream refactor (#284)
This commit is contained in:
parent
d524309e3c
commit
13eadcea45
|
@ -31,7 +31,7 @@ import { isAtCommand } from '../utils/commandUtils.js';
|
||||||
import { useSlashCommandProcessor } from './slashCommandProcessor.js';
|
import { useSlashCommandProcessor } from './slashCommandProcessor.js';
|
||||||
import { useShellCommandProcessor } from './shellCommandProcessor.js';
|
import { useShellCommandProcessor } from './shellCommandProcessor.js';
|
||||||
import { handleAtCommand } from './atCommandProcessor.js';
|
import { handleAtCommand } from './atCommandProcessor.js';
|
||||||
import { findSafeSplitPoint } from '../utils/markdownUtilities.js';
|
import { findLastSafeSplitPoint } from '../utils/markdownUtilities.js';
|
||||||
import { useStateAndRef } from './useStateAndRef.js';
|
import { useStateAndRef } from './useStateAndRef.js';
|
||||||
import { UseHistoryManagerReturn } from './useHistoryManager.js';
|
import { UseHistoryManagerReturn } from './useHistoryManager.js';
|
||||||
|
|
||||||
|
@ -174,14 +174,12 @@ export const useGeminiStream = (
|
||||||
signal,
|
signal,
|
||||||
);
|
);
|
||||||
|
|
||||||
let currentGeminiText = '';
|
let geminiMessageBuffer = '';
|
||||||
|
|
||||||
for await (const event of stream) {
|
for await (const event of stream) {
|
||||||
if (signal.aborted) break;
|
if (signal.aborted) break;
|
||||||
|
|
||||||
if (event.type === ServerGeminiEventType.Content) {
|
if (event.type === ServerGeminiEventType.Content) {
|
||||||
currentGeminiText += event.value;
|
|
||||||
|
|
||||||
if (pendingHistoryItemRef.current?.type !== 'gemini') {
|
if (pendingHistoryItemRef.current?.type !== 'gemini') {
|
||||||
// Flush out existing pending history item.
|
// Flush out existing pending history item.
|
||||||
if (pendingHistoryItemRef.current) {
|
if (pendingHistoryItemRef.current) {
|
||||||
|
@ -189,19 +187,22 @@ export const useGeminiStream = (
|
||||||
}
|
}
|
||||||
setPendingHistoryItem({
|
setPendingHistoryItem({
|
||||||
type: 'gemini',
|
type: 'gemini',
|
||||||
text: currentGeminiText,
|
text: '',
|
||||||
});
|
});
|
||||||
|
geminiMessageBuffer = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Split large messages for better rendering performance
|
geminiMessageBuffer += event.value;
|
||||||
const splitPoint = findSafeSplitPoint(currentGeminiText);
|
|
||||||
if (splitPoint === currentGeminiText.length) {
|
// Split large messages for better rendering performance. Ideally,
|
||||||
|
// we should maximize the amount of output sent to <Static />.
|
||||||
|
const splitPoint = findLastSafeSplitPoint(geminiMessageBuffer);
|
||||||
|
if (splitPoint === geminiMessageBuffer.length) {
|
||||||
// Update the existing message with accumulated content
|
// Update the existing message with accumulated content
|
||||||
setPendingHistoryItem((pending) => ({
|
setPendingHistoryItem({
|
||||||
// There might be a more typesafe way to do this.
|
type: 'gemini',
|
||||||
...pending!,
|
text: geminiMessageBuffer,
|
||||||
text: currentGeminiText,
|
});
|
||||||
}));
|
|
||||||
} else {
|
} else {
|
||||||
// This indicates that we need to split up this Gemini Message.
|
// This indicates that we need to split up this Gemini Message.
|
||||||
// Splitting a message is primarily a performance consideration. There is a
|
// Splitting a message is primarily a performance consideration. There is a
|
||||||
|
@ -211,21 +212,19 @@ export const useGeminiStream = (
|
||||||
// multiple times per-second (as streaming occurs). Prior to this change you'd
|
// multiple times per-second (as streaming occurs). Prior to this change you'd
|
||||||
// see heavy flickering of the terminal. This ensures that larger messages get
|
// see heavy flickering of the terminal. This ensures that larger messages get
|
||||||
// broken up so that there are more "statically" rendered.
|
// broken up so that there are more "statically" rendered.
|
||||||
const beforeText = currentGeminiText.substring(0, splitPoint);
|
const beforeText = geminiMessageBuffer.substring(0, splitPoint);
|
||||||
const afterText = currentGeminiText.substring(splitPoint);
|
const afterText = geminiMessageBuffer.substring(splitPoint);
|
||||||
currentGeminiText = afterText; // Continue accumulating from split point
|
geminiMessageBuffer = afterText; // Continue accumulating from split point
|
||||||
addItem(
|
addItem(
|
||||||
{ type: 'gemini_content', text: beforeText },
|
{ type: 'gemini', text: beforeText },
|
||||||
userMessageTimestamp,
|
userMessageTimestamp,
|
||||||
);
|
);
|
||||||
setPendingHistoryItem({
|
setPendingHistoryItem({
|
||||||
type: 'gemini_content',
|
type: 'gemini',
|
||||||
text: afterText,
|
text: afterText,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (event.type === ServerGeminiEventType.ToolCallRequest) {
|
} else if (event.type === ServerGeminiEventType.ToolCallRequest) {
|
||||||
currentGeminiText = '';
|
|
||||||
|
|
||||||
const { callId, name, args } = event.value;
|
const { callId, name, args } = event.value;
|
||||||
const cliTool = toolRegistry.getTool(name);
|
const cliTool = toolRegistry.getTool(name);
|
||||||
if (!cliTool) {
|
if (!cliTool) {
|
||||||
|
|
|
@ -184,3 +184,35 @@ export const findSafeSplitPoint = (
|
||||||
// to keep the entire content as one piece.
|
// to keep the entire content as one piece.
|
||||||
return content.length;
|
return content.length;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const findLastSafeSplitPoint = (content: string) => {
|
||||||
|
const enclosingBlockStart = findEnclosingCodeBlockStart(
|
||||||
|
content,
|
||||||
|
content.length,
|
||||||
|
);
|
||||||
|
if (enclosingBlockStart !== -1) {
|
||||||
|
// The end of the content is contained in a code block. Split right before.
|
||||||
|
return enclosingBlockStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search for the last double newline (\n\n) not in a code block.
|
||||||
|
let searchStartIndex = content.length;
|
||||||
|
while (searchStartIndex >= 0) {
|
||||||
|
const dnlIndex = content.lastIndexOf('\n\n', searchStartIndex);
|
||||||
|
if (dnlIndex === -1) {
|
||||||
|
// No more double newlines found after idealMaxLength
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const potentialSplitPoint = dnlIndex + 2;
|
||||||
|
if (!isIndexInsideCodeBlock(content, potentialSplitPoint)) {
|
||||||
|
return potentialSplitPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
searchStartIndex = potentialSplitPoint; // Continue search after the found \n\n
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no safe double newline found after idealMaxLength, return content.length
|
||||||
|
// to keep the entire content as one piece.
|
||||||
|
return content.length;
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in New Issue