Files
2026-05-24 20:10:21 +00:00

91 lines
2.5 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Simple streaming AI agent using OpenAI ChatCompletion API.
The module exposes a single function ``stream_chat`` that yields
text fragments as they arrive from the model. It is intentionally
lightweight so it can be used as a dropin component in larger
applications.
Dependencies
------------
* ``openai`` official OpenAI SDK
Environment
-----------
An OpenAI API key must be available either as the environment
variable ``OPENAI_API_KEY`` or passed explicitly via the
``api_key`` argument.
"""
from __future__ import annotations
import os
from typing import Iterable, Generator, Dict, List, Any
import openai
# Ensure the OpenAI key is set when the module is imported.
# Users can override by passing ``api_key`` to ``stream_chat``.
openai.api_key = os.getenv("OPENAI_API_KEY")
def stream_chat(
messages: List[Dict[str, str]],
*,
model: str = "gpt-3.5-turbo",
temperature: float = 0.7,
api_key: str | None = None,
) -> Generator[str, None, None]:
"""Yield model output token by token.
Parameters
----------
messages:
A list of message objects compatible with the ChatCompletion API.
model:
The model to use. Defaults to ``gpt-3.5-turbo``.
temperature:
Sampling temperature. Defaults to 0.7.
api_key:
Optional API key. If provided it overrides the environment
variable.
Yields
------
str
The next fragment of the assistant's reply.
"""
if api_key is not None:
openai.api_key = api_key
# The SDK returns an iterator over chunks when stream=True.
response = openai.ChatCompletion.create(
model=model,
messages=messages,
temperature=temperature,
stream=True,
)
# The response is an iterator of chunks. Each chunk contains a
# ``choices[0].delta`` dict with the text fragment.
for chunk in response:
try:
delta = chunk["choices"][0]["delta"]
if "content" in delta:
yield delta["content"]
except Exception as exc: # pragma: no cover defensive
# In a real application you might log this.
print(f"Error processing chunk: {exc}")
continue
# If the module is executed directly, run a small demo.
if __name__ == "__main__": # pragma: no cover
demo_messages = [
{"role": "user", "content": "Write a short poem about the ocean."}
]
print("Streaming response: ")
for token in stream_chat(demo_messages):
print(token, end="", flush=True)
print()