refactor: Backend-Deduplizierung – Parameter-Objekte, Shared Helpers, KeyCheckDispatcher
- ToolDefinitionOptions + ToolExecutionContext ersetzen lange Parameterlisten in ToolService - AppendToLastMessage-Helper in ChatService eliminiert 5x duplizierte Logik - OpenAiService: ResolveProviderConfig + BuildApiMessages + ConvertToOpenAiTools statt doppeltem Code - Tool-Definitionen aus einer Quelle (Anthropic→OpenAI Konvertierung statt hardcodierter Liste) - KeyCheckDispatcher zentralisiert Key-Check-Dispatch (ApiKeysEndpoint, KeyScanner, GitHubEventScanner) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
f3069cedf7
commit
3e19da4d48
12 files changed
+428
-487
No files matched your search
+31
-79
@@ -86,7 +86,12 @@ public class ChatService
|
||||
var toolChoice = hasTools ? "any" : null;
|
||||
|
||||
var toolDefs = hasTools
|
||||
? _toolService.GetToolDefinitions(includeFileTools: hasWorkspace, includeMemory: includeMemory, includeUserChoice: agent.AllowUserChoice, includeRemote: hasRemoteConnection, includeGitHubSearch: includeGitHubSearch, includeSerperSearch: includeSerperSearch)
|
||||
? _toolService.GetToolDefinitions(new ToolDefinitionOptions
|
||||
{
|
||||
IncludeFileTools = hasWorkspace, IncludeMemory = includeMemory,
|
||||
IncludeUserChoice = agent.AllowUserChoice, IncludeRemote = hasRemoteConnection,
|
||||
IncludeGitHubSearch = includeGitHubSearch, IncludeSerperSearch = includeSerperSearch
|
||||
})
|
||||
: Array.Empty<object>();
|
||||
var toolNames = toolDefs.Length > 0
|
||||
? JsonSerializer.Serialize(toolDefs.Select(td =>
|
||||
@@ -370,21 +375,7 @@ Für welche Plattform soll die App entwickelt werden? Web, Mobile oder Desktop?
|
||||
? (object)new { type = "tool_use", id = msg.ToolCallId, name = msg.ToolName, input = inputEl, thought_signature = msg.ThoughtSignature }
|
||||
: new { type = "tool_use", id = msg.ToolCallId, name = msg.ToolName, input = inputEl };
|
||||
|
||||
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(toolUseBlock);
|
||||
}
|
||||
else
|
||||
{
|
||||
history.Add(new Dictionary<string, object>
|
||||
{
|
||||
["role"] = "assistant",
|
||||
["content"] = new List<object> { toolUseBlock }
|
||||
});
|
||||
}
|
||||
AppendToLastMessage(history, "assistant", toolUseBlock);
|
||||
}
|
||||
else if (msg.Role == "assistant" && msg.Type == "server_tool_use")
|
||||
{
|
||||
@@ -393,21 +384,7 @@ Für welche Plattform soll die App entwickelt werden? Web, Mobile oder Desktop?
|
||||
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 }
|
||||
});
|
||||
}
|
||||
AppendToLastMessage(history, "assistant", useEl);
|
||||
}
|
||||
else if (msg.Role == "assistant" && msg.Type == "server_tool_result")
|
||||
{
|
||||
@@ -417,21 +394,7 @@ Für welche Plattform soll die App entwickelt werden? Web, Mobile oder Desktop?
|
||||
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 }
|
||||
});
|
||||
}
|
||||
AppendToLastMessage(history, "assistant", resultEl);
|
||||
}
|
||||
else if (msg.Role == "assistant" && msg.Type == "web_search_result")
|
||||
{
|
||||
@@ -445,42 +408,12 @@ Für welche Plattform soll die App entwickelt werden? Web, Mobile oder Desktop?
|
||||
// Diese überspringen, da sie kein gültiger web_search_tool_result-Block sind
|
||||
if (resultEl.ValueKind == JsonValueKind.Array) 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 }
|
||||
});
|
||||
}
|
||||
AppendToLastMessage(history, "assistant", resultEl);
|
||||
}
|
||||
else if (msg.Role == "user" && msg.Type == "tool_result")
|
||||
{
|
||||
var lastUser = history.Count > 0 ? history[history.Count - 1] : null;
|
||||
if (lastUser is Dictionary<string, object> dict &&
|
||||
dict.TryGetValue("role", out var role) && role.ToString() == "user" &&
|
||||
dict.TryGetValue("content", out var existingContent) && existingContent is List<object> contentList)
|
||||
{
|
||||
contentList.Add(new { type = "tool_result", tool_use_id = msg.ToolCallId, content = SseHelper.TruncateToolResult(msg.Content ?? ""), is_error = msg.IsError });
|
||||
}
|
||||
else
|
||||
{
|
||||
history.Add(new Dictionary<string, object>
|
||||
{
|
||||
["role"] = "user",
|
||||
["content"] = new List<object>
|
||||
{
|
||||
new { type = "tool_result", tool_use_id = msg.ToolCallId, content = SseHelper.TruncateToolResult(msg.Content ?? ""), is_error = msg.IsError }
|
||||
}
|
||||
});
|
||||
}
|
||||
var toolResultBlock = new { type = "tool_result", tool_use_id = msg.ToolCallId, content = SseHelper.TruncateToolResult(msg.Content ?? ""), is_error = msg.IsError };
|
||||
AppendToLastMessage(history, "user", toolResultBlock);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -630,6 +563,25 @@ Für welche Plattform soll die App entwickelt werden? Web, Mobile oder Desktop?
|
||||
return history;
|
||||
}
|
||||
|
||||
private static void AppendToLastMessage(List<object> history, string role, object block)
|
||||
{
|
||||
var last = history.Count > 0 ? history[history.Count - 1] : null;
|
||||
if (last is Dictionary<string, object> dict &&
|
||||
dict.TryGetValue("role", out var r) && r.ToString() == role &&
|
||||
dict.TryGetValue("content", out var existing) && existing is List<object> contentList)
|
||||
{
|
||||
contentList.Add(block);
|
||||
}
|
||||
else
|
||||
{
|
||||
history.Add(new Dictionary<string, object>
|
||||
{
|
||||
["role"] = role,
|
||||
["content"] = new List<object> { block }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Entfernt verwaiste web_search-Blöcke aus assistant-Messages:
|
||||
/// - server_tool_use ohne zugehörigen web_search_tool_result (z.B. alte DB-Einträge)
|
||||
|
||||
Reference in new issue
Block a user