3.5 KiB
02.3 — Native reasoning + tool-arg streaming
tinyagents 1.3.0 MessageDelta has a reasoning field and
StreamAccumulator::reasoning(); ModelStreamItem::ToolCallDelta streams
tool-arg fragments. The out-of-band ThinkingForwarder is not one-shot
deletable: streaming reasoning can ride the crate stream, but OpenHuman still
uses the forwarder for non-streaming post-hoc reasoning and tool-argument
progress that preserves the UI's tool_name/start-event contract.
Current status (2026-07-02): streaming provider ThinkingDelta values are
mapped to MessageDelta::reasoning(...), and OpenhumanEventBridge projects
delta.reasoning into the same parent/sub-agent thinking progress events as the
old forwarder path. Tool-call argument fragments now ride the crate stream
too: forward_delta maps each ProviderDelta::ToolCallArgsDelta onto a
ModelStreamItem::ToolCallDelta(ToolDelta { call_id, content }), the crate
agent-loop surfaces it as MessageDelta.tool_call, and OpenhumanEventBridge
projects it into AgentProgress::ToolCallArgsDelta. Because the crate
ToolDelta has only call_id/content (no tool_name), the tool-call
start event — the empty-delta ToolCallArgsDelta that carries the tool name
and opens the UI timeline row — still rides ThinkingForwarder.note_tool_call,
which records the name into a call_id → tool_name map (ToolNameMap) shared
with the bridge so the streamed fragments stay labelled. The forwarder remains
live for that start marker and for non-streaming post-hoc reasoning; its
emit_tool_args argument-fragment path has been removed. The StreamAccumulator
treats the terminal Completed response as authoritative, so streaming the
fragments never disturbs the final native tool calls.
Steps
- Done for streaming providers:
src/openhuman/tinyagents/model.rsmaps provider thinking deltas toMessageDelta::reasoning(...)on the crate stream, andOpenhumanEventBridge(observability.rs) projectsdelta.reasoninginto OpenHuman progress events. - Done: provider tool-call argument fragments map onto
ModelStreamItem::ToolCallDelta(crateToolDelta { call_id, content }) and are projected byOpenhumanEventBridge.ToolDeltacarries notool_name, so thetool_name/start-event half of the UI timeline contract stays onThinkingForwarder.note_tool_call, which shares acall_id → tool_namemap (ToolNameMap) with the bridge so the streamed fragments keep their label. OnlyThinkingForwarder.emit_tool_argswas removed. - Verify sub-agent child thinking deltas still reach the scope-aware bridge (parity with the current forwarder behavior).
Deletions
- Reasoning side of
ThinkingForwarder: moved for streaming providers; keep the non-streaming fallback until the non-streaming path has an equivalent event. - Argument-fragment side of
ThinkingForwarder(emit_tool_args, intinyagents/model.rs): removed — fragments rideToolCallDeltaand are projected by the bridge. The tool-call start marker (note_tool_call, empty-deltaToolCallArgsDeltawithtool_name) stays, since the crateToolDeltacannot carry a tool name; it will only move if a future crate revision addstool_name/start semantics toToolDelta.
Acceptance
- UI receives visible-text, reasoning, and tool-arg deltas from crate stream
items alone, including
tool_name/start-event semantics; streaming e2e (chat- sub-agent) green.
- Non-streaming providers emit post-hoc reasoning once.