Files
ClawdDotNet/src/ClawdDotNet.Core/Api/Models/ChatResponse.cs
T
RichardandClaude Opus 5 ea3844006f Harness gegen stillen Verlust gehaertet (#547-#551)
Fuenf Befunde mit einem gemeinsamen Fehlermodus: Keiner erzeugt eine
Fehlermeldung. Der Lauf wird fertig, das Ergebnis sieht plausibel aus,
und nur die Qualitaet ist schlechter.

#547 Der Begleittext zum Werkzeugaufruf ging verloren. AssistantWithToolCalls
baute eine neue Nachricht aus nur den Aufrufen; der Text, den das Modell
daneben geschrieben hatte, fiel weg. Damit verlor das Modell zwischen zwei
Schritten seinen eigenen Plan, und im Audit stand, dass ein Werkzeug lief,
nicht warum.

#548 finish_reason wurde nirgends gelesen. Eine an der Ausgabegrenze
abgeschnittene Antwort galt als fertiges Ergebnis. Das trifft besonders
Laeufe im Hintergrund, wo niemand zusieht. Neuer Status Truncated: Das
Teilergebnis geht weiter, aber gekennzeichnet.

#549 reasoning_details wurde verworfen. Bei Werkzeugnutzung muss der
Denkverlauf unveraendert zurueck, sonst beginnt ein Thinking-Modell jedes
Mal von vorn. Wird jetzt als rohes JSON durchgereicht - ohne Auslegung,
weil Reihenfolge und Signatur daran haengen.

#550 Die Anfrage verlangt jetzt einen Anbieter, der alle mitgeschickten
Parameter beherrscht. Ohne das durfte ein Anbieter cache_control, reasoning
oder sogar tools stillschweigend uebergehen - der Lauf lieferte dann eine
gewoehnliche Textantwort, wo ein Werkzeugaufruf hingehoert haette. Dazu
usage.include: der abgerechnete Betrag schlaegt die Eigenrechnung, die als
Rueckfallebene bleibt. Denktiefe je Agent einstellbar, Vorgabe off.

#551 Werkzeuge eines Zuges laufen gleichzeitig, sofern sie sich dafuer
freigeben. Die Vorgabe von IsParallelSafe ist false, weil die falsche
Antwort hier nicht auffaellt: Zwei schreibende Aufrufe auf derselben Datei
scheitern nicht zuverlaessig, sondern gelegentlich. Freigegeben ist bisher
nur WebFetch - es liest ausschliesslich. Die Reihenfolge der Ergebnisse
folgt den Aufrufen, nicht der Fertigstellung; daran haengen die
tool_call_id-Zuordnung und der Cache-Praefix.

27 neue Tests, 887 gesamt, keine Regression. Build ohne Warnungen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-12 13:34:36 +02:00

80 lines
2.1 KiB
C#

using System.Text.Json.Serialization;
namespace ClawdDotNet.Core.Api.Models;
public sealed class ChatResponse
{
[JsonPropertyName("id")]
public string Id { get; set; } = "";
[JsonPropertyName("choices")]
public List<Choice> Choices { get; set; } = new();
[JsonPropertyName("usage")]
public Usage? Usage { get; set; }
[JsonPropertyName("model")]
public string? Model { get; set; }
[JsonPropertyName("error")]
public ApiError? Error { get; set; }
}
public sealed class Choice
{
[JsonPropertyName("index")]
public int Index { get; set; }
[JsonPropertyName("message")]
public ChatMessage Message { get; set; } = new();
[JsonPropertyName("finish_reason")]
public string? FinishReason { get; set; }
}
public sealed class Usage
{
[JsonPropertyName("prompt_tokens")]
public int PromptTokens { get; set; }
[JsonPropertyName("completion_tokens")]
public int CompletionTokens { get; set; }
[JsonPropertyName("total_tokens")]
public int TotalTokens { get; set; }
[JsonPropertyName("prompt_tokens_details")]
public PromptTokensDetails? PromptTokensDetails { get; set; }
/// <summary>
/// Der vom Gateway tatsächlich abgerechnete Betrag. Kommt nur, wenn die Anfrage
/// <c>usage.include</c> gesetzt hat. Null heißt „nicht geliefert" — dann bleibt
/// es bei der Eigenrechnung aus Preisliste mal Tokenzahl.
/// </summary>
[JsonPropertyName("cost")]
public decimal? Cost { get; set; }
/// <summary>
/// Anteil der Prompt-Tokens, der aus dem Cache gelesen wurde. Nur damit lässt sich
/// belegen, ob das Prompt-Caching tatsächlich greift — ein still wirkungsloser
/// Cache wäre sonst nicht zu bemerken.
/// </summary>
[JsonIgnore]
public int CachedTokens => PromptTokensDetails?.CachedTokens ?? 0;
}
public sealed class PromptTokensDetails
{
[JsonPropertyName("cached_tokens")]
public int CachedTokens { get; set; }
}
public sealed class ApiError
{
[JsonPropertyName("message")]
public string Message { get; set; } = "";
[JsonPropertyName("code")]
public int? Code { get; set; }
}