Skip to content

feat(server): OpenAI tools pass-through (/v1/chat/completions)#64

Merged
magicnight merged 2 commits into
mainfrom
feat/server-tools-passthrough
Jul 8, 2026
Merged

feat(server): OpenAI tools pass-through (/v1/chat/completions)#64
magicnight merged 2 commits into
mainfrom
feat/server-tools-passthrough

Conversation

@magicnight

Copy link
Copy Markdown
Owner

The server half of tool-calling — completes the story alongside the just-merged GUI agent loop (#63). The server does NOT run an agent loop: it passes OpenAI tools into the chat template and returns detected tool_calls in OpenAI shape; external clients (Zed, Cursor, the OpenAI SDK) run their own loop.

What lands (HummingbirdServer.swift only)

  • ChatCompletionRequest gains tools + tool_choice (decodeIfPresent, back-compat). tools forwarded verbatim into GenerateRequest.tools; tool_choice:"none" suppresses, anything else offers (fine-grained forcing deferred).
  • Non-streaming: message.tool_calls in exact OpenAI shape — arguments as a JSON-encoded string (the Chat inference aborts when switching sidebar tabs #1 thing clients choke on, done right + tested), content:null when tool-calls-only, finish_reason:"tool_calls".
  • Streaming: buffered-text delta → delta.tool_calls (complete-in-one-delta with index) → final finish_reason:"tool_calls" frame; non-tool path byte-identical; [DONE] preserved.
  • Anthropic/Ollama pass-through + fine-grained tool_choice = documented follow-ups.

Review + verification

Adversarially reviewed APPROVE — all 6 wire behaviors (serialization shape, content:null, streaming frames, tool_choice, back-compat, interaction with reasoning/watchdog/lock) confirmed correct against the OpenAI contract; back-compat preserved on three axes; no scope creep; no force-unwrap. 5 non-tautological tests. Independently verified green (merged with wave-2 core): ** TEST SUCCEEDED **, 244 tests, bare swift test clean, CLI builds.

Server half of tool-calling (wave 1 shipped engine detection; the GUI
agent loop is separate). The server does NOT run an agent loop — it
passes tools into the template and returns detected tool_calls; external
clients (Zed/Cursor/etc.) run their own loop.

- ChatCompletionRequest gains tools:[JSONValue]? + tool_choice:JSONValue?
  (decodeIfPresent — back-compat). tools forwarded verbatim into
  GenerateRequest.tools (template + engine consume the OpenAI function-spec
  shape directly). tool_choice "none" suppresses tools; anything else
  offers them (fine-grained forcing deferred, documented).
- Non-streaming: terminal chunk's toolCalls → message.tool_calls in exact
  OpenAI shape (id/type:function/function.name + arguments as a JSON-encoded
  STRING); content=null when tool-calls-only; finish_reason:tool_calls.
- Streaming: buffered-text delta → delta.tool_calls (complete-in-one-delta
  with index) → final finish_reason:tool_calls frame; non-tool path
  byte-identical; [DONE] still emitted.
- Anthropic/Ollama pass-through + fine-grained tool_choice = follow-ups.

Reviewed APPROVE (all 6 wire behaviors correct, back-compat preserved,
no scope creep, no force-unwrap). 5 new tests; 241 MacMLXCore tests green
under xcodebuild + bare swift test; CLI builds.
@cursor

cursor Bot commented Jul 8, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@magicnight magicnight merged commit 042458c into main Jul 8, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant