From 624733d631ac3197afa4f9aa701f56f184fdde69 Mon Sep 17 00:00:00 2001 From: Aldehir Rojas Date: Tue, 31 Mar 2026 06:52:42 -0500 Subject: [PATCH] common : gpt-oss handle builtin and unsolicited tool calls (#21213) --- common/chat.cpp | 6 +++++- tests/test-chat.cpp | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/common/chat.cpp b/common/chat.cpp index bf43087285..c2ca17c743 100644 --- a/common/chat.cpp +++ b/common/chat.cpp @@ -989,6 +989,10 @@ static common_chat_params common_chat_params_init_gpt_oss(const common_chat_temp auto analysis = p.ref("analysis"); auto preamble = p.rule("preamble", p.literal("<|channel|>commentary<|message|>") + p.content(content) + end); auto final_msg = p.rule("final", p.literal("<|channel|>final<|message|>") + p.content(content)); + + // Consume any unsolicited tool calls, e.g. builtin functions + auto unsolicited = p.rule("unsolicited", p.atomic(p.optional(channel) + p.literal(" to=") + content + end)); + auto any = p.rule("any", preamble | analysis); if (has_response_format) { @@ -1032,7 +1036,7 @@ static common_chat_params common_chat_params_init_gpt_oss(const common_chat_temp return p.zero_or_more(start + any) + start + (tool_call | final_msg); } - return p.zero_or_more(start + any) + start + final_msg; + return p.zero_or_more(start + any) + start + (final_msg | unsolicited); }); data.parser = parser.save(); diff --git a/tests/test-chat.cpp b/tests/test-chat.cpp index 6e11252e12..1c4da68195 100644 --- a/tests/test-chat.cpp +++ b/tests/test-chat.cpp @@ -3077,6 +3077,27 @@ static void test_template_output_peg_parsers(bool detailed_debug) { .expect_reasoning("I need to output the invoice details in JSON") .expect_content(R"({"amount": 123.45, "date": "2025-12-03"})") .run(); + + + // Unsolicited tool calls. There is no good way to handle these, so we return empty content. + + // Builtin function - recipient in role + tst.test( + "<|channel|>analysis<|message|>I will execute python to say hello<|end|>" + "<|start|>assistant to=container.exec<|channel|>commentary<|message|>python3 -c 'print(\"hello\")'") + .reasoning_format(COMMON_REASONING_FORMAT_AUTO) + .expect_reasoning("I will execute python to say hello") + .expect_content("") + .run(); + + // Builtin function - recipient in channel + tst.test( + "<|channel|>analysis<|message|>I will execute python to say hello<|end|>" + "<|start|>assistant<|channel|>commentary to=python <|constrain|>code<|message|>print(\"hello\")") + .reasoning_format(COMMON_REASONING_FORMAT_AUTO) + .expect_reasoning("I will execute python to say hello") + .expect_content("") + .run(); } {