> ## Documentation Index
> Fetch the complete documentation index at: https://www.edgee.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Rust SDK - Stream Method

> Complete guide to the stream() method in the Rust SDK.

The `stream()` method is used to make streaming chat completion requests to the Edgee AI Gateway. It returns a `Result` containing a `Stream` that yields `Result<StreamChunk>` objects as they arrive from the API.

## Arguments

| Parameter                                                                                                                      | Type                | Description                                                                                          |
| ------------------------------------------------------------------------------------------------------------------------------ | ------------------- | ---------------------------------------------------------------------------------------------------- |
| `model` <Tooltip headline="Required" tip="The field is required."><Icon icon="asterisk" size={15} color="#8924A6" /></Tooltip> | `impl Into<String>` | The model identifier to use (e.g., `"gpt-5.2"`)                                                      |
| `input` <Tooltip headline="Required" tip="The field is required."><Icon icon="asterisk" size={15} color="#8924A6" /></Tooltip> | `impl Into<Input>`  | The input for the completion. Can be a string (`&str` or `String`), `Vec<Message>`, or `InputObject` |

### Input Types

The `stream()` method accepts the same input types as `send()`:

#### String Input

When `input` is a string, it's automatically converted to a user message:

```rust theme={"dark"}
use tokio_stream::StreamExt;

let mut stream = client.stream("gpt-5.2", "Tell me a story").await?;

while let Some(result) = stream.next().await {
    match result {
        Ok(chunk) => {
            if let Some(text) = chunk.text() {
                print!("{}", text);
            }
            
            if let Some(reason) = chunk.finish_reason() {
                println!("\nFinished: {}", reason);
            }
        }
        Err(e) => eprintln!("Stream error: {}", e),
    }
}
// Equivalent to: input: InputObject::new(vec![Message::user("Tell me a story")])
```

#### `Vec<Message>` or `InputObject`

When `input` is a `Vec<Message>` or `InputObject`, you have full control over the conversation:

| Property                                                                                                                          | Type                        | Description                                                                                                                                                                     |
| --------------------------------------------------------------------------------------------------------------------------------- | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `messages` <Tooltip headline="Required" tip="The field is required."><Icon icon="asterisk" size={15} color="#8924A6" /></Tooltip> | `Vec<Message>`              | Array of conversation messages                                                                                                                                                  |
| `tools`                                                                                                                           | `Option<Vec<Tool>>`         | Array of function tools available to the model                                                                                                                                  |
| `tool_choice`                                                                                                                     | `Option<serde_json::Value>` | Controls which tool (if any) the model should call. See [Tools documentation](/sdk/rust/tools) for details                                                                      |
| `tags`                                                                                                                            | `Option<Vec<String>>`       | Optional tags to categorize and label the request for analytics and filtering. Can also be sent via the `x-edgee-tags` header (comma-separated)                                 |
| `compression_model`                                                                                                               | `Option<String>`            | Compression model for this request: `"claude"`, `"codex"`, `"opencode"`, `"cursor"`. Each model is a bundle of compression strategies. Overrides API key settings when present. |

For details about `Message` type, see the [Send Method documentation](/sdk/rust/send#message-object).
For details about `Tool` and `ToolChoice` types, see the [Tools documentation](/sdk/rust/tools).

**Example - Streaming with Messages:**

```rust theme={"dark"}
use edgee::Message;
use tokio_stream::StreamExt;

let messages = vec![
    Message::system("You are a helpful assistant."),
    Message::user("Write a poem about coding"),
];

let mut stream = client.stream("gpt-5.2", messages).await?;

while let Some(result) = stream.next().await {
    if let Ok(chunk) = result {
        if let Some(text) = chunk.text() {
            print!("{}", text);
        }
    }
}
```

## Return Value

The `stream()` method returns a `Result` containing a `Stream` that yields `Result<StreamChunk>`. Each chunk contains incremental updates to the response.

### StreamChunk Object

Each chunk yielded by the stream has the following structure:

| Property      | Type                  | Description                                            |
| ------------- | --------------------- | ------------------------------------------------------ |
| `id`          | `String`              | Unique identifier for the completion                   |
| `object`      | `String`              | Object type (typically `"chat.completion.chunk"`)      |
| `created`     | `u64`                 | Unix timestamp of when the chunk was created           |
| `model`       | `String`              | Model identifier used for the completion               |
| `choices`     | `Vec<StreamChoice>`   | Array of streaming choices (typically one)             |
| `compression` | `Option<Compression>` | Token compression metrics (if compression was applied) |

### StreamChoice Object

Each choice in the `choices` array contains:

| Property        | Type             | Description                                                                                                                                              |
| --------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `index`         | `u32`            | The index of this choice in the array                                                                                                                    |
| `delta`         | `StreamDelta`    | The incremental update to the message                                                                                                                    |
| `finish_reason` | `Option<String>` | Reason why the generation stopped. Only present in the final chunk. Possible values: `"stop"`, `"length"`, `"tool_calls"`, `"content_filter"`, or `None` |

**Example - Handling Multiple Choices:**

```rust theme={"dark"}
use tokio_stream::StreamExt;

let mut stream = client.stream("gpt-5.2", "Give me creative ideas").await?;

while let Some(result) = stream.next().await {
    if let Ok(chunk) = result {
        for choice in &chunk.choices {
            if let Some(content) = &choice.delta.content {
                println!("Choice {}: {}", choice.index, content);
            }
        }
    }
}
```

### StreamDelta Object

The `delta` object contains incremental updates:

| Property     | Type                    | Description                                                                                |
| ------------ | ----------------------- | ------------------------------------------------------------------------------------------ |
| `role`       | `Option<Role>`          | The role of the message (typically `Role::Assistant`). Only present in the **first chunk** |
| `content`    | `Option<String>`        | Incremental text content. Each chunk contains a portion of the full response               |
| `tool_calls` | `Option<Vec<ToolCall>>` | Array of tool calls (if any). See [Tools documentation](/sdk/rust/tools) for details       |

## Convenience Methods

The `StreamChunk` struct provides convenience methods for easier access:

| Method            | Return Type     | Description                                                                              |
| ----------------- | --------------- | ---------------------------------------------------------------------------------------- |
| `text()`          | `Option<&str>`  | Shortcut to `choices[0].delta.content.as_deref()` - the incremental text content         |
| `role()`          | `Option<&Role>` | Shortcut to `choices[0].delta.role.as_ref()` - the message role (first chunk only)       |
| `finish_reason()` | `Option<&str>`  | Shortcut to `choices[0].finish_reason.as_deref()` - the finish reason (final chunk only) |

**Example - Using Convenience Methods:**

```rust theme={"dark"}
use tokio_stream::StreamExt;

let mut stream = client.stream("gpt-5.2", "Explain quantum computing").await?;

while let Some(result) = stream.next().await {
    match result {
        Ok(chunk) => {
            // Content chunks
            if let Some(text) = chunk.text() {
                print!("{}", text);
            }

            // First chunk contains the role
            if let Some(role) = chunk.role() {
                println!("\nRole: {:?}", role);
            }

            // Last chunk contains finish reason
            if let Some(reason) = chunk.finish_reason() {
                println!("\nFinish reason: {}", reason);
            }
        }
        Err(e) => eprintln!("Stream error: {}", e),
    }
}
```

## Understanding Streaming Behavior

### Chunk Structure

1. **First chunk**: Contains `role` (typically `Role::Assistant`) and may contain initial `content`
2. **Content chunks**: Contain incremental `content` updates
3. **Final chunk**: Contains `finish_reason` indicating why generation stopped

**Example - Collecting Full Response:**

```rust theme={"dark"}
use tokio_stream::StreamExt;

let mut stream = client.stream("gpt-5.2", "Tell me a story").await?;
let mut full_text = String::new();

while let Some(result) = stream.next().await {
    match result {
        Ok(chunk) => {
            if let Some(text) = chunk.text() {
                full_text.push_str(text);
                print!("{}", text); // Also display as it streams
            }
        }
        Err(e) => eprintln!("Stream error: {}", e),
    }
}

println!("\n\nFull response ({} characters):", full_text.len());
println!("{}", full_text);
```

### Finish Reasons

| Value              | Description                                               |
| ------------------ | --------------------------------------------------------- |
| `"stop"`           | Model generated a complete response and stopped naturally |
| `"length"`         | Response was cut off due to token limit                   |
| `"tool_calls"`     | Model requested tool/function calls                       |
| `"content_filter"` | Content was filtered by safety systems                    |
| `None`             | Generation is still in progress (not the final chunk)     |

### Empty Chunks

Some chunks may not contain `content`. This is normal and can happen when:

* The chunk only contains metadata (role, finish\_reason)
* The chunk is part of tool call processing
* Network buffering creates empty chunks

Always check for `chunk.text()` before using it:

```rust theme={"dark"}
use tokio_stream::StreamExt;

let mut stream = client.stream("gpt-5.2", "Hello").await?;

while let Some(result) = stream.next().await {
    if let Ok(chunk) = result {
        if let Some(text) = chunk.text() {  // ✅ Good: Check before using
            println!("{}", text);
        }
        // ❌ Bad: println!("{:?}", chunk.text()) - may print None
    }
}
```

## Error Handling

The `stream()` method can return errors at two levels:

1. **Initial error**: When creating the stream (returns `Result<Stream>`)
2. **Stream errors**: Individual chunks may contain errors (returns `Result<StreamChunk>`)

```rust theme={"dark"}
use edgee::Error;
use tokio_stream::StreamExt;

// Handle initial error
let mut stream = match client.stream("gpt-5.2", "Hello!").await {
    Ok(stream) => stream,
    Err(Error::Api { status, message }) => {
        eprintln!("API error {}: {}", status, message);
        return;
    }
    Err(e) => {
        eprintln!("Error creating stream: {}", e);
        return;
    }
};

// Handle stream errors
while let Some(result) = stream.next().await {
    match result {
        Ok(chunk) => {
            if let Some(text) = chunk.text() {
                print!("{}", text);
            }
        }
        Err(e) => {
            eprintln!("Stream error: {}", e);
        }
    }
}
```
