Skip to main content

ChatCompletionChunk

Struct ChatCompletionChunk 

Source
pub struct ChatCompletionChunk { /* private fields */ }
Expand description

A streaming chunk from a chat completion response.

Each chunk represents a delta update from the model as it generates the response.

Implementations§

Source§

impl ChatCompletionChunk

Source

pub fn new(response: CreateChatCompletionStreamResponse) -> Self

Create a new chunk from a stream response.

Source

pub fn content(&self) -> Option<&str>

Get the content delta from this chunk, if any.

Returns the text content that was generated in this chunk.

Examples found in repository?
examples/chat_streaming.rs (line 59)
49async fn basic_streaming(client: &Client) -> Result<()> {
50    println!("Question: Tell me a short joke");
51
52    let builder = client.chat().user("Tell me a short joke");
53
54    let mut stream = client.send_chat_stream(builder).await?;
55
56    print!("Response: ");
57    while let Some(chunk) = stream.next().await {
58        let chunk = chunk?;
59        if let Some(content) = chunk.content() {
60            print!("{}", content);
61        }
62    }
63    println!();
64
65    Ok(())
66}
67
68async fn streaming_with_parameters(client: &Client) -> Result<()> {
69    println!("Question: Write a creative tagline for a bakery");
70
71    let builder = client
72        .chat()
73        .user("Write a creative tagline for a bakery")
74        .temperature(0.9)
75        .max_tokens(50);
76
77    let mut stream = client.send_chat_stream(builder).await?;
78
79    print!("Response: ");
80    while let Some(chunk) = stream.next().await {
81        let chunk = chunk?;
82        if let Some(content) = chunk.content() {
83            print!("{}", content);
84        }
85    }
86    println!();
87
88    Ok(())
89}
90
91async fn collect_content(client: &Client) -> Result<()> {
92    println!("Question: What is the capital of France?");
93
94    let builder = client.chat().user("What is the capital of France?");
95
96    let mut stream = client.send_chat_stream(builder).await?;
97
98    // Manually collect all content
99    let mut content = String::new();
100    while let Some(chunk) = stream.next().await {
101        let chunk = chunk?;
102        if let Some(text) = chunk.content() {
103            content.push_str(text);
104        }
105    }
106    println!("Full response: {}", content);
107
108    Ok(())
109}
110
111async fn streaming_with_system(client: &Client) -> Result<()> {
112    println!("System: You are a helpful assistant that speaks like a pirate");
113    println!("Question: Tell me about the weather");
114
115    let builder = client
116        .chat()
117        .system("You are a helpful assistant that speaks like a pirate")
118        .user("Tell me about the weather")
119        .max_tokens(100);
120
121    let mut stream = client.send_chat_stream(builder).await?;
122
123    print!("Response: ");
124    while let Some(chunk) = stream.next().await {
125        let chunk = chunk?;
126        if let Some(content) = chunk.content() {
127            print!("{}", content);
128        }
129    }
130    println!();
131
132    Ok(())
133}
134
135async fn multiple_turns(client: &Client) -> Result<()> {
136    println!("Building a conversation with multiple turns...\n");
137
138    // First turn
139    println!("User: What is 2+2?");
140    let builder = client.chat().user("What is 2+2?");
141
142    let mut stream = client.send_chat_stream(builder).await?;
143
144    print!("Assistant: ");
145    let mut first_response = String::new();
146    while let Some(chunk) = stream.next().await {
147        let chunk = chunk?;
148        if let Some(content) = chunk.content() {
149            print!("{}", content);
150            first_response.push_str(content);
151        }
152    }
153    println!();
154
155    // Second turn - continuing the conversation
156    println!("\nUser: Now multiply that by 3");
157    let builder = client
158        .chat()
159        .user("What is 2+2?")
160        .assistant(&first_response)
161        .user("Now multiply that by 3");
162
163    let mut stream = client.send_chat_stream(builder).await?;
164
165    print!("Assistant: ");
166    while let Some(chunk) = stream.next().await {
167        let chunk = chunk?;
168        if let Some(content) = chunk.content() {
169            print!("{}", content);
170        }
171    }
172    println!();
173
174    Ok(())
175}
More examples
Hide additional examples
examples/langfuse_streaming.rs (line 105)
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    // Manually collect content (interceptor hooks are still called for each chunk)
154    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}
examples/quickstart.rs (line 146)
38async fn main() -> Result<()> {
39    // Initialize logging to see what's happening under the hood
40    tracing_subscriber::fmt().with_env_filter("info").init();
41
42    println!(" OpenAI Ergonomic Quickstart");
43    println!("==============================\n");
44
45    // ==========================================
46    // 1. ENVIRONMENT SETUP & CLIENT CREATION
47    // ==========================================
48
49    println!(" Step 1: Setting up the client");
50
51    // The simplest way to get started - reads OPENAI_API_KEY from environment
52    let client = match Client::from_env() {
53        Ok(client_builder) => {
54            println!(" Client created successfully!");
55            client_builder.build()
56        }
57        Err(e) => {
58            eprintln!(" Failed to create client: {e}");
59            eprintln!(" Make sure you've set OPENAI_API_KEY environment variable");
60            eprintln!("   Example: export OPENAI_API_KEY=\"sk-your-key-here\"");
61            return Err(e);
62        }
63    };
64
65    // ==========================================
66    // 2. BASIC CHAT COMPLETION
67    // ==========================================
68
69    println!("\n Step 2: Basic chat completion");
70
71    // The simplest way to get a response from ChatGPT
72    let builder = client.chat_simple("What is Rust programming language in one sentence?");
73    let response = client.send_chat(builder).await;
74
75    match response {
76        Ok(chat_response) => {
77            println!(" Got response!");
78            if let Some(content) = chat_response.content() {
79                println!(" AI: {content}");
80            }
81
82            // Show usage information for cost tracking
83            if let Some(usage) = &chat_response.inner().usage {
84                println!(
85                    " Usage: {} prompt + {} completion = {} total tokens",
86                    usage.prompt_tokens, usage.completion_tokens, usage.total_tokens
87                );
88            }
89        }
90        Err(e) => {
91            println!(" Chat completion failed: {e}");
92            // Continue with other examples even if this one fails
93        }
94    }
95
96    // ==========================================
97    // 3. CHAT WITH SYSTEM MESSAGE
98    // ==========================================
99
100    println!("\n Step 3: Chat with system context");
101
102    // System messages help set the AI's behavior and context
103    let builder = client.chat_with_system(
104        "You are a helpful coding mentor who explains things simply",
105        "Explain what a HashMap is in Rust",
106    );
107    let response = client.send_chat(builder).await;
108
109    match response {
110        Ok(chat_response) => {
111            println!(" Got contextual response!");
112            if let Some(content) = chat_response.content() {
113                println!("‍ Mentor: {content}");
114            }
115        }
116        Err(e) => {
117            println!(" Contextual chat failed: {e}");
118        }
119    }
120
121    // ==========================================
122    // 4. STREAMING RESPONSES
123    // ==========================================
124
125    println!("\n Step 4: Streaming response (real-time)");
126
127    // Streaming lets you see the response as it's being generated
128    // This is great for chatbots and interactive applications
129    print!(" AI is typing: ");
130    io::stdout().flush().unwrap();
131
132    let builder = client
133        .responses()
134        .user("Write a short haiku about programming")
135        .temperature(0.7);
136
137    // Use send_responses_stream for real streaming
138    let stream_result = client.send_responses_stream(builder).await;
139
140    match stream_result {
141        Ok(mut stream) => {
142            // Process each chunk as it arrives
143            while let Some(chunk_result) = stream.next().await {
144                match chunk_result {
145                    Ok(chunk) => {
146                        if let Some(content) = chunk.content() {
147                            print!("{content}");
148                            io::stdout().flush().unwrap();
149                        }
150                    }
151                    Err(e) => {
152                        println!("\n Error processing chunk: {e}");
153                        break;
154                    }
155                }
156            }
157            println!(); // New line after streaming
158        }
159        Err(e) => {
160            println!("\n Failed to get streaming response: {e}");
161        }
162    }
163
164    // ==========================================
165    // 5. FUNCTION/TOOL CALLING
166    // ==========================================
167
168    println!("\n Step 5: Using tools/functions");
169
170    // Tools let the AI call external functions to get real data
171    // Here we define a weather function as an example
172    let weather_tool = tool_function(
173        "get_current_weather",
174        "Get the current weather for a given location",
175        json!({
176            "type": "object",
177            "properties": {
178                "location": {
179                    "type": "string",
180                    "description": "The city name, e.g. 'San Francisco, CA'"
181                },
182                "unit": {
183                    "type": "string",
184                    "enum": ["celsius", "fahrenheit"],
185                    "description": "Temperature unit"
186                }
187            },
188            "required": ["location"]
189        }),
190    );
191
192    let builder = client
193        .responses()
194        .user("What's the weather like in Tokyo?")
195        .tool(weather_tool);
196    let response = client.send_responses(builder).await;
197
198    match response {
199        Ok(chat_response) => {
200            println!(" Got response with potential tool calls!");
201
202            // Check if the AI wants to call our weather function
203            let tool_calls = chat_response.tool_calls();
204            if !tool_calls.is_empty() {
205                println!(" AI requested tool calls:");
206                for tool_call in tool_calls {
207                    let function_name = tool_call.function_name();
208                    println!("   Function: {function_name}");
209                    let function_args = tool_call.function_arguments();
210                    println!("   Arguments: {function_args}");
211
212                    // In a real app, you'd execute the function here
213                    // and send the result back to the AI
214                    println!("    In a real app, you'd call your weather API here");
215                }
216            } else if let Some(content) = chat_response.content() {
217                println!(" AI: {content}");
218            }
219        }
220        Err(e) => {
221            println!(" Tool calling example failed: {e}");
222        }
223    }
224
225    // ==========================================
226    // 6. ERROR HANDLING PATTERNS
227    // ==========================================
228
229    println!("\n Step 6: Error handling patterns");
230
231    // Show how to handle different types of errors gracefully
232    let builder = client.chat_simple(""); // Empty message might cause an error
233    let bad_response = client.send_chat(builder).await;
234
235    match bad_response {
236        Ok(response) => {
237            println!(" Unexpectedly succeeded with empty message");
238            if let Some(content) = response.content() {
239                println!(" AI: {content}");
240            }
241        }
242        Err(Error::Api {
243            status, message, ..
244        }) => {
245            println!(" API Error (HTTP {status}):");
246            println!("   Message: {message}");
247            println!(" This is normal - we sent an invalid request");
248        }
249        Err(Error::RateLimit { .. }) => {
250            println!(" Rate limited - you're sending requests too fast");
251            println!(" In a real app, you'd implement exponential backoff");
252        }
253        Err(Error::Http(_)) => {
254            println!(" HTTP/Network error");
255            println!(" Check your internet connection and API key");
256        }
257        Err(e) => {
258            println!(" Other error: {e}");
259        }
260    }
261
262    // ==========================================
263    // 7. COMPLETE REAL-WORLD EXAMPLE
264    // ==========================================
265
266    println!("\n Step 7: Complete real-world example");
267    println!("Building a simple AI assistant that can:");
268    println!("- Answer questions with context");
269    println!("- Track conversation costs");
270    println!("- Handle errors gracefully");
271
272    let mut total_tokens = 0;
273
274    // Simulate a conversation with context and cost tracking
275    let questions = [
276        "What is the capital of France?",
277        "What's special about that city?",
278        "How many people live there?",
279    ];
280
281    for (i, question) in questions.iter().enumerate() {
282        println!("\n User: {question}");
283
284        let builder = client
285            .responses()
286            .system(
287                "You are a knowledgeable geography expert. Keep answers concise but informative.",
288            )
289            .user(*question)
290            .temperature(0.1); // Lower temperature for more factual responses
291        let response = client.send_responses(builder).await;
292
293        match response {
294            Ok(chat_response) => {
295                if let Some(content) = chat_response.content() {
296                    println!(" Assistant: {content}");
297                }
298
299                // Track token usage for cost monitoring
300                if let Some(usage) = chat_response.usage() {
301                    total_tokens += usage.total_tokens;
302                    println!(
303                        " This exchange: {} tokens (Running total: {})",
304                        usage.total_tokens, total_tokens
305                    );
306                }
307            }
308            Err(e) => {
309                println!(" Question {} failed: {}", i + 1, e);
310                // In a real app, you might retry or log this error
311            }
312        }
313    }
314
315    // ==========================================
316    // 8. WRAP UP & NEXT STEPS
317    // ==========================================
318
319    println!("\n Quickstart Complete!");
320    println!("======================");
321    println!("You've successfully:");
322    println!(" Created an OpenAI client");
323    println!(" Made basic chat completions");
324    println!(" Used streaming responses");
325    println!(" Implemented tool/function calling");
326    println!(" Handled errors gracefully");
327    println!(" Built a complete conversational AI");
328    println!("\n Total tokens used in examples: {total_tokens}");
329    println!(
330        " Estimated cost: ~${:.4} (assuming GPT-4 pricing)",
331        f64::from(total_tokens) * 0.03 / 1000.0
332    );
333
334    println!("\n Next Steps:");
335    println!("- Check out other examples in the examples/ directory");
336    println!("- Read the documentation: https://docs.rs/openai-ergonomic");
337    println!("- Explore advanced features like vision, audio, and assistants");
338    println!("- Build your own AI-powered applications!");
339
340    Ok(())
341}
Source

pub fn role(&self) -> Option<&str>

Get the role from this chunk, if any.

This is typically only present in the first chunk.

Source

pub fn tool_calls(&self) -> Option<&Vec<ChatCompletionMessageToolCallChunk>>

Get tool calls from this chunk, if any.

Source

pub fn finish_reason(&self) -> Option<&str>

Get the finish reason, if any.

This indicates why the generation stopped and is only present in the last chunk.

Source

pub fn is_final(&self) -> bool

Check if this is the last chunk in the stream.

Source

pub fn raw_response(&self) -> &CreateChatCompletionStreamResponse

Get the underlying raw response.

Source

pub fn delta(&self) -> Option<&ChatCompletionStreamResponseDelta>

Get the delta object directly.

Trait Implementations§

Source§

impl Clone for ChatCompletionChunk

Source§

fn clone(&self) -> ChatCompletionChunk

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ChatCompletionChunk

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> FutureExt for T

Source§

fn with_context(self, otel_cx: Context) -> WithContext<Self>

Attaches the provided Context to this type, returning a WithContext wrapper. Read more
Source§

fn with_current_context(self) -> WithContext<Self>

Attaches the current Context to this type, returning a WithContext wrapper. Read more
Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more