Skip to content

refactor(llm): Introduce typed EventPart#514

Merged
JeanMertz merged 3 commits intomainfrom
prr83
Apr 5, 2026
Merged

refactor(llm): Introduce typed EventPart#514
JeanMertz merged 3 commits intomainfrom
prr83

Conversation

@JeanMertz
Copy link
Copy Markdown
Collaborator

Replace ConversationEvent inside Event::Part with a purpose-built EventPart enum (Message, Reasoning, Structured, ToolCall(ToolCallPart)), implementing RFD 012. This decouples the streaming transport layer from the persistence layer.

Previously, every streaming chunk allocated a full ConversationEvent with a timestamp and metadata map, even though most of the type went unused during streaming. The EventBuilder also had to silently ignore five EventKind variants (ChatRequest, TurnStart, etc.) that are invalid in a streaming context.

EventBuilder moves from jp_conversation into jp_llm, where it belongs — translating streaming EventParts into persistence ConversationEvents on Flush. Its handle_part signature changes to (usize, EventPart, Map<String, Value>). The persistence layer (ConversationEvent, ConversationStream) is unchanged.

ToolCallRequestAggregator is removed. Providers now emit ToolCallPart::Start { id, name } when a tool call begins, and ToolCallPart::ArgumentChunk(json) for each argument delta. The EventBuilder accumulates these into a complete ToolCallRequest on Flush. AggregationError is removed from the public jp_llm API.

Factory methods on Event (message, reasoning, structured, tool_call_start, tool_call_args) reduce construction boilerplate across all providers and tests. Flush metadata is changed from IndexMap<String, Value> to Map<String, Value> for consistency.

RFD 012 is marked as Implemented.

Replace `ConversationEvent` inside `Event::Part` with a purpose-built
`EventPart` enum (`Message`, `Reasoning`, `Structured`,
`ToolCall(ToolCallPart)`), implementing RFD 012. This decouples the
streaming transport layer from the persistence layer.

Previously, every streaming chunk allocated a full `ConversationEvent`
with a timestamp and metadata map, even though most of the type went
unused during streaming. The `EventBuilder` also had to silently ignore
five `EventKind` variants (`ChatRequest`, `TurnStart`, etc.) that are
invalid in a streaming context.

`EventBuilder` moves from `jp_conversation` into `jp_llm`, where it
belongs — translating streaming `EventPart`s into persistence
`ConversationEvent`s on `Flush`. Its `handle_part` signature changes to
`(usize, EventPart, Map<String, Value>)`. The persistence layer
(`ConversationEvent`, `ConversationStream`) is unchanged.

`ToolCallRequestAggregator` is removed. Providers now emit
`ToolCallPart::Start { id, name }` when a tool call begins, and
`ToolCallPart::ArgumentChunk(json)` for each argument delta. The
`EventBuilder` accumulates these into a complete `ToolCallRequest` on
`Flush`. `AggregationError` is removed from the public `jp_llm` API.

Factory methods on `Event` (`message`, `reasoning`, `structured`,
`tool_call_start`, `tool_call_args`) reduce construction boilerplate
across all providers and tests. Flush metadata is changed from
`IndexMap<String, Value>` to `Map<String, Value>` for consistency.

RFD 012 is marked as Implemented.

Signed-off-by: Jean Mertz <git@jeanmertz.com>
Signed-off-by: Jean Mertz <git@jeanmertz.com>
Signed-off-by: Jean Mertz <git@jeanmertz.com>
@JeanMertz JeanMertz merged commit 0af019a into main Apr 5, 2026
13 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