fix: server_tool_use/result (bash_code_execution) korrekt verarbeiten

- server_tool_use-Blöcke werden jetzt in DB persistiert und in
  History-Rekonstruktion als Raw-JSON eingefügt
- Generische *_tool_result-Behandlung (bash_code_execution_tool_result,
  code_execution_tool_result etc.) in allen drei Chat-Pfaden:
  StreamChat, ChatNonStreaming, RunAgentChat
- ProcessStream: serverToolResultBlocks analog zu webSearchResultBlocks
- BuildHistoryFromDb: server_tool_use + server_tool_result werden
  korrekt in Assistant-Content-Array rekonstruiert
- Behebt API 400: tool_use without corresponding tool_result
This commit is contained in:
Meleki committed 2026-03-09 11:52:58 +01:00
1 parent 5d090e160b
commit fb9ff23ad4
2 files changed
+101

No files matched your search

+54
View File
@@ -146,6 +146,15 @@ public class AnthropicService
Content = el.GetProperty("input").ToString()
});
}
else if (blockType == "server_tool_use")
{
await _db.SaveMessage(new MessageRecord
{
ChainId = chainId, Iteration = iterations,
Role = "assistant", Type = "server_tool_use",
Content = el.GetRawText()
});
}
else if (blockType == "web_search_tool_result")
{
var sources = ExtractWebSearchSources(el);
@@ -159,6 +168,16 @@ public class AnthropicService
});
}
}
else if (blockType.EndsWith("_tool_result"))
{
// Generische Server-Tool-Results (bash_code_execution_tool_result, code_execution_tool_result etc.)
await _db.SaveMessage(new MessageRecord
{
ChainId = chainId, Iteration = iterations,
Role = "assistant", Type = "server_tool_result",
Content = el.GetRawText()
});
}
}
// Tool Results persistieren
@@ -781,6 +800,19 @@ public class AnthropicService
var (toolId, toolName, inputEl) = ParseServerToolUse(block);
assistantContent.Add(new { type = "server_tool_use", id = toolId, name = toolName, input = inputEl });
}
else if (blockType.EndsWith("_tool_result"))
{
// Generische Server-Tool-Results (bash_code_execution_tool_result, code_execution_tool_result etc.)
assistantContent.Add(JsonSerializer.Deserialize<JsonElement>(block.GetRawText()));
await _db.SaveMessage(new MessageRecord
{
ChainId = chainId,
Iteration = baseIterations + iterations,
Role = "assistant", Type = "server_tool_result",
Content = block.GetRawText(),
Tag = agentType
});
}
}
messages.Add(new Dictionary<string, object> { ["role"] = "assistant", ["content"] = assistantContent });
@@ -945,6 +977,17 @@ public class AnthropicService
var (toolId, toolName, inputEl) = ParseServerToolUse(block);
assistantContent.Add(new { type = "server_tool_use", id = toolId, name = toolName, input = inputEl });
}
else if (blockType.EndsWith("_tool_result"))
{
// Generische Server-Tool-Results (bash_code_execution_tool_result, code_execution_tool_result etc.)
assistantContent.Add(JsonSerializer.Deserialize<JsonElement>(block.GetRawText()));
await _db.SaveMessage(new MessageRecord
{
ChainId = ctx.ChainId, Iteration = iterations,
Role = "assistant", Type = "server_tool_result",
Content = block.GetRawText()
});
}
}
messages.Add(new Dictionary<string, object> { ["role"] = "assistant", ["content"] = assistantContent });
@@ -1028,6 +1071,7 @@ public class AnthropicService
var blockTypes = new Dictionary<int, string>();
var webSearchResultBlocks = new Dictionary<int, object>();
var webSearchSources = new Dictionary<int, List<object>>();
var serverToolResultBlocks = new Dictionary<int, object>(); // bash_code_execution_tool_result, code_execution_tool_result etc.
var executedTools = new List<ToolResult>();
var streamErrors = new List<object>();
@@ -1096,6 +1140,11 @@ public class AnthropicService
webSearchResultBlocks[idx] = rawBlock;
webSearchSources[idx] = ExtractWebSearchSources(cb);
}
else if (cbType.EndsWith("_tool_result"))
{
// Generische Server-Tool-Results (bash_code_execution_tool_result, code_execution_tool_result etc.)
serverToolResultBlocks[idx] = JsonSerializer.Deserialize<JsonElement>(cb.GetRawText());
}
break;
}
@@ -1221,6 +1270,11 @@ public class AnthropicService
var sources = webSearchSources.TryGetValue(idx, out var s) ? s : new List<object>();
await WriteSseEvent(response, "web_search_end", new { id = currentToolId, sources }, cancellationToken);
}
else if (bt.EndsWith("_tool_result") && serverToolResultBlocks.TryGetValue(idx, out var srvResultBlock))
{
// Generische Server-Tool-Results (bash_code_execution_tool_result, code_execution_tool_result etc.)
assistantContent.Add(srvResultBlock);
}
}
break;
}
+47
View File
@@ -363,6 +363,53 @@ Für welche Plattform soll die App entwickelt werden? Web, Mobile oder Desktop?
});
}
}
else if (msg.Role == "assistant" && msg.Type == "server_tool_use")
{
// Server-Tool-Use (web_search, code_execution etc.) – Raw-JSON in Assistant-Content
JsonElement useEl;
try { useEl = JsonSerializer.Deserialize<JsonElement>(msg.Content ?? "{}"); }
catch { continue; }
var lastAssistant = history.Count > 0 ? history[history.Count - 1] : null;
if (lastAssistant is Dictionary<string, object> dict &&
dict.TryGetValue("role", out var role) && role.ToString() == "assistant" &&
dict.TryGetValue("content", out var existingContent) && existingContent is List<object> contentList)
{
contentList.Add(useEl);
}
else
{
history.Add(new Dictionary<string, object>
{
["role"] = "assistant",
["content"] = new List<object> { useEl }
});
}
}
else if (msg.Role == "assistant" && msg.Type == "server_tool_result")
{
// Server-Tool-Results (bash_code_execution_tool_result, code_execution_tool_result etc.)
// werden als Raw-JSON in den Assistant-Content-Block eingefügt
JsonElement resultEl;
try { resultEl = JsonSerializer.Deserialize<JsonElement>(msg.Content ?? "{}"); }
catch { continue; }
var lastAssistant = history.Count > 0 ? history[history.Count - 1] : null;
if (lastAssistant is Dictionary<string, object> dict &&
dict.TryGetValue("role", out var role) && role.ToString() == "assistant" &&
dict.TryGetValue("content", out var existingContent) && existingContent is List<object> contentList)
{
contentList.Add(resultEl);
}
else
{
history.Add(new Dictionary<string, object>
{
["role"] = "assistant",
["content"] = new List<object> { resultEl }
});
}
}
else if (msg.Role == "user" && msg.Type == "tool_result")
{
var lastUser = history.Count > 0 ? history[history.Count - 1] : null;