A streaming terminal REPL for any agent.
Project description
partnuh
A streaming terminal REPL for any agent.
partnuh is purely aesthetic: it's the fast, multi-line, token-streaming
terminal REPL. You build your agent with its own dependencies — smolagents, an
OpenAI-compatible chat model, anything — and hand it to partnuh.run(), which
auto-wraps it. partnuh never builds agents and imports no framework of its own.
import partnuh
from smolagents import ToolCallingAgent, OpenAIServerModel
agent = ToolCallingAgent(tools=[...], model=OpenAIServerModel(...)) # your agent
partnuh.run(agent, name="Private Caller") # partnuh makes it pretty
Run the example locally (from a checkout)
Install the package into a virtualenv in editable mode, then run the example —
examples/basic_agent.py, a complete agent CLI in a few lines:
cd partnuh
python3 -m venv .venv
.venv/bin/pip install -e ".[smolagents,dotenv]" # or ".[all]"
cp .env.template .env # add your OPENROUTER_API_KEY
.venv/bin/python examples/basic_agent.py # interactive
.venv/bin/python examples/basic_agent.py "21 + 21?" # one-shot
It auto-loads .env. See the comments in the file for how each part maps to the
library.
Install (as a dependency)
pip install partnuh # core (bring your own agent)
pip install "partnuh[openai]" # + OpenAI/OpenRouter chat backend
pip install "partnuh[smolagents]" # + smolagents adapter
pip install "partnuh[all]"
Use
Bring your own agent. Build it however you like, then partnuh.run() it.
What you pass is auto-wrapped: an already-CliAgent, a smolagents agent, or a
plain stream function all just work.
import os, partnuh
from smolagents import OpenAIServerModel, ToolCallingAgent, tool
@tool
def add(a: int, b: int) -> int:
"Add two integers.\n\nArgs:\n a: first\n b: second"
return a + b
model = OpenAIServerModel(model_id="openai/gpt-5.4-nano",
api_base="https://openrouter.ai/api/v1",
api_key=os.environ["OPENROUTER_API_KEY"])
agent = ToolCallingAgent(tools=[add], model=model, stream_outputs=True)
partnuh.run(agent, name="Private Caller") # auto-wrapped; no partnuh agent type to learn
Anything is an agent — a generator function is enough (no key, no framework):
import partnuh
from partnuh import TextDelta
def echo(prompt, session_id):
for word in f"you said: {prompt}".split(" "):
yield TextDelta(word + " ")
partnuh.run(echo, name="Echo")
Optional sugar: for the no-framework chat case there's AgentSpec, a thin
helper over the OpenAI/OpenRouter backend. It's convenience only — partnuh
doesn't need it.
agent = partnuh.AgentSpec(name="Private Caller", model="openai/gpt-5.4-nano").build()
partnuh.run(agent)
In the REPL
- Enter submits, Shift+Enter inserts a newline (works across terminals — see below). Backspace on a blank line joins back to the previous line.
/toolslist tools ·/resetclear history ·/help·/quit
Configuring behavior
from partnuh import CliConfig
partnuh.run(agent, config=CliConfig(
stream_speed=0.0, # 0 = as-fast-as-tokens-arrive; 1.0 = slow typewriter
show_tool_calls=True, # render the tool-call markers
banner=True,
commands={"clear": lambda d, args: __import__("os").system("clear")},
))
How it works (ports & adapters)
There is no universal in-process agent object shared across frameworks, so
partnuh defines a tiny contract and ships adapters:
CliAgentprotocol —name,model,tools, andstream(prompt, session_id) -> Iterator[Event].Eventunion —TextDelta,ToolCallStarted,ToolResult,Error,Done. Every adapter translates its framework's native stream into these; a barestris treated as aTextDelta.- Adapters —
from_openai,from_smolagents,from_callable. Add your own by yieldingEvents. Pacer— renders the event stream to the terminal with optional speed control.
The Shift+Enter trick
Terminals encode Shift+Enter differently, and prompt_toolkit's default collapses
the common Ghostty/xterm encoding into plain Enter (so it submits instead of
adding a newline). partnuh registers every known Shift+Enter escape sequence
(\x1b[27;2;13~, kitty's \x1b[13;2u, …) and maps it to "insert newline" — no
terminal config required.
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file partnuh-0.0.2.tar.gz.
File metadata
- Download URL: partnuh-0.0.2.tar.gz
- Upload date:
- Size: 16.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bd85cb030dfe134c0f71c33931d1da45462c4b81ca7b50d48afdf240f7004d68
|
|
| MD5 |
74f99c3de310a32986aec925a5e860e8
|
|
| BLAKE2b-256 |
e6a9b26ef01dca11c3e1e23fe2ab898659c30ac93f82b08b784569c05ef86199
|
File details
Details for the file partnuh-0.0.2-py3-none-any.whl.
File metadata
- Download URL: partnuh-0.0.2-py3-none-any.whl
- Upload date:
- Size: 19.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9a651d286af74aaab403649c680b7c562dbbf7d384102d283e29c05b020be28e
|
|
| MD5 |
5cdc757a5f40d685f942a6f66712a6c5
|
|
| BLAKE2b-256 |
e2663ea1c5c965916960f8d70f3a585b534c65110a8e2f4160986ad607338e71
|