Skip to content

Commit 3bdb7f7

Browse files
committed
add verbose logging
1 parent e2783d5 commit 3bdb7f7

9 files changed

Lines changed: 450 additions & 13 deletions

File tree

‎examples/rlm_example.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
environment_kwargs={},
1919
max_depth=1,
2020
logger=logger,
21+
verbose=True,
2122
)
2223

2324
result = rlm.completion("Using your code, solve 2^(2^(2^(2))). Show your work in Python.")

‎pyproject.toml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ dependencies = [
1212
"openai>=2.14.0",
1313
"portkey-ai>=2.1.0",
1414
"requests>=2.32.5",
15+
"rich>=13.0.0",
1516
]
1617

1718
[project.optional-dependencies]

‎rlm/clients/openai.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
import openai
66
from dotenv import load_dotenv
77

8-
from rlm.clients.base_lm import BaseLM, ModelUsageSummary, UsageSummary
8+
from rlm.clients.base_lm import BaseLM
9+
from rlm.core.types import ModelUsageSummary, UsageSummary
910

1011
load_dotenv()
1112

‎rlm/clients/portkey.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
from portkey_ai import Portkey
55
from portkey_ai.api_resources.types.chat_complete_type import ChatCompletions
66

7-
from rlm.clients.base_lm import BaseLM, ModelUsageSummary, UsageSummary
7+
from rlm.clients.base_lm import BaseLM
8+
from rlm.core.types import ModelUsageSummary, UsageSummary
89

910

1011
class PortkeyClient(BaseLM):

‎rlm/core/rlm.py‎

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
RLMMetadata,
1515
)
1616
from rlm.environments import BaseEnv, get_environment
17-
from rlm.logger.rlm_logger import RLMLogger
17+
from rlm.logger import RLMLogger, VerbosePrinter
1818
from rlm.utils.parsing import (
1919
find_code_blocks,
2020
find_final_answer,
@@ -50,6 +50,7 @@ def __init__(
5050
other_backends: list[ClientBackend] | None = None,
5151
other_backend_kwargs: list[dict[str, Any]] | None = None,
5252
logger: RLMLogger | None = None,
53+
verbose: bool = False,
5354
):
5455
"""
5556
Args:
@@ -58,12 +59,13 @@ def __init__(
5859
environment: The environment to use for the RLM.
5960
environment_kwargs: The kwargs to pass to the environment.
6061
depth: The current depth of the RLM (0-indexed).
61-
max_depth: The maximum depth of the RLM.
62+
max_depth: The maximum depth of the RLM. Currently, only depth 1 is supported.
6263
max_iterations: The maximum number of iterations of the RLM.
6364
custom_system_prompt: The custom system prompt to use for the RLM.
6465
other_backends: A list of other client backends that the environments can use to make sub-calls.
6566
other_backend_kwargs: The kwargs to pass to the other client backends (ordered to match other_backends).
6667
logger: The logger to use for the RLM.
68+
verbose: Whether to print verbose output in rich to console.
6769
"""
6870
# Store config for spawning per-completion
6971
self.backend = backend
@@ -78,9 +80,10 @@ def __init__(
7880
self.max_iterations = max_iterations
7981
self.system_prompt = custom_system_prompt if custom_system_prompt else RLM_SYSTEM_PROMPT
8082
self.logger = logger
83+
self.verbose = VerbosePrinter(enabled=verbose)
8184

8285
# Log metadata if logger is provided
83-
if self.logger:
86+
if self.logger or verbose:
8487
metadata = RLMMetadata(
8588
root_model=backend_kwargs.get("model_name", "unknown"),
8689
max_depth=max_depth,
@@ -91,7 +94,9 @@ def __init__(
9194
environment_kwargs=filter_sensitive_keys(environment_kwargs),
9295
other_backends=other_backends,
9396
)
94-
self.logger.log_metadata(metadata)
97+
if self.logger:
98+
self.logger.log_metadata(metadata)
99+
self.verbose.print_metadata(metadata)
95100

96101
@contextmanager
97102
def _spawn_completion_context(self, prompt: str | dict[str, Any]):
@@ -156,6 +161,11 @@ def completion(
156161
A final answer as a string.
157162
"""
158163
time_start = time.perf_counter()
164+
165+
# If we're at max depth, the RLM is an LM, so we fallback to the regular LM.
166+
if self.depth >= self.max_depth:
167+
return self._fallback_answer(prompt)
168+
159169
with self._spawn_completion_context(prompt) as (lm_handler, environment):
160170
message_history = self._setup_prompt(prompt)
161171

@@ -177,13 +187,19 @@ def completion(
177187
if self.logger:
178188
self.logger.log(iteration)
179189

190+
# Verbose output for this iteration
191+
self.verbose.print_iteration(iteration, i + 1)
192+
180193
if final_answer:
181194
time_end = time.perf_counter()
195+
usage = lm_handler.get_usage_summary()
196+
self.verbose.print_final_answer(final_answer)
197+
self.verbose.print_summary(i + 1, time_end - time_start, usage.to_dict())
182198
return RLMChatCompletion(
183199
root_model=self.backend_kwargs.get("model_name", "unknown"),
184200
prompt=prompt,
185201
response=final_answer,
186-
usage_summary=lm_handler.get_usage_summary(),
202+
usage_summary=usage,
187203
execution_time=time_end - time_start,
188204
)
189205

@@ -194,8 +210,18 @@ def completion(
194210
message_history.extend(new_messages)
195211

196212
# Default behavior: we run out of iterations, provide one final answer
213+
time_end = time.perf_counter()
197214
final_answer = self._default_answer(message_history, lm_handler)
198-
return final_answer
215+
usage = lm_handler.get_usage_summary()
216+
self.verbose.print_final_answer(final_answer)
217+
self.verbose.print_summary(self.max_iterations, time_end - time_start, usage.to_dict())
218+
return RLMChatCompletion(
219+
root_model=self.backend_kwargs.get("model_name", "unknown"),
220+
prompt=prompt,
221+
response=final_answer,
222+
usage_summary=usage,
223+
execution_time=time_end - time_start,
224+
)
199225

200226
def _completion_turn(
201227
self,
@@ -248,3 +274,11 @@ def _default_answer(self, message_history: list[dict[str, Any]], lm_handler: LMH
248274
)
249275

250276
return response
277+
278+
def _fallback_answer(self, message: str | dict[str, Any]) -> str:
279+
"""
280+
Fallback behavior if the RLM is actually at max depth, and should be treated as an LM.
281+
"""
282+
client: BaseLM = get_client(self.backend, self.backend_kwargs)
283+
response = client.completion(message)
284+
return response

‎rlm/environments/__init__.py‎

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,24 @@
55

66

77
def get_environment(
8-
environment: Literal["local", "modal"],
8+
environment: Literal["local", "modal", "docker"],
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']
13+
Currently supported environments: ['local', 'modal', 'docker']
1414
"""
1515
if environment == "local":
1616
return LocalREPL(**environment_kwargs)
1717
elif environment == "modal":
18-
# Lazy import to avoid requiring modal as a hard dependency
1918
from rlm.environments.modal_repl import ModalREPL
2019

2120
return ModalREPL(**environment_kwargs)
21+
elif environment == "docker":
22+
from rlm.environments.docker_repl import DockerREPL
23+
24+
return DockerREPL(**environment_kwargs)
2225
else:
2326
raise ValueError(
24-
f"Unknown environment: {environment}. Supported environments: ['local', 'modal']"
27+
f"Unknown environment: {environment}. Supported: ['local', 'modal', 'docker']"
2528
)

‎rlm/logger/__init__.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
from rlm.logger.rlm_logger import RLMLogger
2+
from rlm.logger.verbose import VerbosePrinter
23

3-
__all__ = ["RLMLogger"]
4+
__all__ = ["RLMLogger", "VerbosePrinter"]

0 commit comments

Comments
 (0)