Problem
GPT 5.4 sometimes returns tool calls as plain assistant text instead of structured OpenAI-compatible tool_calls.
Observed in Chermes/Telegram:
{
"type": "function",
"name": "terminal",
"parameters": {
"cmd": "ls -alh /home/Blockrun"
}
}
Also observed:
terminal
ls -alh /home/Blockrun
[/terminal]
and earlier during the same investigation:
{"name":"session_search","parameters":{"query":"\"I though he never left\" \"Ronaldo\""}}
read_file(parameters={"path":"/home/Blockrun"})
Expected
GPT tool calls should reach downstream clients as structured OpenAI-style tool_calls, with empty assistant content and finish_reason: "tool_calls".
Actual
The model sometimes emits JSON/function-call-looking text in message.content. Downstream chat surfaces then display that raw text to the user instead of dispatching the tool.
Notes
This is similar to #189 / #190 for Gemini 3.5 Flash, but with GPT-specific text shapes instead of [Called function "NAME" with args: {...}].
A local testbed patch following the same approach as #190 recovered these shapes in extractTextualToolCalls:
- standalone
{"name":"NAME","parameters":{...}}
- standalone
{"type":"function","name":"NAME","parameters":{...}}
- whole-content
NAME(parameters={...})
- trailing JSON object after prose only when
type is exactly function
- whole-content
terminal\nCOMMAND\n[/terminal]
Guardrails in the local patch:
- no prose JSON examples unless the whole content is a call
- no trailing prose JSON unless
type:function is explicit
- no incomplete terminal block
- terminal
cmd normalized to command while preserving cmd
Targeted tests on the patched testbed checkout passed:
Checks also passed on the patched testbed checkout:
npm run typecheck
npm run lint
npm run format:check
npm run build
Problem
GPT 5.4 sometimes returns tool calls as plain assistant text instead of structured OpenAI-compatible
tool_calls.Observed in Chermes/Telegram:
Also observed:
and earlier during the same investigation:
Expected
GPT tool calls should reach downstream clients as structured OpenAI-style
tool_calls, with empty assistant content andfinish_reason: "tool_calls".Actual
The model sometimes emits JSON/function-call-looking text in
message.content. Downstream chat surfaces then display that raw text to the user instead of dispatching the tool.Notes
This is similar to #189 / #190 for Gemini 3.5 Flash, but with GPT-specific text shapes instead of
[Called function "NAME" with args: {...}].A local testbed patch following the same approach as #190 recovered these shapes in
extractTextualToolCalls:{"name":"NAME","parameters":{...}}{"type":"function","name":"NAME","parameters":{...}}NAME(parameters={...})typeis exactlyfunctionterminal\nCOMMAND\n[/terminal]Guardrails in the local patch:
type:functionis explicitcmdnormalized tocommandwhile preservingcmdTargeted tests on the patched testbed checkout passed:
Checks also passed on the patched testbed checkout: