Add multi-model panel with Fusion mode
Panel agents answer the user prompt in parallel (read-only, non-streaming); their responses are merged into the worker's last user message as a <panel_responses> block, and the session agent synthesizes one final streamed answer. The RunPanel engine is built layer-ready for future MoA. - New session fields: multi_model_mode (off/fusion), panel_agent_ids (CSV) with idempotent column migration - New partial Services/AnthropicService.Panel.cs holding the RunPanel engine - Provider-agnostic members: Anthropic RunNonStreamingToolLoop / OpenAI CompleteText / CLI plan-mode; all forced read-only, persisted with Tag=panel - SSE events: panel_start, panel_member_start, panel_member_done, panel_done - Frontend: multi-agent picker, live panel bubbles, history rendering - Graceful degradation: panel errors fall back to worker-only Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
b23eba32b4
commit
268f11fa6d
20 files changed
+647
-35
No files matched your search
@@ -230,6 +230,26 @@ public static class ChatEndpoint
|
||||
|
||||
try
|
||||
{
|
||||
// ── Multi-Modell-Panel (Fusion) ──
|
||||
// Vor dem Worker: Panel-Agenten parallel antworten lassen und ihre Antworten dem
|
||||
// Worker als Synthese-Block in die letzte User-Message einmergen. Der Worker streamt
|
||||
// danach als Synthesizer über den unveränderten Dispatch unten. Heartbeat läuft bereits,
|
||||
// Cancellation (workCt/sseCt) ist verfügbar. Nur bei aktivem Fusion-Modus + nicht-leerem
|
||||
// Panel + keiner Choice-Antwort.
|
||||
if (!isChoiceResponse && ctx.MultiModelMode == "fusion" && ctx.PanelAgents != null && ctx.PanelAgents.Count > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
await anthropic.RunPanel(res, ctx.PanelAgents, messages, ctx.ChainId, sessionId, userApiKey?.ApiKey, workCt, sseCt);
|
||||
}
|
||||
catch (OperationCanceledException) { throw; }
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Panel-Fehler darf den Worker nicht killen → ohne Synthese normal weiter.
|
||||
Console.WriteLine($"[Panel] Fehler, fahre ohne Synthese fort: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(ctx.Provider))
|
||||
{
|
||||
// OpenAI-kompatibler Provider – Key des Users für diesen Agent verwenden
|
||||
|
||||
@@ -146,8 +146,10 @@ public static class SessionEndpoint
|
||||
var maxReviewIterations = body.TryGetProperty("maxReviewIterations", out var mri) ? mri.GetInt32() : 3;
|
||||
var finalizerAgentId = body.TryGetProperty("finalizerAgentId", out var fa) ? fa.GetString() : null;
|
||||
var preparerAgentId = body.TryGetProperty("preparerAgentId", out var pra) ? pra.GetString() : null;
|
||||
var multiModelMode = body.TryGetProperty("multiModelMode", out var mmm) ? mmm.GetString() : null;
|
||||
var panelAgentIds = body.TryGetProperty("panelAgentIds", out var pai) && pai.ValueKind != JsonValueKind.Null ? pai.GetString() : null;
|
||||
|
||||
var session = await db.CreateSession(userId, name, description, image, imageSmall, agentId, workspace, context, customCommands, avatarScale, avatarOffsetX, avatarOffsetY, reviewAgentId, maxReviewIterations, finalizerAgentId, preparerAgentId);
|
||||
var session = await db.CreateSession(userId, name, description, image, imageSmall, agentId, workspace, context, customCommands, avatarScale, avatarOffsetX, avatarOffsetY, reviewAgentId, maxReviewIterations, finalizerAgentId, preparerAgentId, multiModelMode, panelAgentIds);
|
||||
await ctx.Response.WriteAsJsonAsync(session);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user