Fix conversation reset during streaming
This commit is contained in:
@@ -77,6 +77,8 @@ class Odidere {
|
||||
this.isScrollPending = false;
|
||||
this.isStopPending = false;
|
||||
this.pendingDeleteId = null;
|
||||
// Promise for the currently in-flight runTurn, so reset() can await it.
|
||||
this._currentRun = null;
|
||||
this.currentController = null;
|
||||
this.totalTokens = 0;
|
||||
|
||||
@@ -128,13 +130,26 @@ class Odidere {
|
||||
this.wakeLock.release();
|
||||
}
|
||||
|
||||
reset() {
|
||||
/**
|
||||
* Reset stops any in-flight turn, waits for it to settle, then
|
||||
* clears the conversation, DOM, attachments, location, and input.
|
||||
* The abort and await pattern ensures message streaming in runTurn completes
|
||||
* before conversation.clear().
|
||||
*/
|
||||
async reset() {
|
||||
this.tts.stop();
|
||||
if (this.currentController) {
|
||||
this.currentController.abort();
|
||||
this.currentController = null;
|
||||
}
|
||||
this.setLoadingState(false);
|
||||
|
||||
// Wait for the current run to settle before clearing.
|
||||
if (this._currentRun) {
|
||||
try {
|
||||
await this._currentRun;
|
||||
} catch {}
|
||||
}
|
||||
this.pendingDeleteId = null;
|
||||
this.conversation.clear();
|
||||
this.renderer.setAutoScroll(true);
|
||||
@@ -523,7 +538,14 @@ class Odidere {
|
||||
}
|
||||
|
||||
/**
|
||||
* runTurn sends a chat request to the streaming SSE endpoint.
|
||||
* Build and send a chat request, then consume the streaming SSE response.
|
||||
*
|
||||
* The body executes inside an async IIFE stored as this._currentRun so
|
||||
* reset() can abort the stream and await the IIFE before clearing state.
|
||||
* On interruption (stop or reset), the catch block persists any partially-
|
||||
* received assistant message or pending tool calls; on reset, clear() runs
|
||||
* afterward, so the persisted data is immediately discarded.
|
||||
*
|
||||
* @param {Object} options
|
||||
* @param {string} [options.text]
|
||||
* @param {string} [options.detectedLanguage]
|
||||
@@ -534,13 +556,13 @@ class Odidere {
|
||||
this.renderer.setAutoScroll(true);
|
||||
this.currentController = new AbortController();
|
||||
|
||||
this._currentRun = (async () => {
|
||||
let streamingBubbles = {
|
||||
assistantId: null,
|
||||
message: null,
|
||||
reasoning: null,
|
||||
text: null,
|
||||
};
|
||||
let done = false;
|
||||
let pendingTools = null;
|
||||
|
||||
const streamingMeta = (usage, timings) => {
|
||||
@@ -737,7 +759,11 @@ class Odidere {
|
||||
}
|
||||
|
||||
// Collect tool results.
|
||||
if (message.role === 'tool' && pendingTools && pendingTools.assistant) {
|
||||
if (
|
||||
message.role === 'tool' &&
|
||||
pendingTools &&
|
||||
pendingTools.assistant
|
||||
) {
|
||||
const appended = this.conversation.append([message]);
|
||||
pendingTools.results.push(appended[0]);
|
||||
|
||||
@@ -758,7 +784,6 @@ class Odidere {
|
||||
}
|
||||
}
|
||||
|
||||
done = true;
|
||||
this.setLoadingState(false);
|
||||
} catch (e) {
|
||||
if (e.name !== 'AbortError') {
|
||||
@@ -766,11 +791,9 @@ class Odidere {
|
||||
this.renderer.renderError(`Error: ${e.message}`);
|
||||
}
|
||||
this.setLoadingState(false);
|
||||
} finally {
|
||||
this.currentController = null;
|
||||
|
||||
// Persist a streaming message interrupted by stop or error.
|
||||
if (!done && streamingBubbles.message) {
|
||||
if (streamingBubbles.message) {
|
||||
const meta = streamingMeta(null, null);
|
||||
const finalMessage = streamingBubbles.message;
|
||||
const hasContent = !!extractText(finalMessage.content);
|
||||
@@ -799,7 +822,7 @@ class Odidere {
|
||||
}
|
||||
|
||||
// Finalize pending tool calls interrupted by stop or error.
|
||||
if (!done && pendingTools) {
|
||||
if (pendingTools) {
|
||||
if (pendingTools.$el) {
|
||||
this.renderer.finalizeStreamingText(
|
||||
pendingTools.$el,
|
||||
@@ -813,6 +836,15 @@ class Odidere {
|
||||
]);
|
||||
pendingTools = null;
|
||||
}
|
||||
} finally {
|
||||
this.currentController = null;
|
||||
}
|
||||
})();
|
||||
|
||||
try {
|
||||
await this._currentRun;
|
||||
} finally {
|
||||
this._currentRun = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1226,6 +1258,7 @@ class Odidere {
|
||||
if ($btn) $btn.classList.remove('pending');
|
||||
}
|
||||
this.pendingDeleteId = null;
|
||||
this._currentRun = null;
|
||||
}
|
||||
|
||||
_cancelAllPending() {
|
||||
|
||||
Reference in New Issue
Block a user