Skip to content

[Bug]: qvac serve openai accepts tools but never returns tool_calls, so models fabricate answers instead of calling tools #4131

Description

@victorchimakanu

What happens

qvac serve openai accepts a tools array on POST /v1/chat/completions but never returns tool_calls. The request succeeds with finish_reason: "stop", no tool_calls field, and the model answers in prose as if no tools were offered. Because the model is never told the tools exist, it fabricates an answer instead of calling them.

The OpenAPI spec the CLI emits does declare both tools and tool_calls, so the capability is advertised.

Reproduction

Serve any chat model, then:

curl -s http://127.0.0.1:11434/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model":"<model>",
    "messages":[{"role":"user","content":"What is the weather in Lagos?"}],
    "tools":[{"type":"function","function":{
      "name":"get_weather",
      "description":"Get current weather for a city",
      "parameters":{"type":"object","properties":{"city":{"type":"string"}},"required":["city"]}
    }}],
    "max_tokens":250
  }'

Expected: finish_reason: "tool_calls" with a tool_calls entry for get_weather.

Actual: finish_reason: "stop", no tool_calls, and a fabricated answer:

"The current weather in Lagos, Nigeria, is as follows: Temperature: Approximately 30°C (86°F), Conditions: Sunny, Humidity: High, around 80% ..."

Passing tool_choice: "auto" changes nothing (and tool_choice is absent from the emitted spec). Same result on Qwen3-4B-Q4_K_M and Qwen3-8B-Q4_K_M.

Spec says otherwise

qvac openai spec > spec.json
grep -c tool_calls spec.json   # present
grep -c '"tools"' spec.json    # present
grep -c tool_choice spec.json  # absent

Why it matters

Silent fabrication is the worst failure mode here. Nothing errors, so a caller cannot tell the difference between a tool result and an invention. Driving a real agent harness through it, I got a confident list of three KYC applicants who do not exist, in an account whose real applicants are entirely different people.

It also blocks agent harnesses generally. I was connecting OpenCode to a local QVAC model through a skill (the @qvac/opencode-plugin path). OpenCode sends its tools correctly, serve drops them, and the model narrates tool use instead of performing it. The same harness against a tool-calling endpoint works fine, so the harness is not the problem.

Practically this is the gap between "QVAC can replace your local model server" and "QVAC can replace it for agentic work". Everything conversational is unaffected; anything that needs the model to call a tool is blocked today.

Ask

Either implement tools / tool_calls (plus tool_choice) in the chat completions adapter, or, until then, reject requests containing tools with a clear error rather than silently ignoring them. A loud failure is far safer than a fabricated answer.

Environment

  • qvac CLI 0.9.0
  • macOS 26.5.1, arm64
  • Models: Qwen3-4B-Q4_K_M, Qwen3-8B-Q4_K_M

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions