UniterRouter
Guides

Streaming Responses

Handle real-time streaming responses from LLM APIs.

Streaming Responses

Streaming allows you to receive responses token-by-token as they're generated, providing a better user experience for chat applications.

How It Works

When you enable streaming, the API returns a series of Server-Sent Events (SSE) instead of waiting for the complete response. Each event contains a chunk of the response.

Enabling Streaming

Add stream: true to your request:

{
  "model": "gpt-4o",
  "messages": [{"role": "user", "content": "Tell me a story"}],
  "stream": true
}

Implementation Examples

from openai import OpenAI

client = OpenAI(
    base_url="https://api.uniterouter.ai/v1",
    api_key="your_api_key",
)

stream = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "Tell me a short story"}],
    stream=True
)

for chunk in stream:
    if chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://api.uniterouter.ai/v1",
  apiKey: process.env.UNITEROUTER_API_KEY,
});

const stream = await client.chat.completions.create({
  model: "gpt-4o",
  messages: [{ role: "user", content: "Tell me a short story" }],
  stream: true,
});

for await (const chunk of stream) {
  const content = chunk.choices[0]?.delta?.content;
  if (content) {
    process.stdout.write(content);
  }
}
curl https://api.uniterouter.ai/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $UNITEROUTER_API_KEY" \
  -d '{
    "model": "gpt-4o",
    "messages": [{"role": "user", "content": "Tell me a story"}],
    "stream": true
  }'
package main

import (
    "context"
    "fmt"
    "io"
    openai "github.com/sashabaranov/go-openai"
)

func main() {
    config := openai.DefaultConfig("your_api_key")
    config.BaseURL = "https://api.uniterouter.ai/v1"
    client := openai.NewClientWithConfig(config)

    stream, _ := client.CreateChatCompletionStream(
        context.Background(),
        openai.ChatCompletionRequest{
            Model: "gpt-4o",
            Messages: []openai.ChatCompletionMessage{
                {Role: "user", Content: "Tell me a story"},
            },
            Stream: true,
        },
    )
    defer stream.Close()

    for {
        response, err := stream.Recv()
        if err == io.EOF {
            break
        }
        fmt.Print(response.Choices[0].Delta.Content)
    }
}

Stream Event Format

Each SSE event contains a JSON chunk:

data: {"id":"chatcmpl-xxx","object":"chat.completion.chunk","created":1234567890,"model":"gpt-4o","choices":[{"index":0,"delta":{"content":"Hello"},"finish_reason":null}]}

data: {"id":"chatcmpl-xxx","object":"chat.completion.chunk","created":1234567890,"model":"gpt-4o","choices":[{"index":0,"delta":{"content":" world"},"finish_reason":null}]}

data: {"id":"chatcmpl-xxx","object":"chat.completion.chunk","created":1234567890,"model":"gpt-4o","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}

data: [DONE]

Vercel AI SDK Integration

For React/Next.js applications, use the Vercel AI SDK:

// app/api/chat/route.ts
import { createOpenAI } from "@ai-sdk/openai";
import { streamText } from "ai";

const uniterouter = createOpenAI({
  baseURL: "https://api.uniterouter.ai/v1",
  apiKey: process.env.UNITEROUTER_API_KEY!,
});

export async function POST(req: Request) {
  const { messages } = await req.json();

  const result = streamText({
    model: uniterouter("gpt-4o"),
    messages,
  });

  return result.toDataStreamResponse();
}
// components/Chat.tsx
"use client";
import { useChat } from "ai/react";

export function Chat() {
  const { messages, input, handleInputChange, handleSubmit } = useChat();

  return (
    <div>
      {messages.map((m) => (
        <div key={m.id}>
          <strong>{m.role}:</strong> {m.content}
        </div>
      ))}
      <form onSubmit={handleSubmit}>
        <input value={input} onChange={handleInputChange} />
        <button type="submit">Send</button>
      </form>
    </div>
  );
}

Handling Stream Options

stream_options

Get usage information with streaming:

stream = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "Hello"}],
    stream=True,
    stream_options={"include_usage": True}
)

for chunk in stream:
    if chunk.usage:
        print(f"Tokens used: {chunk.usage.total_tokens}")

Error Handling

Handle errors during streaming:

try:
    stream = client.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": "Hello"}],
        stream=True
    )
    
    for chunk in stream:
        if chunk.choices[0].delta.content:
            print(chunk.choices[0].delta.content, end="")
            
except Exception as e:
    print(f"Stream error: {e}")

Best Practices

Performance Tips

  1. Use streaming for long responses — Better UX for stories, code, explanations
  2. Handle connection drops — Implement retry logic
  3. Buffer output appropriately — Don't update UI on every token
  4. Consider timeout settings — Streaming connections may last longer

Non-Streaming vs Streaming

AspectNon-StreamingStreaming
Time to first tokenSlow (wait for full response)Fast (immediate)
Total response timeSameSame
User experienceLoading stateProgressive display
ImplementationSimplerSlightly more complex
Use caseBackground processingInteractive chat

Streaming with Function Calls

When using tools/functions with streaming:

stream = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "What's the weather?"}],
    tools=[weather_tool],
    stream=True
)

tool_calls = []
for chunk in stream:
    delta = chunk.choices[0].delta
    
    if delta.tool_calls:
        for tc in delta.tool_calls:
            if tc.index >= len(tool_calls):
                tool_calls.append({"id": tc.id, "function": {"name": "", "arguments": ""}})
            if tc.function.name:
                tool_calls[tc.index]["function"]["name"] = tc.function.name
            if tc.function.arguments:
                tool_calls[tc.index]["function"]["arguments"] += tc.function.arguments

Next Steps

How is this guide?