minion: a 4600-line coding agent that spends about 625 tokens before you type
A tiny single-file coding agent for self-hosted models (llama.cpp / vLLM / SGLang).
At a glance
- What is it?
- Sentdex/minion is a single-file Python coding agent for self-hosted models, built around one claim: the prompt overhead a harness sends before you say hello is paid on every turn. It ships five tools, raw terminal escapes and no plugin system.
- Who is it for?
- minion is worth the short read if you host your own model and care about what it costs you in context on every turn, since the whole design is one file you can read end to end and modify. It is not the right tool if you want a plugin ecosystem, a graphical interface or an orchestration layer, because none of those exist here by choice.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 79 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 11, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The whole agent is one 4600-line file with five tools
There is no TUI framework, no plugin system and no config file format. The entire agent is `minion.py`, roughly 4600 lines. It reads configuration from environment variables and automatically loads `~/.env` at startup, it talks directly to the OpenAI SDK, and it draws its interface with raw terminal escapes. The stated pitch is that if you want to understand or modify how it works, you read one file.
The capability set is correspondingly small: an agent that can read, write, edit and run shell commands in your project, driven by five tool functions. That number is not incidental, because the tool schemas are the single largest cost in the prompt.
Installation is correspondingly plain. Point it at any OpenAI-compatible endpoint, which covers a local llama.cpp, vLLM or SGLang server as well as a remote API such as Z.ai or OpenAI itself, then start it:
pip install openai
export MINION_BASE_URL=http://localhost:8080/v1
export MINION_MODEL=your-model-name
export MINION_API_KEY=sk-noop # any string; local servers ignore it
python minion.pyTwo details in that block matter. The API key can be any string because local servers ignore it, so there is no credential to manage for the common case. And if `MINION_MODEL` is left unset, minion asks the server what it is serving rather than failing.
A bare hey costs about 625 tokens, and 475 of them are tool schemas
The argument for the design is a token budget, and it is given concretely. On a bare `hey`, the entire prompt minion sends breaks down like this:
system prompt (SYSTEM) ~98
tool schemas (5 functions, TOOLS) ~475
the word "hey" 1
chat-template framing ~50
─────
~625 tokensThe system prompt is cheap. The tool schemas are three quarters of the total, which tells you where the leverage is: drop tools and you drop tokens. Three reasons are given for caring about the first stretch of context. It is the fastest part for the model, it is the part where attention is at its best, and it rides along with every message you send, so it bills you over time even on a paid API.
The documentation is careful about the last line. The real total lands somewhere in the low 600s because every server's chat template wraps the tool section differently: Qwen and Hermes add per-tool tags, llama.cpp adds a functions header, and OpenAI injects its own. The number to hold on to is the floor rather than the exact figure, and the comparison being made is against harnesses that spend the first 20K to 50K of your context before you have said anything.
Named sources switch endpoints mid-conversation without losing context
Configuration comes in two shapes. The simple one is a single endpoint, which is the three variables in the quick start. The other defines named endpoints and switches between them at runtime:
MINION_SOURCES=local,zai
MINION_SOURCE_LOCAL_BASE_URL=http://localhost:8080/v1
MINION_SOURCE_LOCAL_API_KEY=sk-noop
MINION_SOURCE_ZAI_BASE_URL=https://api.z.ai/api/paas/v4
MINION_SOURCE_ZAI_API_KEY=$zai_test # $name = look up a key from env / ~/.env
MINION_SOURCE_ZAI_MODEL=glm-x-previewThe naming convention is mechanical: `MINION_SOURCE_` plus the source name in capitals, then the field. The `$zai_test` syntax is worth noticing, since it is a lookup rather than a literal, pulling a key from the environment or `~/.env` so a secret does not have to be pasted into a config file. A fuller annotated example lives in `sources.example.env`.
Switching happens with `/source [name]`, and the conversation context is preserved across the switch, with `/reset` available when you want a clean slate. Preserving context across a jump from a local 8B model to a hosted frontier model is the feature that makes multi-source configuration more than a convenience: you can escalate one hard question without re-priming the agent.
The context ceiling is probed once per source, and remote hosts are probed by over-asking
Each source's maximum context window is the server's limit for the active model, and it is shown in the `/source` list and again on the switch line. The per-turn stats footer prints it next to current usage as something like `ctx 12K/150K`, with the current half of the bar colorized by how full the window is: green below 30 percent, yellow from 30 to 60, red at 60 and above. That is a glance rather than a calculation.
How the ceiling gets discovered differs by source, and the reason is latency. For local servers it reads `/v1/models` and `/props`, which are sub-millisecond round-trips on a LAN. For remote hosts it leads with an over-`max_tokens` chat probe: a deliberately oversized request is rejected with the limit named in the error, resolving in well under a second. The stated benefit is that the limit appears on the first turn instead of after a multi-second `/v1/models` round-trip that returns nothing. The result is cached per source and model, so this happens once.
Auto-compression is tied to the same number. When the conversation fills `MINION_AUTOCOMPRESS_PERCENT` of the window, by default 85, older turns are folded into a summary while the last third is kept verbatim, and `/autocompress` exposes it. Keeping a third intact matters more than the compression ratio, because that tail is where the current task lives.
Tool calling degrades to text delimiters when a server has no native support
Self-hosted servers vary in what they implement, so minion has a fallback path. If the server does not support native tool calling, it parses standalone `[minion_tool_call]...[/minion_tool_call]` messages out of the model's text. That is the kind of feature that goes wrong quietly, so the handling around it is the interesting part.
Quoted examples or source code containing those delimiters are treated as normal text rather than as instructions, which prevents a file the agent is reading from injecting a tool call into its own context. Tool results also have the delimiters escaped before they enter the next model context, so a shell command that echoes the marker cannot create a phantom call on the following turn. Both are containment measures for the same failure mode.
There is a parallel accommodation for reasoning output. If the server streams a separate `reasoning_content` field, as MiniMax-M3 and DeepSeek-R1 do, minion renders it as a dim thinking block above the answer instead of discarding it. The overall attitude is stated as degrading gracefully rather than demanding a perfect server, which is the right default when the server is whatever you happened to run locally.
httpx is held below 0.28 because the openai SDK stopped accepting proxies
The packaging is where the real constraints are documented, and there are only three files. `requirements.txt` holds two lines, `openai` and `httpx<0.28`. The upper bound on httpx is not a precaution, it is a workaround, and the comment beside it says why: openai below 1.55 passes `proxies=`, and httpx 0.28 removed it. Installing a newer httpx against an older openai breaks the call.
`setup.py` declares version 0.1.1, requires Python 3.9 or newer, and uses `py_modules=["minion"]` so setuptools packages the single module rather than looking for a package directory. The console script is `minion=minion:main`, which is what produces the `minion` command.
That command comes from an editable install of the checkout, so edits you make are picked up immediately:
pip install -e .Use `pip install .` without the editable flag when you want a copied install instead. `pyproject.toml` is minimal, requiring setuptools 64 or newer plus wheel with `setuptools.build_meta` as the backend, which tells you the build is plain setuptools rather than anything more modern. The top level of the repository is small too: the single module, `setup.py`, `pyproject.toml`, `requirements.txt`, `sources.example.env`, a `changelog.md` and a `tests/` directory.
AWS Bedrock arrives through a LiteLLM proxy and needs a prefixed model ID
Because minion speaks the OpenAI-compatible protocol, anything that exposes that protocol works, including a local proxy in front of a model that does not. AWS Bedrock is the documented case, run through LiteLLM:
pip install 'litellm[proxy]'
litellm --model bedrock/us.anthropic.claude-sonnet-4-20250514-v1:0 --port 4000Then the endpoint variables point at the proxy on port 4000 and the model name carries the `bedrock/` prefix. LiteLLM reads standard AWS credentials from `~/.aws/credentials`, from `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`, or from SSO and instance profiles, and `AWS_DEFAULT_REGION` covers the region.
One Bedrock detail causes real failures if missed. On-demand throughput requires inference profiles, so the model ID needs a `us.` or `global.` prefix rather than a bare model ID. A convenience wrapper that starts the proxy automatically is included for people who would rather not repeat the command, which is the shape of the ergonomics elsewhere in this project too: one file, a few environment variables, and no framework in the way.
Editorial conclusion
minion is worth the short read if you host your own model and care about what it costs you in context on every turn, since the whole design is one file you can read end to end and modify. It is not the right tool if you want a plugin ecosystem, a graphical interface or an orchestration layer, because none of those exist here by choice. Before you rely on it, check two things: whether your server supports native tool calling, since the fallback parses delimiters out of model text and the escaping of tool results is what keeps that safe, and whether your `httpx` resolves below 0.28, because the SDK stopped accepting the proxy argument at that version. Pin an editable install from the checkout so your edits take effect immediately, and remember the setup version is 0.1.1 with no tagged releases, and that the last push was on 2026-07-25.
Frequently asked questions
What is Sentdex minion?
A deliberately tiny single-file coding agent for self-hosted models, about 4600 lines in minion.py. It can read, write, edit and run shell commands in your project through five tool functions, works against any OpenAI-compatible endpoint, and uses raw terminal escapes with no TUI framework or plugin system.
How many tokens does minion send before I type anything?
About 625 on a bare hey, broken down as roughly 98 for the system prompt, 475 for five tool schemas, 1 for the word itself and about 50 for chat-template framing. The real figure lands in the low 600s because each server wraps tool schemas differently, and the documentation presents the number as a floor rather than an exact total.
How do I point minion at a local llama.cpp or vLLM server?
Set MINION_BASE_URL to the server address, for example http://localhost:8080/v1, set MINION_MODEL to the model name, and set MINION_API_KEY to any string since local servers ignore it. If MINION_MODEL is unset, minion asks the server what it is serving. Configuration is also loaded automatically from ~/.env at startup.
What happens in minion when the server has no native tool calling?
It falls back to parsing standalone [minion_tool_call] messages out of the model's text. Quoted examples and source code containing those delimiters are treated as normal text, and tool results have the delimiters escaped before entering the next model context. A separate reasoning_content field is rendered as a dim thinking block above the answer.
How do I install minion as a command?
Run pip install -e . from the checkout, which registers a minion console script pointing at that directory so your edits take effect immediately. Use pip install . without the editable flag for a copied install. Python 3.9 or newer is required, and httpx must resolve below 0.28.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/sentdex-minion)