86 lines
2.8 KiB
C#
86 lines
2.8 KiB
C#
using System.Diagnostics;
|
||
using System.Text.Json;
|
||
using System.Threading;
|
||
using System.Threading.Tasks;
|
||
using Gott.Models;
|
||
using Microsoft.AspNetCore.Http;
|
||
|
||
namespace Gott.Services;
|
||
|
||
/// <summary>
|
||
/// Shared SSE-Streaming-Utilities für AnthropicService und OpenAiService.
|
||
/// </summary>
|
||
public static class SseHelper
|
||
{
|
||
/// <summary>
|
||
/// Schreibt ein SSE-Event (wirft bei CancellationToken).
|
||
/// </summary>
|
||
public static async Task WriteSseEvent(HttpResponse response, string eventType, object data, CancellationToken ct = default)
|
||
{
|
||
ct.ThrowIfCancellationRequested();
|
||
var json = JsonSerializer.Serialize(data);
|
||
await response.WriteAsync($"event: {eventType}\ndata: {json}\n\n", ct);
|
||
await response.Body.FlushAsync(ct);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Versucht ein SSE-Event zu schreiben, ignoriert Fehler (Client disconnected).
|
||
/// </summary>
|
||
public static async Task TryWriteSse(HttpResponse response, string eventType, object data, CancellationToken ct)
|
||
{
|
||
try
|
||
{
|
||
if (!ct.IsCancellationRequested)
|
||
await WriteSseEvent(response, eventType, data, ct);
|
||
}
|
||
catch
|
||
{
|
||
// Client bereits disconnected – ignorieren
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Finalisiert eine Chain in der Datenbank (Token-Counts, Stopgrund, Dauer).
|
||
/// </summary>
|
||
public static async Task FinalizeChain(DatabaseService db, ChainRecord chain, TokenUsage usage, int iterations, string stopReason, Stopwatch sw, string streamErrorInfo = null)
|
||
{
|
||
chain.InputTokens = usage.InputTokens;
|
||
chain.OutputTokens = usage.OutputTokens;
|
||
chain.CacheCreationTokens = usage.CacheCreationTokens;
|
||
chain.CacheReadTokens = usage.CacheReadTokens;
|
||
chain.Iterations = iterations;
|
||
chain.StopReason = stopReason;
|
||
chain.StreamErrorInfo = streamErrorInfo;
|
||
chain.DurationMs = sw.ElapsedMilliseconds;
|
||
await db.UpdateChain(chain);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Kürzt Tool-Results auf max. Zeichenzahl.
|
||
/// </summary>
|
||
public const int MaxToolResultChars = 20_000;
|
||
|
||
public static string TruncateToolResult(string result)
|
||
{
|
||
return result.Length > MaxToolResultChars
|
||
? result[..MaxToolResultChars] + $"\n\n[... gekürzt, {result.Length - MaxToolResultChars} weitere Zeichen ...]"
|
||
: result;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Token-Zähler für eine Chat-Session (Input, Output, Cache).
|
||
/// </summary>
|
||
public class TokenUsage
|
||
{
|
||
public long InputTokens { get; set; }
|
||
public long OutputTokens { get; set; }
|
||
public long CacheCreationTokens { get; set; }
|
||
public long CacheReadTokens { get; set; }
|
||
}
|
||
|
||
/// <summary>
|
||
/// Ergebnis einer Tool-Ausführung.
|
||
/// </summary>
|
||
public record ToolResult(string Id, string Result, bool IsError);
|