fix: Verwaiste tool_result-Blöcke aus History entfernen (API 400)
Wenn eine ältere ask_user_choice-Chain als Text-only geladen wird, geht der tool_use verloren, aber das tool_result in der nächsten Chain bleibt – Anthropic API antwortet mit 400 "unexpected tool_use_id". Neue FixOrphanedToolResults()-Funktion entfernt diese verwaisten Blöcke. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
55477742a9
commit
9391b5a6fa
1 file changed
+64
@@ -553,6 +553,11 @@ Für welche Plattform soll die App entwickelt werden? Web, Mobile oder Desktop?
|
||||
// Post-Processing: Offene tool_use-Blöcke ohne tool_result → synthetische tool_results einfügen
|
||||
FixOrphanedToolUse(history);
|
||||
|
||||
// Post-Processing: tool_result-Blöcke ohne passendes tool_use entfernen
|
||||
// Tritt auf wenn eine ältere ask_user_choice-Chain als Text-only geladen wird:
|
||||
// Der tool_use(ask_user_choice) geht verloren, aber das tool_result(iter 999) in der nächsten Chain bleibt.
|
||||
FixOrphanedToolResults(history);
|
||||
|
||||
// Post-Processing: Orphaned web_search server_tool_use entfernen
|
||||
// Bei alten DB-Einträgen wurde web_search_tool_result nur als Sources-Array gespeichert,
|
||||
// nicht als Raw-JSON → BuildHistoryFromDb überspringt sie (Array-Check) →
|
||||
@@ -656,6 +661,65 @@ Für welche Plattform soll die App entwickelt werden? Web, Mobile oder Desktop?
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Entfernt verwaiste tool_result-Blöcke aus user-Messages:
|
||||
/// tool_results deren tool_use_id in keinem vorherigen assistant-Block existiert.
|
||||
/// Tritt auf wenn eine ältere ask_user_choice-Chain als Text-only geladen wird –
|
||||
/// der tool_use geht verloren, aber das tool_result in der nächsten Chain bleibt.
|
||||
/// </summary>
|
||||
private static void FixOrphanedToolResults(List<object> history)
|
||||
{
|
||||
// Alle tool_use IDs in der gesamten History sammeln
|
||||
var allToolUseIds = new HashSet<string>();
|
||||
foreach (var msg in history)
|
||||
{
|
||||
if (msg is not Dictionary<string, object> dict) continue;
|
||||
if (!dict.TryGetValue("role", out var role) || role?.ToString() != "assistant") continue;
|
||||
if (!dict.TryGetValue("content", out var content) || content is not List<object> contentList) continue;
|
||||
|
||||
foreach (var block in contentList)
|
||||
{
|
||||
var json = JsonSerializer.Serialize(block);
|
||||
JsonElement el;
|
||||
try { el = JsonSerializer.Deserialize<JsonElement>(json); }
|
||||
catch { continue; }
|
||||
|
||||
if (!el.TryGetProperty("type", out var t) || t.GetString() != "tool_use") continue;
|
||||
if (el.TryGetProperty("id", out var idProp))
|
||||
{
|
||||
var id = idProp.GetString();
|
||||
if (!string.IsNullOrEmpty(id))
|
||||
allToolUseIds.Add(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// User-Messages durchgehen und orphaned tool_results entfernen
|
||||
for (int i = history.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (history[i] is not Dictionary<string, object> udict) continue;
|
||||
if (!udict.TryGetValue("role", out var urole) || urole?.ToString() != "user") continue;
|
||||
if (!udict.TryGetValue("content", out var ucontent) || ucontent is not List<object> ucontentList) continue;
|
||||
|
||||
ucontentList.RemoveAll(block =>
|
||||
{
|
||||
var json = JsonSerializer.Serialize(block);
|
||||
JsonElement el;
|
||||
try { el = JsonSerializer.Deserialize<JsonElement>(json); }
|
||||
catch { return false; }
|
||||
|
||||
if (!el.TryGetProperty("type", out var t) || t.GetString() != "tool_result") return false;
|
||||
if (!el.TryGetProperty("tool_use_id", out var tid)) return false;
|
||||
var toolUseId = tid.GetString();
|
||||
return !string.IsNullOrEmpty(toolUseId) && !allToolUseIds.Contains(toolUseId);
|
||||
});
|
||||
|
||||
// Wenn die user-Message nach dem Entfernen leer ist, ganze Message entfernen
|
||||
if (ucontentList.Count == 0)
|
||||
history.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prüft die gesamte History auf tool_use-Blöcke ohne passendes tool_result in der nächsten user-Message.
|
||||
/// Falls gefunden, werden synthetische tool_results eingefügt – verhindert Anthropic API 400.
|
||||
|
||||
Reference in new issue
Block a user