Skip to content

Commit 5ff63bd

Browse files
committed
Enhance RLM Playground with new features and UI improvements
- Added `custom_system_prompt` and `verbose` fields to `RunRequest` model for enhanced configuration. - Implemented verbose output capturing in the `run_rlm` function, allowing detailed console output to be returned in `RunResponse`. - Introduced `KeyValueEditor` component for dynamic key-value pair management in the frontend. - Updated `PlaygroundForm` to support custom system prompts, environment kwargs, and other backends with their respective configurations. - Enhanced `PlaygroundResults` to display verbose output if available, improving user feedback on execution details.
1 parent 97793f0 commit 5ff63bd

6 files changed

Lines changed: 328 additions & 58 deletions

File tree

‎playground/models.py‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ class RunRequest(BaseModel):
3232
other_backend_kwargs: list[dict[str, Any]] | None = Field(
3333
None, description="Configuration for additional backends"
3434
)
35+
custom_system_prompt: str | None = Field(
36+
None, description="Custom system prompt to override the default"
37+
)
38+
verbose: bool = Field(False, description="Enable verbose console output")
3539
enable_logging: bool = Field(False, description="Whether to save logs to file")
3640

3741

@@ -43,4 +47,5 @@ class RunResponse(BaseModel):
4347
root_model: str | None = Field(None, description="Model name used")
4448
execution_time: float | None = Field(None, description="Total execution time in seconds")
4549
usage_summary: dict[str, Any] | None = Field(None, description="Token usage summary per model")
50+
verbose_output: str | None = Field(None, description="Captured verbose console output")
4651
error: str | None = Field(None, description="Error message if completion failed")

‎playground/server.py‎

Lines changed: 45 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
"""FastAPI server for RLM playground."""
22

3+
import io
34
import os
5+
import sys
46

57
from dotenv import load_dotenv
68
from fastapi import FastAPI
@@ -56,25 +58,47 @@ def run_rlm(request: RunRequest) -> RunResponse:
5658
log_dir = os.getenv("RLM_LOG_DIR", "./logs")
5759
logger = RLMLogger(log_dir=log_dir)
5860

59-
# Create RLM instance
60-
rlm = RLM(
61-
backend=request.backend,
62-
backend_kwargs=request.backend_kwargs or {},
63-
environment=request.environment,
64-
environment_kwargs=request.environment_kwargs or {},
65-
max_depth=request.max_depth,
66-
max_iterations=request.max_iterations,
67-
other_backends=request.other_backends,
68-
other_backend_kwargs=request.other_backend_kwargs,
69-
logger=logger,
70-
verbose=False, # Disable console output in API
71-
)
72-
73-
# Run completion
74-
result = rlm.completion(
75-
prompt=request.prompt,
76-
root_prompt=request.root_prompt,
77-
)
61+
# Capture verbose output if enabled
62+
verbose_output = None
63+
if request.verbose:
64+
stdout_capture = io.StringIO()
65+
stderr_capture = io.StringIO()
66+
old_stdout = sys.stdout
67+
old_stderr = sys.stderr
68+
69+
try:
70+
if request.verbose:
71+
sys.stdout = stdout_capture
72+
sys.stderr = stderr_capture
73+
74+
# Create RLM instance
75+
rlm = RLM(
76+
backend=request.backend,
77+
backend_kwargs=request.backend_kwargs or {},
78+
environment=request.environment,
79+
environment_kwargs=request.environment_kwargs or {},
80+
max_depth=request.max_depth,
81+
max_iterations=request.max_iterations,
82+
other_backends=request.other_backends,
83+
other_backend_kwargs=request.other_backend_kwargs,
84+
custom_system_prompt=request.custom_system_prompt,
85+
logger=logger,
86+
verbose=request.verbose,
87+
)
88+
89+
# Run completion
90+
result = rlm.completion(
91+
prompt=request.prompt,
92+
root_prompt=request.root_prompt,
93+
)
94+
finally:
95+
# Restore stdout/stderr and capture output
96+
if request.verbose:
97+
sys.stdout = old_stdout
98+
sys.stderr = old_stderr
99+
stdout_text = stdout_capture.getvalue()
100+
stderr_text = stderr_capture.getvalue()
101+
verbose_output = stdout_text + stderr_text if stderr_text else stdout_text
78102

79103
# Extract response - handle both string and tuple (type, content) formats
80104
response_text = result.response
@@ -104,6 +128,7 @@ def run_rlm(request: RunRequest) -> RunResponse:
104128
root_model=result.root_model,
105129
execution_time=result.execution_time,
106130
usage_summary=result.usage_summary.to_dict(),
131+
verbose_output=verbose_output,
107132
error=None,
108133
)
109134

@@ -115,5 +140,6 @@ def run_rlm(request: RunRequest) -> RunResponse:
115140
root_model=None,
116141
execution_time=None,
117142
usage_summary=None,
143+
verbose_output=None,
118144
error=str(e),
119145
)

‎rlm/clients/openai.py‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,9 @@ async def acompletion(
8282

8383
def _track_cost(self, response: openai.ChatCompletion, model: str):
8484
self.model_call_counts[model] += 1
85+
if not hasattr(response, "usage") or response.usage is None:
86+
print("WARNING: No usage data received!")
87+
return
8588
self.model_input_tokens[model] += response.usage.prompt_tokens
8689
self.model_output_tokens[model] += response.usage.completion_tokens
8790
self.model_total_tokens[model] += response.usage.total_tokens
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
'use client';
2+
3+
import { Button } from '@/components/ui/button';
4+
5+
interface KeyValuePair {
6+
key: string;
7+
value: string;
8+
}
9+
10+
interface KeyValueEditorProps {
11+
pairs: KeyValuePair[];
12+
onChange: (pairs: KeyValuePair[]) => void;
13+
disabled?: boolean;
14+
keyPlaceholder?: string;
15+
valuePlaceholder?: string;
16+
}
17+
18+
export function KeyValueEditor({
19+
pairs,
20+
onChange,
21+
disabled = false,
22+
keyPlaceholder = 'key',
23+
valuePlaceholder = 'value',
24+
}: KeyValueEditorProps) {
25+
const updatePair = (index: number, field: 'key' | 'value', newValue: string) => {
26+
const newPairs = [...pairs];
27+
newPairs[index] = { ...newPairs[index], [field]: newValue };
28+
onChange(newPairs);
29+
};
30+
31+
const addPair = () => {
32+
onChange([...pairs, { key: '', value: '' }]);
33+
};
34+
35+
const removePair = (index: number) => {
36+
onChange(pairs.filter((_, i) => i !== index));
37+
};
38+
39+
return (
40+
<div className="space-y-2">
41+
{pairs.map((pair, index) => (
42+
<div key={index} className="flex gap-2 items-center">
43+
<input
44+
type="text"
45+
value={pair.key}
46+
onChange={(e) => updatePair(index, 'key', e.target.value)}
47+
placeholder={keyPlaceholder}
48+
className="flex-1 px-3 py-2 bg-background border border-input rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-ring"
49+
disabled={disabled}
50+
/>
51+
<input
52+
type="text"
53+
value={pair.value}
54+
onChange={(e) => updatePair(index, 'value', e.target.value)}
55+
placeholder={valuePlaceholder}
56+
className="flex-1 px-3 py-2 bg-background border border-input rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-ring"
57+
disabled={disabled}
58+
/>
59+
<Button
60+
type="button"
61+
variant="outline"
62+
size="sm"
63+
onClick={() => removePair(index)}
64+
disabled={disabled}
65+
className="shrink-0"
66+
>
67+
Remove
68+
</Button>
69+
</div>
70+
))}
71+
<Button
72+
type="button"
73+
variant="outline"
74+
size="sm"
75+
onClick={addPair}
76+
disabled={disabled}
77+
className="w-full"
78+
>
79+
+ Add Key-Value Pair
80+
</Button>
81+
</div>
82+
);
83+
}
84+

0 commit comments

Comments
 (0)