Skip to content

Commit ecfbdff

Browse files
committed
Add Docker local env
1 parent 182f830 commit ecfbdff

3 files changed

Lines changed: 396 additions & 6 deletions

File tree

‎README.md‎

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11

22
---
33

4-
<h1 align="center">
5-
Recursive Language Models
4+
<h1 align="center" style="font-size:2.8em">
5+
<span>Recursive Language Models (<span style="color:orange">RLM</span>s)</span>
66
</h1>
77

8-
<p align="center">
8+
<p align="center" style="font-size:1.3em">
99
<a href="https://arxiv.org/abs/2512.24601">Full Paper</a> •
1010
<a href="https://alexzhang13.github.io/blog/2025/rlm/">Blogpost</a>
1111
</p>
1212

13+
<p align="center">
14+
<a href="https://arxiv.org/abs/2512.24601">
15+
<img src="media/paper_preview.png" alt="Paper Preview" width="300"/>
16+
</a>
17+
</p>
1318

1419
## Overview
1520
Recursive Language Models (RLMs) are a task-agnostic inference paradigm for language models (LMs) to handle near-infinite length contexts by enabling the LM to *programmatically* examine, decompose, and recursively call itself over its input. RLMs replace the canonical `llm.completion(prompt, model)` call with a `rlm.completion(prompt, model)` call. RLMs offload the context as a variable in a REPL environment that the LM can interact with and launch sub-LM calls inside of.
@@ -52,13 +57,15 @@ We support two types of REPL environments -- isolated, and non-isolated. Non-iso
5257

5358
```python
5459
rlm = RLM(
55-
environment="...", # "local", "modal", "prime"
60+
environment="...", # "local", "docker", "modal", "prime"
5661
environment_kwargs={...},
5762
)
5863
```
5964

6065
### Local Environments
61-
The default `local` environment runs in the same process as the RLM itself, with specified global and local namespaces for minimal security. Using this REPL is generally safe, but should not be used for production settings. It also shares the same virtual environment (e.g. Conda or uv) as the host process.
66+
The default `local` environment `LocalREPL` runs in the same process as the RLM itself, with specified global and local namespaces for minimal security. Using this REPL is generally safe, but should not be used for production settings. It also shares the same virtual environment (e.g. Conda or uv) as the host process.
67+
68+
**Docker** <img src="https://github.com/docker.png" alt="Docker" height="20" style="vertical-align: middle;"/> (*requires [Docker installed](https://docs.docker.com/desktop/setup/install/)*). We also support a Docker-based environment called `DockerREPL` that launches the REPL environment as a Docker image. By default, we use the `python:3.11-slim` image, but the user can specify custom images as well.
6269

6370
### Isolated Environments
6471
We support several different REPL environments that run on separate, cloud-based machines. Whenever a recursive sub-call is made in these instances, it is requested from the host process.
@@ -113,5 +120,5 @@ npm run dev # default localhost:3001
113120

114121
You'll have the option to select saved `.jsonl` files
115122
<p align="center">
116-
<img src="media/visualizer.png" alt="RLM Visualizer Example" width="600"/>
123+
<img src="media/visualizer.png" alt="RLM Visualizer Example" width="800"/>
117124
</p>

‎examples/docker_repl_example.py‎

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
"""
2+
Docker REPL example with code execution and LLM queries.
3+
4+
Setup:
5+
1. Ensure Docker is running
6+
2. Run: python -m examples.docker_repl_example
7+
8+
The default image (python:3.11-slim) will be pulled automatically.
9+
"""
10+
11+
from rlm.clients.base_lm import BaseLM
12+
from rlm.core.lm_handler import LMHandler
13+
from rlm.core.types import ModelUsageSummary, UsageSummary
14+
from rlm.environments.docker_repl import DockerREPL
15+
16+
17+
class MockLM(BaseLM):
18+
def __init__(self):
19+
super().__init__(model_name="mock")
20+
21+
def completion(self, prompt):
22+
return f"Mock: {str(prompt)[:50]}"
23+
24+
async def acompletion(self, prompt):
25+
return self.completion(prompt)
26+
27+
def get_usage_summary(self):
28+
return UsageSummary({"mock": ModelUsageSummary(1, 10, 10)})
29+
30+
def get_last_usage(self):
31+
return self.get_usage_summary()
32+
33+
34+
def main():
35+
print("=" * 50)
36+
print("Docker REPL Example")
37+
print("=" * 50)
38+
39+
# Basic execution (no LLM)
40+
print("\n[1] Basic code execution")
41+
with DockerREPL() as repl:
42+
result = repl.execute_code("x = 1 + 2")
43+
print(f" x = 1 + 2 → locals: {result.locals}")
44+
45+
result = repl.execute_code("print(x * 2)")
46+
print(f" print(x * 2) → {result.stdout.strip()}")
47+
48+
# With LLM handler
49+
print("\n[2] With LLM handler")
50+
with LMHandler(client=MockLM()) as handler:
51+
print(f" Handler at {handler.address}")
52+
53+
with DockerREPL(lm_handler_address=handler.address) as repl:
54+
result = repl.execute_code('r = llm_query("Hello!")')
55+
print(f" llm_query → stderr: {result.stderr or '(none)'}")
56+
57+
result = repl.execute_code("print(r)")
58+
print(f" Response: {result.stdout.strip()}")
59+
60+
result = repl.execute_code('rs = llm_query_batched(["Q1", "Q2"])')
61+
result = repl.execute_code("print(len(rs))")
62+
print(f" Batched count: {result.stdout.strip()}")
63+
64+
print("\n" + "=" * 50)
65+
print("Done!")
66+
67+
68+
if __name__ == "__main__":
69+
main()

0 commit comments

Comments
 (0)