refactor: Chat-Payload vereinfacht + Inline-Bilder statt Platzhalter

Backend:
- ChatEndpoint empfängt nur noch content + sessionId (nicht mehr die gesamte History)
- History wird komplett aus DB aufgebaut via BuildHistoryFromDb
- Gelöschte Nachrichten (deleted_on) werden aus History gefiltert
- Leere Nachrichten werden übersprungen (kein API 400 mehr)
- BuildHistoryFromDb optimiert: nur Text für ältere Chains, Tool-Calls nur für aktuelle Chain
- GetChainMessages: includeDeleted-Parameter (Default: false)
- Archiv-Export nutzt includeDeleted: true

Frontend:
- messages[]-Array komplett entfernt (kein Client-seitiger History-State mehr)
- apiChat() sendet nur content + sessionId
- Inline-Bilder: [Bild: name] wird durch <img> ersetzt (live mit DataURL, History per API-Nachladen)
- _autoLoadImageAttachments() für History/Archiv/ältere Nachrichten
- Klick auf Inline-Bild öffnet Vollbild-Modal
This commit is contained in:
Meleki committed 2026-03-04 14:29:33 +01:00
1 parent 51db01752f
commit 8fc29c82d9
12 files changed
+222 -106

No files matched your search

+27 -50
View File
@@ -23,10 +23,10 @@ public static class ChatEndpoint
private static async Task HandleChat(HttpRequest req, HttpResponse res, IConfiguration config, ChatService chatService, DatabaseService db, StreamingTracker streamingTracker, AnthropicService anthropic, CancellationToken cancellationToken)
{
var body = await JsonSerializer.DeserializeAsync<ChatRequest>(req.Body);
if (body == null || body.Messages == null || body.Messages.Count == 0)
if (body == null || body.Content.ValueKind == JsonValueKind.Undefined)
{
res.StatusCode = 400;
await res.WriteAsJsonAsync(new { error = "messages required" });
await res.WriteAsJsonAsync(new { error = "content required" });
return;
}
@@ -49,58 +49,35 @@ public static class ChatEndpoint
return;
}
// Neue User-Message aus Request-Content aufbereiten und speichern
var userMessage = new ChatMessage { Role = "user", Content = body.Content };
var textContent = userMessage.GetTextContent();
var attachmentBlocks = userMessage.GetAttachmentBlocks();
var attachmentRecords = attachmentBlocks.Select(b => new AttachmentRecord
{
Type = b.Type,
MediaType = b.MediaType,
FileName = b.FileName,
Data = b.Data
}).ToList();
await db.SaveMessageWithAttachments(new MessageRecord
{
ChainId = ctx.ChainId,
Iteration = 0,
Role = "user",
Type = "text",
Content = textContent
}, attachmentRecords);
// History komplett aus DB aufbauen (inkl. gerade gespeicherter Message)
var messages = await chatService.BuildHistoryFromDb(sessionId);
res.Headers["Content-Type"] = "text/event-stream";
res.Headers["Cache-Control"] = "no-cache";
res.Headers["Connection"] = "keep-alive";
// Messages aus dem Request aufbereiten
var messages = new List<object>();
foreach (var msg in body.Messages)
{
JsonElement content = msg.Content;
if (msg.Content.ValueKind == JsonValueKind.Array)
{
var node = System.Text.Json.Nodes.JsonNode.Parse(msg.Content.GetRawText());
foreach (var block in node.AsArray())
{
if (block is System.Text.Json.Nodes.JsonObject obj)
obj.Remove("_file_name");
}
content = JsonSerializer.Deserialize<JsonElement>(node.ToJsonString());
}
messages.Add(new Dictionary<string, JsonElement>
{
["role"] = JsonSerializer.Deserialize<JsonElement>($"\"{msg.Role}\""),
["content"] = content
});
}
// Nur die letzte User-Message speichern (= die neue)
var lastUserMsg = body.Messages.LastOrDefault(m => m.Role == "user");
if (lastUserMsg != null)
{
var textContent = lastUserMsg.GetTextContent();
var attachmentBlocks = lastUserMsg.GetAttachmentBlocks();
var attachmentRecords = attachmentBlocks.Select(b => new AttachmentRecord
{
Type = b.Type,
MediaType = b.MediaType,
FileName = b.FileName,
Data = b.Data
}).ToList();
await db.SaveMessageWithAttachments(new MessageRecord
{
ChainId = ctx.ChainId,
Iteration = 0,
Role = "user",
Type = "text",
Content = textContent
}, attachmentRecords);
}
streamingTracker.Start(sessionId, ctx.ChainId);
try
{