feat: zuverlässiger Stop-Button für Chat-Streaming

- StreamingTracker: CancellationTokenSource pro Session, Cancel()-Methode
- ChatEndpoint: POST /api/chat/stop Endpoint, linkedCts verknüpft
  Stop-Token mit Client-Disconnect-Token
- AnthropicService: cancellationToken an ExecuteTool durchgereicht
- ToolService: CancellationToken-Parameter, execute-Tool verknüpft
  externen Token mit 180s-Timeout, killt Prozess bei Abbruch
- Frontend: apiStopChat() + stopStreaming() ruft Backend-Stop auf
This commit is contained in:
Meleki committed 2026-03-06 15:44:08 +01:00
1 parent 01b3e2c230
commit 40c8fee531
6 files changed
+72 -9

No files matched your search

+6 -2
View File
@@ -232,7 +232,7 @@ public class ToolService
};
}
public async Task<string> ExecuteTool(string name, JsonElement input, string workspace, MemoryService memoryService = null, string agentId = null)
public async Task<string> ExecuteTool(string name, JsonElement input, string workspace, MemoryService memoryService = null, string agentId = null, CancellationToken cancellationToken = default)
{
switch (name)
{
@@ -322,7 +322,9 @@ public class ToolService
if (proc == null) return "Fehler: Prozess konnte nicht gestartet werden";
await proc.StandardInput.WriteAsync(command);
proc.StandardInput.Close();
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(180));
// Timeout (180s) UND externen CancellationToken verknüpfen
using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
cts.CancelAfter(TimeSpan.FromSeconds(180));
try
{
await proc.WaitForExitAsync(cts.Token);
@@ -330,6 +332,8 @@ public class ToolService
catch (OperationCanceledException)
{
proc.Kill(entireProcessTree: true);
// Wenn der externe Token abgebrochen wurde, weiterwerfen
cancellationToken.ThrowIfCancellationRequested();
return "Fehler: Timeout nach 180 Sekunden - Prozess abgebrochen";
}
var stdout = await proc.StandardOutput.ReadToEndAsync();