fix(tui): stop a stream outlived by /new from leaking into the next conversation
package / wheel (pull_request) Waiting to run
package / wheel (pull_request) Waiting to run
_finish_stream appended the completed reply to self.history - whatever list that name currently pointed at - not to the conversation the stream was actually answering. /new reassigns self.history to a fresh list; a stream still running when that happens finished by silently appending the old conversation's trailing reply onto the new one, which then rides along in that new conversation's next /chat/stream history payload. The conversation_id was already captured by closure for this exact reason (see the tool-denial path); self.history needed the same treatment.
This commit is contained in:
+10
-5
@@ -359,14 +359,19 @@ class NexusTUI:
|
||||
if not self.conversation_id:
|
||||
self.conversation_id = str(uuid.uuid4())
|
||||
conversation_id = self.conversation_id
|
||||
# The list object itself, not self.history - /new reassigns
|
||||
# self.history to a fresh list, and a stream that outlives that
|
||||
# must keep appending its reply to the conversation it actually
|
||||
# belongs to, not whatever self.history now points at.
|
||||
history_ref = self.history
|
||||
body: dict[str, Any] = {
|
||||
"message": message,
|
||||
"conversation_id": conversation_id,
|
||||
"history": list(self.history),
|
||||
"history": list(history_ref),
|
||||
}
|
||||
if self._model:
|
||||
body["model"] = self._model
|
||||
self.history.append({"role": "user", "content": message})
|
||||
history_ref.append({"role": "user", "content": message})
|
||||
|
||||
async def stream_worker():
|
||||
reply_parts: list[str] = []
|
||||
@@ -474,19 +479,19 @@ class NexusTUI:
|
||||
if self._stream_cancel == (loop, task):
|
||||
self._stream_cancel = None
|
||||
text = "".join(reply_parts).strip()
|
||||
self._call_ui(self._finish_stream, text)
|
||||
self._call_ui(self._finish_stream, text, history_ref)
|
||||
|
||||
threading.Thread(
|
||||
target=lambda: asyncio.run(stream_worker()), daemon=True
|
||||
).start()
|
||||
|
||||
def _finish_stream(self, text: str) -> None:
|
||||
def _finish_stream(self, text: str, history_ref: list) -> None:
|
||||
log = self.query_one("#log", RichLog)
|
||||
live = self.query_one("#live", Static)
|
||||
try:
|
||||
if text:
|
||||
log.write(format_assistant_line(text))
|
||||
self.history.append(
|
||||
history_ref.append(
|
||||
{"role": "assistant", "content": text}
|
||||
)
|
||||
finally:
|
||||
|
||||
Reference in New Issue
Block a user