Skip to content

Commit 625a69e

Browse files
feat: support prime sandboxes (#4)
* support prime sandboxes * fix bug in sandbox start to handle timeouts * change log msg * increase timeout * increase sandbox creation timeout * update prime repl * update readme * update readme * increase timeout for exec_script * refactor packages into constants * refactor constants out, lint * skip prime_sandboxes import on test * update description / warning on prime sandboxes --------- Co-authored-by: az <alex.lx.zhang@gmail.com>
1 parent cb9cab7 commit 625a69e

12 files changed

Lines changed: 1979 additions & 1251 deletions

File tree

‎AGENTS.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ uv pip install -e .
1818

1919
# For Modal sandbox support
2020
uv pip install -e ".[modal]"
21+
22+
# For Prime sandbox support
23+
uv pip install -e ".[prime]"
2124
```
2225

2326
## General Guidelines

‎README.md‎

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -98,14 +98,13 @@ modal setup # authenticate account
9898
```
9999

100100
#### Prime Intellect Sandboxes <img src="https://github.com/PrimeIntellect-ai.png" alt="Prime Intellect" height="20" style="vertical-align: middle;"/>
101-
> [!WARNING]
102-
> **Prime Intellect Sandboxes** are currently a beta feature. See the [documentation](https://docs.primeintellect.ai/sandboxes/overview) for more information.
101+
> [!NOTE]
102+
> **Prime Intellect Sandboxes** are currently a beta feature. See the [documentation](https://docs.primeintellect.ai/sandboxes/overview) for more information. We noticed slow runtimes when using these sandboxes, which is currently an open issue.
103103
104-
> [!IMPORTANT]
105-
> **Prime Intellect Sandboxes are not yet implemented** in `rlm`. This feature is not available at the moment until we patch some bugs.
106104

107-
See [Prime CLI setup instructions](https://docs.primeintellect.ai/inference/overview) for getting setup. You will need to set your CLI key.
105+
To use [Prime Sandboxes](https://docs.primeintellect.ai/sandboxes/sdk), install the SDK and set your API key:
108106
```bash
107+
uv pip install -e ".[prime]"
109108
export PRIME_API_KEY=...
110109
```
111110

‎examples/lm_in_prime_repl.py‎

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
"""
2+
Example: Using llm_query() from within a Prime Intellect Sandbox.
3+
4+
This demonstrates the LM Handler + PrimeREPL integration where code
5+
running in a cloud sandbox can query the LLM via the HTTP broker pattern.
6+
"""
7+
8+
import os
9+
10+
from dotenv import load_dotenv
11+
12+
from rlm.clients.portkey import PortkeyClient
13+
from rlm.core.lm_handler import LMHandler
14+
from rlm.environments.prime_repl import PrimeREPL
15+
16+
load_dotenv()
17+
18+
setup_code = """
19+
secret = "1424424"
20+
"""
21+
22+
context_payload = """
23+
This is a test context. It should print out, revealing the magic number to be 4.
24+
"""
25+
26+
code = """
27+
response = llm_query("What is 2 + 2? Reply with just the number.")
28+
print(response)
29+
print(type(response))
30+
print(context)
31+
print("Secret from setup code: ", secret)
32+
"""
33+
34+
35+
def main():
36+
api_key = os.environ.get("PORTKEY_API_KEY")
37+
if not api_key:
38+
print("Error: PORTKEY_API_KEY not set")
39+
return
40+
print(f"PORTKEY_API_KEY: {api_key[:8]}...")
41+
42+
client = PortkeyClient(api_key=api_key, model_name="@openai/gpt-5-nano")
43+
print("Created Portkey client with model: @openai/gpt-5-nano")
44+
45+
# Start LM Handler
46+
with LMHandler(client=client) as handler:
47+
print(f"LM Handler started at {handler.address}")
48+
49+
# Create Prime REPL with handler connection
50+
print("\nCreating Prime sandbox...")
51+
with PrimeREPL(
52+
name="rlm-lm-demo",
53+
docker_image="python:3.11-slim",
54+
timeout_minutes=30,
55+
lm_handler_address=handler.address,
56+
context_payload=context_payload,
57+
setup_code=setup_code,
58+
) as repl:
59+
print(f"PrimeREPL created, sandbox ID: {repl.sandbox_id}")
60+
print(f"Broker URL: {repl.broker_url}\n")
61+
62+
# Run code that uses llm_query
63+
print(f"Executing: {code}")
64+
65+
result = repl.execute_code(code)
66+
67+
print(f"stdout: {result.stdout!r}")
68+
print(f"stderr: {result.stderr!r}")
69+
print(f"response variable: {result.locals.get('response')!r}")
70+
print(f"locals: {result.locals!r}")
71+
print(f"execution time: {result.execution_time:.3f}s")
72+
print(f"rlm_calls made: {len(result.rlm_calls)}")
73+
74+
75+
if __name__ == "__main__":
76+
main()

‎examples/prime_repl_example.py‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import os
2+
3+
from dotenv import load_dotenv
4+
5+
from rlm import RLM
6+
from rlm.logger import RLMLogger
7+
8+
load_dotenv()
9+
10+
logger = RLMLogger(log_dir="./logs")
11+
12+
rlm = RLM(
13+
backend="openai",
14+
backend_kwargs={
15+
"api_key": os.getenv("OPENAI_API_KEY"),
16+
"model_name": "gpt-5-nano",
17+
},
18+
environment="prime",
19+
environment_kwargs={
20+
"name": "rlm-prime-demo",
21+
"docker_image": "python:3.11-slim",
22+
"timeout_minutes": 30,
23+
},
24+
max_depth=1,
25+
logger=logger,
26+
verbose=True,
27+
)
28+
29+
result = rlm.completion("Using your code, solve 2^(2^(2^(2))). Show your work in Python.")
30+
print(result.response)

‎pyproject.toml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ dependencies = [
2020

2121
[project.optional-dependencies]
2222
modal = ["modal>=0.73.0", "dill>=0.3.7"]
23+
prime = ["prime-sandboxes>=0.2.0", "dill>=0.3.7"]
2324

2425
[build-system]
2526
requires = ["setuptools>=61.0"]

‎rlm/core/types.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"azure_openai",
1414
"gemini",
1515
]
16-
EnvironmentType = Literal["local", "prime", "modal"]
16+
EnvironmentType = Literal["local", "docker", "modal", "prime"]
1717

1818

1919
def _serialize_value(value: Any) -> Any:

‎rlm/environments/__init__.py‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@
77

88

99
def get_environment(
10-
environment: Literal["local", "modal", "docker"],
10+
environment: Literal["local", "modal", "docker", "prime"],
1111
environment_kwargs: dict[str, Any],
1212
) -> BaseEnv:
1313
"""
1414
Routes a specific environment and the args (as a dict) to the appropriate environment if supported.
15-
Currently supported environments: ['local', 'modal', 'docker']
15+
Currently supported environments: ['local', 'modal', 'docker', 'prime']
1616
"""
1717
if environment == "local":
1818
return LocalREPL(**environment_kwargs)
@@ -24,7 +24,11 @@ def get_environment(
2424
from rlm.environments.docker_repl import DockerREPL
2525

2626
return DockerREPL(**environment_kwargs)
27+
elif environment == "prime":
28+
from rlm.environments.prime_repl import PrimeREPL
29+
30+
return PrimeREPL(**environment_kwargs)
2731
else:
2832
raise ValueError(
29-
f"Unknown environment: {environment}. Supported: ['local', 'modal', 'docker']"
33+
f"Unknown environment: {environment}. Supported: ['local', 'modal', 'docker', 'prime']"
3034
)

‎rlm/environments/constants.py‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Default packages for isolated REPL environments (Modal, Prime, etc.)
2+
3+
APT_PACKAGES = [
4+
"build-essential",
5+
"git",
6+
"curl",
7+
"wget",
8+
"libopenblas-dev",
9+
"liblapack-dev",
10+
]
11+
12+
PIP_PACKAGES = [
13+
# Data science essentials
14+
"numpy>=1.26.0",
15+
"pandas>=2.1.0",
16+
"scipy>=1.11.0",
17+
# Math & symbolic computation
18+
"sympy>=1.12",
19+
# HTTP & APIs
20+
"requests>=2.31.0",
21+
"httpx>=0.25.0",
22+
"flask>=3.0.0",
23+
# Data formats
24+
"pyyaml>=6.0",
25+
"toml>=0.10.2",
26+
# Utilities
27+
"tqdm>=4.66.0",
28+
"python-dateutil>=2.8.2",
29+
"regex>=2023.0.0",
30+
# For state serialization
31+
"dill>=0.3.7",
32+
]

‎rlm/environments/modal_repl.py‎

Lines changed: 3 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from rlm.core.comms_utils import LMRequest, send_lm_request, send_lm_request_batched
1111
from rlm.core.types import REPLResult, RLMChatCompletion
1212
from rlm.environments.base_env import IsolatedEnv
13+
from rlm.environments.constants import APT_PACKAGES, PIP_PACKAGES
1314

1415
# =============================================================================
1516
# Default Modal Image
@@ -23,35 +24,8 @@ def get_default_image() -> modal.Image:
2324
"""
2425
return (
2526
modal.Image.debian_slim(python_version="3.11")
26-
.apt_install(
27-
"build-essential",
28-
"git",
29-
"curl",
30-
"wget",
31-
"libopenblas-dev",
32-
"liblapack-dev",
33-
)
34-
.pip_install(
35-
# Data science essentials
36-
"numpy>=1.26.0",
37-
"pandas>=2.1.0",
38-
"scipy>=1.11.0",
39-
# Math & symbolic computation
40-
"sympy>=1.12",
41-
# HTTP & APIs
42-
"requests>=2.31.0",
43-
"httpx>=0.25.0",
44-
"flask>=3.0.0",
45-
# Data formats
46-
"pyyaml>=6.0",
47-
"toml>=0.10.2",
48-
# Utilities
49-
"tqdm>=4.66.0",
50-
"python-dateutil>=2.8.2",
51-
"regex>=2023.0.0",
52-
# For state serialization
53-
"dill>=0.3.7",
54-
)
27+
.apt_install(*APT_PACKAGES)
28+
.pip_install(*PIP_PACKAGES)
5529
)
5630

5731

0 commit comments

Comments
 (0)