Summary
When calling the OpenAI-compatible /v1/chat/completions endpoint with tool_choice="auto" (the OpenAI default, and what most agent frameworks send), Foundry Local returns the tool call as raw text inside message.content with an empty tool_calls array and finish_reason="stop". The structured tool_calls are only populated when the caller explicitly sets tool_choice="required".
This breaks standard OpenAI clients and agent frameworks (e.g. anything built on the OpenAI SDK, LangChain, custom agents), because they read message.tool_calls and — per the OpenAI spec — expect tool_choice="auto" to let the model decide and still return structured tool calls when it chooses to call a tool. With Foundry Local they instead receive prose containing an XML/JSON blob that they cannot dispatch.
Reproduction
Model: qwen2.5-coder-7b-instruct-generic-cpu (also reproduces on other tool-capable models), Foundry Local 0.10.0.
Request (only tool_choice differs):
curl -s http://127.0.0.1:<port>/v1/chat/completions -H "Content-Type: application/json" -d '{
"model": "qwen2.5-coder-7b-instruct-generic-cpu",
"messages": [{"role": "user", "content": "What is the weather in Paris? Use the tool."}],
"tools": [{"type": "function", "function": {
"name": "get_weather",
"description": "Get weather for a city",
"parameters": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]}}}],
"tool_choice": "auto",
"temperature": 0
}'
Actual behavior
tool_choice="auto" — tool call leaks into content as text, tool_calls empty:
finish_reason: stop
tool_calls: []
content: "```xml\n<tools>\n {\"name\": \"get_weather\", \"arguments\": {\"city\": \"Paris\"}}\n</tools>\n```"
tool_choice="required" — structured tool call is returned as expected:
finish_reason: tool_calls
tool_calls: [{"index": 0, "id": "09xikmhSQ", "type": "function",
"function": {"name": "get_weather", "arguments": "{\n \"city\": \"Paris\"\n}"}}]
Expected behavior
With tool_choice="auto", when the model decides to call a tool, Foundry Local should parse the model's tool-call output and populate message.tool_calls with finish_reason="tool_calls" — the same as tool_choice="required" — instead of returning the call as raw text. auto should differ from required only in whether a tool must be called, not in how a chosen call is surfaced.
Impact
- Any OpenAI-compatible agent/framework that sends the default
tool_choice="auto" cannot use Foundry Local tool calling without a custom text-scraping workaround.
- Forcing
tool_choice="required" is not a general fix, because it prevents the model from answering directly when no tool is needed.
Environment
- Foundry Local:
0.10.0
- Endpoint: OpenAI-compatible
/v1/chat/completions
- Models:
qwen2.5-coder-7b-instruct-generic-cpu (reproduces broadly across tool-capable models)
- OS: Linux
Question
Is the auto → text vs required → structured behavior intentional, or a gap in the server-side tool-call parsing? Ideally auto would run the same tool-call extraction that required already does and emit structured tool_calls.
Summary
When calling the OpenAI-compatible
/v1/chat/completionsendpoint withtool_choice="auto"(the OpenAI default, and what most agent frameworks send), Foundry Local returns the tool call as raw text insidemessage.contentwith an emptytool_callsarray andfinish_reason="stop". The structuredtool_callsare only populated when the caller explicitly setstool_choice="required".This breaks standard OpenAI clients and agent frameworks (e.g. anything built on the OpenAI SDK, LangChain, custom agents), because they read
message.tool_callsand — per the OpenAI spec — expecttool_choice="auto"to let the model decide and still return structured tool calls when it chooses to call a tool. With Foundry Local they instead receive prose containing an XML/JSON blob that they cannot dispatch.Reproduction
Model:
qwen2.5-coder-7b-instruct-generic-cpu(also reproduces on other tool-capable models), Foundry Local0.10.0.Request (only
tool_choicediffers):Actual behavior
tool_choice="auto"— tool call leaks into content as text,tool_callsempty:tool_choice="required"— structured tool call is returned as expected:Expected behavior
With
tool_choice="auto", when the model decides to call a tool, Foundry Local should parse the model's tool-call output and populatemessage.tool_callswithfinish_reason="tool_calls"— the same astool_choice="required"— instead of returning the call as raw text.autoshould differ fromrequiredonly in whether a tool must be called, not in how a chosen call is surfaced.Impact
tool_choice="auto"cannot use Foundry Local tool calling without a custom text-scraping workaround.tool_choice="required"is not a general fix, because it prevents the model from answering directly when no tool is needed.Environment
0.10.0/v1/chat/completionsqwen2.5-coder-7b-instruct-generic-cpu(reproduces broadly across tool-capable models)Question
Is the
auto→ text vsrequired→ structured behavior intentional, or a gap in the server-side tool-call parsing? Ideallyautowould run the same tool-call extraction thatrequiredalready does and emit structuredtool_calls.