You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
mellea/stdlib/streaming.py (landing in PR #942, closing #901) provides Phase 1 streaming validation — a call-site ChunkingStrategy with three built-in chunkers (SentenceChunker, WordChunker, ParagraphChunker) and an orchestrator (stream_with_chunking). This is a scoped pragmatic choice. Phase 2 refines that foundation and extends it, broken into the sub-issues below.
Consolidated design summary
Motivation
Phase 1 collapses two semantic concerns onto the call site: how to chunk the stream (a property of the output type) and how to validate chunks (a property of the requirement). These are independent and belong in different owners.
Type semantics. What counts as a complete chunk of this kind of output? JSON value, prose sentence, code statement, audio segment, image region. Invariant across requirements.
Constraint semantics. What makes a particular output acceptable? Max three sentences, matches schema X, no hallucinated entities. Invariant across outputs of the same type.
Under Phase 1, both are author-written at the call site. Phase 2 separates them: chunking becomes a per-requirement concern, and the streaming parsed representation becomes its own capability.
Motivating output types (all in scope)
Phase 1 chunkers cover prose only (sentence/word/paragraph, all operating on accumulated_text: str). Phase 2 must support at least these output types, each with genuinely different chunk semantics:
Prose — sentence, word, paragraph boundaries. Already covered by Phase 1 chunkers; Phase 2 should subsume them.
Structured text — JSON values, YAML documents, code statements/blocks. Chunk boundary is "one complete parseable unit."
Multi-modal is first-class motivation for this work, not deferred scope. Epic #891 explicitly names the audio case ("Audio that goes wrong in the first few seconds can't be caught until the full clip is done"), and the ChunkingStrategy.split(accumulated_text: str) -> list[str] signature in Phase 1 forecloses on multi-modal by design — that foreclosure is what this issue exists to address.
Sub-issues
Phase 2 is broken into four sub-issues (an earlier version of this issue proposed moving chunking onto the MOT; the direction has since changed — chunking is handled per-Requirement, and stream_parsed_repr is a separate concern from chunking):
feat: per-requirement chunking strategies #1441 — Per-validator chunking — each Requirement carries its own ChunkingStrategy so different validators can validate at different granularities. Open: where the per-validator chunk state lives.
feat: allow streaming for sampling results #403 — Sampling integration — give stream() the instruct-validate-repair retry/repair behavior SamplingStrategy provides for the non-streaming path.
#1440 blocks the rest; #1441, #403, and #1442 depend only on it and can proceed in parallel.
Open design questions (for comments)
Signature and generator shape. Is stream_parsed_repr an async generator on the MOT? Does it share a queue with the raw astream(), or run in parallel? For multi-modal, does the signature accept bytes / frames / tensors rather than str?
Chunking boundary authority. Which component decides what a "complete chunk" is — the MOT's parser, a pluggable chunk-boundary predicate, or both? Answer likely differs between text and multi-modal.
Backpressure. If parsed chunks are slower to produce than raw tokens/frames, where does the buffering live?
Backwards compatibility. How to migrate from the Phase 1 call sites?
Typed output. Does stream_parsed_repr yield values of type S (the MOT's parsed_repr type parameter), or a richer container that carries partial-parse state? Multi-modal MOTs may need the latter.
Error handling. What happens if the MOT's parser fails on a partial stream — surface immediately, wait for more data, or fall back to raw chunking?
Testability. Each MOT type's stream_parsed_repr needs verification against its non-streaming parsed_repr. Shape of the shared test harness? Multi-modal test fixtures are their own problem.
Agent authoring. If we want new MOT types to be agent-writable (see PR feat(stdlib): add stream_with_chunking() with per-chunk validation (#901) #942 discussion), what contracts are needed on the MOT base class? Clear APIs and guidelines are the substrate; a skill can sit on top where the framework supports one.
Broader scope (to discuss)
The PR #942 thread raised a parallel observation. Both stream_validate authoring and (future) stream_parsed_repr authoring have deterministic checks against a non-streaming counterpart (validate() and parsed_repr respectively), which makes them plausible candidates for agent-friendly extension patterns — potentially skills in frameworks that support them. Worth discussing whether this issue should cover just stream_parsed_repr or also the broader agent-authoring story, or whether the latter warrants its own separate issue.
Context
mellea/stdlib/streaming.py(landing in PR #942, closing #901) provides Phase 1 streaming validation — a call-siteChunkingStrategywith three built-in chunkers (SentenceChunker,WordChunker,ParagraphChunker) and an orchestrator (stream_with_chunking). This is a scoped pragmatic choice. Phase 2 refines that foundation and extends it, broken into the sub-issues below.Consolidated design summary
Motivation
Phase 1 collapses two semantic concerns onto the call site: how to chunk the stream (a property of the output type) and how to validate chunks (a property of the requirement). These are independent and belong in different owners.
Under Phase 1, both are author-written at the call site. Phase 2 separates them: chunking becomes a per-requirement concern, and the streaming parsed representation becomes its own capability.
Motivating output types (all in scope)
Phase 1 chunkers cover prose only (sentence/word/paragraph, all operating on
accumulated_text: str). Phase 2 must support at least these output types, each with genuinely different chunk semantics:Multi-modal is first-class motivation for this work, not deferred scope. Epic #891 explicitly names the audio case ("Audio that goes wrong in the first few seconds can't be caught until the full clip is done"), and the
ChunkingStrategy.split(accumulated_text: str) -> list[str]signature in Phase 1 forecloses on multi-modal by design — that foreclosure is what this issue exists to address.Sub-issues
Phase 2 is broken into four sub-issues (an earlier version of this issue proposed moving chunking onto the MOT; the direction has since changed — chunking is handled per-
Requirement, andstream_parsed_repris a separate concern from chunking):stream()primitive (blocks all others) — replace the two-taskstream_with_chunking+StreamChunkingResultwith a singlestream()/Streamerconsumed by a plainasync for. IncludesModelOutputThunk.__aiter__, event emission via theSTREAMING_EVENThook, an events-consumption example, and factoring chunk-boundary state into aChunkerobject. A working POC exists on PR feat: validated streaming refactor POC + Phase 2 epic breakdown #1409.Requirementcarries its ownChunkingStrategyso different validators can validate at different granularities. Open: where the per-validator chunk state lives.stream()the instruct-validate-repair retry/repair behaviorSamplingStrategyprovides for the non-streaming path.stream_parsed_repr— a streaming counterpart toparsed_repr: expose the MOT's typed parsed representation incrementally, separate from chunking and validation. Multi-modal folds in here.#1440 blocks the rest; #1441, #403, and #1442 depend only on it and can proceed in parallel.
Open design questions (for comments)
stream_parsed_repran async generator on the MOT? Does it share a queue with the rawastream(), or run in parallel? For multi-modal, does the signature accept bytes / frames / tensors rather thanstr?stream_parsed_repryield values of typeS(the MOT'sparsed_reprtype parameter), or a richer container that carries partial-parse state? Multi-modal MOTs may need the latter.stream_parsed_reprneeds verification against its non-streamingparsed_repr. Shape of the shared test harness? Multi-modal test fixtures are their own problem.Broader scope (to discuss)
The PR #942 thread raised a parallel observation. Both
stream_validateauthoring and (future)stream_parsed_reprauthoring have deterministic checks against a non-streaming counterpart (validate()andparsed_reprrespectively), which makes them plausible candidates for agent-friendly extension patterns — potentially skills in frameworks that support them. Worth discussing whether this issue should cover juststream_parsed_repror also the broader agent-authoring story, or whether the latter warrants its own separate issue.Dependencies
base.pycollision.References
mellea/stdlib/__init__.pyandmellea/stdlib/chunking.py