## 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 <noreply@anthropic.com>
3.4 KiB
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
# 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 workersLOCAL_EVENTS: Workers send events to master for indexingCOMMANDS: Workers/API send commands to masterELECTION_MESSAGES: Election protocol messagesCONNECTION_MESSAGES: libp2p connection updates
Event Sourcing
The system uses event sourcing for state management:
State(src/exo/shared/types/state.py): Immutable state objectapply()(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 typesevents.py: Event types (discriminated union)commands.py: Command typestasks.py: Task types for worker executionstate.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 Pythonsystem_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.NewTypefor primitives - Pydantic models with
frozen=Trueandstrict=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
@finaland 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.