lc_core/runnables/events.rs
1// lc-core/src/runnables/events.rs
2//! Stream events for fine-grained LCEL pipeline observability.
3//!
4//! `astream_events` produces a stream of `StreamEvent` values that
5//! describe what's happening inside a pipeline at each step. This is
6//! useful for building UIs that show real-time progress.
7//!
8//! v0.9.0 implements 5 core event types. Full v2 event coverage
9//! is planned for v0.10.0.
10
11use serde_json::Value;
12use std::collections::HashMap;
13use uuid::Uuid;
14
15/// Fine-grained event emitted during LCEL pipeline execution.
16///
17/// v0.9.0 supports 5 core event types:
18/// - `OnLlmStart` / `OnLlmStream` / `OnLlmEnd`: LLM lifecycle
19/// - `OnToolEnd`: Tool execution completion
20/// - `OnChainEnd`: Chain execution completion
21///
22/// v0.10.0 will add: OnRetrieverStart/End, OnPromptStart/End,
23/// OnToolStart, OnChainStart, OnToolError, OnChainError, etc.
24///
25/// Note: Named `LcelStreamEvent` to avoid collision with
26/// `lc_langgraph::StreamEvent` (graph execution events).
27#[derive(Debug, Clone)]
28pub enum LcelStreamEvent {
29 /// LLM invocation started.
30 OnLlmStart {
31 /// Unique run identifier.
32 run_id: Uuid,
33 /// Name of the LLM (e.g. "gpt-4o", "claude-3-opus").
34 name: String,
35 /// Additional metadata.
36 metadata: HashMap<String, Value>,
37 },
38
39 /// LLM streaming token.
40 OnLlmStream {
41 /// Unique run identifier.
42 run_id: Uuid,
43 /// Name of the LLM.
44 name: String,
45 /// The token text.
46 token: String,
47 },
48
49 /// LLM invocation completed.
50 OnLlmEnd {
51 /// Unique run identifier.
52 run_id: Uuid,
53 /// Name of the LLM.
54 name: String,
55 /// The full LLM output.
56 output: Value,
57 },
58
59 /// Tool execution completed.
60 OnToolEnd {
61 /// Unique run identifier.
62 run_id: Uuid,
63 /// Name of the tool.
64 name: String,
65 /// The tool output.
66 output: String,
67 },
68
69 /// Chain execution completed.
70 OnChainEnd {
71 /// Unique run identifier.
72 run_id: Uuid,
73 /// Name of the chain.
74 name: String,
75 /// The chain output.
76 output: Value,
77 },
78}
79
80impl LcelStreamEvent {
81 /// Get the run_id for this event.
82 pub fn run_id(&self) -> &Uuid {
83 match self {
84 LcelStreamEvent::OnLlmStart { run_id, .. } => run_id,
85 LcelStreamEvent::OnLlmStream { run_id, .. } => run_id,
86 LcelStreamEvent::OnLlmEnd { run_id, .. } => run_id,
87 LcelStreamEvent::OnToolEnd { run_id, .. } => run_id,
88 LcelStreamEvent::OnChainEnd { run_id, .. } => run_id,
89 }
90 }
91
92 /// Get the name associated with this event.
93 pub fn name(&self) -> &str {
94 match self {
95 LcelStreamEvent::OnLlmStart { name, .. } => name,
96 LcelStreamEvent::OnLlmStream { name, .. } => name,
97 LcelStreamEvent::OnLlmEnd { name, .. } => name,
98 LcelStreamEvent::OnToolEnd { name, .. } => name,
99 LcelStreamEvent::OnChainEnd { name, .. } => name,
100 }
101 }
102
103 /// Get the event kind as a string.
104 pub fn kind(&self) -> &str {
105 match self {
106 LcelStreamEvent::OnLlmStart { .. } => "on_llm_start",
107 LcelStreamEvent::OnLlmStream { .. } => "on_llm_stream",
108 LcelStreamEvent::OnLlmEnd { .. } => "on_llm_end",
109 LcelStreamEvent::OnToolEnd { .. } => "on_tool_end",
110 LcelStreamEvent::OnChainEnd { .. } => "on_chain_end",
111 }
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118
119 #[test]
120 fn stream_event_kind() {
121 let event = LcelStreamEvent::OnLlmStart {
122 run_id: Uuid::new_v4(),
123 name: "gpt-4o".to_string(),
124 metadata: HashMap::new(),
125 };
126 assert_eq!(event.kind(), "on_llm_start");
127 assert_eq!(event.name(), "gpt-4o");
128 }
129
130 #[test]
131 fn stream_event_variants() {
132 let id = Uuid::new_v4();
133 let event = LcelStreamEvent::OnLlmStream {
134 run_id: id,
135 name: "claude".to_string(),
136 token: "Hello".to_string(),
137 };
138 assert_eq!(event.kind(), "on_llm_stream");
139 assert_eq!(*event.run_id(), id);
140 }
141}