This file guides coding agents working in this repository. It applies to the entire repo. Deeper AGENTS.md files may further specialize instructions and take precedence within their subtrees.
- Keep changes minimal, focused, and reversible; avoid broad refactors.
- Do not alter public API without explicit instruction; prefer additive changes.
- Never commit secrets (
.env, API keys). Verify.gitignoreis respected. - Follow style tools; let formatters fix whitespace vs. hand edits.
- Create env:
python -m venv .venv && source .venv/bin/activate - Install dev deps:
pip install -e '.[dev]' - Install hooks:
pre-commit install
- Quick check:
make ci-fast(lint + typecheck + focused tests, coverage >= 80) - Full local CI:
make ci(lint, typecheck, tests) - Tests:
make test(filter:pytest -k <pattern> -q) - Lint/format:
make lint/make formatthenmake precommit - Typecheck:
make typecheck - Examples (fake backend):
make examples-quick - Provider examples (need keys):
make examples-openai|anthropic|gemini|ollama - Docs:
make docs-serve(live) ormake docs-build(strict) - Release (trusted publishing): create and push a tag, see
make release
- Unit/contracts/providers:
make ci-fast - Integration (requires keys):
make itestor provider‑specificmake itest-openai|anthropic|gemini|ollama - Skip integ locally:
ALLOY_IT_MODEL=none pytest -q - Smoke examples without keys:
make smoke-examples(usesALLOY_BACKEND=fake)
- Default model:
gpt-5-mini(override withALLOY_MODEL) - Useful vars:
ALLOY_TEMPERATURE,ALLOY_MAX_TOKENS,ALLOY_SYSTEM/ALLOY_DEFAULT_SYSTEM,ALLOY_RETRY,ALLOY_MAX_TOOL_TURNS,ALLOY_EXTRA_JSON - Provider keys:
OPENAI_API_KEY,ANTHROPIC_API_KEY,GOOGLE_API_KEY; use.envlocally anddotenv -f .env run -- <cmd>
- Python: PEP 8 + type hints; 4‑space indent. Run
ruffandblack. - Docstrings: concise, imperative; include non‑obvious types.
- Imports: re‑export public API from
alloy/__init__.py; import viafrom alloy import ...in code/docs. - Decorators: bare
@command/@toolwhen no options;@command(...)/@tool(...)when passing options.
- Provider‑agnostic logic lives outside
alloy/models/; provider adapters go inalloy/models/<provider>.py. - Tools: prefer Design‑by‑Contract via
@require/@ensure; raiseToolErrorwith short, instructive messages. - Streaming: text‑only; commands with tools or non‑string outputs do not stream.
- Do: use
rgfor fast search; keep diffs tight; update docs/tests when touching behavior. - Do: run
make precommitbefore proposing a PR. - Don’t: bump dependencies or change public symbols to satisfy lint without discussion.
- Don’t: introduce provider‑specific behavior into core modules.
src/alloy/: Core library.ask.py,command.py,tool.py: Prompt, command, and tool orchestration.config.py,types.py,errors.py: Configuration, shared types, errors.models/: Model adapters (base.py,openai.py,anthropic.py,gemini.py,ollama.py). Put provider‑specific code here.
examples/: Runnable scripts (basic_usage.py,tools_demo.py).alloy-spec-v1.md: Reference spec for behaviors and interfaces..env: Local secrets (e.g.,OPENAI_API_KEY). Do not commit..venv/: Local virtual environment (git‑ignored). Do not commit.
- Create env:
python -m venv .venv && source .venv/bin/activate - Install dev deps (editable):
pip install -e '.[dev]' - Pre-commit:
pre-commit installthenmake precommit(enforces imports and decorator style in code + docs) - Run examples:
python examples/basic_usage.pyorpython examples/tools_demo.py(more underexamples/patterns/)- Streaming (text‑only):
python examples/basic/streaming_outputs.py
- Streaming (text‑only):
- Lint/format:
ruff .andblack .- Without installing the package, examples/tests add
src/tosys.pathfor convenience.
- Without installing the package, examples/tests add
Defaults: The global config uses model="gpt-5-mini" so you can call ask(...) and commands without configure(...). Use configure(...) to override.
Streaming is text‑only across providers; commands with tools or non‑string outputs do not stream.
- Indentation: 4 spaces; follow PEP 8 + type hints.
- Naming:
snake_casefor functions/vars,PascalCasefor classes,UPPER_SNAKE_CASEfor constants. - Docstrings: Use concise, imperative summaries. Include argument/return types when non‑obvious.
- Public API: Re‑export only stable symbols in
alloy/__init__.py; import public symbols viafrom alloy import ...(not submodules) in code and docs. - Decorators: use bare
@command/@toolwhen no options; use@command(...)/@tool(...)when passing options. - Models: Keep provider‑agnostic logic outside
models/; implement providers underalloy/models/<provider>.py.
- Default static return type for commands when
outputis omitted: sync →str, async →Awaitable[str]. - With
output=T, calls returnT(async →Awaitable[T]);.stream()yieldsstrchunks;.async_()awaits toT. ask.stream_async(...)is typed asAsyncIterable[str].- ParamSpec preserves prompt function parameters on the wrapped command.
- Framework: Prefer
pytest. - Layout:
tests/mirroring package modules; files namedtest_*.py. - Running:
pytest -q(add-k patternto filter). - Integration tests: require real API keys and model selection via env.
- OpenAI: set
OPENAI_API_KEY(optionalALLOY_IT_MODEL, defaultgpt-5-mini). - Anthropic: set
ANTHROPIC_API_KEY(andALLOY_IT_MODEL=claude-*). - Gemini: set
GOOGLE_API_KEY(andALLOY_IT_MODEL=gemini-*). - Ollama: run a local Ollama instance (and
ALLOY_IT_MODEL=ollama:<name>). - To skip provider integ tests locally:
ALLOY_IT_MODEL=none pytest -q.
- OpenAI: set
- Coverage (target): Aim for meaningful tests around prompts/contracts and model adapters; include edge cases for parsing/validation.
- Commits: Use imperative mood; consider Conventional Commits (e.g.,
feat:,fix:,docs:) for clarity. - Scope small, message specific: what/why over how.
- PRs: Include description, rationale, and screenshots/console snippets if behavior changes. Link related issues. Note any breaking changes.
- Checks: Ensure examples run (
examples/*.py) and no regressions in public API.
- Secrets: Keep
OPENAI_API_KEYin.envlocally; never commit secrets. - Git ignore: Ensure
.envand.venv/are ignored (they are in this repo). Never publish them. - Fail‑safe defaults: Validate config in
config.py; handle missing keys with clear errors. - Environment overrides: You can set process env vars to avoid code changes:
ALLOY_MODEL,ALLOY_TEMPERATURE,ALLOY_MAX_TOKENS,ALLOY_SYSTEM/ALLOY_DEFAULT_SYSTEM,ALLOY_RETRY,ALLOY_MAX_TOOL_TURNS,ALLOY_EXTRA_JSON(provider‑specific extras). Example:export ALLOY_MODEL=gpt-5-mini.- Default
max_tool_turnsis 10; increase if your workflows require more tool rounds. - Anthropic extras:
anthropic_tool_choice(e.g.,{ "type": "auto"|"any"|"tool"|"none" }),anthropic_disable_parallel_tool_use(bool).
- Default
- Local runs: Use the
dotenvCLI to load.env(e.g.,dotenv -f .env run -- pytest). In CI, configure provider API keys as encrypted secrets and pass via environment variables (do not store keys in the repo).
- Use
@require(predicate, message)to validate preconditions (receivesinspect.BoundArguments). - Use
@ensure(predicate, message)to validate postconditions (receives the tool result). - On failure, raise a
ToolErrormessage back to the model: OpenAI/Anthropic backends surface this as the tool output so the model can adjust (instead of a hard failure). - Keep messages short and instructive (e.g., "run validate_data first", "must be even").
- Streaming is text‑only for all providers.
- Commands with tools or non‑string outputs do not stream; call them normally to get a typed result.
- Docs pages: Typing, Equivalence Guide, Tool Recipes (HTTP fetch, file search, provider‑backed web search, DBC sequence).
- Examples: see
examples/basic_*.py,examples/tools_demo.py,examples/basic/streaming_outputs.py, andexamples/patterns/*for orchestration patterns expressed via commands/tools. - Keep
griffeon the current major version pinned inpyproject.tomluntilmkdocstringshandlessrc/alloy/__init__.pyicleanly duringmkdocs build; major bumps currently break the docs workflow.
- Version in
pyproject.toml; bump with changelog entries. - Trusted Publishing: tag and push (e.g.,
git tag v0.2.0 && git push origin v0.2.0) to publish to PyPI via CI.