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:
1 parent
01b3e2c230
commit
40c8fee531
6 files changed
+72
-9
No files matched your search
@@ -18,6 +18,7 @@ public static class ChatEndpoint
|
||||
public static void MapChatEndpoint(this IEndpointRouteBuilder app)
|
||||
{
|
||||
app.MapPost("/api/chat", HandleChat);
|
||||
app.MapPost("/api/chat/stop", HandleStop);
|
||||
}
|
||||
|
||||
private static async Task HandleChat(HttpRequest req, HttpResponse res, IConfiguration config, ChatService chatService, DatabaseService db, StreamingTracker streamingTracker, AnthropicService anthropic, CancellationToken cancellationToken)
|
||||
@@ -78,14 +79,33 @@ public static class ChatEndpoint
|
||||
res.Headers["Cache-Control"] = "no-cache";
|
||||
res.Headers["Connection"] = "keep-alive";
|
||||
|
||||
streamingTracker.Start(sessionId, ctx.ChainId);
|
||||
// CancellationToken: Verknüpft Stop-Button (über StreamingTracker) + Client-Disconnect
|
||||
var stopCts = streamingTracker.Start(sessionId, ctx.ChainId);
|
||||
using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, stopCts.Token);
|
||||
|
||||
try
|
||||
{
|
||||
await anthropic.StreamChat(res, ctx.ModelId, ctx.SystemPrompt, messages, ctx.ChainId, ctx.Workspace, sessionId, agentId: ctx.AgentId, useMemory: ctx.UseMemory, webSearchVersion: ctx.WebSearchVersion, maxIterations: ctx.MaxIterations, toolChoice: ctx.ToolChoice, cancellationToken: cancellationToken);
|
||||
await anthropic.StreamChat(res, ctx.ModelId, ctx.SystemPrompt, messages, ctx.ChainId, ctx.Workspace, sessionId, agentId: ctx.AgentId, useMemory: ctx.UseMemory, webSearchVersion: ctx.WebSearchVersion, maxIterations: ctx.MaxIterations, toolChoice: ctx.ToolChoice, cancellationToken: linkedCts.Token);
|
||||
}
|
||||
finally
|
||||
{
|
||||
streamingTracker.Stop(sessionId);
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task HandleStop(HttpRequest req, HttpResponse res, StreamingTracker streamingTracker)
|
||||
{
|
||||
var body = await JsonSerializer.DeserializeAsync<JsonElement>(req.Body);
|
||||
var sessionId = body.TryGetProperty("sessionId", out var sid) ? sid.GetString() : null;
|
||||
|
||||
if (string.IsNullOrEmpty(sessionId))
|
||||
{
|
||||
res.StatusCode = 400;
|
||||
await res.WriteAsJsonAsync(new { error = "sessionId required" });
|
||||
return;
|
||||
}
|
||||
|
||||
var cancelled = streamingTracker.Cancel(sessionId);
|
||||
await res.WriteAsJsonAsync(new { stopped = cancelled });
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user