Skip to content

Latest commit

 

History

9 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

 ____  ____   ___  __  __  _____ _____ _   _ _____ _   _ ____
|  _ \|  _ \ / _ \|  \/  || ____|_   _| | | | ____| | | / ___|
| |_) | |_) | | | | |\/| ||  _|   | | | |_| |  _| | | | \___ \
|  __/|  _ <| |_| | |  | || |___  | | |  _  | |___| |_| |___) |
|_|   |_| \_\\___/|_|  |_||_____| |_| |_| |_|_____|\___/|____/

Prometheus

Python Agent Runner + Tools

Give an agent a loop. Give the loop tools. Let it steal the fire.

Python Poetry License


Prometheus is a Python agent runner: a tight execution loop that calls a model, dispatches tools, and feeds results back until the job is done.

No framework maze. No hidden magic. One runner. Pluggable tools.

  you ──► runner ──► model
              ▲         │
              │         ▼
              └── tools ◄┘

Why Prometheus

Runner-first The loop is the product. Everything else plugs into it.
Tools as contracts Register a function, get a schema, run it.
Poetry-native Reproducible installs, locked deps, one command to start.
Small surface Read the source in an afternoon. Extend it in an evening.

Quick start

# Clone
git clone git@github.com:FedericoGabrielCastro/Prometheus.git
cd Prometheus

# Install (Python 3.12+)
poetry install

# Echo loop (no API key)
poetry run prometheus run --model echo "steal the fire"

# Real model (needs OPENAI_API_KEY)
poetry run prometheus run "what files are here?"

# Run tests
poetry run pytest

The loop

Plug in any model that implements complete(messages) -> AssistantReply. The runner does the rest.

from prometheus import AgentRunner, AssistantReply

class Echo:
    def complete(self, messages):
        last = messages[-1].content
        return AssistantReply(content=f"heard: {last}")

result = AgentRunner(Echo(), system_prompt="keep it short").run("steal the fire")

print(result.output)        # heard: steal the fire
print(result.stop_reason)   # completed
print(len(result.turns))    # 1

Stop conditions:

Reason When
completed The model replies with no tool calls
max_turns The loop hits the turn budget (default 16)

If the model asks for a tool, the runner executes it (or records an error) and feeds the result back as a tool message. Tool exceptions never kill the loop.

Tools

Register a function. Prometheus builds the JSON schema from type hints, Annotated metadata, and the docstring.

from prometheus import AgentRunner, ToolRegistry, tool

@tool
def spark(n: int = 1) -> str:
    """Make n sparks."""
    return "ember" * n

tools = ToolRegistry([spark])
result = AgentRunner(your_model, tools=tools).run("ignite")

The runner passes tools.schemas() into every model.complete(...) call so the model can see what it is allowed to use. Unknown names, missing arguments, and extra arguments come back as error: tool messages — they do not crash the loop.

Built-ins

Filesystem paths that resolve outside the workspace are rejected. run_command starts with cwd at the workspace root. HTTP is http(s) only. Output is clipped so a huge file cannot flood the model.

from prometheus import AgentRunner, builtin_tools

tools = builtin_tools(".", shell=True, http=True)
result = AgentRunner(your_model, tools=tools).run("what files are here?")
Tool Role
read_file / write_file / list_dir UTF-8 files under the workspace
run_command Shell command with cwd at the workspace root
http_get Fetch an http or https URL

Disable a group when you do not want it: builtin_tools(root, shell=False, http=False).

CLI

prometheus run "steal the fire"
prometheus run --model echo "no cloud required"
prometheus run --root . --no-shell --no-http -v "what files are here?"
Flag Meaning
--model echo | openai Local echo, or OpenAI-compatible chat. Default is openai when OPENAI_API_KEY is set.
--model-name Remote model id (default gpt-4o-mini)
--base-url OpenAI-compatible API root
--root Workspace for built-in tools
--no-filesystem / --no-shell / --no-http Disable a built-in group
--max-turns / --system / -v Turn budget, system prompt, trace tool hops

Compatible servers (Ollama, vLLM, LiteLLM, …) work via --base-url.

Architecture

flowchart LR
    U[User / CLI] --> R[Agent Runner]
    R --> M[Model]
    M -->|tool call| R
    R --> T[Tool Registry]
    T --> F[Filesystem]
    T --> S[Shell]
    T --> H[HTTP]
    T --> X[Your tool]
    F --> R
    S --> R
    H --> R
    X --> R
    R -->|final answer| U
Loading

The runner owns the loop. Tools never talk to the model. The model never touches the filesystem. That boundary is the whole design.

Project layout

Prometheus/
├── src/prometheus/
│   ├── cli.py          # `prometheus run`
│   ├── providers.py    # Echo + OpenAI-compatible models
│   ├── runner.py       # Agent loop, turn state, stop conditions
│   ├── tools.py        # @tool, registry, JSON schemas
│   ├── builtins.py     # Filesystem, shell, HTTP
│   ├── schema.py       # Type hints → JSON Schema
│   ├── model.py        # Model protocol
│   └── types.py        # Messages, turns, stop reasons
├── tests/
├── pyproject.toml
└── README.md

Roadmap

Built as a sequence of small, reviewable PRs:

  • 0 — Bootstrap — Poetry project, layout, tests, this README
  • 1 — Runner — Agent loop, turn state, stop conditions
  • 2 — Tools — Tool protocol, registry, schema generation
  • 3 — Built-ins — First-party tools the runner can actually use
  • 4 — CLI — prometheus run from the terminal

Requirements

License

MIT. See LICENSE.

Steal the fire. Keep the loop honest.

About

Python agent runner: a tight loop that calls a model, dispatches tools, and feeds results back until the job is done.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages