Created
February 27, 2026 21:18
-
-
Save epequeno/c1219fb15f18aa7c5c0f49b2f0fe8957 to your computer and use it in GitHub Desktop.
Patch for the pi coding harness to work with opencode's GLM-5 model
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Patch file (pi-openai-completions-glm5-fix.patch): | |
| - 82-line unified diff against @mariozechner/pi-ai@0.55.1 | |
| - Portable path headers (a/dist/... → b/dist/...) | |
| - To apply on another machine: | |
| cd <pi-ai-install-path> # e.g. ~/.local/share/fnm/.../node_modules/@mariozechner/pi-ai | |
| patch -p1 < pi-openai-completions-glm5-fix.patch |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- a/dist/providers/openai-completions.js | |
| +++ b/dist/providers/openai-completions.js | |
| @@ -94,6 +94,12 @@ | |
| else if (block.type === "toolCall") { | |
| block.arguments = parseStreamingJson(block.partialArgs); | |
| delete block.partialArgs; | |
| + // Emit deferred start if it was never sent | |
| + if (!block._startEmitted) { | |
| + stream.push({ type: "toolcall_start", contentIndex: blockIndex(), partial: output }); | |
| + block._startEmitted = true; | |
| + } | |
| + delete block._startEmitted; | |
| stream.push({ | |
| type: "toolcall_end", | |
| contentIndex: blockIndex(), | |
| @@ -194,9 +200,14 @@ | |
| } | |
| if (choice?.delta?.tool_calls) { | |
| for (const toolCall of choice.delta.tool_calls) { | |
| - if (!currentBlock || | |
| + // Use function.name to detect new tool calls (not id alone), | |
| + // because some providers (e.g. OpenCode Zen/GLM-5) send a | |
| + // unique id on every streaming chunk. A new tool call always | |
| + // has a non-empty function.name; continuation chunks don't. | |
| + const isNewToolCall = !currentBlock || | |
| currentBlock.type !== "toolCall" || | |
| - (toolCall.id && currentBlock.id !== toolCall.id)) { | |
| + (toolCall.function?.name && toolCall.function.name.length > 0); | |
| + if (isNewToolCall) { | |
| finishCurrentBlock(currentBlock); | |
| currentBlock = { | |
| type: "toolCall", | |
| @@ -204,13 +215,15 @@ | |
| name: toolCall.function?.name || "", | |
| arguments: {}, | |
| partialArgs: "", | |
| + _startEmitted: false, | |
| }; | |
| output.content.push(currentBlock); | |
| - stream.push({ type: "toolcall_start", contentIndex: blockIndex(), partial: output }); | |
| } | |
| if (currentBlock.type === "toolCall") { | |
| - if (toolCall.id) | |
| - currentBlock.id = toolCall.id; | |
| + // Do NOT update id on continuation chunks. | |
| + // GLM-5 via OpenCode Zen sends a unique id per chunk, | |
| + // and the TUI uses id to track tool call components — | |
| + // changing it causes a new "$ ..." widget per chunk. | |
| if (toolCall.function?.name) | |
| currentBlock.name = toolCall.function.name; | |
| let delta = ""; | |
| @@ -218,13 +231,24 @@ | |
| delta = toolCall.function.arguments; | |
| currentBlock.partialArgs += toolCall.function.arguments; | |
| currentBlock.arguments = parseStreamingJson(currentBlock.partialArgs); | |
| + // Defer both toolcall_start and toolcall_delta until | |
| + // the arguments JSON is complete, to avoid TUI flicker | |
| + // from per-token streaming (GLM-5 via OpenCode Zen). | |
| + let argsComplete = false; | |
| + try { JSON.parse(currentBlock.partialArgs); argsComplete = true; } catch {} | |
| + if (argsComplete) { | |
| + if (!currentBlock._startEmitted) { | |
| + stream.push({ type: "toolcall_start", contentIndex: blockIndex(), partial: output }); | |
| + currentBlock._startEmitted = true; | |
| + } | |
| + stream.push({ | |
| + type: "toolcall_delta", | |
| + contentIndex: blockIndex(), | |
| + delta, | |
| + partial: output, | |
| + }); | |
| + } | |
| } | |
| - stream.push({ | |
| - type: "toolcall_delta", | |
| - contentIndex: blockIndex(), | |
| - delta, | |
| - partial: output, | |
| - }); | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment