# Streaming chat CLI quickstart > Build a terminal chat client that prints Kiln's reply as it streams in, token by token. Source: https://kiln.bricksum.com/docs/en/cookbook/streaming-chat-cli ## Goal Build a terminal chat client that prints Kiln's reply as it streams in, one token at a time, instead of waiting for the whole response. ## Outcome A runnable Python script, chat.py, that keeps a conversation history, sends each line you type as a new user turn, and streams the assistant's reply to your terminal as it is generated. ## Copy this prompt for your agent **prompt** ```text Write a Python CLI named chat.py that: reads KILN_API_KEY (required) and KILN_BASE_URL (optional, default "https://api.bricksum.com/v1") from the environment, creates an OpenAI client pointed at that base URL, keeps a `messages` list, and loops reading a line from stdin. Each line becomes a user message. Call client.chat.completions.create with model="qwen3-32b", stream=True and max_tokens=800, print chunk.choices[0].delta.content as each chunk arrives, then append the full reply as an assistant message only if it is non-empty. Exit the loop when the input is "exit". ``` ## Prerequisites - Python 3.10 or later and the openai package (pip install openai). The openai release this page was verified with requires 3.10; on 3.9, pip silently installs an older major version. - A Kiln API key from Console → API keys, exported as KILN_API_KEY. ## 1. Install the SDK The official OpenAI Python client is the streaming CLI's only dependency. **pip** ```bash pip install openai ``` ## 2. Write chat.py Save the script below. It reads one line at a time, sends it as a user turn, and prints each delta as it streams instead of waiting for the finished reply. **chat.py** ```python import os from openai import OpenAI client = OpenAI( base_url=os.environ.get("KILN_BASE_URL", "https://api.bricksum.com/v1"), api_key=os.environ["KILN_API_KEY"], # sk-bk-... ) messages = [] print("Streaming chat CLI. Type 'exit' to quit.") while True: user_input = input("> ") if user_input.strip().lower() == "exit": break messages.append({"role": "user", "content": user_input}) stream = client.chat.completions.create( model="qwen3-32b", messages=messages, stream=True, max_tokens=800, ) reply = "" for chunk in stream: delta = chunk.choices[0].delta.content or "" print(delta, end="", flush=True) reply += delta print() if reply: messages.append({"role": "assistant", "content": reply}) ``` ## 3. Run it Export your key and run the script. Type a message and press Enter; the reply prints progressively. Type exit to quit. ```bash export KILN_API_KEY="sk-bk-..." python chat.py ``` **expected output** ```text Streaming chat CLI. Type 'exit' to quit. > Say hello in five words. Hello there, nice meeting you! > exit ``` ## Check your work Send a message and watch the reply print word by word instead of appearing all at once. Send a follow-up (for example, "summarize that in one line") and confirm the reply refers back to the first turn — that's the messages list carrying history. If a reply comes back as an empty line, the model spent the whole max_tokens budget on reasoning before it wrote any text (finish_reason "length"): raise max_tokens above 800 in chat.py, for example to 1600, and ask again. ## Next steps - [Streaming](https://kiln.bricksum.com/docs/en/streaming) — the SSE event format, keepalive comments and how cost is added to the final chunk - [Tool calling, end to end](https://kiln.bricksum.com/docs/en/cookbook/tool-calling) — give the same client a tool it can call mid-conversation Note Snippets on this page were run against the Kiln API on 2026-09-21 with openai 3.16.2 (Python 3.14.7). Not verified: The automated snippet sweep cannot feed input to this interactive script; it was run by hand instead, with the same interpreter and SDK version above, on gpt-oss-120b. The script now names qwen3-32b, which it has not yet been run against.