Rund 2.600 Kommentarblöcke in 310 C#-Dateien übersetzt, Begründungen vollständig erhalten: blockweise gleiche Anzahl wie vorher, kein Block unter 60 % seiner Länge, der Code außerhalb der Kommentare per Tokenizer byte-identisch geprüft. Zitate deutscher Oberflächentexte und Testdaten bleiben deutsch. WebUI: Kommentare in JS/CSS/HTML übersetzt, JSDoc-@param an die in 4b umbenannten Parameter angeglichen. Dabei behoben: - 4b hatte "tag" (HTML-Tag) als deutsches "Tag" gelesen und zu "dayValue" gemacht (makeElement, svg, Tagesübersicht) - zurück auf tag/day. - Aufgaben "InProgress" bekamen die Plakettenart "laufend", für die es nie eine CSS-Klasse gab - jetzt "success" wie laufende Instanzen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
84 lines
3.4 KiB
C#
84 lines
3.4 KiB
C#
using System.Collections.Concurrent;
|
|
using System.Text.Json;
|
|
using ClawdDotNet.Core.Tools;
|
|
|
|
namespace ClawdDotNet.Core.Tests.Infrastructure;
|
|
|
|
/// <summary>
|
|
/// A tool that commissions another agent — the replica of what
|
|
/// <c>AgentComm.send_message</c> and <c>AgentSpawn.spawn_agent</c> do.
|
|
///
|
|
/// <para>The replica is necessary because both tools lie in projects of their own that
|
|
/// <c>ClawdDotNet.Core.Tests</c> does not reference. All that matters anyway is the
|
|
/// one line in which they are the same here: the delegation chain of the own run
|
|
/// is passed on. A tool that suppresses it turns a cycle into a
|
|
/// new commission — exactly the case B8 describes.</para>
|
|
///
|
|
/// <para>Whom an agent commissions is in <paramref name="routes"/>: agentId → target.
|
|
/// Whoever does not appear there does not delegate and simply answers.</para>
|
|
/// </summary>
|
|
internal sealed class DelegatingTool : IAgentTool
|
|
{
|
|
private readonly IReadOnlyDictionary<string, string> _routes;
|
|
private readonly bool _spawn;
|
|
private readonly TimeSpan _delayBeforeHandoff;
|
|
|
|
public string Name => "TestTool";
|
|
public string Description => "Beauftragt einen anderen Agenten.";
|
|
|
|
public JsonElement InputSchema { get; } = JsonDocument.Parse("""
|
|
{ "type": "object", "properties": { "message": { "type": "string" } } }
|
|
""").RootElement.Clone();
|
|
|
|
/// <summary>What came back — caller, target and the result of the hand-off.</summary>
|
|
public ConcurrentBag<(string From, string To, bool Delivered, string? Error)> Handoffs { get; } = new();
|
|
|
|
/// <param name="delayBeforeHandoff">
|
|
/// Waiting time before handing off. The fake client answers synchronously; without
|
|
/// this pause a chat runs through completely before the next one even
|
|
/// begins, and two chains cannot overlap.
|
|
/// </param>
|
|
public DelegatingTool(
|
|
IReadOnlyDictionary<string, string> routes,
|
|
bool spawn = false,
|
|
TimeSpan delayBeforeHandoff = default)
|
|
{
|
|
_routes = routes;
|
|
_spawn = spawn;
|
|
_delayBeforeHandoff = delayBeforeHandoff;
|
|
}
|
|
|
|
public async Task<ToolResult> ExecuteAsync(
|
|
JsonElement input, AgentToolContext context, CancellationToken ct)
|
|
{
|
|
if (!_routes.TryGetValue(context.AgentId, out var target))
|
|
return ToolResult.Ok("nichts zu delegieren");
|
|
|
|
if (_delayBeforeHandoff > TimeSpan.Zero)
|
|
await Task.Delay(_delayBeforeHandoff, ct);
|
|
|
|
if (_spawn)
|
|
{
|
|
var spawned = await context.MessageRouter!.SpawnAgentAsync(
|
|
context.AgentId, target, "Auftrag", context.InstanceId, context.Delegation, ct);
|
|
|
|
Handoffs.Add((context.AgentId, target, spawned.Started, spawned.Error));
|
|
|
|
return spawned.Started
|
|
? ToolResult.Ok(spawned.FinalMessage ?? "")
|
|
: ToolResult.Ok(JsonSerializer.Serialize(new { spawned = false, error = spawned.Error }));
|
|
}
|
|
|
|
var result = await context.MessageRouter!.SendMessageAsync(
|
|
context.AgentId, target, "Frage", context.InstanceId, context.Delegation, ct);
|
|
|
|
Handoffs.Add((context.AgentId, target, result.Delivered, result.Error));
|
|
|
|
// Like the real tool: a rejection is not a throw, but a result that
|
|
// the agent can read and react to differently.
|
|
return result.Delivered
|
|
? ToolResult.Ok(result.Response ?? "")
|
|
: ToolResult.Ok(JsonSerializer.Serialize(new { delivered = false, error = result.Error }));
|
|
}
|
|
}
|