From 4d197e4bd3fed7f531fcaa07fabf8eac81cec674 Mon Sep 17 00:00:00 2001 From: JianFeeeee Date: Tue, 1 Sep 2026 09:30:54 +0800 Subject: [PATCH] fix(trae): recover legacy [Called tool:...] tool call format Root cause: trae-local-api is deployed to users without the "Fold past assistant tool_calls into [Called tool: name({...})]" text format that their own histories already contained. Trae-Local-API-LLM then mimics this format in subsequent responses. The trae.lua parser only recognized ... or ... tags, so [Called tool: ...] responses were left unparsed and the client received plain text where a structured tool_calls array should be. Fix: Add a legacy pattern match at the END of parse_text_tool_calls to catch the [Called tool: name({args})] shape and emit proper tool_calls. This is a fallback; models should emit tags per system prompt, but we tolerate the mimicked form for robustness. --- internal/lua/adapters/trae.lua | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/internal/lua/adapters/trae.lua b/internal/lua/adapters/trae.lua index d05b45a..45949cc 100644 --- a/internal/lua/adapters/trae.lua +++ b/internal/lua/adapters/trae.lua @@ -46,9 +46,6 @@ end -- Returns (tool_calls_array_or_nil, content_with_blocks_removed). local function parse_text_tool_calls(content) if type(content) ~= "string" or content == "" then return nil, content end - if not (content:find("]*>%s*(.-)%s*") collect("]*>%s*(.-)%s*") + -- Legacy / mimicked form: models that saw past assistant turns folded as + -- [Called tool: name({...})] + -- tend to emit the same shape instead of a real block. Recover it so + -- the agent loop keeps working; otherwise the client receives plain text + -- where a structured tool call should be. Capture the name and the JSON + -- object literal independently (the JSON is the only {...} run here). + if #tcs == 0 and content:find("%[Called tool") then + for name, argsraw in content:gmatch("%[Called tool%s*:%s*([%w_%-%.]+)%s*%((%b{})%s*%)%s*%]") do + local aok, decoded = pcall(json.decode, argsraw) + idx = idx + 1 + table.insert(tcs, { + id = "call_legacy_" .. idx, + type = "function", + name = name, + arguments = aok and decoded or {} + }) + end + end + if #tcs == 0 then return nil, content end -- drop the blocks from user-visible content; keep any surrounding prose local stripped = content:gsub("]*>%s*.-%s*", "") stripped = stripped:gsub("]*>%s*.-%s*", "") + stripped = stripped:gsub("%[Called tool%s*:%s*[%w_%-%.]+%s*%(%b{}%s*%)%s*%]", "") stripped = stripped:gsub("^%s+", ""):gsub("%s+$", "") return tcs, stripped end