Add agent_stream.py
This commit is contained in:
@@ -0,0 +1,90 @@
|
|||||||
|
"""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
|
||||||
|
light‑weight so it can be used as a drop‑in 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()
|
||||||
Reference in New Issue
Block a user