Expand description
§graphflow-stream
The astream_events LangGraph gives Python, for graph-flow in Rust.
§Install
cargo add graphflow-stream§Usage
use graph_flow::{Context, NextAction, Task, TaskResult, error::Result};
use graphflow_stream::{emit_token, spawn_task};
struct MyLlmTask;
#[async_trait::async_trait]
impl Task for MyLlmTask {
fn id(&self) -> &str { "my_llm_task" }
async fn run(&self, _context: Context) -> Result<TaskResult> {
for delta in ["Hel", "lo", "!"] { // e.g. deltas from Rig
emit_token("my_llm_task", delta).await;
}
Ok(TaskResult::new(Some("Hello!".into()), NextAction::Continue))
}
}
let (mut rx, handle) = spawn_task(Arc::new(MyLlmTask), Context::new(), 32);
while let Some(event) = rx.recv().await {
// forward over SSE / WebSocket / stdout as it arrives
}
let result = handle.await??; // same TaskResult you'd get from task.run()Just want the full streamed text back, no manual loop? One line:
let text = graphflow_stream::collect_text(Arc::new(MyLlmTask), Context::new(), 32).await?;emit_token/emit_started/emit_finished/emit_failed are ambient — call them from anywhere inside Task::run, no new trait to implement, no-op if nothing is listening. spawn_graph(flow_runner, session_id, buffer) does the same for a whole FlowRunner run; SubgraphTask wraps a nested Graph as one Task and streams through automatically.
Replay debugging: record(rx).await turns a run into a Recording (serializable, so it can be saved to disk), and recording.replay(buffer) plays it back on a fresh channel with the original timing — inspect a past run, or demo a UI without hitting an LLM again.
More examples (full_graph, sse_axum, websocket_axum) in examples/.
§Benchmarks
cargo bench (criterion, benches/overhead.rs):
| Scenario | Time |
|---|---|
task.run() direct — no graphflow-stream involved | ~1.0 µs |
emit_token() with nobody listening (ambient no-op) | ~30 ns / call |
spawn_task() streaming 100 tokens to a draining receiver | ~2.0 µs / token |
record() capturing 100 streamed tokens | ~1.4 µs / token |
§License
MIT
Structs§
- Recorded
Event - A
StreamEventplus its offset from the start of the recording. - Recording
- A recorded run: every
StreamEventaStreamReceiverproduced, with timing. - Subgraph
Task - A
Taskthat drives a whole innerGraph, so a graph can be a node in another graph.
Enums§
- Stream
Event - An incremental event emitted while a task or graph run is in flight.
Functions§
- collect_
text - Run a
Taskand join its streamedStreamEvent::Tokendeltas into oneString. - emit
- Send an event on the ambient stream, if one is active; a no-op otherwise.
- emit_
failed - Emit a
StreamEvent::TaskFailedon the ambient stream. - emit_
finished - Emit a
StreamEvent::TaskFinishedon the ambient stream. - emit_
started - Emit a
StreamEvent::TaskStartedon the ambient stream. - emit_
token - Emit a
StreamEvent::Tokenon the ambient stream. - forward_
text_ stream - Drain any
Stream<Item = String>(e.g. a mapped LLM completion) intoemit_tokencalls. - record
- Drain a
StreamReceiverinto aRecording, timestamping each event from the start. - run_
streaming - Run
futwith an ambient stream scope active; the primitivespawn_task/spawn_graphuse. - spawn_
graph - Drive a
FlowRunnerto completion, streaming every task’semit_*calls out. - spawn_
task - Run a single
Task, streaming itsemit_*calls out through the returned receiver.
Type Aliases§
- Stream
Receiver - Receiving half of a stream channel; drain this to observe a run in progress.
- Stream
Sender - Sending half of a stream channel; a running task’s
emit_*calls write here.