B2 beheben: Chat-Laeufe pro Agent serialisieren
Der Konversationskontext _chatContexts[agentId] ist eine geteilte List<ChatMessage>. Nur der Lookup lief unter Lock, alle Add-Aufrufe im Schleifenkoerper waren ungeschuetzt. Da WebView, ToolJob-Wakeups und AgentComm denselben Agenten gleichzeitig ansprechen koennen, verschraenkten sich ihre Nachrichten zu einer ungueltigen Tool-Sequenz, die die API mit HTTP 400 ablehnt. ChatAsync laeuft jetzt hinter einem SemaphoreSlim(1,1) pro Agent; verschiedene Agenten bleiben unabhaengig. Die Timeout-Uhr startet erst nach dem Eintritt, damit Wartezeit in der Warteschlange den Lauf nicht aufzehrt. Zwei Folgeprobleme mit demselben Ursprung: - _runningChats hielt nur EINE CancellationTokenSource je Agent; der zweite Lauf ueberschrieb den ersten. AbortChat brach dadurch nur einen ab, der andere lief bis ins Run-Timeout. Jetzt eine Liste, die auch wartende Laeufe erfasst. - ExecuteToolCallAsync fing OperationCanceledException mit ab und gab sie als Tool-Ergebnis zurueck, wodurch der Abbruch erst einen Schritt spaeter griff. Cancellation wird nun durchgereicht. Ausserdem: send_message an den eigenen Agenten wird abgelehnt — es waere mit dem neuen Gate in einen Deadlock gelaufen. Neu: GetChatContext(agentId) als Momentaufnahme des Kontexts, fuer Diagnose und Kontextgroessen-Anzeige. Build-Fix: Das WinForms-Projekt globbt **/*.cs und kompilierte dadurch die Test-Quellen mit. tests\** wird jetzt wie src\** ausgeschlossen. Alle 49 Tests gruen. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
eae13771cf
commit
6bbe9f9a80
@@ -0,0 +1,63 @@
|
||||
using ClawdDotNet.Core.Config;
|
||||
using ClawdDotNet.Core.Engine;
|
||||
using ClawdDotNet.Core.Security;
|
||||
using ClawdDotNet.Core.Tools;
|
||||
|
||||
namespace ClawdDotNet.Core.Tests.Infrastructure;
|
||||
|
||||
/// <summary>
|
||||
/// Baut eine einsatzbereite AgentEngine mit Fake-Client, Fake-Tools und
|
||||
/// In-Memory-State zusammen.
|
||||
/// </summary>
|
||||
internal sealed class EngineFixture
|
||||
{
|
||||
public FakeChatClient Client { get; } = new();
|
||||
public ToolRegistry Registry { get; } = new();
|
||||
public InMemoryStateStore StateStore { get; } = new();
|
||||
public AgentEngine Engine { get; }
|
||||
|
||||
private readonly List<AgentConfig> _agents = new();
|
||||
|
||||
public EngineFixture()
|
||||
{
|
||||
Engine = new AgentEngine(
|
||||
Client,
|
||||
Registry,
|
||||
new PermissionGate(),
|
||||
StateStore,
|
||||
TestLogging.Factory);
|
||||
|
||||
// Kein Agent-Verzeichnis → keine Persistenz auf die Platte während der Tests.
|
||||
Engine.SetAgentConfigProvider(() => _agents, "test-instance", _ => null);
|
||||
}
|
||||
|
||||
public EngineFixture WithTool(IAgentTool tool)
|
||||
{
|
||||
Registry.Register(tool);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AgentConfig AddAgent(string agentId, params string[] toolNames)
|
||||
{
|
||||
var config = new AgentConfig
|
||||
{
|
||||
AgentId = agentId,
|
||||
DisplayName = agentId,
|
||||
Model = "test/model",
|
||||
SystemPrompt = $"Du bist {agentId}.",
|
||||
LoopGuard = new LoopGuardConfig
|
||||
{
|
||||
MaxSteps = 50,
|
||||
MaxCumulativeTokens = 10_000_000,
|
||||
TimeoutSeconds = 60,
|
||||
MaxContextTokens = 10_000_000 // Compaction in Engine-Tests ausschalten
|
||||
}
|
||||
};
|
||||
|
||||
foreach (var name in toolNames)
|
||||
config.Tools[name] = new Dictionary<string, object?>();
|
||||
|
||||
_agents.Add(config);
|
||||
return config;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user