Development Guide
This document covers Aleph's architecture and development workflow.
Overview
Aleph is an MCP server implementing the Recursive Language Model (RLM) paradigm for document analysis. Instead of stuffing context into prompts, Aleph stores documents in a sandboxed Python REPL and provides tools for iterative exploration.
Project Structure
aleph/
├── core.py # Main Aleph class, RLM loop, message handling
├── types.py # Dataclasses: Budget, AlephResponse, TrajectoryStep, etc.
├── config.py # AlephConfig, create_aleph() factory
├── cli.py # CLI entry points (aleph-rlm install/doctor)
├── mcp/
│ ├── local_server.py # MCP server (main entry point)
│ └── server.py # Compatibility entry point (aliases local_server)
├── repl/
│ ├── sandbox.py # REPLEnvironment - sandboxed code execution
│ └── helpers.py # 80+ helper functions (peek, search, extract_*, etc.)
├── sub_query/
│ ├── __init__.py # SubQueryConfig, detect_backend()
│ ├── cli_backend.py # Claude/Codex CLI spawning
│ └── api_backend.py # OpenAI-compatible API calls
├── providers/
│ ├── base.py # LLMProvider protocol
│ ├── anthropic.py # Anthropic provider
│ └── openai.py # OpenAI provider
└── prompts/
└── system.py # Default system prompt template
Development Setup
# Clone and install in development mode
git clone https://github.com/Hmbown/aleph.git
cd aleph
pip install -e '.[dev,mcp]'
# Run tests
python3 -m pytest -q
# Run MCP server locally (with action tools enabled)
aleph --enable-actions --tool-docs concise
Architecture
Core Loop (core.py)
The Aleph class implements the RLM execution loop:
- Context is stored in a sandboxed REPL namespace (
ctx) - LLM receives metadata about context (format, size, preview) — not the full content
- LLM writes Python code blocks to explore via helper functions
- Aleph executes code, feeds truncated output back
- Loop continues until LLM emits
FINAL(answer)orFINAL_VAR(variable_name)
MCP Server (mcp/local_server.py)
The primary entry point for IDE integration. Exposes tools:
- Context management:
load_context,peek_context,search_context - Code execution:
exec_python,get_variable - Sub-queries:
sub_query(RLM-style recursive calls) - Reasoning:
think,evaluate_progress,summarize_so_far - Output:
finalize,get_evidence,get_status - Actions:
run_command,read_file,write_file,run_tests
Sandbox (repl/sandbox.py)
The REPLEnvironment provides a sandboxed Python execution environment:
- AST validation: Blocks dunder access, forbidden builtins
- Import whitelist:
re,json,csv,math,statistics,collections,itertools,functools,datetime,textwrap,difflib,random,string,hashlib,base64,urllib.parse,html - Output truncation: Prevents token explosions
- Helper injection: 80+ functions for document analysis
The sandbox is best-effort, not hardened. For untrusted input, use container isolation.
Sub-Query System (sub_query/)
Enables RLM-style recursive reasoning:
# Backend detection priority (when backend="auto"):
# 1. ALEPH_SUB_QUERY_BACKEND env var (explicit override)
# 2. API (if ALEPH_SUB_QUERY_API_KEY or OPENAI_API_KEY set)
# 3. claude CLI (if installed)
# 4. codex CLI (if installed)
# 5. gemini CLI (if installed)
CLI backend: Spawns subprocess, passes prompt via stdin or temp file
API backend: OpenAI-compatible HTTP calls (any provider with /v1/chat/completions)
Budget System (types.py)
Budget dataclass controls resource limits:
@dataclass
class Budget:
max_tokens: int = 100_000
max_cost_usd: float = 1.0
max_iterations: int = 100
max_depth: int = 5
max_wall_time_seconds: float = 300.0
max_sub_queries: int = 50
BudgetStatus tracks consumption and is checked at each iteration.
Provider Protocol (providers/base.py)
Custom providers must implement:
class LLMProvider(Protocol):
def complete(self, messages, model, **kwargs) -> tuple[str, int, int, float]:
"""Returns (response_text, input_tokens, output_tokens, cost_usd)"""
def count_tokens(self, text: str, model: str) -> int: ...
def get_context_limit(self, model: str) -> int: ...
def get_output_limit(self, model: str) -> int: ...
Testing
# Run all tests
pytest
# Run with coverage
pytest --cov=aleph --cov-report=term-missing
# Run specific test file
pytest tests/test_sub_query.py
# Run tests matching pattern
pytest -k "test_search"
Code Style
- Python 3.10+ with type hints
- Formatted with
blackandisort - Linted with
ruff
# Format
black aleph tests
isort aleph tests
# Lint
ruff check aleph tests
Adding a New Tool
- Add the tool function in
mcp/local_server.pyinside_register_tools() - Decorate with
@self.server.tool() - Include comprehensive docstring (shown to AI users)
- Update
_Sessionif tool needs state tracking - Add tests in
tests/
Example:
@self.server.tool()
async def my_new_tool(
arg1: str,
arg2: int = 10,
context_id: str = "default",
) -> str:
"""One-line description.
Longer description of what this tool does.
Args:
arg1: Description
arg2: Description (default: 10)
context_id: Session identifier
Returns:
Description of return value
"""
session = self._sessions.get(context_id)
if not session:
return f"Error: No context loaded with ID '{context_id}'"
# Implementation
result = do_something(arg1, arg2)
return f"## Result\n\n{result}"
Adding a New Helper
- Add the function in
repl/helpers.py - Add to
HELPER_FUNCTIONSdict at bottom of file - Add tests in
tests/test_helpers.py
Example:
def my_helper(ctx: str, arg: int = 5) -> list[str]:
"""One-line description.
Args:
ctx: The context string
arg: Description (default: 5)
Returns:
List of results
"""
# Implementation using ctx
return results
# At bottom of file:
HELPER_FUNCTIONS = {
# ... existing helpers ...
"my_helper": my_helper,
}
Environment Variables
| Variable | Purpose |
|---|---|
ALEPH_SUB_QUERY_BACKEND |
Force sub-query backend: api, claude, codex, gemini |
ALEPH_SUB_QUERY_API_KEY |
API key (fallback: OPENAI_API_KEY) |
ALEPH_SUB_QUERY_URL |
API base URL (fallback: OPENAI_BASE_URL) |
ALEPH_SUB_QUERY_MODEL |
Model name (required for API backend) |
ALEPH_MAX_ITERATIONS |
Iteration limit |
ALEPH_MAX_COST |
Cost limit in USD |
Release Process
- Update version in
pyproject.toml - Sync versioned files:
python scripts/sync_versions.py - Update
CHANGELOG.md - Run full test suite:
pytest - Build:
python -m build - Upload to PyPI:
twine upload dist/* - Tag release:
git tag v0.x.0 && git push --tags
Related Documentation
- docs/prompts/aleph.md — workflow prompt + tool reference
- README.md — User documentation
- CHANGELOG.md — release notes