Skip to content

Commit a4cb5d9

Browse files
committed
feat: add Daytona REPL environment support
- Introduced a new Daytona REPL environment for executing Python code in Daytona sandboxes. - Updated `pyproject.toml` to include `daytona` as an optional dependency. - Added example usage of Daytona REPL in `examples/daytona_repl_example.py`. - Modified existing code to support the new `daytona` environment in `rlm/environments/__init__.py` and `rlm/core/types.py`. - Enhanced the `RLM` initialization in `examples/quickstart.py` to utilize the Daytona environment. This implementation allows users to run code in isolated Daytona sandboxes, facilitating LLM queries and state management.
1 parent db41d15 commit a4cb5d9

6 files changed

Lines changed: 731 additions & 7 deletions

File tree

‎examples/daytona_repl_example.py‎

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
"""
2+
Example usage of Daytona REPL with code execution and LLM queries.
3+
4+
Run with: python -m examples.daytona_repl_example
5+
"""
6+
7+
from rlm.clients.base_lm import BaseLM
8+
from rlm.core.lm_handler import LMHandler
9+
from rlm.core.types import ModelUsageSummary, UsageSummary
10+
from rlm.environments.daytona_repl import DaytonaREPL
11+
12+
13+
class MockLM(BaseLM):
14+
"""Simple mock LM that echoes prompts."""
15+
16+
def __init__(self):
17+
super().__init__(model_name="mock-model")
18+
19+
def completion(self, prompt):
20+
return f"Mock response to: {prompt[:50]}"
21+
22+
async def acompletion(self, prompt):
23+
return self.completion(prompt)
24+
25+
def get_usage_summary(self):
26+
return UsageSummary(
27+
model_usage_summaries={
28+
"mock-model": ModelUsageSummary(
29+
total_calls=1, total_input_tokens=10, total_output_tokens=10
30+
)
31+
}
32+
)
33+
34+
def get_last_usage(self):
35+
return self.get_usage_summary()
36+
37+
38+
def main():
39+
print("=" * 60)
40+
print("Daytona REPL Example")
41+
print("=" * 60)
42+
43+
# Note: Requires DAYTONA_API_KEY environment variable to be set
44+
# or passed explicitly to DaytonaREPL(api_key="...")
45+
46+
# Example 1: Basic code execution
47+
print("\n[1] Basic code execution (no LLM handler)")
48+
print("-" * 40)
49+
50+
try:
51+
with DaytonaREPL(name="rlm-example") as repl:
52+
result = repl.execute_code("x = 1 + 2")
53+
print("Executed: x = 1 + 2")
54+
print(f"Locals: {result.locals}")
55+
56+
result = repl.execute_code("print(x * 2)")
57+
print("Executed: print(x * 2)")
58+
print(f"Stdout: {result.stdout.strip()}")
59+
60+
result = repl.execute_code("answer = 42")
61+
result = repl.execute_code('print(FINAL_VAR("answer"))')
62+
print(f"FINAL_VAR('answer'): {result.stdout.strip()}")
63+
64+
# Example 2: With LLM handler
65+
print("\n[2] Code execution with LLM handler")
66+
print("-" * 40)
67+
68+
mock_client = MockLM()
69+
70+
with LMHandler(client=mock_client) as handler:
71+
print(f"LM Handler started at {handler.address}")
72+
73+
with DaytonaREPL(
74+
name="rlm-example-handler",
75+
lm_handler_address=handler.address,
76+
) as repl:
77+
# Single LLM query
78+
result = repl.execute_code('response = llm_query("What is 2+2?")')
79+
print("Executed: response = llm_query('What is 2+2?')")
80+
if result.stderr:
81+
print(f"Stderr: {result.stderr}")
82+
83+
result = repl.execute_code("print(response)")
84+
print(f"Response: {result.stdout.strip()}")
85+
86+
# Batched LLM query
87+
result = repl.execute_code(
88+
'responses = llm_query_batched(["Question 1", "Question 2", "Question 3"])'
89+
)
90+
print("\nExecuted: responses = llm_query_batched([...])")
91+
92+
result = repl.execute_code("print(f'Got {len(responses)} responses')")
93+
print(f"Result: {result.stdout.strip()}")
94+
95+
result = repl.execute_code("print(responses[0])")
96+
print(f"First response: {result.stdout.strip()}")
97+
98+
except Exception as e:
99+
print(f"Error: {e}")
100+
print("\nMake sure Daytona is configured correctly and DAYTONA_API_KEY is set.")
101+
102+
print("\n" + "=" * 60)
103+
print("Done!")
104+
print("=" * 60)
105+
106+
107+
if __name__ == "__main__":
108+
main()

‎examples/quickstart.py‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@
1212
rlm = RLM(
1313
backend="openai", # or "portkey", etc.
1414
backend_kwargs={
15-
"model_name": "gpt-5-nano",
15+
"model_name": "openai/gpt-4.1-mini",
1616
"api_key": os.getenv("OPENAI_API_KEY"),
1717
},
18-
environment="local",
19-
environment_kwargs={},
18+
environment="daytona",
19+
environment_kwargs={
20+
"name": "rlm-sandbox",
21+
},
2022
max_depth=1,
2123
logger=logger,
2224
verbose=True, # For printing to console with rich, disabled by default.

‎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+
daytona = ["daytona>=0.128.1", "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
@@ -5,7 +5,7 @@
55
ClientBackend = Literal[
66
"openai", "portkey", "openrouter", "vllm", "litellm", "anthropic", "azure_openai", "gemini"
77
]
8-
EnvironmentType = Literal["local", "prime", "modal"]
8+
EnvironmentType = Literal["local", "prime", "modal", "daytona"]
99

1010

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

‎rlm/environments/__init__.py‎

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

66

77
def get_environment(
8-
environment: Literal["local", "modal", "docker"],
8+
environment: Literal["local", "modal", "docker", "daytona"],
99
environment_kwargs: dict[str, Any],
1010
) -> BaseEnv:
1111
"""
1212
Routes a specific environment and the args (as a dict) to the appropriate environment if supported.
13-
Currently supported environments: ['local', 'modal', 'docker']
13+
Currently supported environments: ['local', 'modal', 'docker', 'daytona']
1414
"""
1515
if environment == "local":
1616
return LocalREPL(**environment_kwargs)
@@ -22,7 +22,11 @@ def get_environment(
2222
from rlm.environments.docker_repl import DockerREPL
2323

2424
return DockerREPL(**environment_kwargs)
25+
elif environment == "daytona":
26+
from rlm.environments.daytona_repl import DaytonaREPL
27+
28+
return DaytonaREPL(**environment_kwargs)
2529
else:
2630
raise ValueError(
27-
f"Unknown environment: {environment}. Supported: ['local', 'modal', 'docker']"
31+
f"Unknown environment: {environment}. Supported: ['local', 'modal', 'docker', 'daytona']"
2832
)

0 commit comments

Comments
 (0)