Repository: RLM (Recursive Language Models) Review Date: January 2026 Reviewed by: Claude
RLM is a well-architected inference engine that enables LLMs to handle near-infinite context through recursive self-calling via a REPL environment. The codebase demonstrates strong design principles with a plugin architecture, comprehensive type safety, and clear separation of concerns. While the project shows excellent fundamentals, there are opportunities for improvement in async support, error handling patterns, and test coverage.
RLM replaces standard llm.completion() calls with a recursive paradigm where models can:
- Programmatically examine and decompose large contexts
- Execute Python code in sandboxed environments
- Make sub-LLM calls for complex reasoning
- Return structured answers via
FINAL()orFINAL_VAR()markers
User Code → RLM.completion() → Environment (REPL) → LM Handler → LLM Client
↑ ↓
└─── Iteration Loop ←┘
The repository excels at extensibility through abstract base classes:
Clients (rlm/clients/):
BaseLMabstract class defines the interface- Factory pattern via
get_client()for instantiation - Supports 9+ providers: OpenAI, Anthropic, Portkey, Gemini, Azure, LiteLLM, vLLM, OpenRouter, Vercel
Environments (rlm/environments/):
BaseEnv,IsolatedEnv,NonIsolatedEnvhierarchySupportsPersistenceProtocol for multi-turn support- Four implementations: LocalREPL, DockerREPL, ModalREPL, PrimeREPL
This design allows adding new LLM providers or execution environments with minimal changes to core logic.
# Comprehensive type hints throughout
def completion(
self, prompt: str | dict[str, Any], root_prompt: str | None = None
) -> RLMChatCompletion:- Heavy use of
@dataclassfor data structures Protocolclasses for structural subtyping (SupportsPersistence)- Type literals for enums (
ClientBackend,EnvironmentType) - Modern Python 3.11+ type syntax
# pyproject.toml
[tool.ruff.lint]
select = ["E", "W", "F", "I", "B", "UP"] # Excellent lint rule selection- ruff for linting and formatting (modern, fast)
- ty for type checking
- pre-commit hooks for consistency
- pytest with coverage reporting
- CI/CD via GitHub Actions
- Safe builtins whitelist in LocalREPL (blocks
eval,exec,input) - Isolated sandbox options (Docker, Modal, Prime)
- Sensitive key filtering in logs (
filter_sensitive_keys()) - Thread-safe operations with locks
- Comprehensive README with usage examples
- Well-documented Protocol classes with implementation guidance
- CONTRIBUTING.md with clear roadmap
- Example files for each environment type
- Docstrings on public methods
The CONTRIBUTING.md explicitly states the goal of keeping core/ minimal and readable. This is evident in:
rlm/core/rlm.py: ~400 lines, well-organized- Clear separation: core logic vs. clients vs. environments vs. utilities
The main completion loop is synchronous despite async methods existing on BaseLM:
# rlm/core/rlm.py:308
response = lm_handler.completion(prompt) # SynchronousImpact: Cannot efficiently handle concurrent RLM calls or leverage async LLM SDKs.
The get_client() function uses an if-elif chain:
# rlm/clients/__init__.py
if backend == "openai":
from rlm.clients.openai import OpenAIClient
return OpenAIClient(**backend_kwargs)
elif backend == "vllm":
# ... similar pattern repeatsThis could be replaced with a registry pattern for easier extension:
CLIENT_REGISTRY = {
"openai": "rlm.clients.openai:OpenAIClient",
"anthropic": "rlm.clients.anthropic:AnthropicClient",
# ...
}Some areas use exception raising, others return error strings:
# LocalREPL returns error strings
def FINAL_VAR(var_name):
if var_name not in locals_dict:
return f"Error: Variable '{var_name}' not found" # String errorvs.
# RLM raises exceptions
if len(other_backends) != 1:
raise ValueError("We currently only support one additional backend...")While tests exist, coverage could be improved:
- No tests for remote environments (Docker, Modal, Prime)
- Client implementations lack unit tests (only integration tests excluded in CI)
- Missing edge case tests for socket communication
- Type checking in CI uses
exit-zero(warnings only)
Some values are hardcoded without constants:
# rlm/core/lm_handler.py (inferred from types)
# 4-byte big-endian length prefix - magic numberCurrently restricted to one additional backend:
if len(other_backends) != 1:
raise ValueError(
"We currently only support one additional backend for the recursive sub-calls!"
)Only LocalREPL implements SupportsPersistence. Other environments (Docker, Modal, Prime) don't support multi-turn conversations.
Context Managers for Resource Management:
@contextmanager
def _spawn_completion_context(self, prompt: str | dict[str, Any]):
# Clean setup and teardown
try:
yield lm_handler, environment
finally:
lm_handler.stop()
if not self.persistent and hasattr(environment, "cleanup"):
environment.cleanup()Clear Method Naming:
_spawn_completion_context()- internal, spawns resourcescompletion()- public entry point_completion_turn()- single iteration helper
Lazy Imports (for optional dependencies):
if backend == "modal":
from rlm.clients.modal import ModalClient # Only imported when neededLong Methods: completion() at ~100 lines could be decomposed further.
Nested Type Checking:
if isinstance(environment, SupportsPersistence)This pattern appears multiple times; could use a helper method.
-
Add Async Completion API
async def acompletion(self, prompt: str | dict[str, Any]) -> RLMChatCompletion: """Async version of completion for concurrent workloads."""
-
Registry-Based Client/Environment Loading
- Replace if-elif chains with declarative registry
- Allow third-party plugins via entry points
-
Standardize Error Handling
- Define custom exception hierarchy (
RLMError,ClientError,EnvironmentError) - Use Result types or consistent exception patterns
- Define custom exception hierarchy (
-
Increase Test Coverage
- Add mock-based tests for remote environments
- Enable strict type checking in CI
- Add integration test suite that can run with real APIs (optional)
-
Multi-Model Routing
- Remove single
other_backendlimitation - Allow dynamic model selection based on task characteristics
- Remove single
-
Configuration Management
- Add Pydantic or dataclass-based configuration validation
- Support configuration from YAML/JSON files
-
Observability Improvements
- Add structured logging with levels
- OpenTelemetry integration for tracing
- Metrics collection (iteration counts, token usage, latency)
-
Persistence for Remote Environments
- Implement
SupportsPersistencefor DockerREPL - Consider session management for cloud environments
- Implement
-
Code Block Extraction
- Support for multiple languages beyond Python
- Better handling of nested code blocks
-
Rate Limiting / Retry Logic
- Built-in exponential backoff for LLM calls
- Configurable rate limits per client
-
Caching Layer
- LRU cache for repeated prompts
- Prefix caching support (noted in CONTRIBUTING.md as research)
┌─────────────────────────────────────────────────────────────────┐
│ User Application │
└─────────────────────────────────┬───────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ RLM Class │
│ ┌──────────────┐ ┌──────────────┐ ┌───────────────────────┐ │
│ │ completion() │ │ _setup_prompt│ │ _spawn_completion_ctx │ │
│ └──────┬───────┘ └──────────────┘ └───────────────────────┘ │
└─────────┼───────────────────────────────────────────────────────┘
│
▼
┌─────────────────────┐ ┌─────────────────────────────────┐
│ LMHandler │◄──────►│ Environment │
│ (TCP Socket Server)│ │ ┌───────────┐ ┌─────────────┐ │
│ - completion() │ │ │LocalREPL │ │DockerREPL │ │
│ - register_client()│ │ │ModalREPL │ │PrimeREPL │ │
│ - get_usage() │ │ └───────────┘ └─────────────┘ │
└─────────┬───────────┘ └────────────────────┬────────────┘
│ │
▼ │
┌─────────────────────────────────────────────┐ │
│ Client Layer │◄───┘
│ ┌─────────┐ ┌──────────┐ ┌───────────────┐ │ (llm_query())
│ │ OpenAI │ │Anthropic │ │ Others │ │
│ └─────────┘ └──────────┘ └───────────────┘ │
└─────────────────────────────────────────────┘
| Aspect | Score | Notes |
|---|---|---|
| Architecture | 8/10 | Excellent plugin design, clear separation |
| Code Quality | 8/10 | Clean, readable, good tooling |
| Type Safety | 9/10 | Comprehensive hints, protocols, dataclasses |
| Testing | 6/10 | Good unit tests, gaps in integration |
| Documentation | 7/10 | Solid README/docs, could use more API docs |
| Extensibility | 9/10 | Easy to add clients/environments |
| Security | 7/10 | Good sandboxing options, safe builtins |
| Performance | 6/10 | Synchronous only, no caching |
| Error Handling | 6/10 | Inconsistent patterns |
| Overall | 7.5/10 | Production-ready research codebase |
RLM is a well-designed research codebase that successfully balances simplicity with extensibility. The plugin architecture for clients and environments is particularly well-executed, making it easy to adapt to different LLM providers and execution contexts.
The main areas for improvement are:
- Async support for better concurrency
- Standardized error handling across the codebase
- Extended test coverage especially for remote environments
The explicit philosophy of keeping the core minimal (as stated in CONTRIBUTING.md) is commendable and has resulted in a codebase that is genuinely readable in a short sitting. This is a strong foundation for the ambitious research goals outlined in the project roadmap.