feat(responses): tool calling on /v1/responses (#158) #159

Merged
grenade merged 1 commits from feat/158-responses-tool-calls into main 2026-07-15 07:22:55 +00:00
Owner

Fixes #158. Follow-up to #156/#157 — ZeroClaw chats stream answers now; this makes the agent able to act.

Request side (request_to_chat): Responses-flat tool definitions ({type:"function", name, description, parameters}) are normalized into the chat-wrapped shape and carried through extra["tools"], which the chat path already consumes for Jinja prompt rendering and argument-type coercion (build_tool_schemas tolerates both shapes). Already-wrapped tools pass through; hosted tool types (web_search, …) are dropped since there's no server-side implementation.

Response side (run_projection): harness InferenceEvent::ToolCalls project into the OpenAI function_call event family:

event: response.output_item.added        {item: {type:"function_call", id, call_id, name, arguments:"", status:"in_progress"}, output_index: 1+i}
event: response.function_call_arguments.delta   {item_id, output_index, delta}
event: response.function_call_arguments.done    {item_id, output_index, call_id, name, arguments}
event: response.output_item.done         {item: {…, status:"completed"}}

and the terminal response.completed output array carries the completed function_call items after the message item. ZeroClaw's parser consumes exactly this family (dedup across arguments.done / output_item.done / completed, verified against its source).

Input-side round-tripping (function_call → assistant tool_calls, function_call_output → tool-role message) already existed — chat templates read both — so the full agentic loop closes.

Non-streaming /v1/responses stays text-only (matches the chat non-streaming path; ZeroClaw streams).

Tests: tools normalization (flat/wrapped/hosted), full function_call event-family projection, plus the existing frame-shape suite.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Wpr9zRuucU5KXa7TCrQV6k

Fixes #158. Follow-up to #156/#157 — ZeroClaw chats stream answers now; this makes the agent able to act. **Request side** (`request_to_chat`): Responses-flat tool definitions (`{type:"function", name, description, parameters}`) are normalized into the chat-wrapped shape and carried through `extra["tools"]`, which the chat path already consumes for Jinja prompt rendering and argument-type coercion (`build_tool_schemas` tolerates both shapes). Already-wrapped tools pass through; hosted tool types (`web_search`, …) are dropped since there's no server-side implementation. **Response side** (`run_projection`): harness `InferenceEvent::ToolCall`s project into the OpenAI function_call event family: ``` event: response.output_item.added {item: {type:"function_call", id, call_id, name, arguments:"", status:"in_progress"}, output_index: 1+i} event: response.function_call_arguments.delta {item_id, output_index, delta} event: response.function_call_arguments.done {item_id, output_index, call_id, name, arguments} event: response.output_item.done {item: {…, status:"completed"}} ``` and the terminal `response.completed` output array carries the completed `function_call` items after the message item. ZeroClaw's parser consumes exactly this family (dedup across `arguments.done` / `output_item.done` / `completed`, verified against its source). Input-side round-tripping (`function_call` → assistant `tool_calls`, `function_call_output` → tool-role message) already existed — chat templates read both — so the full agentic loop closes. Non-streaming `/v1/responses` stays text-only (matches the chat non-streaming path; ZeroClaw streams). Tests: tools normalization (flat/wrapped/hosted), full function_call event-family projection, plus the existing frame-shape suite. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01Wpr9zRuucU5KXa7TCrQV6k
grenade added 1 commit 2026-07-15 07:08:44 +00:00
feat(responses): tool calling on /v1/responses — forward tools, emit function_call events (#158)
All checks were successful
CI / Format (push) Successful in 7s
CI / Format (pull_request) Successful in 7s
CI / Clippy (push) Successful in 2m18s
CI / Test (push) Successful in 6m34s
CI / CUDA type-check (push) Successful in 13m50s
CI / Build cortex SRPM (push) Has been skipped
CI / Build neuron SRPM (push) Has been skipped
CI / Publish cortex to COPR (push) Has been skipped
CI / Publish neuron to COPR (push) Has been skipped
CI / Bump version in source (push) Has been skipped
CI / CUDA type-check (pull_request) Successful in 13m55s
CI / Clippy (pull_request) Successful in 3m0s
CI / Test (pull_request) Successful in 7m29s
CI / Build cortex SRPM (pull_request) Has been skipped
CI / Build neuron SRPM (pull_request) Has been skipped
CI / Publish cortex to COPR (pull_request) Has been skipped
CI / Publish neuron to COPR (pull_request) Has been skipped
CI / Bump version in source (pull_request) Has been skipped
ffa96fb053
Request side: request_to_chat now normalizes Responses-flat tool
definitions ({type:"function", name, …}) into the chat-wrapped shape
({type:"function", function:{…}}) and carries them through extra so
the chat template renders them and build_tool_schemas coerces
arguments. Hosted tool types (web_search, …) are dropped.

Response side: the streaming projector maps InferenceEvent::ToolCall
into the OpenAI function_call event family — output_item.added,
function_call_arguments.delta, function_call_arguments.done,
output_item.done — and the terminal response.completed output array
carries the completed function_call items after the message item.

Input-side round-tripping (function_call → assistant tool_calls,
function_call_output → tool-role message) already existed and is now
exercised end-to-end.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wpr9zRuucU5KXa7TCrQV6k
grenade merged commit 456b69e03c into main 2026-07-15 07:22:55 +00:00
grenade deleted branch feat/158-responses-tool-calls 2026-07-15 07:22:55 +00:00
Sign in to join this conversation.