Refactor loop detection logic in ChatInput
- Use message finish state instead of session status for loop detection - Add lastProcessedMessageId to prevent duplicate loop triggers - Add XCOpenCodeWeb.exe to .gitignore Note: Single-file exe now builds to ~150MB (down from ~300MB with Electron)
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -54,3 +54,4 @@ packages/intellij/
|
||||
|
||||
# OS
|
||||
Thumbs.db
|
||||
web/XCOpenCodeWeb.exe
|
||||
|
||||
@@ -167,11 +167,9 @@ export const ChatInput: React.FC<ChatInputProps> = ({ onOpenSettings, scrollToBo
|
||||
current: number;
|
||||
total: number;
|
||||
prompt: string;
|
||||
lastProcessedMessageId: string | null;
|
||||
} | null>(null);
|
||||
|
||||
// Get session status from store for loop detection
|
||||
const sessionStatus = useSessionStore((state) => state.sessionStatus);
|
||||
|
||||
const isDesktopExpanded = isExpandedInput && !isMobile;
|
||||
|
||||
const sendableAttachedFiles = React.useMemo(
|
||||
@@ -856,6 +854,7 @@ export const ChatInput: React.FC<ChatInputProps> = ({ onOpenSettings, scrollToBo
|
||||
current: 1,
|
||||
total: loopNum,
|
||||
prompt: loopArgs,
|
||||
lastProcessedMessageId: null,
|
||||
});
|
||||
toast.info(`Loop started: ${loopNum} iterations`);
|
||||
// Clear input
|
||||
@@ -952,37 +951,46 @@ export const ChatInput: React.FC<ChatInputProps> = ({ onOpenSettings, scrollToBo
|
||||
// Update ref with latest handleSubmit on every render
|
||||
handleSubmitRef.current = handleSubmit;
|
||||
|
||||
// Handle loop: detect when AI finishes and send next iteration
|
||||
// Handle loop: detect when AI finishes (finish === 'stop') and send next iteration
|
||||
React.useEffect(() => {
|
||||
if (!loopState?.active || !currentSessionId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const status = sessionStatus?.get(currentSessionId);
|
||||
if (status?.type === 'idle') {
|
||||
// AI finished, check if we should continue looping
|
||||
if (loopState.current <= loopState.total) {
|
||||
if (loopState.current < loopState.total) {
|
||||
const next = loopState.current + 1;
|
||||
setLoopState({ ...loopState, current: next });
|
||||
useSessionStore.getState().sendMessage(
|
||||
loopState.prompt,
|
||||
currentProviderId,
|
||||
currentModelId,
|
||||
currentAgentName,
|
||||
[],
|
||||
undefined,
|
||||
undefined,
|
||||
currentVariant,
|
||||
inputMode
|
||||
);
|
||||
} else {
|
||||
// Loop complete - all iterations sent
|
||||
setLoopState(null);
|
||||
}
|
||||
const messages = useMessageStore.getState().messages.get(currentSessionId);
|
||||
if (!messages) return;
|
||||
|
||||
const assistantMessages = messages.filter((m) => m.info.role === 'assistant');
|
||||
const lastAssistant = assistantMessages[assistantMessages.length - 1];
|
||||
if (!lastAssistant) return;
|
||||
|
||||
const messageId = lastAssistant.info.id;
|
||||
const finish = lastAssistant.info.finish;
|
||||
|
||||
if (finish === 'stop' && messageId !== loopState.lastProcessedMessageId) {
|
||||
if (loopState.current < loopState.total) {
|
||||
const next = loopState.current + 1;
|
||||
setLoopState({
|
||||
...loopState,
|
||||
current: next,
|
||||
lastProcessedMessageId: messageId,
|
||||
});
|
||||
useSessionStore.getState().sendMessage(
|
||||
loopState.prompt,
|
||||
currentProviderId,
|
||||
currentModelId,
|
||||
currentAgentName,
|
||||
[],
|
||||
undefined,
|
||||
undefined,
|
||||
currentVariant,
|
||||
inputMode
|
||||
);
|
||||
} else {
|
||||
setLoopState(null);
|
||||
}
|
||||
}
|
||||
}, [loopState, sessionStatus, currentSessionId, currentProviderId, currentModelId, currentAgentName, currentVariant, inputMode]);
|
||||
}, [loopState, sessionMessages, currentSessionId, currentProviderId, currentModelId, currentAgentName, currentVariant, inputMode]);
|
||||
|
||||
// Primary action for send button - respects queue mode setting
|
||||
const handlePrimaryAction = React.useCallback(() => {
|
||||
|
||||
Reference in New Issue
Block a user