Enable GLM web search and 5.2+ effort over the z.ai CLI path
GLM models cannot run Anthropic's server-side WebSearch/WebFetch, so when a GLM model has web search enabled and ZaiApiKey is set, GenerateMcpConfig registers z.ai's web_search_prime HTTP MCP server and CreateCliStartInfo allows mcp__web-search-prime__* while disallowing the native WebSearch/WebFetch tools — otherwise the model tries the (headless-ungranted) native tool first and only falls back to the MCP search on the second attempt. Effort is now passed through for GLM-5.2 and newer (version compare, not a prefix match, so 5.3/6 are picked up automatically and glm-5.2[1m]/glm-5-turbo parse correctly); older GLM models still drop CLAUDE_CODE_EFFORT_LEVEL. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
0f3d658fd3
commit
5418dc9225
2 files changed
+82
-7
No files matched your search
@@ -73,7 +73,7 @@ public partial class AnthropicService
|
||||
var remoteConn = remoteMgr != null && !string.IsNullOrEmpty(sessionId) ? remoteMgr.GetBySessionId(sessionId) : null;
|
||||
var hasRemote = remoteConn != null;
|
||||
var remoteOs = remoteConn?.OsVersion ?? "";
|
||||
mcpConfigPath = GenerateMcpConfig(sessionId, agentId, hasRemote, unityMcpUrl, unityMcpToken, remoteOs);
|
||||
mcpConfigPath = GenerateMcpConfig(sessionId, agentId, hasRemote, unityMcpUrl, unityMcpToken, remoteOs, modelId, webSearch);
|
||||
|
||||
// Python-Verfügbarkeit prüfen und ggf. warnen
|
||||
if (FindPythonCommand() == null)
|
||||
@@ -903,7 +903,7 @@ public partial class AnthropicService
|
||||
/// Generiert eine temporäre MCP-Config-Datei für die Claude CLI.
|
||||
/// Enthält den MCP-Server für remote_execute.
|
||||
/// </summary>
|
||||
private string GenerateMcpConfig(string sessionId, string agentId = null, bool hasRemote = false, string unityMcpUrl = null, string unityMcpToken = null, string remoteOs = null)
|
||||
private string GenerateMcpConfig(string sessionId, string agentId = null, bool hasRemote = false, string unityMcpUrl = null, string unityMcpToken = null, string remoteOs = null, string modelId = null, bool webSearch = false)
|
||||
{
|
||||
// Port aus Konfiguration oder Default
|
||||
var port = 19000;
|
||||
@@ -963,6 +963,27 @@ public partial class AnthropicService
|
||||
mcpServers["unity"] = unityConfig;
|
||||
}
|
||||
|
||||
// z.ai Web-Search-Prime-MCP: GLM-Modelle können Anthropics native WebSearch nicht nutzen.
|
||||
// Läuft ein GLM-Modell mit aktivierter Web-Suche und ist der z.ai-Key gesetzt, registrieren
|
||||
// wir z.ais HTTP-MCP-Suchserver (Bearer-Auth mit demselben Key). Tool wird in den
|
||||
// --allowed-tools der CLI (ClaudeCliService) als "mcp__web-search-prime__*" freigegeben.
|
||||
if (webSearch && ClaudeCliService.IsZaiModel(modelId))
|
||||
{
|
||||
var zaiKey = _settings?.Get("ZaiApiKey");
|
||||
if (!string.IsNullOrEmpty(zaiKey))
|
||||
{
|
||||
mcpServers["web-search-prime"] = new Dictionary<string, object>
|
||||
{
|
||||
["type"] = "http",
|
||||
["url"] = "https://api.z.ai/api/mcp/web_search_prime/mcp",
|
||||
["headers"] = new Dictionary<string, string>
|
||||
{
|
||||
["Authorization"] = $"Bearer {zaiKey}"
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
var config = new { mcpServers };
|
||||
|
||||
var json = System.Text.Json.JsonSerializer.Serialize(config, new System.Text.Json.JsonSerializerOptions { WriteIndented = true });
|
||||
|
||||
@@ -65,6 +65,31 @@ public class ClaudeCliService
|
||||
public static bool IsZaiModel(string modelId)
|
||||
=> !string.IsNullOrEmpty(modelId) && modelId.StartsWith("glm-", System.StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
/// <summary>
|
||||
/// GLM-5.2 (und höher) hat (laut z.ai-Doku) ein Reasoning-Effort-Mapping:
|
||||
/// Claude Code mappt low/medium/high → GLM-"high", xhigh/max → GLM-"max". Ältere GLM-Modelle
|
||||
/// (4.x, 5, 5-turbo, 5.1) kennen kein Effort. Bewusst ein Versionsvergleich statt Prefix-Match,
|
||||
/// damit künftige Releases (5.3, 6, …) automatisch mitgenommen werden. Erfasst auch
|
||||
/// Varianten-Suffixe wie "glm-5.2[1m]" oder "glm-5-turbo".
|
||||
/// </summary>
|
||||
private static bool IsZaiEffortModel(string modelId)
|
||||
{
|
||||
if (string.IsNullOrEmpty(modelId) || !modelId.StartsWith("glm-", System.StringComparison.OrdinalIgnoreCase))
|
||||
return false;
|
||||
// Führende Versionsziffern nach "glm-" extrahieren: "5.2[1m]" → 5.2, "5-turbo" → 5(.0).
|
||||
var rest = modelId.Substring(4);
|
||||
int i = 0;
|
||||
while (i < rest.Length && (char.IsDigit(rest[i]) || rest[i] == '.')) i++;
|
||||
var versionStr = rest.Substring(0, i).Trim('.');
|
||||
if (versionStr.Length == 0) return false;
|
||||
var parts = versionStr.Split('.');
|
||||
if (!int.TryParse(parts[0], out var major)) return false;
|
||||
var minor = 0;
|
||||
if (parts.Length > 1) int.TryParse(parts[1], out minor);
|
||||
// GLM-5.2 oder höher.
|
||||
return major > 5 || (major == 5 && minor >= 2);
|
||||
}
|
||||
|
||||
private ProcessStartInfo CreateCliStartInfo(List<string> args, bool redirectInput = false, string effort = null, string workingDirectory = null, string modelId = null)
|
||||
{
|
||||
var psi = new ProcessStartInfo
|
||||
@@ -112,8 +137,12 @@ public class ClaudeCliService
|
||||
psi.Environment["ANTHROPIC_DEFAULT_SONNET_MODEL"] = modelId;
|
||||
psi.Environment["ANTHROPIC_DEFAULT_OPUS_MODEL"] = modelId;
|
||||
psi.Environment["ANTHROPIC_DEFAULT_HAIKU_MODEL"] = "glm-4.5-air";
|
||||
// Effort/Reasoning-Budget ist Claude-spezifisch und für z.ai nicht definiert → nicht senden.
|
||||
psi.Environment.Remove("CLAUDE_CODE_EFFORT_LEVEL");
|
||||
// Effort/Reasoning: NUR GLM-5.2 hat ein definiertes Effort-Mapping bei z.ai
|
||||
// (Claude Code mappt es selbst: low/medium/high → "high", xhigh/max → "max").
|
||||
// Für 5.2 reichen wir CLAUDE_CODE_EFFORT_LEVEL daher durch; alle anderen GLM-Modelle
|
||||
// kennen kein Effort → Env-Var entfernen, damit nichts Undefiniertes gesendet wird.
|
||||
if (!IsZaiEffortModel(modelId))
|
||||
psi.Environment.Remove("CLAUDE_CODE_EFFORT_LEVEL");
|
||||
// Ein evtl. global gesetzter Anthropic-API-Key darf den z.ai-Token nicht überschreiben.
|
||||
psi.Environment.Remove("ANTHROPIC_API_KEY");
|
||||
}
|
||||
@@ -207,10 +236,12 @@ public class ClaudeCliService
|
||||
"--output-format", "stream-json",
|
||||
"--include-partial-messages",
|
||||
"--no-session-persistence",
|
||||
"--permission-mode", permissionMode,
|
||||
"--disallowed-tools", "AskUserQuestion"
|
||||
"--permission-mode", permissionMode
|
||||
};
|
||||
|
||||
// Tools, die das Modell gar nicht erst anbieten/versuchen soll.
|
||||
var disallowedTools = new List<string> { "AskUserQuestion" };
|
||||
|
||||
// Tools konfigurieren
|
||||
var tools = new List<string>();
|
||||
if (hasWorkspace && planMode)
|
||||
@@ -228,7 +259,27 @@ public class ClaudeCliService
|
||||
// Kein Workspace: Read für Temp-Dateien (z.B. gesendete Bilder)
|
||||
tools.Add("Read");
|
||||
}
|
||||
if (webSearch) { tools.Add("WebFetch"); tools.Add("WebSearch"); }
|
||||
if (webSearch)
|
||||
{
|
||||
// GLM/z.ai kann Anthropics server-seitige WebSearch/WebFetch nicht ausführen →
|
||||
// stattdessen z.ais Web-Search-Prime-MCP-Tool zulassen. Der zugehörige MCP-Server
|
||||
// wird in GenerateMcpConfig injiziert, sobald GLM + Web-Suche + ZaiApiKey gesetzt sind.
|
||||
if (IsZaiModel(modelId) && !string.IsNullOrEmpty(_settings?.Get("ZaiApiKey")))
|
||||
{
|
||||
tools.Add("mcp__web-search-prime__*");
|
||||
// Die nativen Web-Tools komplett verbieten: sonst versucht das Modell zuerst
|
||||
// eine (über z.ai nicht ausführbare) WebSearch, scheitert headless an der
|
||||
// Berechtigung und greift erst im zweiten Anlauf zum Prime-MCP. Mit dem Verbot
|
||||
// werden sie dem Modell gar nicht angeboten → direkt das Prime-Tool.
|
||||
disallowedTools.Add("WebSearch");
|
||||
disallowedTools.Add("WebFetch");
|
||||
}
|
||||
else
|
||||
{
|
||||
tools.Add("WebFetch");
|
||||
tools.Add("WebSearch");
|
||||
}
|
||||
}
|
||||
// MCP-Tools erlauben (wenn MCP-Config vorhanden)
|
||||
if (!string.IsNullOrEmpty(mcpConfigPath))
|
||||
{
|
||||
@@ -246,6 +297,9 @@ public class ClaudeCliService
|
||||
args.Add("--allowed-tools");
|
||||
args.Add(tools.Count > 0 ? string.Join(" ", tools) : "\"\"");
|
||||
|
||||
args.Add("--disallowed-tools");
|
||||
args.Add(string.Join(" ", disallowedTools));
|
||||
|
||||
if (!string.IsNullOrEmpty(workspace))
|
||||
{
|
||||
args.Add("--add-dir");
|
||||
|
||||
Reference in new issue
Block a user