From 225e89bcdf9b8101c39fcb777f4d880684f618c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A0=D0=B8=D0=BD=D0=B0=D1=80=20=D0=9C=D0=B8=D1=80=D0=B7?= =?UTF-8?q?=D0=B0=D0=B3=D0=B8=D1=82=D0=BE=D0=B2?= Date: Tue, 12 May 2026 21:44:28 +0000 Subject: [PATCH] Add stream_agent.py --- stream_agent.py | 77 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 stream_agent.py diff --git a/stream_agent.py b/stream_agent.py new file mode 100644 index 0000000..779bf13 --- /dev/null +++ b/stream_agent.py @@ -0,0 +1,77 @@ +from __future__ import annotations + +import argparse +import os +import time +from collections.abc import Iterable + + +class StreamingAIAgent: + """Small AI-agent with a streaming interface and an offline fallback.""" + + def __init__(self, model: str | None = None) -> None: + self.model = model or os.getenv("OPENAI_MODEL", "openai/gpt-oss-20b:free") + + def invoke(self, question: str) -> str: + return "".join(self.stream(question)) + + def stream(self, question: str) -> Iterable[str]: + client = self._build_client() + if client is None: + yield from self._offline_stream(question) + return + + response = client.chat.completions.create( + model=self.model, + messages=[ + { + "role": "system", + "content": "You are a concise educational assistant.", + }, + {"role": "user", "content": question}, + ], + stream=True, + ) + for chunk in response: + delta = chunk.choices[0].delta.content + if delta: + yield delta + + def _build_client(self): + api_key = os.getenv("OPENAI_API_KEY") + if not api_key: + return None + try: + from openai import OpenAI + except ImportError: + return None + return OpenAI( + api_key=api_key, + base_url=os.getenv("OPENAI_BASE_URL") or None, + ) + + def _offline_stream(self, question: str) -> Iterable[str]: + answer = ( + "Stream mode returns an answer piece by piece instead of waiting " + "for the whole response. This makes an AI-agent feel faster and " + f"lets the UI show progress while processing: {question}" + ) + for token in answer.split(" "): + yield token + " " + time.sleep(0.02) + + +def main() -> None: + parser = argparse.ArgumentParser(description="Streaming AI-agent demo") + parser.add_argument("question", nargs="*", help="User question") + args = parser.parse_args() + + question = " ".join(args.question).strip() or "Explain streaming mode" + agent = StreamingAIAgent() + for token in agent.stream(question): + print(token, end="", flush=True) + print() + + +if __name__ == "__main__": + main()