fix sse HANGING tasks
This commit is contained in:
42
RETRIEX_SSE_RUNNING_RECONNECT_FINALIZE_FIX_README.md
Normal file
42
RETRIEX_SSE_RUNNING_RECONNECT_FINALIZE_FIX_README.md
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# RetrieX SSE running reconnect / final cleanup fix
|
||||||
|
|
||||||
|
Patch-only fix for the streamed answer UI after the job-based SSE flow.
|
||||||
|
|
||||||
|
## Problem
|
||||||
|
|
||||||
|
After introducing stream jobs, an EventSource reconnect to an already running job could receive:
|
||||||
|
|
||||||
|
```text
|
||||||
|
retry: 15000
|
||||||
|
|
||||||
|
: duplicate-or-finished-stream
|
||||||
|
|
||||||
|
event: done
|
||||||
|
data: [DONE]
|
||||||
|
```
|
||||||
|
|
||||||
|
Because `running` jobs were treated like harmless duplicate/finished streams, the browser could finalize an incomplete answer as if it had completed successfully. In addition, the final `done` cleanup still used the streaming cleanup path, which can keep the last `.think` block visible.
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
|
||||||
|
- `running` duplicate/reconnect streams are no longer silently closed as successful `[DONE]` completions.
|
||||||
|
- Only already `completed` duplicate streams are silently closed with `done`.
|
||||||
|
- A reconnect to a still-running job now follows the existing explicit error path, so the UI can show a clear interruption message instead of silently accepting a partial answer.
|
||||||
|
- Final stream completion now removes all `.think` blocks and the loader class.
|
||||||
|
|
||||||
|
## Changed files
|
||||||
|
|
||||||
|
- `src/Controller/AskSseController.php`
|
||||||
|
- `public/assets/js/base.js`
|
||||||
|
|
||||||
|
## After installing
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
php bin/console cache:clear
|
||||||
|
php bin/console mto:agent:config:validate
|
||||||
|
php bin/console mto:agent:regression:test
|
||||||
|
```
|
||||||
|
|
||||||
|
Also hard-refresh the browser or clear browser cache because `public/assets/js/base.js` is client-side JavaScript.
|
||||||
@@ -232,16 +232,11 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function cleanupThinkSpans(container, final = false) {
|
function cleanupThinkSpans(container) {
|
||||||
if (!container) {
|
if (!container) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (final) {
|
|
||||||
removeThinkSpansOnly(container);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const thinkSpans = Array.from(container.querySelectorAll('.think'));
|
const thinkSpans = Array.from(container.querySelectorAll('.think'));
|
||||||
|
|
||||||
if (thinkSpans.length === 0) {
|
if (thinkSpans.length === 0) {
|
||||||
@@ -259,9 +254,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
removeThinkSpansOnly(container);
|
removeThinkSpansOnly(container);
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderBubbleContent(bubble, raw, final = false) {
|
function renderBubbleContent(bubble, raw) {
|
||||||
bubble.innerHTML = renderMarkdown(raw);
|
bubble.innerHTML = renderMarkdown(raw);
|
||||||
cleanupThinkSpans(bubble, final);
|
cleanupThinkSpans(bubble);
|
||||||
enhanceChatLinks(bubble);
|
enhanceChatLinks(bubble);
|
||||||
scrollChatToBottom();
|
scrollChatToBottom();
|
||||||
}
|
}
|
||||||
@@ -338,8 +333,11 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
|
|
||||||
function finalizeStream(bubble, raw) {
|
function finalizeStream(bubble, raw) {
|
||||||
clearScheduledRender();
|
clearScheduledRender();
|
||||||
|
bubble.innerHTML = renderMarkdown(raw);
|
||||||
|
removeThinkSpansOnly(bubble);
|
||||||
bubble.classList.remove('loader');
|
bubble.classList.remove('loader');
|
||||||
renderBubbleContent(bubble, raw, true);
|
enhanceChatLinks(bubble);
|
||||||
|
scrollChatToBottom();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function releaseStreamResources() {
|
async function releaseStreamResources() {
|
||||||
@@ -573,7 +571,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
if (raw.trim() !== '') {
|
if (raw.trim() !== '') {
|
||||||
const formattedMessage = `<em>${userMessage}</em>`;
|
const formattedMessage = `<em>${userMessage}</em>`;
|
||||||
raw += raw.trim() === '' ? formattedMessage : `\n\n${formattedMessage}`;
|
raw += raw.trim() === '' ? formattedMessage : `\n\n${formattedMessage}`;
|
||||||
renderBubbleContent(bubble, raw, true);
|
renderBubbleContent(bubble, raw);
|
||||||
} else {
|
} else {
|
||||||
bubble.innerHTML = `<em>${userMessage}</em>`;
|
bubble.innerHTML = `<em>${userMessage}</em>`;
|
||||||
enhanceChatLinks(bubble);
|
enhanceChatLinks(bubble);
|
||||||
|
|||||||
@@ -526,8 +526,7 @@ final readonly class AskSseController
|
|||||||
|
|
||||||
$status = (string) ($claim['status'] ?? '');
|
$status = (string) ($claim['status'] ?? '');
|
||||||
|
|
||||||
return $status === self::JOB_STATUS_RUNNING
|
return $status === self::JOB_STATUS_COMPLETED;
|
||||||
|| $status === self::JOB_STATUS_COMPLETED;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user