Persist panel member token costs to DB via usage seed
Replace the separate panel_usage SSE event + frontend accumulator with server-side seeding of the worker's usage object. Both the done-event and FinalizeChain read from the same usage, so panel (Fusion/MoA) member tokens now flow into the live badge AND the DB as a single source of truth — the badge no longer drops to the worker-only value after F5. - RunPanel takes a TokenUsage usageSink; accumulates member costs after the Task.WhenAll barrier (single-threaded, no lock) - ChatEndpoint passes panelUsage as seedUsage to all three worker paths - StreamChat / StreamChatViaCli / OpenAI StreamChat fold seedUsage into usage at creation time, so every exit path persists it - OpenAI done-events extended with cacheCreation/cacheRead fields for live consistency when seeded by Anthropic members - chat.js: remove redundant _panelUsage / _addPanelUsage / panel_usage - TODO.md: mark the live-only v1 boundary closed, note the new mechanism Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
1b203707ec
commit
5c89adb6df
7 files changed
+41
-46
No files matched your search
@@ -236,13 +236,19 @@ public static class ChatEndpoint
|
||||
// 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.
|
||||
//
|
||||
// Panel-Token-Kosten (alle Member über alle Runden) werden in panelUsage gesammelt und
|
||||
// unten als seedUsage in die Worker-Streaming-Methode gefaltet → fließen über das
|
||||
// done-Event (Live-Badge) UND FinalizeChain (DB) als Teil der Chain-Kosten und überleben
|
||||
// F5. Bleibt {0} wenn kein Panel läuft → Seeding ist dann ein No-op.
|
||||
var panelUsage = new TokenUsage();
|
||||
if (!isChoiceResponse && (ctx.MultiModelMode == "fusion" || ctx.MultiModelMode == "moa") && ctx.PanelAgents != null && ctx.PanelAgents.Count > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Fusion = 1 Runde, MoA = MoaLayers Proposer-Runden vor der Synthese.
|
||||
var panelLayers = ctx.MultiModelMode == "moa" ? System.Math.Max(1, ctx.MoaLayers) : 1;
|
||||
await anthropic.RunPanel(res, ctx.PanelAgents, messages, ctx.ChainId, sessionId, userApiKey?.ApiKey, panelLayers, workCt, sseCt);
|
||||
await anthropic.RunPanel(res, ctx.PanelAgents, messages, ctx.ChainId, sessionId, userApiKey?.ApiKey, panelLayers, panelUsage, workCt, sseCt);
|
||||
}
|
||||
catch (OperationCanceledException) { throw; }
|
||||
catch (Exception ex)
|
||||
@@ -265,7 +271,7 @@ public static class ChatEndpoint
|
||||
return;
|
||||
}
|
||||
|
||||
await openAi.StreamChat(res, ctx.ModelId, ctx.SystemPromptText, messages, ctx.ChainId, workspace: ctx.Workspace, sessionId: sessionId, agentId: ctx.AgentId, maxIterations: ctx.MaxIterations, apiKeyOverride: openAiKeyOverride, useAutoContext: ctx.Agent?.UseAutoContext ?? true, workCt: workCt, sseCt: sseCt);
|
||||
await openAi.StreamChat(res, ctx.ModelId, ctx.SystemPromptText, messages, ctx.ChainId, workspace: ctx.Workspace, sessionId: sessionId, agentId: ctx.AgentId, maxIterations: ctx.MaxIterations, apiKeyOverride: openAiKeyOverride, useAutoContext: ctx.Agent?.UseAutoContext ?? true, seedUsage: panelUsage, workCt: workCt, sseCt: sseCt);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -286,7 +292,7 @@ public static class ChatEndpoint
|
||||
|
||||
if (anthropic.IsCliAvailable)
|
||||
{
|
||||
await anthropic.StreamChatViaCli(res, ctx.ModelId, ctx.SystemPrompt, messages, ctx.ChainId, workspace: ctx.Workspace, allowUserChoice: ctx.Agent?.AllowUserChoice ?? true, planMode: body.PlanMode, webSearch: !string.IsNullOrEmpty(ctx.WebSearchVersion), sessionId: sessionId, agentId: ctx.AgentId, remoteMgr: remoteMgr, unityMcpUrl: ctx.Session?.UnityMcpUrl, unityMcpToken: ctx.Session?.UnityMcpToken, useAutoContext: ctx.Agent?.UseAutoContext ?? true, effort: ctx.Effort, workCt: workCt, sseCt: sseCt);
|
||||
await anthropic.StreamChatViaCli(res, ctx.ModelId, ctx.SystemPrompt, messages, ctx.ChainId, workspace: ctx.Workspace, allowUserChoice: ctx.Agent?.AllowUserChoice ?? true, planMode: body.PlanMode, webSearch: !string.IsNullOrEmpty(ctx.WebSearchVersion), sessionId: sessionId, agentId: ctx.AgentId, remoteMgr: remoteMgr, unityMcpUrl: ctx.Session?.UnityMcpUrl, unityMcpToken: ctx.Session?.UnityMcpToken, useAutoContext: ctx.Agent?.UseAutoContext ?? true, effort: ctx.Effort, seedUsage: panelUsage, workCt: workCt, sseCt: sseCt);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -296,7 +302,7 @@ public static class ChatEndpoint
|
||||
return;
|
||||
}
|
||||
|
||||
await anthropic.StreamChat(res, ctx.ModelId, ctx.SystemPrompt, messages, ctx.ChainId, ctx.Workspace, apiToken: anthropicToken, sessionId: sessionId, agentId: ctx.AgentId, userId: userId, useMemory: ctx.UseMemory, webSearchVersion: ctx.WebSearchVersion, maxIterations: ctx.MaxIterations, toolChoice: effectiveToolChoice, review: ctx.Review, finalizer: ctx.Finalizer, allowUserChoice: ctx.Agent?.AllowUserChoice ?? true, planMode: ctx.PlanMode, useAutoContext: ctx.Agent?.UseAutoContext ?? true, useGitHubSearch: ctx.Agent?.UseGitHubSearch ?? false, useSerperSearch: ctx.Agent?.UseSerperSearch ?? false, effort: ctx.Effort, workCt: workCt, hardCt: hardCt, sseCt: sseCt, remoteMgr: remoteMgr);
|
||||
await anthropic.StreamChat(res, ctx.ModelId, ctx.SystemPrompt, messages, ctx.ChainId, ctx.Workspace, apiToken: anthropicToken, sessionId: sessionId, agentId: ctx.AgentId, userId: userId, useMemory: ctx.UseMemory, webSearchVersion: ctx.WebSearchVersion, maxIterations: ctx.MaxIterations, toolChoice: effectiveToolChoice, review: ctx.Review, finalizer: ctx.Finalizer, allowUserChoice: ctx.Agent?.AllowUserChoice ?? true, planMode: ctx.PlanMode, useAutoContext: ctx.Agent?.UseAutoContext ?? true, useGitHubSearch: ctx.Agent?.UseGitHubSearch ?? false, useSerperSearch: ctx.Agent?.UseSerperSearch ?? false, effort: ctx.Effort, seedUsage: panelUsage, workCt: workCt, hardCt: hardCt, sseCt: sseCt, remoteMgr: remoteMgr);
|
||||
}
|
||||
}
|
||||
finally
|
||||
|
||||
Reference in new issue
Block a user