Spaces:
Running
Running
Trisha Tomy
commited on
Commit
Β·
d116fe5
1
Parent(s):
6a0e448
generalised prompts + return formatted correctly
Browse files- app.py +273 -84
- src/proxy_lite/agents/proxy_lite_agent.py +2 -2
- src/proxy_lite/environments/webbrowser.py +18 -7
app.py
CHANGED
@@ -1,64 +1,138 @@
|
|
1 |
import gevent.monkey
|
2 |
gevent.monkey.patch_all(asyncio=True) # Keep this at the very top
|
3 |
|
4 |
-
import asyncio
|
5 |
from flask import Flask, request, jsonify
|
6 |
from proxy_lite import Runner, RunnerConfig
|
7 |
import os
|
8 |
import logging
|
|
|
|
|
9 |
|
10 |
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
11 |
logger = logging.getLogger(__name__)
|
12 |
|
13 |
app = Flask(__name__)
|
14 |
|
15 |
-
_runner = None
|
16 |
|
17 |
-
async def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
18 |
global _runner
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
"
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
"
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
62 |
return _runner
|
63 |
|
64 |
|
@@ -66,12 +140,18 @@ async def initialize_runner():
|
|
66 |
async def run_proxy_task_endpoint():
|
67 |
data = request.json
|
68 |
request_task_instruction = data.get('task')
|
|
|
69 |
|
70 |
if not request_task_instruction:
|
71 |
logger.warning("Received request without 'task' field. Returning 400.")
|
72 |
return jsonify({"error": "No 'task' provided in request body"}), 400
|
|
|
|
|
|
|
|
|
73 |
|
74 |
logger.info(f"Received user request task: '{request_task_instruction}'")
|
|
|
75 |
|
76 |
salesforce_username = os.environ.get("SALESFORCE_USERNAME")
|
77 |
salesforce_password = os.environ.get("SALESFORCE_PASSWORD")
|
@@ -80,54 +160,163 @@ async def run_proxy_task_endpoint():
|
|
80 |
logger.error("Salesforce credentials (SALESFORCE_USERNAME, SALESFORCE_PASSWORD) environment variables not set.")
|
81 |
return jsonify({"error": "Salesforce credentials not configured. Please set SALESFORCE_USERNAME and SALESFORCE_PASSWORD as Space secrets."}), 500
|
82 |
|
83 |
-
|
84 |
-
account_forecast_url = "https://dwd000006jia1mae.lightning.force.com/lightning/setup/AccountForecastSettings/home"
|
85 |
-
|
86 |
-
# Define the tool code block to open a new tab and navigate after login
|
87 |
-
# Using a raw f-string for multiline tool code block
|
88 |
-
tool_code_block_new_tab = fr"""
|
89 |
-
<tool_code>
|
90 |
-
await browser.open_new_tab_and_go_to(url='{account_forecast_url}')
|
91 |
-
</tool_code>
|
92 |
-
"""
|
93 |
-
|
94 |
-
# Refined agent_task instruction to be sequential and robust to Salesforce redirects
|
95 |
-
agent_task = f"""
|
96 |
-
**Task Instructions for Proxy Lite Agent:**
|
97 |
-
1. **Start on Login Page:** Navigate to the Salesforce login page.
|
98 |
-
2. **Perform Login:** Log in to Salesforce using the provided username '{salesforce_username}' and password '{salesforce_password}'. Ensure all login fields are filled and the 'Log In' button is clicked.
|
99 |
-
3. **Handle Post-Login Redirect:** After clicking the 'Log In' button:
|
100 |
-
* Observe the current URL. If the URL has changed from the initial login domain (e.g., from `login.salesforce.com` or `my.salesforce.com`) **immediately execute the following tool code block to open a new tab and navigate directly to the Account Forecast Settings page (`{account_forecast_url}`) to bypass any persistent loading issues or internal redirects:**
|
101 |
-
{tool_code_block_new_tab.strip()}
|
102 |
-
4. **Confirm Target Page Load:** After successfully navigating to '{account_forecast_url}' (either directly after login or via the new tab strategy), ensure the page is fully loaded and stable. This means no loading spinners should be visible, and the main content for 'Account Forecast Settings' (like a clear heading, relevant toggles, or data tables) should be present and interactive.
|
103 |
-
5. **Execute Main Task:** Once the Account Forecast Settings page is confirmed loaded and stable, proceed with the original user request: {request_task_instruction}.
|
104 |
-
6. **Report Final Status:** Report the final status of the requested action, confirming both successful login and complete page load of the Account Forecast Settings.
|
105 |
-
"""
|
106 |
-
|
107 |
-
logger.info(f"Executing agent task (truncated for log): '{agent_task[:500]}...'")
|
108 |
|
109 |
try:
|
110 |
-
|
111 |
-
|
|
|
|
|
|
|
112 |
|
113 |
-
|
114 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
115 |
except Exception as e:
|
116 |
-
logger.exception(f"
|
117 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
118 |
|
119 |
@app.route('/')
|
120 |
def root():
|
121 |
logger.info("Root endpoint accessed.")
|
122 |
return "Proxy-lite API is running. Send POST requests to /run_proxy_task with a 'task' in JSON body."
|
123 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
124 |
if __name__ == '__main__':
|
125 |
-
# It is crucial to set HF_API_TOKEN as an environment variable (e.g., in a .env file or directly)
|
126 |
-
# for local testing as well, otherwise initialize_runner will fail.
|
127 |
if not os.environ.get("HF_API_TOKEN"):
|
128 |
logger.error("HF_API_TOKEN environment variable is not set. Please set it for local testing.")
|
129 |
-
|
130 |
-
|
131 |
-
# For full functionality, the token is essential.
|
132 |
-
logger.info("Starting Flask development server on 0.0.0.0:7860...")
|
133 |
-
app.run(host='0.0.0.0', port=7860, debug=True)
|
|
|
1 |
import gevent.monkey
|
2 |
gevent.monkey.patch_all(asyncio=True) # Keep this at the very top
|
3 |
|
4 |
+
import asyncio
|
5 |
from flask import Flask, request, jsonify
|
6 |
from proxy_lite import Runner, RunnerConfig
|
7 |
import os
|
8 |
import logging
|
9 |
+
from datetime import datetime
|
10 |
+
from playwright.async_api import async_playwright, TimeoutError as PlaywrightTimeoutError
|
11 |
|
12 |
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
13 |
logger = logging.getLogger(__name__)
|
14 |
|
15 |
app = Flask(__name__)
|
16 |
|
17 |
+
_runner = None
|
18 |
|
19 |
+
async def perform_hardcoded_salesforce_login_and_get_cookies(username, password, login_url, target_url):
|
20 |
+
logger.info("Attempting hardcoded Salesforce login with Playwright to obtain cookies...")
|
21 |
+
async with async_playwright() as p:
|
22 |
+
browser = await p.chromium.launch(headless=True, args=["--no-sandbox", "--disable-setuid-sandbox"])
|
23 |
+
context = await browser.new_context()
|
24 |
+
page = await context.new_page()
|
25 |
+
|
26 |
+
try:
|
27 |
+
await page.goto(login_url, wait_until="domcontentloaded", timeout=60000)
|
28 |
+
logger.info(f"Playwright: Navigated to Salesforce login page: {page.url}")
|
29 |
+
|
30 |
+
await page.fill("#username", username)
|
31 |
+
await page.fill("#password", password)
|
32 |
+
await page.click("#Login")
|
33 |
+
logger.info("Playwright: Filled credentials and clicked Login. Waiting for post-login state...")
|
34 |
+
|
35 |
+
try:
|
36 |
+
await page.wait_for_url(lambda url: "login.salesforce.com" not in url and "unauthorized" not in url.lower(), timeout=60000)
|
37 |
+
logger.info(f"Playwright: Successfully redirected from login page. Current URL: {page.url}")
|
38 |
+
await page.wait_for_selector('button[title="App Launcher"]', timeout=30000)
|
39 |
+
logger.info("Playwright: Main Salesforce Lightning UI (e.g., App Launcher) detected after login.")
|
40 |
+
|
41 |
+
except PlaywrightTimeoutError:
|
42 |
+
logger.error(f"Playwright: Did not detect main UI or expected URL change within timeout after login. Current URL: {page.url}. Login might have failed or stuck on a redirect loop.")
|
43 |
+
raise Exception("Salesforce login redirection failed or main UI not detected.")
|
44 |
+
|
45 |
+
logger.info(f"Playwright: Navigating to target URL: {target_url} to ensure all relevant cookies are captured.")
|
46 |
+
await page.goto(target_url, wait_until="domcontentloaded", timeout=60000)
|
47 |
+
|
48 |
+
try:
|
49 |
+
# Wait for generic Salesforce setup page elements to load
|
50 |
+
await page.wait_for_selector('.setupPage, .slds-page-header, .slds-card, [data-aura-class*="setup"], .forcePageBlockSectionView', timeout=30000)
|
51 |
+
logger.info("Playwright: Detected Salesforce setup page elements loaded successfully.")
|
52 |
+
except PlaywrightTimeoutError:
|
53 |
+
logger.warning("Playwright: Specific setup page elements not found. Trying generic page load check...")
|
54 |
+
try:
|
55 |
+
# Fallback: wait for page to reach network idle state
|
56 |
+
await page.wait_for_load_state("networkidle", timeout=10000)
|
57 |
+
logger.info("Playwright: Page reached network idle state - proceeding with task.")
|
58 |
+
except PlaywrightTimeoutError:
|
59 |
+
logger.info("Playwright: Page load validation timed out, but continuing as page may still be functional.")
|
60 |
+
|
61 |
+
await asyncio.sleep(2)
|
62 |
+
logger.info(f"Playwright: Successfully navigated to and confirmed content on {page.url}")
|
63 |
+
|
64 |
+
cookies = await context.cookies()
|
65 |
+
logger.info(f"Playwright: Extracted {len(cookies)} cookies after successful login and navigation.")
|
66 |
+
return cookies
|
67 |
+
|
68 |
+
except PlaywrightTimeoutError as e:
|
69 |
+
logger.error(f"Playwright login/navigation failed (Timeout): {e}. Current URL: {page.url}")
|
70 |
+
raise
|
71 |
+
except Exception as e:
|
72 |
+
logger.error(f"Playwright login/navigation failed (General Error): {e}. Current URL: {page.url}")
|
73 |
+
raise
|
74 |
+
finally:
|
75 |
+
if browser:
|
76 |
+
await browser.close()
|
77 |
+
|
78 |
+
|
79 |
+
async def initialize_runner_with_cookies(cookies: list, target_url: str):
|
80 |
global _runner
|
81 |
+
logger.info("Initializing Proxy-lite Runner with provided cookies...")
|
82 |
+
|
83 |
+
hf_api_token = os.environ.get("HF_API_TOKEN")
|
84 |
+
if not hf_api_token:
|
85 |
+
logger.error("HF_API_TOKEN environment variable not set. Cannot initialize Runner.")
|
86 |
+
raise ValueError("HF_API_TOKEN environment variable not set. Please set it as a Space secret.")
|
87 |
+
|
88 |
+
config_dict = {
|
89 |
+
"environment": {
|
90 |
+
"name": "webbrowser",
|
91 |
+
"homepage": "about:blank", # Safe startup, we'll open new tab programmatically
|
92 |
+
"headless": False,
|
93 |
+
"launch_args": ["--no-sandbox", "--disable-setuid-sandbox"],
|
94 |
+
"screenshot_delay": 0.5,
|
95 |
+
"include_html": True,
|
96 |
+
"include_poi_text": True,
|
97 |
+
"record_pois": True,
|
98 |
+
"viewport_width": 1280,
|
99 |
+
"viewport_height": 720,
|
100 |
+
"browserbase_timeout": 7200,
|
101 |
+
"keep_original_image": False,
|
102 |
+
"no_pois_in_image": False,
|
103 |
+
"initial_cookies": cookies
|
104 |
+
},
|
105 |
+
"solver": {
|
106 |
+
"name": "simple",
|
107 |
+
"agent": {
|
108 |
+
"name": "proxy_lite", # Corrected as per previous error
|
109 |
+
"client": {
|
110 |
+
"name": "convergence",
|
111 |
+
"model_id": "convergence-ai/proxy-lite-3b",
|
112 |
+
"api_base": "https://convergence-ai-demo-api.hf.space/v1",
|
113 |
+
"api_key": hf_api_token,
|
114 |
+
"http_timeout": 50.0,
|
115 |
+
"http_concurrent_connections": 50,
|
116 |
+
},
|
117 |
+
"history_messages_limit": {
|
118 |
+
"screenshot": 1
|
119 |
+
},
|
120 |
+
"history_messages_include": None,
|
121 |
+
}
|
122 |
+
},
|
123 |
+
"environment_timeout": 1800.0,
|
124 |
+
"action_timeout": 1800.0,
|
125 |
+
"task_timeout": 18000.0,
|
126 |
+
"max_steps": 150,
|
127 |
+
"logger_level": "DEBUG",
|
128 |
+
"save_every_step": True,
|
129 |
+
"detailed_logger_name": False
|
130 |
+
}
|
131 |
+
config = RunnerConfig.from_dict(config_dict)
|
132 |
+
|
133 |
+
logger.info(f"DEBUG: app.py - Initializing Proxy-lite Runner with config (cookies to be injected).")
|
134 |
+
_runner = Runner(config=config)
|
135 |
+
logger.info("Proxy-lite Runner initialized successfully with injected cookies.")
|
136 |
return _runner
|
137 |
|
138 |
|
|
|
140 |
async def run_proxy_task_endpoint():
|
141 |
data = request.json
|
142 |
request_task_instruction = data.get('task')
|
143 |
+
target_url = data.get('url')
|
144 |
|
145 |
if not request_task_instruction:
|
146 |
logger.warning("Received request without 'task' field. Returning 400.")
|
147 |
return jsonify({"error": "No 'task' provided in request body"}), 400
|
148 |
+
|
149 |
+
if not target_url:
|
150 |
+
logger.warning("Received request without 'url' field. Returning 400.")
|
151 |
+
return jsonify({"error": "No 'url' provided in request body"}), 400
|
152 |
|
153 |
logger.info(f"Received user request task: '{request_task_instruction}'")
|
154 |
+
logger.info(f"Target URL: '{target_url}'")
|
155 |
|
156 |
salesforce_username = os.environ.get("SALESFORCE_USERNAME")
|
157 |
salesforce_password = os.environ.get("SALESFORCE_PASSWORD")
|
|
|
160 |
logger.error("Salesforce credentials (SALESFORCE_USERNAME, SALESFORCE_PASSWORD) environment variables not set.")
|
161 |
return jsonify({"error": "Salesforce credentials not configured. Please set SALESFORCE_USERNAME and SALESFORCE_PASSWORD as Space secrets."}), 500
|
162 |
|
163 |
+
salesforce_login_url = "https://login.salesforce.com/"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
164 |
|
165 |
try:
|
166 |
+
logger.info("Executing hardcoded login via Playwright to get session cookies...")
|
167 |
+
session_cookies = await perform_hardcoded_salesforce_login_and_get_cookies(
|
168 |
+
salesforce_username, salesforce_password, salesforce_login_url, target_url
|
169 |
+
)
|
170 |
+
logger.info(f"Successfully obtained {len(session_cookies)} cookies. These will be injected into the agent's browser.")
|
171 |
|
172 |
+
runner = await initialize_runner_with_cookies(session_cookies, target_url)
|
173 |
+
logger.info("Proxy-lite Runner initialized with pre-set cookies.")
|
174 |
+
|
175 |
+
logger.info("Agent will use mandatory new tab tool to bypass loading issues.")
|
176 |
+
|
177 |
+
# MANDATORY new tab navigation task - this is critical to avoid loading issues
|
178 |
+
agent_task = f"""
|
179 |
+
CRITICAL FIRST STEP - MANDATORY:
|
180 |
+
Your VERY FIRST action must be to use the open_new_tab_and_go_to tool to navigate to {target_url}
|
181 |
+
|
182 |
+
DO NOT skip this step. DO NOT use goto. You MUST use: open_new_tab_and_go_to(url='{target_url}')
|
183 |
+
|
184 |
+
This is necessary because direct navigation to this URL gets stuck loading. The new tab approach bypasses this issue.
|
185 |
+
|
186 |
+
STEP 1: Use open_new_tab_and_go_to(url='{target_url}')
|
187 |
+
STEP 2: Wait for the page to be fully loaded (no loading spinners visible)
|
188 |
+
STEP 3: {request_task_instruction}
|
189 |
+
|
190 |
+
Report success/failure for each step.
|
191 |
+
"""
|
192 |
+
|
193 |
+
logger.info("Executing agent task with mandatory new tab navigation...")
|
194 |
+
result = await runner.run(task=agent_task)
|
195 |
+
|
196 |
+
# Extract the actual result value from the Run object
|
197 |
+
if hasattr(result, 'value') and result.value:
|
198 |
+
task_result = str(result.value)
|
199 |
+
elif hasattr(result, 'result') and result.result:
|
200 |
+
task_result = str(result.result)
|
201 |
+
else:
|
202 |
+
task_result = str(result)
|
203 |
+
|
204 |
+
logger.info(f"Proxy-lite task completed. Output (truncated for log): {task_result[:500]}...")
|
205 |
+
|
206 |
+
# Structure response for LWC integration
|
207 |
+
response = {
|
208 |
+
"status": "success",
|
209 |
+
"message": "Task completed successfully",
|
210 |
+
"data": {
|
211 |
+
"task_result": task_result,
|
212 |
+
"steps_completed": [
|
213 |
+
"Hardcoded Salesforce login completed",
|
214 |
+
"Browser session initialized with cookies",
|
215 |
+
"New tab navigation executed",
|
216 |
+
"Target Salesforce setup page accessed",
|
217 |
+
"Task execution completed successfully"
|
218 |
+
],
|
219 |
+
"environment": {
|
220 |
+
"target_url": target_url,
|
221 |
+
"cookies_count": len(session_cookies),
|
222 |
+
"navigation_method": "new_tab_bypass"
|
223 |
+
}
|
224 |
+
},
|
225 |
+
"timestamp": datetime.now().isoformat(),
|
226 |
+
"task_request": request_task_instruction
|
227 |
+
}
|
228 |
+
|
229 |
+
return jsonify(response)
|
230 |
+
|
231 |
+
except PlaywrightTimeoutError as e:
|
232 |
+
logger.exception(f"Playwright timeout during login/navigation: {e}")
|
233 |
+
error_response = {
|
234 |
+
"status": "error",
|
235 |
+
"error_type": "navigation_timeout",
|
236 |
+
"message": "Page loading timed out during login or navigation",
|
237 |
+
"data": {
|
238 |
+
"error_details": str(e),
|
239 |
+
"suggested_action": "Retry the request - network issues may be temporary",
|
240 |
+
"steps_completed": ["Login attempted", "Navigation failed due to timeout"]
|
241 |
+
},
|
242 |
+
"timestamp": datetime.now().isoformat(),
|
243 |
+
"task_request": request_task_instruction
|
244 |
+
}
|
245 |
+
return jsonify(error_response), 500
|
246 |
+
|
247 |
+
except ValueError as e:
|
248 |
+
logger.exception(f"Configuration error: {e}")
|
249 |
+
error_response = {
|
250 |
+
"status": "error",
|
251 |
+
"error_type": "configuration_error",
|
252 |
+
"message": "System configuration issue",
|
253 |
+
"data": {
|
254 |
+
"error_details": str(e),
|
255 |
+
"suggested_action": "Check environment variables and system configuration",
|
256 |
+
"steps_completed": ["Configuration validation failed"]
|
257 |
+
},
|
258 |
+
"timestamp": datetime.now().isoformat(),
|
259 |
+
"task_request": request_task_instruction
|
260 |
+
}
|
261 |
+
return jsonify(error_response), 500
|
262 |
+
|
263 |
except Exception as e:
|
264 |
+
logger.exception(f"Unexpected error processing Salesforce task: {e}")
|
265 |
+
error_response = {
|
266 |
+
"status": "error",
|
267 |
+
"error_type": "unexpected_error",
|
268 |
+
"message": "An unexpected error occurred during task execution",
|
269 |
+
"data": {
|
270 |
+
"error_details": str(e),
|
271 |
+
"error_class": type(e).__name__,
|
272 |
+
"suggested_action": "Check logs for detailed error information and retry",
|
273 |
+
"steps_completed": ["Login attempted", "Error occurred during execution"]
|
274 |
+
},
|
275 |
+
"timestamp": datetime.now().isoformat(),
|
276 |
+
"task_request": request_task_instruction
|
277 |
+
}
|
278 |
+
return jsonify(error_response), 500
|
279 |
|
280 |
@app.route('/')
|
281 |
def root():
|
282 |
logger.info("Root endpoint accessed.")
|
283 |
return "Proxy-lite API is running. Send POST requests to /run_proxy_task with a 'task' in JSON body."
|
284 |
|
285 |
+
@app.route('/health', methods=['GET'])
|
286 |
+
def health_check():
|
287 |
+
"""Health check endpoint for monitoring and debugging"""
|
288 |
+
logger.info("Health check endpoint accessed.")
|
289 |
+
|
290 |
+
# Check environment variables
|
291 |
+
env_status = {
|
292 |
+
"HF_API_TOKEN": "β" if os.environ.get("HF_API_TOKEN") else "β",
|
293 |
+
"SALESFORCE_USERNAME": "β" if os.environ.get("SALESFORCE_USERNAME") else "β",
|
294 |
+
"SALESFORCE_PASSWORD": "β" if os.environ.get("SALESFORCE_PASSWORD") else "β"
|
295 |
+
}
|
296 |
+
|
297 |
+
health_response = {
|
298 |
+
"status": "healthy",
|
299 |
+
"message": "Proxy-lite API is running",
|
300 |
+
"environment_variables": env_status,
|
301 |
+
"endpoints": {
|
302 |
+
"POST /run_proxy_task": "Execute Salesforce automation tasks (requires 'task' and 'url' parameters)",
|
303 |
+
"GET /health": "Health check and status",
|
304 |
+
"GET /": "API information"
|
305 |
+
},
|
306 |
+
"supported_pages": [
|
307 |
+
"Warranty Lifecycle Management",
|
308 |
+
"Account Forecasting Settings",
|
309 |
+
"Sales Agreements",
|
310 |
+
"Account Manager Targets",
|
311 |
+
"Any Salesforce Setup page"
|
312 |
+
],
|
313 |
+
"timestamp": datetime.now().isoformat()
|
314 |
+
}
|
315 |
+
|
316 |
+
return jsonify(health_response)
|
317 |
+
|
318 |
if __name__ == '__main__':
|
|
|
|
|
319 |
if not os.environ.get("HF_API_TOKEN"):
|
320 |
logger.error("HF_API_TOKEN environment variable is not set. Please set it for local testing.")
|
321 |
+
logger.info("Starting Flask development server on 0.0.0.0:6101...")
|
322 |
+
app.run(host='0.0.0.0', port=6101, debug=True)
|
|
|
|
|
|
src/proxy_lite/agents/proxy_lite_agent.py
CHANGED
@@ -13,8 +13,8 @@ You were developed by Convergence AI.
|
|
13 |
The user will instuct you to perform a task.
|
14 |
You will be shown a screen as well as relevant interactable elements highlighted by mark_ids and you will be given a set of tools to use to perform the task.
|
15 |
You should make observations about the screen, putting them in <observation></observation> tags.
|
16 |
-
You should then reason about what needs to be done to complete the task, putting your thoughts in <thinking></thinking> tags.
|
17 |
-
You should then use the tools to perform the task, putting the tool calls in <tool_call></tool_call> tags.
|
18 |
""" # noqa: E501
|
19 |
|
20 |
MAX_MESSAGES_FOR_CONTEXT_WINDOW = {
|
|
|
13 |
The user will instuct you to perform a task.
|
14 |
You will be shown a screen as well as relevant interactable elements highlighted by mark_ids and you will be given a set of tools to use to perform the task.
|
15 |
You should make observations about the screen, putting them in <observation></observation> tags.
|
16 |
+
You should then reason about what needs to be done, but follow user instructions as first priority, to complete the task, putting your thoughts in <thinking></thinking> tags.
|
17 |
+
You should then use the tools to perform the task, but follow user instructions as first priority, putting the tool calls in <tool_call></tool_call> tags.
|
18 |
""" # noqa: E501
|
19 |
|
20 |
MAX_MESSAGES_FOR_CONTEXT_WINDOW = {
|
src/proxy_lite/environments/webbrowser.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
import base64
|
2 |
from functools import cached_property
|
3 |
-
from typing import Any, Literal, Optional, Self
|
4 |
|
5 |
from proxy_lite.browser.browser import BrowserSession
|
6 |
from proxy_lite.environments.environment_base import (
|
@@ -12,8 +12,7 @@ from proxy_lite.environments.environment_base import (
|
|
12 |
State,
|
13 |
)
|
14 |
from proxy_lite.tools import BrowserTool, Tool, ToolExecutionResponse
|
15 |
-
|
16 |
-
from proxy_lite.logger import logger # Assuming you want to use the same logger
|
17 |
|
18 |
@Environments.register_environment_config("webbrowser")
|
19 |
class WebBrowserEnvironmentConfig(BaseEnvironmentConfig):
|
@@ -30,6 +29,10 @@ class WebBrowserEnvironmentConfig(BaseEnvironmentConfig):
|
|
30 |
headless: bool = True
|
31 |
keep_original_image: bool = False
|
32 |
no_pois_in_image: bool = False
|
|
|
|
|
|
|
|
|
33 |
|
34 |
|
35 |
@Environments.register_environment("webbrowser")
|
@@ -50,8 +53,12 @@ class WebBrowserEnvironment(BaseEnvironment):
|
|
50 |
)
|
51 |
await self.browser.__aenter__()
|
52 |
# Initialize other resources if necessary
|
53 |
-
|
54 |
-
|
|
|
|
|
|
|
|
|
55 |
self.logger.info("π [bold blue]Browser session started.[/]")
|
56 |
return self
|
57 |
|
@@ -71,10 +78,14 @@ class WebBrowserEnvironment(BaseEnvironment):
|
|
71 |
def browser_session(self) -> type[BrowserSession]:
|
72 |
return BrowserSession
|
73 |
|
|
|
|
|
|
|
74 |
@property
|
75 |
def cookies(self) -> list[dict]:
|
76 |
-
return []
|
77 |
-
|
|
|
78 |
async def initialise(self) -> Observation:
|
79 |
self.logger.debug(f"DEBUG: Initialising WebBrowserEnvironment. Homepage: {self.config.homepage}")
|
80 |
try:
|
|
|
1 |
import base64
|
2 |
from functools import cached_property
|
3 |
+
from typing import Any, Literal, Optional, Self, List # Added List import
|
4 |
|
5 |
from proxy_lite.browser.browser import BrowserSession
|
6 |
from proxy_lite.environments.environment_base import (
|
|
|
12 |
State,
|
13 |
)
|
14 |
from proxy_lite.tools import BrowserTool, Tool, ToolExecutionResponse
|
15 |
+
from proxy_lite.logger import logger
|
|
|
16 |
|
17 |
@Environments.register_environment_config("webbrowser")
|
18 |
class WebBrowserEnvironmentConfig(BaseEnvironmentConfig):
|
|
|
29 |
headless: bool = True
|
30 |
keep_original_image: bool = False
|
31 |
no_pois_in_image: bool = False
|
32 |
+
# --- MODIFICATION START ---
|
33 |
+
# Added to accept initial cookies from the RunnerConfig
|
34 |
+
initial_cookies: Optional[List[dict]] = None
|
35 |
+
# --- MODIFICATION END ---
|
36 |
|
37 |
|
38 |
@Environments.register_environment("webbrowser")
|
|
|
53 |
)
|
54 |
await self.browser.__aenter__()
|
55 |
# Initialize other resources if necessary
|
56 |
+
# --- MODIFICATION START ---
|
57 |
+
# Changed to use self.config.initial_cookies
|
58 |
+
if self.config.initial_cookies:
|
59 |
+
self.logger.info(f"π [bold blue]Adding {len(self.config.initial_cookies)} initial cookies to browser context.[/]")
|
60 |
+
await self.browser.context.add_cookies(self.config.initial_cookies)
|
61 |
+
# --- MODIFICATION END ---
|
62 |
self.logger.info("π [bold blue]Browser session started.[/]")
|
63 |
return self
|
64 |
|
|
|
78 |
def browser_session(self) -> type[BrowserSession]:
|
79 |
return BrowserSession
|
80 |
|
81 |
+
# --- MODIFICATION START ---
|
82 |
+
# Modified this property to return cookies from the config.
|
83 |
+
# It was previously hardcoded to return an empty list.
|
84 |
@property
|
85 |
def cookies(self) -> list[dict]:
|
86 |
+
return self.config.initial_cookies if self.config.initial_cookies is not None else []
|
87 |
+
# --- MODIFICATION END ---
|
88 |
+
|
89 |
async def initialise(self) -> Observation:
|
90 |
self.logger.debug(f"DEBUG: Initialising WebBrowserEnvironment. Homepage: {self.config.homepage}")
|
91 |
try:
|