Skip to content

Commit c3243ea

Browse files
committed
Merge upstream/main: add Daytona support and bug fixes
Merged 4 commits from upstream (alexzhang13/rlm): - fix: ensure docker env uses shared workspace dir (alexzhang13#48) - fix: resolve type mismatch in usage summary propagation (alexzhang13#55) - feat: add Daytona sandbox support (alexzhang13#36) - fix: propagate depth and persistent parameters in DaytonaREPL (alexzhang13#71) Resolved conflicts in rlm/core/types.py and rlm/environments/__init__.py to integrate Daytona with our registry-based environment system. https://claude.ai/code/session_01VU6KvWSCkKinCMxQDXdNxy
2 parents 072d1b1 + b052ed1 commit c3243ea

11 files changed

Lines changed: 1081 additions & 16 deletions

File tree

‎examples/daytona_repl_example.py‎

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
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+
print(f"Daytona sandbox started with ID: {repl.sandbox.id}")
53+
result = repl.execute_code("x = 1 + 2")
54+
print("Executed: x = 1 + 2")
55+
print(f"Locals: {result.locals}")
56+
57+
result = repl.execute_code("print(x * 2)")
58+
print("Executed: print(x * 2)")
59+
print(f"Stdout: {result.stdout.strip()}")
60+
61+
result = repl.execute_code("answer = 42")
62+
result = repl.execute_code('print(FINAL_VAR("answer"))')
63+
print(f"FINAL_VAR('answer'): {result.stdout.strip()}")
64+
65+
# Example 2: With LLM handler
66+
print("\n[2] Code execution with LLM handler")
67+
print("-" * 40)
68+
69+
mock_client = MockLM()
70+
71+
with LMHandler(client=mock_client) as handler:
72+
print(f"LM Handler started at {handler.address}")
73+
74+
with DaytonaREPL(
75+
name="rlm-example-handler",
76+
lm_handler_address=handler.address,
77+
) as repl:
78+
print(f"Daytona sandbox started with ID: {repl.sandbox.id}")
79+
print(f"Broker URL: {repl.broker_url}")
80+
81+
# Single LLM query
82+
result = repl.execute_code('response = llm_query("What is 2+2?")')
83+
print("Executed: response = llm_query('What is 2+2?')")
84+
if result.stderr:
85+
print(f"Stderr: {result.stderr}")
86+
87+
result = repl.execute_code("print(response)")
88+
print(f"Response: {result.stdout.strip()}")
89+
90+
# Batched LLM query
91+
result = repl.execute_code(
92+
'responses = llm_query_batched(["Question 1", "Question 2", "Question 3"])'
93+
)
94+
print("\nExecuted: responses = llm_query_batched([...])")
95+
96+
result = repl.execute_code("print(f'Got {len(responses)} responses')")
97+
print(f"Result: {result.stdout.strip()}")
98+
99+
result = repl.execute_code("print(responses[0])")
100+
print(f"First response: {result.stdout.strip()}")
101+
102+
except Exception as e:
103+
print(f"Error: {e}")
104+
print("\nMake sure Daytona is configured correctly and DAYTONA_API_KEY is set.")
105+
106+
print("\n" + "=" * 60)
107+
print("Done!")
108+
print("=" * 60)
109+
110+
111+
if __name__ == "__main__":
112+
main()

‎examples/quickstart.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@
1313
backend_kwargs={
1414
"model_name": "gpt-5-nano",
1515
},
16-
environment="local",
16+
environment="docker",
1717
environment_kwargs={},
1818
max_depth=1,
1919
logger=logger,
2020
verbose=True, # For printing to console with rich, disabled by default.
2121
)
2222

23-
result = rlm.completion("Print me the first 100 powers of two, each on a newline.")
23+
result = rlm.completion("Print me the first 5 powers of two, each on a newline.")
2424

2525
print(result)

‎pyproject.toml‎

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

2222
[project.optional-dependencies]
2323
modal = ["modal>=0.73.0", "dill>=0.3.7"]
24+
daytona = ["daytona>=0.128.1", "dill>=0.3.7"]
2425
prime = ["prime-sandboxes>=0.2.0", "dill>=0.3.7"]
2526

2627
[build-system]

‎rlm/clients/base_lm.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from abc import ABC, abstractmethod
22
from typing import Any
33

4-
from rlm.core.types import UsageSummary
4+
from rlm.core.types import ModelUsageSummary, UsageSummary
55

66

77
class BaseLM(ABC):
@@ -28,7 +28,7 @@ def get_usage_summary(self) -> UsageSummary:
2828
raise NotImplementedError
2929

3030
@abstractmethod
31-
def get_last_usage(self) -> UsageSummary:
31+
def get_last_usage(self) -> ModelUsageSummary:
3232
"""Get the last cost summary of the model."""
3333
raise NotImplementedError
3434

‎rlm/core/lm_handler.py‎

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,12 @@ def _handle_single(self, request: LMRequest, handler: "LMHandler") -> LMResponse
5858
content = client.completion(request.prompt)
5959
end_time = time.perf_counter()
6060

61-
usage_summary = client.get_last_usage()
61+
model_usage = client.get_last_usage()
62+
root_model = request.model or client.model_name
63+
usage_summary = UsageSummary(model_usage_summaries={root_model: model_usage})
6264
return LMResponse.success_response(
6365
chat_completion=RLMChatCompletion(
64-
root_model=request.model or client.model_name,
66+
root_model=root_model,
6567
prompt=request.prompt,
6668
response=content,
6769
usage_summary=usage_summary,
@@ -83,11 +85,13 @@ async def run_all():
8385
end_time = time.perf_counter()
8486

8587
total_time = end_time - start_time
86-
usage_summary = client.get_last_usage()
88+
model_usage = client.get_last_usage()
89+
root_model = request.model or client.model_name
90+
usage_summary = UsageSummary(model_usage_summaries={root_model: model_usage})
8791

8892
chat_completions = [
8993
RLMChatCompletion(
90-
root_model=request.model or client.model_name,
94+
root_model=root_model,
9195
prompt=prompt,
9296
response=content,
9397
usage_summary=usage_summary,

‎rlm/core/types.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"gemini",
1717
"huggingface",
1818
]
19-
EnvironmentType = Literal["local", "subprocess", "docker", "modal", "prime"]
19+
EnvironmentType = Literal["local", "subprocess", "docker", "modal", "prime", "daytona"]
2020

2121

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

‎rlm/environments/__init__.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,14 @@
2323

2424

2525
def get_environment(
26-
environment: Literal["local", "subprocess", "modal", "docker", "prime"],
26+
environment: Literal["local", "subprocess", "modal", "docker", "prime", "daytona"],
2727
environment_kwargs: dict[str, Any],
2828
) -> BaseEnv:
2929
"""
3030
Routes a specific environment and the args (as a dict) to the appropriate environment if supported.
3131
3232
Args:
33-
environment: The environment type (e.g., "local", "docker", "modal", "prime")
33+
environment: The environment type (e.g., "local", "docker", "modal", "prime", "daytona")
3434
environment_kwargs: Keyword arguments to pass to the environment constructor
3535
3636
Returns:

0 commit comments

Comments
 (0)