fix: CLI-Stream überlebt Page-Reload (Token-Trennung workCt/sseCt)
workCt steuert den CLI-Prozess (nur Stop-Button), sseCt die SSE-Writes (Client-Disconnect). Page-Reload cancelt nur sseCt → CLI läuft weiter. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
7d55e8e818
commit
ee0b817f25
2 files changed
+26
-17
No files matched your search
@@ -1886,12 +1886,13 @@ public class AnthropicService
|
||||
bool planMode = false,
|
||||
bool webSearch = false,
|
||||
string sessionId = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
CancellationToken workCt = default,
|
||||
CancellationToken sseCt = default)
|
||||
{
|
||||
if (_cliService == null || !_cliService.IsAvailable)
|
||||
{
|
||||
await SseHelper.TryWriteSse(response, "error", new { error = "Claude CLI nicht verfügbar" }, cancellationToken);
|
||||
await SseHelper.TryWriteSse(response, "done", new { chainId }, cancellationToken);
|
||||
await SseHelper.TryWriteSse(response, "error", new { error = "Claude CLI nicht verfügbar" }, sseCt);
|
||||
await SseHelper.TryWriteSse(response, "done", new { chainId }, sseCt);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1964,14 +1965,14 @@ Danach STOPPE die Textgenerierung sofort. Nutze dies sparsam — nur wenn die En
|
||||
switch (evt.Type)
|
||||
{
|
||||
case CliEventType.TextStart:
|
||||
await SseHelper.TryWriteSse(response, "text_start", new { index = 0 }, cancellationToken);
|
||||
await SseHelper.TryWriteSse(response, "text_start", new { index = 0 }, sseCt);
|
||||
break;
|
||||
case CliEventType.TextDelta:
|
||||
fullText.Append(evt.Text);
|
||||
await SseHelper.TryWriteSse(response, "text_delta", new { text = evt.Text }, cancellationToken);
|
||||
await SseHelper.TryWriteSse(response, "text_delta", new { text = evt.Text }, sseCt);
|
||||
break;
|
||||
case CliEventType.TextEnd:
|
||||
await SseHelper.TryWriteSse(response, "text_end", new { }, cancellationToken);
|
||||
await SseHelper.TryWriteSse(response, "text_end", new { }, sseCt);
|
||||
break;
|
||||
case CliEventType.ToolStart:
|
||||
{
|
||||
@@ -1989,7 +1990,7 @@ Danach STOPPE die Textgenerierung sofort. Nutze dies sparsam — nur wenn die En
|
||||
fullText.Clear();
|
||||
}
|
||||
var mappedName = MapCliToolName(evt.ToolName);
|
||||
await SseHelper.TryWriteSse(response, "tool_start", new { name = mappedName, id = evt.ToolId }, cancellationToken);
|
||||
await SseHelper.TryWriteSse(response, "tool_start", new { name = mappedName, id = evt.ToolId }, sseCt);
|
||||
break;
|
||||
}
|
||||
case CliEventType.ToolEnd:
|
||||
@@ -2007,7 +2008,7 @@ Danach STOPPE die Textgenerierung sofort. Nutze dies sparsam — nur wenn die En
|
||||
ToolCallId = evt.ToolId,
|
||||
Content = mappedInput
|
||||
});
|
||||
await SseHelper.TryWriteSse(response, "tool_end", new { name = mappedName, id = evt.ToolId, input = mappedInput }, cancellationToken);
|
||||
await SseHelper.TryWriteSse(response, "tool_end", new { name = mappedName, id = evt.ToolId, input = mappedInput }, sseCt);
|
||||
break;
|
||||
}
|
||||
case CliEventType.ToolResult:
|
||||
@@ -2030,7 +2031,7 @@ Danach STOPPE die Textgenerierung sofort. Nutze dies sparsam — nur wenn die En
|
||||
id = evt.ToolId,
|
||||
result = resultContent,
|
||||
isError = evt.ToolResultIsError
|
||||
}, cancellationToken);
|
||||
}, sseCt);
|
||||
break;
|
||||
}
|
||||
case CliEventType.UserChoice:
|
||||
@@ -2066,7 +2067,7 @@ Danach STOPPE die Textgenerierung sofort. Nutze dies sparsam — nur wenn die En
|
||||
toolCallId = choiceToolCallId,
|
||||
question = evt.ChoiceQuestion,
|
||||
choices = evt.ChoiceOptions
|
||||
}, cancellationToken);
|
||||
}, sseCt);
|
||||
|
||||
isUserChoice = true;
|
||||
break;
|
||||
@@ -2078,7 +2079,7 @@ Danach STOPPE die Textgenerierung sofort. Nutze dies sparsam — nur wenn die En
|
||||
hasWorkspace: !string.IsNullOrEmpty(workspace),
|
||||
planMode: planMode,
|
||||
webSearch: webSearch,
|
||||
cancellationToken: cancellationToken
|
||||
cancellationToken: workCt
|
||||
);
|
||||
|
||||
// Bei CLI-Fehler → Fehlermeldung an Client senden und in DB speichern
|
||||
@@ -2094,7 +2095,7 @@ Danach STOPPE die Textgenerierung sofort. Nutze dies sparsam — nur wenn die En
|
||||
Type = "error",
|
||||
Content = $"CLI-Fehler: {errorMsg}"
|
||||
});
|
||||
await SseHelper.TryWriteSse(response, "error", new { error = $"CLI-Fehler: {errorMsg}" }, cancellationToken);
|
||||
await SseHelper.TryWriteSse(response, "error", new { error = $"CLI-Fehler: {errorMsg}" }, sseCt);
|
||||
}
|
||||
|
||||
// Antwort in DB speichern (nur wenn nicht schon im UserChoice-Handler gespeichert)
|
||||
@@ -2132,10 +2133,11 @@ Danach STOPPE die Textgenerierung sofort. Nutze dies sparsam — nur wenn die En
|
||||
durationMs = sw.ElapsedMilliseconds,
|
||||
stopReason,
|
||||
viaCli = true
|
||||
}, cancellationToken);
|
||||
}, sseCt);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
catch (OperationCanceledException) when (workCt.IsCancellationRequested)
|
||||
{
|
||||
// Stop-Button gedrückt → CLI-Prozess wurde gestoppt
|
||||
await FinalizeChain(chain, usage, 1, "stopped", sw);
|
||||
await SseHelper.TryWriteSse(response, "done", new
|
||||
{
|
||||
@@ -2146,6 +2148,13 @@ Danach STOPPE die Textgenerierung sofort. Nutze dies sparsam — nur wenn die En
|
||||
viaCli = true
|
||||
}, CancellationToken.None);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// Client-Disconnect (sseCt gecancelled, aber NICHT workCt)
|
||||
// CLI-Prozess läuft weiter im Hintergrund – hier nur Chain finalisieren
|
||||
// (sollte nicht mehr passieren, da CLI jetzt workCt nutzt)
|
||||
await FinalizeChain(chain, usage, 1, "client_disconnected", sw);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var errorMsg = $"CLI-Fehler: {ex.Message}";
|
||||
@@ -2157,7 +2166,7 @@ Danach STOPPE die Textgenerierung sofort. Nutze dies sparsam — nur wenn die En
|
||||
Type = "error",
|
||||
Content = errorMsg
|
||||
});
|
||||
await SseHelper.TryWriteSse(response, "error", new { error = errorMsg }, cancellationToken);
|
||||
await SseHelper.TryWriteSse(response, "error", new { error = errorMsg }, sseCt);
|
||||
await FinalizeChain(chain, usage, 1, "error", sw);
|
||||
await SseHelper.TryWriteSse(response, "done", new
|
||||
{
|
||||
@@ -2166,7 +2175,7 @@ Danach STOPPE die Textgenerierung sofort. Nutze dies sparsam — nur wenn die En
|
||||
durationMs = sw.ElapsedMilliseconds,
|
||||
stopReason = "error",
|
||||
viaCli = true
|
||||
}, cancellationToken);
|
||||
}, sseCt);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user