From e388f594807d8454656743c0ea640f075953197b Mon Sep 17 00:00:00 2001 From: Alex Cheema <41707476+AlexCheema@users.noreply.github.com> Date: Tue, 13 Jan 2026 13:05:47 +0000 Subject: [PATCH] docs: add AGENTS.md for AI coding agents guidance (#1132) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Motivation Add documentation to help AI coding agents (Claude Code, Cursor, GitHub Copilot, etc.) understand the exo codebase and contribute effectively. ## Changes - Add `AGENTS.md` with guidance for AI agents working on the codebase - Add symlink `CLAUDE.md -> AGENTS.md` for backwards compatibility with Claude Code ## Why It Works `AGENTS.md` is becoming a standard convention for AI agent instructions. The symlink ensures Claude Code (which looks for `CLAUDE.md`) continues to work while supporting the broader `AGENTS.md` convention. ## Test Plan ### Manual Testing - Verified symlink works correctly ### Automated Testing - N/A (documentation only) 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.5 --- AGENTS.md | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ CLAUDE.md | 1 + 2 files changed, 97 insertions(+) create mode 100644 AGENTS.md create mode 120000 CLAUDE.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..ec3a3b19 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,96 @@ +# AGENTS.md + +This file provides guidance to AI coding agents when working with code in this repository. + +## Project Overview + +exo is a distributed AI inference system that connects multiple devices into a cluster. It enables running large language models across multiple machines using MLX as the inference backend and libp2p for peer-to-peer networking. + +## Build & Run Commands + +```bash +# Build the dashboard (required before running exo) +cd dashboard && npm install && npm run build && cd .. + +# Run exo (starts both master and worker with API at http://localhost:52415) +uv run exo + +# Run with verbose logging +uv run exo -v # or -vv for more verbose + +# Run tests (excludes slow tests by default) +uv run pytest + +# Run all tests including slow tests +uv run pytest -m "" + +# Run a specific test file +uv run pytest src/exo/shared/tests/test_election.py + +# Run a specific test function +uv run pytest src/exo/shared/tests/test_election.py::test_function_name + +# Type checking (strict mode) +uv run basedpyright + +# Linting +uv run ruff check + +# Format code (using nix) +nix fmt +``` + +## Architecture + +### Node Composition +A single exo `Node` (src/exo/main.py) runs multiple components: +- **Router**: libp2p-based pub/sub messaging via Rust bindings (exo_pyo3_bindings) +- **Worker**: Handles inference tasks, downloads models, manages runner processes +- **Master**: Coordinates cluster state, places model instances across nodes +- **Election**: Bully algorithm for master election +- **API**: FastAPI server for OpenAI-compatible chat completions + +### Message Flow +Components communicate via typed pub/sub topics (src/exo/routing/topics.py): +- `GLOBAL_EVENTS`: Master broadcasts indexed events to all workers +- `LOCAL_EVENTS`: Workers send events to master for indexing +- `COMMANDS`: Workers/API send commands to master +- `ELECTION_MESSAGES`: Election protocol messages +- `CONNECTION_MESSAGES`: libp2p connection updates + +### Event Sourcing +The system uses event sourcing for state management: +- `State` (src/exo/shared/types/state.py): Immutable state object +- `apply()` (src/exo/shared/apply.py): Pure function that applies events to state +- Master indexes events and broadcasts; workers apply indexed events + +### Key Type Hierarchy +- `src/exo/shared/types/`: Pydantic models for all shared types + - `events.py`: Event types (discriminated union) + - `commands.py`: Command types + - `tasks.py`: Task types for worker execution + - `state.py`: Cluster state model + +### Rust Components +Rust code in `rust/` provides: +- `networking`: libp2p networking (gossipsub, peer discovery) +- `exo_pyo3_bindings`: PyO3 bindings exposing Rust to Python +- `system_custodian`: System-level operations + +### Dashboard +Svelte 5 + TypeScript frontend in `dashboard/`. Build output goes to `dashboard/build/` and is served by the API. + +## Code Style Requirements + +From .cursorrules: +- Strict, exhaustive typing - never bypass the type-checker +- Use `Literal[...]` for enum-like sets, `typing.NewType` for primitives +- Pydantic models with `frozen=True` and `strict=True` +- Pure functions with injectable effect handlers for side-effects +- Descriptive names - no abbreviations or 3-letter acronyms +- Catch exceptions only where you can handle them meaningfully +- Use `@final` and immutability wherever applicable + +## Testing + +Tests use pytest-asyncio with `asyncio_mode = "auto"`. Tests are in `tests/` subdirectories alongside the code they test. The `EXO_TESTS=1` env var is set during tests. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 120000 index 00000000..47dc3e3d --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +AGENTS.md \ No newline at end of file