Skip to main content

AgentAImee

A very minimal LLM agent loop for Python, with tools and skills. One runtime dependency (httpx). Drop it into an existing project; you own the config, the tools, and the behavior. Aimee gives you the loop, the OpenAI-compatible client, skill discovery, AGENTS.md support, and callback hooks.

  • Model: any OpenAI-compatible chat/completions endpoint via OPENAI_API_BASE + OPENAI_API_KEY (no openai package).
  • Tools: built-in read, write, edit, bash — all opt-in — plus your own tools.
  • Skills: SKILL.md directories; a catalog is injected into the system prompt and the agent reads the full skill on demand.
  • Hooks: extend the run with callbacks — stream tokens, approve/modify tool calls, observe turns, handle errors.
  • Sync or async: agent.run(task) works in any script; await agent.run_async(task) for async code.

Install

uv add agent-aimee        # or: pip install agent-aimee
from aimee import Aimee, AimeeConfig  # module name is `aimee`

Development

uv sync
uv run pytest -q          # test suite (no network needed)

Quickstart

export OPENAI_API_BASE=https://api.openai.com/v1   # or any compatible gateway
export OPENAI_API_KEY=sk-...
uv run python examples/basic.py "your task here"   # or --repl

The example wires everything up: multi-root workspace, a skills directory, AGENTS.md, three basic tools, and console hooks.

Usage

from pathlib import Path
from aimee import Aimee, AimeeConfig
from aimee.tools import basic_tools

config = AimeeConfig(
    roots=[Path.cwd(), Path.home() / ".myapp"],  # workspace roots (first = primary)
    skills_dirs=[Path.home() / ".myapp" / "skills"],
    model="gpt-4o-mini",  # default: "default"
)
agent = Aimee(config, tools=basic_tools())  # read, write, edit, bash
# agent = Aimee(config, tools=[read()])          # or just the tools you want

report = agent.run("Summarize AGENTS.md")  # sync (also safe inside a running loop)
# report = await agent.run_async("...")          # async

print(report.final_text, report.turns, report.tool_calls, report.usage)

RunReport fields: final_text, turns, tool_calls, usage (tokens, when the provider reports them), truncated (max turns hit), messages (full transcript).

Sessions (multi-turn memory)

agent.run(task) is stateless — every run starts fresh. For a conversation that remembers, create a session:

session = agent.session()
session.run("hello, who am I talking to?")
session.run("what did I just ask?")  # the model sees the whole prior exchange

session.history  # snapshot: [system, user, assistant, user, assistant, ...]
session.clear()  # start over (a fresh system prompt is built on the next run)
  • A session is bound to one agent (sharing its tools/hooks/config); one agent can hold many independent sessions at once.
  • session.run() / await session.run_async() mirror Aimee.run() / run_async() — same hooks fire, same RunReport comes back (with messages being the full session history).
  • Tool calls and their results are part of the history too, so the model can reference earlier tool output in later turns.
  • History is unbounded by design: long conversations will eventually hit the model's context limit — session.clear() resets when that gets close. (history returns a snapshot; subclass or wrap the session for custom trimming strategies.)

Configuration (AimeeConfig)

Field Default Meaning
roots [Path.cwd()] Workspace roots, in priority order (see below). All paths are configurable here.
model "default" Model name passed to the endpoint.
api_base $OPENAI_API_BASE → https://api.openai.com/v1 Endpoint base URL.
api_key $OPENAI_API_KEY Bearer token.
system_prompt built-in minimal prompt Replaces the base prompt.
agents_md nearest AGENTS.md walking up from roots Explicit AGENTS.md path.
skills_dirs [] Directories containing SKILL.md skill folders.
max_turns 30 Model-call budget per run.
temperature / max_tokens None Passed through when set.
bash_timeout 120 Default shell timeout (seconds).
output_limit 100_000 Tool output truncation (chars).

Multi-root rules. roots[0] is the primary root. read/edit resolve relative paths against roots in order (first existing file wins); write updates the first root that contains the file, and creates new files under the primary root; bash runs with the primary root as cwd; AGENTS.md discovery walks up from each root in order until one is found.

Tools

Tools are OpenAI function-calling specs plus a handler:

from aimee import Tool

Tool(
    name="current_time",
    description="Get the current local date and time (ISO format).",
    parameters={"type": "object", "properties": {}},  # JSON Schema
    handler=lambda args, ctx: "...",  # sync or async; returns str
)
agent.add_tool(tool)

ctx is a ToolContext giving handlers the same workspace view as the built-ins: ctx.roots, ctx.resolve(path, must_exist=...), ctx.config.bash_timeout, ctx.config.output_limit.

Built-ins (opt-in): basic_tools() → all four, basic_tools(["read", "edit"]) → a subset, or import individually: from aimee.tools import read, write, edit, bash.

Tool Behavior
read(path, offset?, limit?) Numbered lines (paged), or directory listing.
write(path, content) Create/overwrite; parent dirs created.
edit(path, old_text, new_text) Exact-string replace; old_text must match exactly once.
bash(command, timeout?) Shell in the primary root; [exit N] + combined output, truncated.

Security note. bash has no built-in approval. The on_tool_call hook is the safety mechanism — see examples/custom_tool.py for a hook that denies commands containing rm . Don't enable bash without one.

Hooks

Register any object defining any subset of these methods (sync or async), via Aimee(..., hooks=[...]) or agent.add_hook(obj):

Hook Purpose
on_delta(chunk: str) One streamed chunk of assistant text (streaming on by default).
on_tool_call(name, args) -> ToolDecision | None Approval gate. None = allow; ToolDecision.deny(reason) blocks (reason goes to the model); ToolDecision.modify(new_args) replaces args. First non-None decision wins.
on_tool_result(name, args, result) After a tool finished (including denials/errors).
on_turn(turn, response) After each completed model response.
on_error(error) Tool errors (run continues, error text goes to the model) and API errors (run aborts).
on_done(report) Once, when the run finishes.

Skills

A skill is a directory with a SKILL.md:

myapp/skills/
└── pdf-extract/
    └── SKILL.md
---
name: pdf-extract
description: Extract text from PDF files. Use when the task involves PDFs.
---
Full instructions here. The agent reads this file (via the read tool)
before using the skill.

Set skills_dirs=[...]; Aimee injects only the catalog (name, description, path) into the system prompt, so skills cost almost nothing until used. Frontmatter is parsed minimally (flat key: value, no PyYAML).

AGENTS.md

If agents_md is unset, Aimee finds the nearest AGENTS.md walking up from each root (in root order) and includes it under a "Project instructions" heading. Give an explicit path to override.

Tests

uv run pytest -q

The suite is fully offline: the HTTP client is tested against httpx.MockTransport, and the agent loop against a scripted fake model.

Project layout

src/aimee/
  agent.py    # the loop + Aimee facade (sync/async)
  client.py   # OpenAI-compatible client (chat + SSE streaming)
  config.py   # AimeeConfig
  hooks.py    # hook dispatch (sync/async)
  prompt.py   # system prompt assembly (base + AGENTS.md + skills)
  skills.py   # SKILL.md discovery + catalog
  types.py    # messages, responses, reports
  tools/      # base primitives + built-in read/write/edit/bash
examples/     # basic.py, custom_tool.py + workspace fixtures
tests/        # offline test suite

Metadata

Release files for agent-aimee 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for agent-aimee 0.1.0
File Size Uploaded Provenance
agent_aimee-0.1.0.tar.gz 45.1 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for agent-aimee 0.1.0
File Interpreter ABI Platform
agent_aimee-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 67.8 kB

Release files / agent_aimee-0.1.0.tar.gz

Download URL agent_aimee-0.1.0.tar.gz
Size 45.1 kB
Tags Source
SHA-256 checksum
How to use checksums
04982dadc8600a9eae30c0b211857f73dcd936a1a59bceeb0bf04e823f197ee1
BLAKE2b-256 checksum
How to use checksums
061af3aca0b947cc8d7c3b336c807b034ff12bb187594e9ce5b32d389e6cc186
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.16 {"installer":{"name":"uv","version":"0.11.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / agent_aimee-0.1.0-py3-none-any.whl

Download URL agent_aimee-0.1.0-py3-none-any.whl
Size 22.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ca1089dd72f7f1393684d9e4762afb234c04efe868cb685b7a7aa2abf6adb022
BLAKE2b-256 checksum
How to use checksums
925bae52ff16275cc601a4894feeb479b9128ea12adc30561933810f7bdb8c15
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.16 {"installer":{"name":"uv","version":"0.11.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Security for agent-aimee 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page