From 2c7d5d049c8eec2dc8f7c7b34d86afed780fec82 Mon Sep 17 00:00:00 2001 From: OG T Date: Thu, 9 Apr 2026 18:15:01 +0800 Subject: [PATCH] =?UTF-8?q?fix(openclaw):=20Nemotron=20tool=20call=20?= =?UTF-8?q?=E5=9B=9E=E5=A1=AB=20kubectl=5Fcommand=EF=BC=8C=E8=AE=93?= =?UTF-8?q?=E6=89=B9=E5=87=86=E5=BE=8C=E5=9F=B7=E8=A1=8C=E5=99=A8=E8=83=BD?= =?UTF-8?q?=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 根本問題:Nemotron 產生的 restart_deployment(deployment_name=sentry) tool call 只存在 nemotron_tools[],沒有回填到 proposal["kubectl_command"]。 proposal_service 拿到的 kubectl_command 是空的,approval_records.action 存空值, parse_operation_from_action 永遠返回 None,execute_approved_action 永遠 skip。 修正:Nemotron (和 Gemini fallback) 成功後,將 tool call 轉換為 kubectl 指令 並回填 proposal["kubectl_command"],讓 proposal_service 能取到可執行指令。 Co-Authored-By: Claude Sonnet 4.6 --- apps/api/src/services/openclaw.py | 43 +++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/apps/api/src/services/openclaw.py b/apps/api/src/services/openclaw.py index ff78140a..d3176469 100644 --- a/apps/api/src/services/openclaw.py +++ b/apps/api/src/services/openclaw.py @@ -1557,6 +1557,28 @@ Focus on: proposal["nemotron_tool_model"] = nemotron_result.get("tool_model", "") proposal["nemotron_tool_backend"] = nemotron_result.get("tool_backend", "") + # 2026-04-09 Claude Sonnet 4.6: 將 Nemotron tool call 回填為 kubectl_command + # 根本問題修復:approval_records.action 需要可執行指令才能被 parse_operation_from_action 解析 + _tools = proposal["nemotron_tools"] + if _tools: + _t = _tools[0] + _tool_name = _t.get("tool", "") + _args = _t.get("args", {}) + _ns = _args.get("namespace", proposal.get("namespace", "awoooi-prod")) + if _tool_name == "restart_deployment": + _deploy = _args.get("deployment_name", proposal.get("target_resource", "")) + if _deploy: + proposal["kubectl_command"] = f"kubectl rollout restart deployment/{_deploy} -n {_ns}" + elif _tool_name == "delete_pod": + _pod = _args.get("pod_name", "") + if _pod: + proposal["kubectl_command"] = f"kubectl delete pod {_pod} -n {_ns}" + elif _tool_name == "scale_deployment": + _deploy = _args.get("deployment_name", "") + _replicas = _args.get("replicas", 2) + if _deploy: + proposal["kubectl_command"] = f"kubectl scale deployment/{_deploy} --replicas={_replicas} -n {_ns}" + logger.info( "nemotron_collaboration_complete", incident_id=incident_id, @@ -1605,6 +1627,27 @@ Focus on: proposal["nemotron_tool_model"] = "gemini-fallback" proposal["nemotron_tool_backend"] = "Gemini 雲端" + # 2026-04-09 Claude Sonnet 4.6: Gemini fallback 同樣回填 kubectl_command + _fb_tools = proposal["nemotron_tools"] + if _fb_tools: + _t = _fb_tools[0] + _tool_name = _t.get("tool", "") + _args = _t.get("args", {}) + _ns = _args.get("namespace", proposal.get("namespace", "awoooi-prod")) + if _tool_name == "restart_deployment": + _deploy = _args.get("deployment_name", proposal.get("target_resource", "")) + if _deploy: + proposal["kubectl_command"] = f"kubectl rollout restart deployment/{_deploy} -n {_ns}" + elif _tool_name == "delete_pod": + _pod = _args.get("pod_name", "") + if _pod: + proposal["kubectl_command"] = f"kubectl delete pod {_pod} -n {_ns}" + elif _tool_name == "scale_deployment": + _deploy = _args.get("deployment_name", "") + _replicas = _args.get("replicas", 2) + if _deploy: + proposal["kubectl_command"] = f"kubectl scale deployment/{_deploy} --replicas={_replicas} -n {_ns}" + return proposal, provider, True async def _call_nemotron_tools(