langfuse_streaming/
langfuse_streaming.rs1#![allow(clippy::uninlined_format_args)]
2use futures::StreamExt;
22use openai_ergonomic::{Client, LangfuseConfig, LangfuseInterceptor, LangfuseState, Result};
23use opentelemetry::{global, trace::TracerProvider};
24use opentelemetry_langfuse::ExporterBuilder;
25use opentelemetry_sdk::{
26 runtime::Tokio,
27 trace::{span_processor_with_async_runtime::BatchSpanProcessor, SdkTracerProvider, Span},
28};
29
30#[tokio::main]
31async fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
32 tracing_subscriber::fmt()
34 .with_env_filter(
35 tracing_subscriber::EnvFilter::from_default_env()
36 .add_directive("openai_ergonomic=debug".parse()?),
37 )
38 .init();
39
40 println!("š Initializing OpenAI client with Langfuse streaming observability...\n");
41
42 let exporter = ExporterBuilder::from_env()?.build()?;
44
45 let provider = SdkTracerProvider::builder()
47 .with_span_processor(BatchSpanProcessor::builder(exporter, Tokio).build())
48 .build();
49
50 global::set_tracer_provider(provider.clone());
52
53 let tracer = provider.tracer("openai-ergonomic");
55 let langfuse_interceptor = LangfuseInterceptor::new(tracer, LangfuseConfig::new());
56
57 let client = Client::from_env()?
59 .with_interceptor(Box::new(langfuse_interceptor))
60 .build();
61
62 println!("ā
Client initialized successfully!");
63 println!("š Streaming traces will be sent to Langfuse for monitoring\n");
64
65 println!("=== Example 1: Basic Streaming ===");
67 basic_streaming(&client).await?;
68
69 println!("\n=== Example 2: Streaming with Parameters ===");
71 streaming_with_parameters(&client).await?;
72
73 println!("\n=== Example 3: Collect Full Content ===");
75 collect_content(&client).await?;
76
77 println!("\nā
Done! Check your Langfuse dashboard to see the streaming traces.");
78 println!(" - Look for traces with operation names 'chat' or 'responses'");
79 println!(" - Each trace includes:");
80 println!(" ⢠before_request: Initial request details");
81 println!(" ⢠on_stream_chunk: Each chunk as it arrives (real-time)");
82 println!(" ⢠on_stream_end: Final token usage and duration");
83
84 tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
86
87 println!("\nā³ Flushing spans to Langfuse...");
89 provider.shutdown()?;
90
91 Ok(())
92}
93
94async fn basic_streaming(client: &Client<LangfuseState<Span>>) -> Result<()> {
95 println!("Question: Tell me a short joke");
96
97 let builder = client.chat().user("Tell me a short joke");
98
99 let mut stream = client.send_chat_stream(builder).await?;
100
101 print!("Response: ");
102 let mut chunk_count = 0;
103 while let Some(chunk) = stream.next().await {
104 let chunk = chunk?;
105 if let Some(content) = chunk.content() {
106 print!("{}", content);
107 chunk_count += 1;
108 }
109 }
110 println!(
111 "\n(Received {} chunks, all traced to Langfuse)",
112 chunk_count
113 );
114
115 Ok(())
116}
117
118async fn streaming_with_parameters(client: &Client<LangfuseState<Span>>) -> Result<()> {
119 println!("Question: Write a creative tagline for a bakery");
120
121 let builder = client
122 .chat()
123 .user("Write a creative tagline for a bakery")
124 .temperature(0.9)
125 .max_tokens(50);
126
127 let mut stream = client.send_chat_stream(builder).await?;
128
129 print!("Response: ");
130 let mut chunk_count = 0;
131 while let Some(chunk) = stream.next().await {
132 let chunk = chunk?;
133 if let Some(content) = chunk.content() {
134 print!("{}", content);
135 chunk_count += 1;
136 }
137 }
138 println!(
139 "\n(Received {} chunks, all traced to Langfuse)",
140 chunk_count
141 );
142
143 Ok(())
144}
145
146async fn collect_content(client: &Client<LangfuseState<Span>>) -> Result<()> {
147 println!("Question: What is the capital of France?");
148
149 let builder = client.chat().user("What is the capital of France?");
150
151 let mut stream = client.send_chat_stream(builder).await?;
152
153 let mut content = String::new();
155 while let Some(chunk) = stream.next().await {
156 let chunk = chunk?;
157 if let Some(text) = chunk.content() {
158 content.push_str(text);
159 }
160 }
161 println!("Full response: {}", content);
162 println!("(All chunks were traced to Langfuse during collection)");
163
164 Ok(())
165}