Files
ModelRouter/cmd/gui/package.json
JianFeeeee 2e3d5b79ad fix(adapters): stop dropping non-streaming tool calls (agent loops died on turn 2)
Four adapters handled tool_calls in transform_stream_chunk but lost them in
transform_response, so any NON-streaming tool-using conversation broke on its
second request: the client received finish_reason:"tool_calls" with no
tool_calls payload, replayed an assistant message whose function
name/arguments were empty, and the upstream rejected the next turn with

    400 invalid tool_call function, function/name/arguments cannot be empty

The production audit trail shows 46 such failures on sensenova alone.

- sensenova.lua: forward message.tool_calls, decoding the arguments JSON string
  into an object as the unified shape expects.
- gemini.lua: collect functionCall parts from candidates[].content.parts. Also
  correct finish_reason, since Gemini reports "STOP" even when it emitted a
  function call and clients keyed on it treat that as a finished answer.
- ollama.lua: the field was initialized to an empty table and never filled;
  fill it and likewise correct done_reason "stop" -> "tool_calls".

trae is a different failure with the same symptom: trae-local-api's OpenAI
endpoint (/v1/chat/completions, src/server.js:353) never reads the request's
`tools` array — only its Anthropic endpoint does — so the relayed model is never
told the tool schema and instead PRINTS a <tool_call>{...}</tool_call> block into
content, leaving message.tool_calls null and finish_reason "stop". An OpenAI
client sees an ordinary completion and its agent loop ends mid-conversation.
trae.lua now recovers the structured call from that text, strips the block from
user-visible content, and corrects finish_reason. Both tag spellings
(<tool_call>/<toolcall>, the latter is what the same codebase's Anthropic prompt
asks for) and all three argument key names (arguments/params/input) are accepted.
This is a defensive fallback: fixing the upstream shim to honour `tools` remains
the real fix, since the model still guesses parameter names.

Tests: TestNonStreamToolCallsPreserved covers all ten OpenAI-shaped adapters,
TestGeminiNonStreamToolCalls and TestOllamaNonStreamToolCalls cover their native
shapes, TestTraeTextToolCallRecovery covers both tag spellings, prose around the
block, and asserts a plain text answer never gains tool_calls.

Verified end-to-end against mock upstreams reproducing each shape: a full
two-round agent loop (tool call -> tool result -> final answer) now completes for
both the structured and the text-emitted variants.
2026-08-31 10:23:35 +08:00

93 lines
2.2 KiB
JSON

{
"name": "modelrouter-gui",
"version": "1.4.2",
"description": "ModelRouter Desktop — embedded ModelRouter core with tray",
"author": "ModelRouter",
"main": "main.js",
"license": "Proprietary",
"scripts": {
"start": "electron . --no-sandbox",
"dev": "electron . --no-sandbox --dev",
"core:linux": "node scripts/prepare-core.js linux",
"core:win": "node scripts/prepare-core.js win",
"dist:linux": "npm run core:linux && electron-builder --linux deb rpm AppImage",
"dist:debian": "npm run core:linux && electron-builder --linux deb",
"dist:win": "npm run core:win && electron-builder --win nsis",
"dist:dir": "npm run core:linux && electron-builder --dir"
},
"devDependencies": {
"electron": "^33.0.0",
"electron-builder": "^26.15.3"
},
"build": {
"appId": "com.modelrouter.gui",
"productName": "ModelRouter",
"linux": {
"target": [
"deb",
"rpm",
"AppImage"
],
"category": "Network",
"icon": "build/icon.png",
"maintainer": "ModelRouter",
"synopsis": "ModelRouter Desktop",
"desktop": {
"entry": {
"StartupWMClass": "modelrouter-gui"
}
},
"extraResources": [
{
"from": "bin/llmsproxy",
"to": "bin/llmsproxy"
},
{
"from": "build/icon.png",
"to": "build/icon.png"
}
]
},
"directories": {
"output": "../build/gui-dist"
},
"mac": {
"target": "dmg",
"icon": "build/icon.png",
"extraResources": [
{
"from": "build/icon.png",
"to": "build/icon.png"
}
]
},
"win": {
"target": "nsis",
"icon": "build/icon.ico",
"extraResources": [
{
"from": "bin/llmsproxy.exe",
"to": "bin/llmsproxy.exe"
},
{
"from": "bin/lua51.dll",
"to": "bin/lua51.dll"
},
{
"from": "build/icon.png",
"to": "build/icon.png"
}
]
},
"files": [
"main.js",
"preload.js",
"renderer/**/*",
"build/**/*",
"!node_modules/**/*"
],
"asar": true
},
"homepage": "https://gitcode.com/JianFeeeee/ModelRouter"
}