File size: 6,936 Bytes
ce3b075
 
97d2a3d
689e710
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229832a
 
689e710
 
 
 
 
229832a
97d2a3d
 
689e710
d40d89b
229832a
d078da9
0cc7808
d078da9
 
 
689e710
97d2a3d
689e710
 
 
 
 
 
d653955
0d53358
689e710
 
0cc7808
 
 
 
 
 
 
 
 
 
 
 
97d2a3d
7e39dbf
 
 
 
 
 
 
6454f76
689e710
 
 
97d2a3d
689e710
0d53358
 
97d2a3d
0d53358
 
 
 
 
97d2a3d
0d53358
 
90940ac
0d53358
689e710
97d2a3d
 
689e710
 
 
 
229832a
 
 
689e710
 
 
229832a
 
 
 
 
 
 
 
 
 
d078da9
 
229832a
 
d078da9
 
 
 
229832a
 
 
 
689e710
 
229832a
689e710
97d2a3d
689e710
 
229832a
689e710
 
 
 
 
 
 
 
 
 
0d53358
689e710
7e39dbf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import gevent.monkey
gevent.monkey.patch_all(asyncio=True)
# app.py
import asyncio
from flask import Flask, request, jsonify
from proxy_lite import Runner, RunnerConfig
import os
import logging

logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

app = Flask(__name__)

_runner = None

async def initialize_runner():
    global _runner
    if _runner is None:
        logger.info("Initializing Proxy-lite Runner...")
        
        # Retrieve Hugging Face API token from environment variables
        hf_api_token = os.environ.get("HF_API_TOKEN")
        if not hf_api_token:
            logger.error("HF_API_TOKEN environment variable not set. Cannot initialize Runner.")
            raise ValueError("HF_API_TOKEN environment variable not set. Please set it as a Space secret.")

        # Define RunnerConfig
        config = RunnerConfig.from_dict({
            "environment": {
                "name": "webbrowser",
                "homepage": "https://dwd000006jia1mae.lightning.force.com/lightning/setup/AccountForecastSettings/home",
                "headless": True, # Keep headless for production environments
                "launch_args": ["--no-sandbox", "--disable-setuid-sandbox"] ,
                # "environment_timeout": 1800.0, # <-- THIS LINE WAS IN THE WRONG PLACE IN app.py
                "screenshot_delay": 3.0, # Increased delay for full page render before screenshot
                "include_html": True, # Include full HTML for richer observations
                "include_poi_text": True, # Keep including points of interest text
            },
            "solver": {
                "name": "simple",
                "agent": {
                    "name": "proxy_lite",
                    "client": {
                        "name": "convergence",
                        "model_id": "convergence-ai/proxy-lite-3b",
                        "api_base": "https://convergence-ai-demo-api.hf.space/v1", 
                        "api_key": hf_api_token
                    }
                }
            },
            # --- MOVE environment_timeout HERE, AS A TOP-LEVEL KEY ---
            "environment_timeout": 1800.0, 
            # You can also set other top-level RunnerConfig fields here if desired,
            # e.g., "action_timeout": 1800.0, "max_steps": 150, "task_timeout": 18000.0,
            # Ensure these match your desired values and not the defaults from RunnerConfig.
            # Based on your logs, action_timeout and task_timeout seem to be default, so let's add them:
            "action_timeout": 1800.0, # As per your earlier runner.py __main__ block
            "task_timeout": 18000.0,  # As per your earlier runner.py __main__ block
            "max_steps": 150,         # As per your earlier runner.py __main__ block
            "logger_level": "DEBUG", # Set this to DEBUG for more detailed logging during troubleshooting
            # --- END OF MOVED KEY ---
        })

        # --- ADDED DEBUG LOGGING HERE ---
        logger.info(f"DEBUG: app.py - Initializing Runner with environment_timeout: {config.environment_timeout} seconds")
        logger.info(f"DEBUG: app.py - Full config used: {config.model_dump_json(indent=2)}") # For Pydantic v2
        # If you are using Pydantic v1, use: logger.info(f"DEBUG: app.py - Full config used: {config.json(indent=2)}")
        # --- END ADDED DEBUG LOGGING ---

        _runner = Runner(config=config)
        logger.info("Proxy-lite Runner initialized successfully.")
    return _runner

# --- MODIFIED run_async_task FUNCTION ---
def run_async_task(coro):
    """
    Helper to run async coroutines in a synchronous context (like Flask routes).
    Ensures an event loop exists and runs the coroutine.
    """
    try:
        # Try to get the running loop (for current thread/greenlet)
        loop = asyncio.get_running_loop()
    except RuntimeError:
        # If no loop is running, create a new one for this thread
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
    
    # Run the coroutine until it completes
    return loop.run_until_complete(coro)
# --- END MODIFIED run_async_task FUNCTION ---


@app.route('/run_proxy_task', methods=['POST'])
def run_proxy_task_endpoint():
    data = request.json
    request_task_instruction = data.get('task')

    if not request_task_instruction:
        logger.warning("Received request without 'task' field. Returning 400.")
        return jsonify({"error": "No 'task' provided in request body"}), 400

    logger.info(f"Received user request task: '{request_task_instruction}'")

    salesforce_username = os.environ.get("SALESFORCE_USERNAME")
    salesforce_password = os.environ.get("SALESFORCE_PASSWORD")

    if not salesforce_username or not salesforce_password:
        logger.error("Salesforce credentials (SALESFORCE_USERNAME, SALESFORCE_PASSWORD) environment variables not set.")
        return jsonify({"error": "Salesforce credentials not configured. Please set SALESFORCE_USERNAME and SALESFORCE_PASSWORD as Space secrets."}), 500

    # Construct the full task for the proxy-lite agent,
    # combining login instructions with the dynamic task from the user,
    # and adding explicit verification steps for login success.
    agent_task = (
        f"Log in to Salesforce. The username is '{salesforce_username}' and the password is '{salesforce_password}'. "
        f"After attempting to log in, observe the page carefully. "
        f"If the login was successful, the URL should change from the login page, and you should see elements indicating a logged-in state (e.g., a Salesforce navigation menu, a home screen, or a profile icon), rather than a login form or an error message. "
        f"If the login is successful, {request_task_instruction}. "
        f"Report the final status of the requested action and confirmation of successful login."
    )
    
    logger.info(f"Executing agent task: '{agent_task[:200]}...'")

    try:
        runner = run_async_task(initialize_runner())
        result = run_async_task(runner.run(agent_task))

        logger.info(f"Proxy-lite task completed. Output: {result[:200]}...")
        return jsonify({"output": result})
    except Exception as e:
        logger.exception(f"Error processing Salesforce task: {e}")
        return jsonify({"error": f"An error occurred: {str(e)}. Check logs for details."}), 500

@app.route('/')
def root():
    logger.info("Root endpoint accessed.")
    return "Proxy-lite API is running. Send POST requests to /run_proxy_task with a 'task' in JSON body."

if __name__ == '__main__':
    if not os.environ.get("HF_API_TOKEN"):
        logger.error("HF_API_TOKEN environment variable is not set. Please set it for local testing.")
        exit(1)
    logger.info("Starting Flask development server on 0.0.0.0:7860...")
    app.run(host='0.0.0.0', port=7860, debug=True)