XanderJC commited on
Commit
dcaf5c9
·
2 Parent(s): 29d02e9 96d36ed

Merge branch 'main' of github.com:convergence-ai/proxy-lite

Browse files
Makefile CHANGED
@@ -6,3 +6,6 @@ proxy:
6
  uv sync
7
  uv pip install -e .
8
  playwright install
 
 
 
 
6
  uv sync
7
  uv pip install -e .
8
  playwright install
9
+
10
+ app:
11
+ streamlit run src/proxy_lite/app.py
README.md CHANGED
@@ -76,6 +76,11 @@ You can directly run Proxy Lite on a task with:
76
  proxy "Book a table for 2 at an Italian restaurant in Kings Cross tonight at 7pm."
77
  ```
78
 
 
 
 
 
 
79
 
80
  ### Proxy Lite Endpoint
81
 
 
76
  proxy "Book a table for 2 at an Italian restaurant in Kings Cross tonight at 7pm."
77
  ```
78
 
79
+ Alternatively you can run the local web ui with:
80
+
81
+ ```bash
82
+ make app
83
+ ```
84
 
85
  ### Proxy Lite Endpoint
86
 
pyproject.toml CHANGED
@@ -17,6 +17,7 @@ dependencies = [
17
  "tenacity>=9.0.0",
18
  "torch>=2.5.1",
19
  "torchvision>=0.20.1",
 
20
  ]
21
 
22
  [project.scripts]
 
17
  "tenacity>=9.0.0",
18
  "torch>=2.5.1",
19
  "torchvision>=0.20.1",
20
+ "streamlit>=1.40.2",
21
  ]
22
 
23
  [project.scripts]
src/proxy_lite/app.py ADDED
@@ -0,0 +1,236 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import asyncio
2
+ import base64
3
+ from io import BytesIO
4
+
5
+ import streamlit as st
6
+ from PIL import Image
7
+
8
+ from proxy_lite import Runner, RunnerConfig
9
+
10
+
11
+ def get_user_config(config_expander):
12
+ config = {
13
+ "environment": {
14
+ "name": "webbrowser",
15
+ "annotate_image": True,
16
+ "screenshot_delay": 2.0,
17
+ "include_html": False,
18
+ "viewport_width": 1280,
19
+ "viewport_height": 1920,
20
+ "include_poi_text": True,
21
+ "homepage": "https://www.google.com",
22
+ "keep_original_image": False,
23
+ "headless": False, # without proxies headless mode often results in getting bot blocked
24
+ },
25
+ "solver": {
26
+ "name": "simple",
27
+ "agent": {
28
+ "name": "proxy_lite",
29
+ "client": {
30
+ "name": "convergence",
31
+ "model_id": "convergence-ai/proxy-lite",
32
+ "api_base": "https://convergence-ai-demo-api.hf.space/v1",
33
+ },
34
+ },
35
+ },
36
+ "local_view": False,
37
+ "verbose": True,
38
+ "task_timeout": 1800, # 30 minutes
39
+ "action_timeout": 300,
40
+ "environment_timeout": 30,
41
+ }
42
+
43
+ with config_expander:
44
+ st.subheader("Environment Settings")
45
+ col1, col2 = st.columns(2)
46
+
47
+ with col1:
48
+ config["environment"]["include_html"] = st.checkbox(
49
+ "Include HTML",
50
+ value=config["environment"]["include_html"],
51
+ help="Include HTML in observations",
52
+ )
53
+ config["environment"]["include_poi_text"] = st.checkbox(
54
+ "Include POI Text",
55
+ value=config["environment"]["include_poi_text"],
56
+ help="Include points of interest text in observations",
57
+ )
58
+ config["environment"]["homepage"] = st.text_input(
59
+ "Homepage",
60
+ value=config["environment"]["homepage"],
61
+ help="Homepage to start from",
62
+ )
63
+
64
+ with col2:
65
+ config["solver"]["agent"]["client"]["api_base"] = st.text_input(
66
+ "VLLM Server URL",
67
+ value=config["solver"]["agent"]["client"]["api_base"],
68
+ help="URL of a vllm server running proxy-lite",
69
+ )
70
+ config["environment"]["screenshot_delay"] = st.slider(
71
+ "Screenshot Delay (seconds)",
72
+ min_value=0.5,
73
+ max_value=10.0,
74
+ value=config["environment"]["screenshot_delay"],
75
+ step=0.5,
76
+ help="Delay before taking screenshots",
77
+ )
78
+
79
+ st.subheader("Advanced Settings")
80
+ config["task_timeout"] = st.number_input(
81
+ "Task Timeout (seconds)",
82
+ min_value=60,
83
+ max_value=3600,
84
+ step=60,
85
+ value=config["task_timeout"],
86
+ help="Maximum time allowed for task completion",
87
+ )
88
+ config["action_timeout"] = st.number_input(
89
+ "Action Timeout (seconds)",
90
+ min_value=10,
91
+ max_value=300,
92
+ step=10,
93
+ value=config["action_timeout"],
94
+ help="Maximum time allowed for an action to complete",
95
+ )
96
+ config["environment_timeout"] = st.number_input(
97
+ "Environment Timeout (seconds)",
98
+ min_value=10,
99
+ max_value=300,
100
+ step=10,
101
+ value=config["environment_timeout"],
102
+ help="Maximum time allowed for environment to respond",
103
+ )
104
+
105
+ return config
106
+
107
+
108
+ async def run_task_async(
109
+ task: str,
110
+ status_placeholder,
111
+ action_placeholder,
112
+ environment_placeholder,
113
+ image_placeholder,
114
+ history_placeholder,
115
+ config: dict,
116
+ ):
117
+ try:
118
+ config = RunnerConfig.from_dict(config)
119
+ except Exception as e:
120
+ st.error(f"Error loading RunnerConfig: {e!s}")
121
+ return
122
+ print(config)
123
+ runner = Runner(config=config)
124
+
125
+ # Add the spinning animation using HTML
126
+ status_placeholder.markdown(
127
+ """
128
+ <style>
129
+ @keyframes spin {
130
+ 0% { content: "⚡"; }
131
+ 25% { content: "⚡."; }
132
+ 50% { content: "⚡.."; }
133
+ 75% { content: "⚡..."; }
134
+ }
135
+ .spinner::before {
136
+ content: "⚡";
137
+ animation: spin 2s linear infinite;
138
+ display: inline-block;
139
+ }
140
+ </style>
141
+ <div><b>Resolving your task </b><span class="spinner"></span></div>
142
+ """,
143
+ unsafe_allow_html=True,
144
+ )
145
+
146
+ all_steps = []
147
+ all_screenshots = []
148
+ all_soms = []
149
+
150
+ async for run in runner.run_generator(task):
151
+ # Update status with latest step
152
+ if run.actions:
153
+ latest_step = run.actions[-1].text
154
+ latest_step += "".join([
155
+ f'<tool_call>{{"name": {tool_call.function["name"]}, "arguments": {tool_call.function["arguments"]}}}</tool_call>' for tool_call in run.actions[-1].tool_calls
156
+ ])
157
+ action_placeholder.write(f"⚡ **Latest Step:** {latest_step}")
158
+ all_steps.append(latest_step)
159
+
160
+ # Update image if available
161
+ if run.observations and run.observations[-1].state.image:
162
+ environment_placeholder.write("🌐 **Environment:**")
163
+ image_bytes = base64.b64decode(run.observations[-1].state.image)
164
+ image = Image.open(BytesIO(image_bytes))
165
+ image_placeholder.image(image, use_container_width=True)
166
+ all_screenshots.append(image)
167
+ som = run.observations[-1].state.text
168
+ all_soms.append(som)
169
+
170
+ # Update history
171
+ with history_placeholder, st.expander("🕝 **History**"):
172
+ for idx, (action, img, som) in enumerate(zip(all_steps, all_screenshots, all_soms, strict=False)):
173
+ st.write(f"**Step {idx + 1}**")
174
+ st.image(img, use_container_width=True)
175
+ st.markdown(som)
176
+ st.write(action)
177
+ action_placeholder.write(" ")
178
+ status_placeholder.write(f"✨ **Result:** {latest_step}")
179
+
180
+
181
+ def main():
182
+ st.title("⚡ Proxy-Lite")
183
+
184
+ def img_to_base64(image_path):
185
+ with open(image_path, "rb") as img_file:
186
+ return base64.b64encode(img_file.read()).decode("utf-8")
187
+
188
+ st.markdown("Powered by **Proxy-Lite**", unsafe_allow_html=True)
189
+
190
+ if "config_expanded" not in st.session_state:
191
+ st.session_state.config_expanded = False
192
+ if "settings_expanded" not in st.session_state:
193
+ st.session_state.settings_expanded = False
194
+
195
+ config_expander = st.expander("⚙️ Proxy-Lite Configuration", expanded=st.session_state.config_expanded)
196
+ config = get_user_config(config_expander)
197
+
198
+ with st.form(key="run_task_form"):
199
+ task = st.text_input(
200
+ "Submit a task",
201
+ key="task_input",
202
+ help="Enter a task to be completed",
203
+ )
204
+ submit_button = st.form_submit_button("Submit a task", type="primary", use_container_width=True)
205
+
206
+ if submit_button:
207
+ st.session_state.config_expanded = False
208
+ if task:
209
+ # Create placeholders for dynamic updates
210
+ status_placeholder = st.empty()
211
+ st.write(" ")
212
+ action_placeholder = st.empty()
213
+ environment_placeholder = st.empty()
214
+ image_placeholder = st.empty()
215
+ history_placeholder = st.empty()
216
+
217
+ # Run the async task
218
+ asyncio.run(
219
+ run_task_async(
220
+ task,
221
+ status_placeholder,
222
+ action_placeholder,
223
+ environment_placeholder,
224
+ image_placeholder,
225
+ history_placeholder,
226
+ config,
227
+ ),
228
+ )
229
+
230
+ st.success("Task completed!", icon="✨")
231
+ else:
232
+ st.error("Please give a task first!")
233
+
234
+
235
+ if __name__ == "__main__":
236
+ main()
src/proxy_lite/recorder.py CHANGED
@@ -80,7 +80,7 @@ class DataRecorder:
80
  self.local_folder = local_folder
81
 
82
  def initialise_run(self, task: str) -> Run:
83
- self.local_folder = Path(os.path.abspath(sys.path[0])) / "local_trajectories"
84
  os.makedirs(self.local_folder, exist_ok=True)
85
  return Run.initialise(task)
86
 
 
80
  self.local_folder = local_folder
81
 
82
  def initialise_run(self, task: str) -> Run:
83
+ self.local_folder = Path(os.path.abspath(".")) / "local_trajectories"
84
  os.makedirs(self.local_folder, exist_ok=True)
85
  return Run.initialise(task)
86
 
src/proxy_lite/runner.py CHANGED
@@ -55,7 +55,7 @@ class RunnerConfig(BaseModel):
55
  solver: SolverConfigTypes
56
 
57
  save_every_step: bool = True
58
- max_steps: int = 100
59
  action_timeout: float = 60.0
60
  environment_timeout: float = 30.0
61
  task_timeout: float = 1800.0
 
55
  solver: SolverConfigTypes
56
 
57
  save_every_step: bool = True
58
+ max_steps: int = 50
59
  action_timeout: float = 60.0
60
  environment_timeout: float = 30.0
61
  task_timeout: float = 1800.0