Streaming responses

Read text incrementally, confirm completion, and collect final usage.

Streaming splits one answer into multiple events. After displaying text, read the terminal status and usage to confirm completion.

This page uses deepseek-flash with POST /openai/v1/chat/completions and claude-opus-5 with POST /anthropic/v1/messages. Both examples were verified on 2026-10-12.

1. Prepare your terminal

Use a Praxis cloud API key in PRAXIS_API_KEY; see API keys and authentication. Do not put the key in source code. Install Python and uv; TypeScript examples use Bun.

test -n "$PRAXIS_API_KEY"

Expected: exit code 0. The examples default to https://api.deepraxis.com. To use another environment, set PRAXIS_API_BASE to its API origin, without a trailing slash or API path.

Save each Python block using its indicated filename. Run it with uv run -q --no-project --with openai --with anthropic python <filename>. Each block is independent.

2. Read text and final usage with Python

Save as streaming.py. Run uv run -q --no-project --with openai --with anthropic python streaming.py.

import os
from openai import OpenAI

client = OpenAI(api_key=os.environ['PRAXIS_API_KEY'],
    base_url=os.environ.get('PRAXIS_API_BASE', 'https://api.deepraxis.com') + '/openai/v1')
text, finish, usage = '', None, None
with client.chat.completions.with_streaming_response.create(
    model='deepseek-flash', messages=[{'role':'user','content':'Reply with exactly: Hello streaming.'}],
    max_tokens=256, stream=True, stream_options={'include_usage':True},
) as response:
    print('X-Request-Id:', response.headers.get('x-request-id'))
    for chunk in response.parse():
        if chunk.usage is not None:
            usage = chunk.usage
        for choice in chunk.choices:
            piece = choice.delta.content or ''
            text += piece
            print(piece, end='', flush=True)
            if choice.finish_reason is not None:
                finish = choice.finish_reason
print('\nfinish_reason:', finish)
assert text.strip() and finish == 'stop', 'Incomplete text response'
assert usage is not None, 'No final usage received'
print('usage:', usage.model_dump())

Expected: incremental Hello streaming., then finish_reason: stop and usage containing input and output token counts. Exit code: 0.

Read Chat Completions in this order:

  1. Collect choices[].delta.content. Role, thinking, or empty deltas may contain no answer text; do not display them as answer text.
  2. Record finish_reason. This example ends with stop; tool_calls requires the tool-calling flow.
  3. With stream_options.include_usage=true, continue reading the final usage chunk. Its choices may be empty; do not access choices[0] unconditionally.
  4. The HTTP stream ends with data: [DONE]. The SDK handles this marker and finishes iteration.

Do not break on finish_reason: usage may arrive afterward. A disconnected stream does not establish completion. Without terminal status or final usage, treat text as partial and usage as unknown.

3. Inspect the events with cURL

Run in the same terminal:

curl --fail-with-body --no-buffer --silent --show-error --include \
  "${PRAXIS_API_BASE:-https://api.deepraxis.com}/openai/v1/chat/completions" \
  -H "Authorization: Bearer $PRAXIS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"deepseek-flash","messages":[{"role":"user","content":"Reply with exactly: Hello streaming."}],"max_tokens":256,"stream":true,"stream_options":{"include_usage":true}}'

Expected: HTTP 200 and text/event-stream. Text deltas precede finish_reason: "stop", a usage chunk, and finally data: [DONE]. cURL exits 0. --no-buffer prevents display buffering. Exit code 0 alone does not prove answer completion; inspect the terminal status too.

4. Use Anthropic Messages

Messages sends message_start, content block content_block_start / content_block_delta / content_block_stop, then message_delta and message_stop. A ping may occur between events. Do not parse these as Chat Completions choices. Input usage appears in the starting message; the SDK aggregates later updates. get_final_message() returns the complete result.

Save as streaming-anthropic.py. Run uv run -q --no-project --with openai --with anthropic python streaming-anthropic.py.

import os
from anthropic import Anthropic

client = Anthropic(api_key=os.environ['PRAXIS_API_KEY'],
    base_url=os.environ.get('PRAXIS_API_BASE', 'https://api.deepraxis.com') + '/anthropic')
with client.messages.stream(model='claude-opus-5', max_tokens=256,
    messages=[{'role':'user','content':'Reply with exactly: Hello streaming.'}]) as stream:
    print('X-Request-Id:', stream.response.headers.get('x-request-id'))
    for text in stream.text_stream:
        print(text, end='', flush=True)
    message = stream.get_final_message()
assert message.stop_reason == 'end_turn'
assert any(block.type == 'text' and block.text.strip() for block in message.content)
print('\nusage:', message.usage.model_dump())

Expected: the same text and final usage, with stop_reason=end_turn. Exit code: 0.

Observed failure and fix

Using text-embedding-3-small in this chat request returned HTTP 404:

openai.chat.completions is not offered for text-embedding-3-small

The model does not offer this chat endpoint. Waiting for more events cannot resolve it. Using the verified deepseek-flash example returned text, terminal status, and final usage successfully.

Protocol details:OpenAI · Anthropic。

API reference → · Cookbook →

Export

On this page