B1 — Die Compaction behielt blind die letzten 6 Nachrichten. Fiel diese Grenze mitten in eine Tool-Sequenz, entstand eine tool-Antwort ohne zugehoerigen assistant-tool_call; die API lehnt das mit HTTP 400 ab. FindSafeTailStart verschiebt die Grenze jetzt rueckwaerts auf eine Blockgrenze. B14 — Bei Konversationen mit hoechstens 6 Nachrichten enthielt der Tail auch die system-Nachricht, die anschliessend ein zweites Mal angehaengt wurde. Ergebnis war ein doppelter System-Prompt und eine duplizierte Konversation — die Compaction vergroesserte den Kontext, statt ihn zu verkleinern. Der Tail beginnt nun grundsaetzlich hinter dem System-Prompt; liegt davor nichts Nennenswertes, wird die Kompaktierung uebersprungen. Gefunden durch den Property-Test. Nebeneffekt: Zusammengefasst wird nur noch der Teil, der tatsaechlich wegfaellt. Der Tail bleibt woertlich erhalten und musste bisher doppelt bezahlt werden. B3 — maxTokens zaehlte kumulativ ueber alle Schritte, wurde aber wie eine Kontextgrenze konfiguriert. Da jeder Schritt den vollen Kontext erneut sendet, brach ein Chat mit 20k Kontext nach vier Schritten ab. Aufgeteilt in maxCumulativeTokens (Kostenbudget, Default 500k) und maxContextTokens (Kontextgroesse). Alte Konfigurationen werden beim Laden migriert, die Fehlermeldungen unterscheiden jetzt Schritt- und Kostenlimit. Alle 31 Tests gruen. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
133 lines
5.2 KiB
C#
133 lines
5.2 KiB
C#
using System.ComponentModel;
|
||
using ClawdDotNet.Core.Config;
|
||
|
||
namespace ClawdDotNet.Models;
|
||
|
||
[TypeConverter(typeof(ExpandableObjectConverter))]
|
||
public sealed class AgentSettingsViewModel
|
||
{
|
||
private readonly AgentConfig _config;
|
||
|
||
public AgentSettingsViewModel(AgentConfig config)
|
||
{
|
||
_config = config;
|
||
}
|
||
|
||
// ──────────────── Identity ────────────────
|
||
|
||
[Category("1 - Identity")]
|
||
[DisplayName("Agent-ID")]
|
||
[Description("Eindeutige ID des Agenten. Wird intern und in Logs verwendet.")]
|
||
[ReadOnly(true)]
|
||
public string AgentId => _config.AgentId;
|
||
|
||
[Category("1 - Identity")]
|
||
[DisplayName("Anzeigename")]
|
||
[Description("Freundlicher Name des Agenten (z.B. 'Marktanalyst').")]
|
||
public string DisplayName
|
||
{
|
||
get => _config.DisplayName;
|
||
set => _config.DisplayName = value;
|
||
}
|
||
|
||
[Category("1 - Identity")]
|
||
[DisplayName("Modell")]
|
||
[Description("LLM-Modell das dieser Agent verwendet. Dropdown zeigt verfügbare Modelle von OpenRouter.")]
|
||
[TypeConverter(typeof(ModelTypeConverter))]
|
||
public string Model
|
||
{
|
||
get => _config.Model;
|
||
set => _config.Model = value;
|
||
}
|
||
|
||
[Category("1 - Identity")]
|
||
[DisplayName("Identity")]
|
||
[Description("Aus Identity.md geladen – definiert WER der Agent ist. Bearbeitung über den Toolbar-Button 'Identity bearbeiten'.")]
|
||
[ReadOnly(true)]
|
||
public string IdentityStatus =>
|
||
string.IsNullOrWhiteSpace(_config.Identity) ? "(nicht definiert)" : $"✔ {_config.Identity.Split('\n').Length} Zeilen";
|
||
|
||
[Category("1 - Identity")]
|
||
[DisplayName("Soul")]
|
||
[Description("Aus Soul.md geladen – definiert WIE der Agent denkt. Bearbeitung über den Toolbar-Button 'Soul bearbeiten'.")]
|
||
[ReadOnly(true)]
|
||
public string SoulStatus =>
|
||
string.IsNullOrWhiteSpace(_config.Soul) ? "(nicht definiert)" : $"✔ {_config.Soul.Split('\n').Length} Zeilen";
|
||
|
||
// ──────────────── Loop-Schutz ────────────────
|
||
|
||
[Category("2 - Loop-Schutz")]
|
||
[DisplayName("Max. Schritte pro Run")]
|
||
[Description("Maximale Anzahl LLM-Aufrufe pro Run. Verhindert Endlosschleifen bei fehlerhaften Tool-Calls.")]
|
||
public int MaxSteps
|
||
{
|
||
get => _config.LoopGuard.MaxSteps;
|
||
set => _config.LoopGuard.MaxSteps = Math.Max(1, value);
|
||
}
|
||
|
||
[Category("2 - Loop-Schutz")]
|
||
[DisplayName("Kostenbudget pro Run (Tokens)")]
|
||
[Description("Summe ALLER abgerechneten Tokens eines Runs über alle Schritte. " +
|
||
"Da jeder Schritt den kompletten Kontext erneut sendet, wächst diese Summe " +
|
||
"überproportional — der Wert liegt deutlich über der Kontextgröße. " +
|
||
"Für die Kontextgröße ist 'Max. Kontext-Tokens' zuständig.")]
|
||
public int MaxCumulativeTokens
|
||
{
|
||
get => _config.LoopGuard.MaxCumulativeTokens;
|
||
set => _config.LoopGuard.MaxCumulativeTokens = Math.Max(10_000, value);
|
||
}
|
||
|
||
[Category("2 - Loop-Schutz")]
|
||
[DisplayName("Timeout (Sekunden)")]
|
||
[Description("Maximale Laufzeit pro Run in Sekunden. Danach wird der Run abgebrochen.")]
|
||
public int TimeoutSeconds
|
||
{
|
||
get => _config.LoopGuard.TimeoutSeconds;
|
||
set => _config.LoopGuard.TimeoutSeconds = Math.Max(10, value);
|
||
}
|
||
|
||
// ──────────────── Kontext-Management ────────────────
|
||
|
||
[Category("3 - Kontext-Management")]
|
||
[DisplayName("Max. Kontext-Tokens")]
|
||
[Description("Maximales Token-Budget für den gesamten Konversationskontext. Bei Überschreitung wird automatisch kompaktiert.")]
|
||
public int MaxContextTokens
|
||
{
|
||
get => _config.LoopGuard.MaxContextTokens;
|
||
set => _config.LoopGuard.MaxContextTokens = Math.Max(10_000, value);
|
||
}
|
||
|
||
[Category("3 - Kontext-Management")]
|
||
[DisplayName("Kompaktierungs-Schwelle (%)")]
|
||
[Description("Ab welchem Prozentsatz der Max. Kontext-Tokens wird kompaktiert. 80 = bei 80% Auslastung. Stufe 1: Tool-Results kürzen. Stufe 2: LLM-Zusammenfassung.")]
|
||
public int CompactionThresholdPercent
|
||
{
|
||
get => (int)(_config.LoopGuard.CompactionThreshold * 100);
|
||
set => _config.LoopGuard.CompactionThreshold = Math.Clamp(value, 50, 95) / 100.0;
|
||
}
|
||
|
||
// ──────────────── Tools (Read-Only) ────────────────
|
||
|
||
[Category("4 - Tools")]
|
||
[DisplayName("Zugewiesene Tools")]
|
||
[Description("Liste der Tool-Namen, die diesem Agent zugewiesen sind. Zuweisung über die Tabelle unten.")]
|
||
[ReadOnly(true)]
|
||
public string AssignedTools =>
|
||
_config.Tools.Count == 0
|
||
? "(keine)"
|
||
: string.Join(", ", _config.Tools.Keys);
|
||
|
||
[Category("4 - Tools")]
|
||
[DisplayName("Anzahl")]
|
||
[ReadOnly(true)]
|
||
public int ToolCount => _config.Tools.Count;
|
||
|
||
// ──────────────── Intern ────────────────
|
||
|
||
[Browsable(false)]
|
||
public AgentConfig UnderlyingConfig => _config;
|
||
|
||
public override string ToString() =>
|
||
string.IsNullOrWhiteSpace(_config.DisplayName) ? _config.AgentId : _config.DisplayName;
|
||
}
|