Responses API: synthesise function_call output items from tool-call deltas
#6
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Scope cut from Step 2 (commit
957f704)The Responses projector currently emits exactly one output item per response — a single
messagecontaining text.function_callitems are defined onResponsesOutputItem(crates/cortex-core/src/responses.rs) but the projector never synthesises them.Two reasons:
<tool_call>{json}</tool_call>blocks inline in its content stream; the parsing happens in helexa-acp (crates/helexa-acp/src/qwen3.rs::ToolCallParser) rather than in neuron.InferenceEvent::ToolCallStartandInferenceEvent::ToolCallArgsDeltaare defined on the event enum (crates/neuron/src/wire/event.rs) but never produced.Compare to how the OpenAI chat projector handles this: it also doesn't split tool calls from content. Today both projectors expose the raw
<tool_call>text and rely on every consumer to parse it.Why it was cut
Tool-call extraction touches the inference inner loop in three sites (CPU, CUDA single-GPU, CUDA TP) the same way reasoning extraction does. We didn't want to bundle that with Step 1's refactor or Step 2's new surface.
What implementation looks like
<think>parser in #5 but for<tool_call>{json}</tool_call>blocks. EmitsInferenceEvent::ToolCallStarton the open tag,ToolCallArgsDeltafor the body, and finalises when the close tag arrives.helexa-acp/src/qwen3.rsandhelexa-acp/src/tools.rs::infer_tool_nameintoneuron::wire.delta: { tool_calls: [{ index, id, function: { name, arguments } }] }shape on each ToolCall* event, plus the finalfinish_reason: "tool_calls"chunk.response.output_item.addedwith a newfunction_callitem (after the message item or instead of it depending on order).response.function_call_arguments.deltafor each args delta.response.function_call_arguments.donewhen the call closes.response.output_item.donefor the function_call item.finish_reasonmapping —FinishReason::ToolCallsis already defined; threads through totool_callson chat and the appropriate Responses status.Acceptance
ToolCallStart+ToolCallArgsDeltaevents on the InferenceEvent stream, with the model's<tool_call>{json}</tool_call>markers consumed (not surfaced as text).tool_callsdelta shape.response.function_call_arguments.*events.ToolCallParserbecomes redundant for cortex-backed sessions.Tracking
Blocks: Responses-API-driven tool use against neuron (helexa-acp's openai-responses provider can't get function-call output items today). Coupled with #5 — both are the same harness-level extraction work, just for different tags. May want to land them together.