Spaces:
Running
Running
File size: 5,447 Bytes
6170d37 ce3b075 6170d37 689e710 6170d37 689e710 6170d37 689e710 6170d37 689e710 6170d37 689e710 6170d37 689e710 6170d37 8b9bbfa 6170d37 8b9bbfa 6170d37 8b9bbfa 689e710 6170d37 689e710 8b9bbfa 689e710 0d53358 689e710 0b5b38e 8b9bbfa 6170d37 92ef27f 689e710 0d53358 6170d37 0d53358 6170d37 0d53358 6170d37 0d53358 689e710 6170d37 689e710 6170d37 689e710 6170d37 689e710 6170d37 689e710 6170d37 689e710 6170d37 689e710 6170d37 689e710 0d53358 689e710 6170d37 |
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 |
# app.py
# IMPORTANT: These two lines MUST be at the very top, before any other imports.
# They patch asyncio to work cooperatively with gevent workers.
import gevent.monkey
gevent.monkey.patch_all(asyncio=True)
import asyncio
from flask import Flask, request, jsonify
from proxy_lite import Runner, RunnerConfig
from proxy_lite.config import RecorderConfig # Needed for explicit instantiation
import os
import logging
# Configure logging for better visibility in Hugging Face Space logs
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
app = Flask(__name__)
# Global runner instance. It will be initialized once per worker process/greenlet
# upon the first request to ensure correct event loop binding.
_runner = None
async def initialize_runner():
"""Initializes the Proxy-lite Runner asynchronously."""
global _runner
if _runner is None:
logger.info("Initializing Proxy-lite Runner...")
# Retrieve Hugging Face API Token from environment variables (set as Space Secret)
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.")
# --- EXPLICITLY CREATE RECORDER_CONFIG ---
# This addresses the PermissionError by ensuring the root_path is explicitly set
# and passed as a Pydantic object, rather than relying on nested dict parsing.
recorder_config_instance = RecorderConfig(root_path="/tmp/proxy_lite_runs")
# --- END EXPLICIT RECORDER_CONFIG ---
# --- EXPLICITLY CREATE RUNNER_CONFIG ---
# Pass all configuration parameters as keyword arguments to the Pydantic model.
config = RunnerConfig(
environment={
"name": "webbrowser",
"homepage": "https://www.google.com",
"headless": True, # Keep headless for server environment
},
solver={
"name": "simple",
"agent": {
"name": "proxy_lite",
"client": {
"name": "convergence",
"model_id": "convergence-ai/proxy-lite-3b",
"api_base": "https://api-inference.huggingface.co/models/convergence-ai/proxy-lite-3b",
"api_key": hf_api_token
}
}
},
recorder=recorder_config_instance # Pass the explicitly created instance
)
# --- END EXPLICIT RUNNER_CONFIG ---
# Instantiate the Runner, passing the config as a keyword argument (Pydantic style)
_runner = Runner(config=config)
logger.info("Proxy-lite Runner initialized successfully.")
return _runner
def run_async_task(coro):
"""
Helper to run async coroutines in a synchronous context (like Flask routes).
Ensures an event loop exists for the current thread/greenlet and runs the coroutine.
This addresses the "no current event loop" errors.
"""
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/greenlet
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
# Run the coroutine until it completes
return loop.run_until_complete(coro)
@app.route('/run_proxy_task', methods=['POST'])
def run_proxy_task_endpoint():
"""API endpoint to receive a task and execute it with Proxy-lite."""
data = request.json
task = data.get('task')
if not task:
logger.warning("Received request without 'task' field. Returning 400.")
return jsonify({"error": "No 'task' provided in request body"}), 400
logger.info(f"Received task for proxy-lite: '{task}'")
try:
# Ensure runner is initialized (and event loop handled) before use
runner = run_async_task(initialize_runner())
# Run the task using the proxy-lite runner
result = run_async_task(runner.run(task))
logger.info(f"Proxy-lite task completed. Output: {result[:200]}...") # Log truncated output
# Return the result as a JSON response
return jsonify({"output": result})
except Exception as e:
logger.exception(f"Error processing task '{task}':") # Logs full traceback for debugging
return jsonify({"error": f"An error occurred: {str(e)}. Check logs for details."}), 500
@app.route('/')
def root():
"""Basic root endpoint for health check and user info."""
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__':
# During local development, ensure HF_API_TOKEN is set manually
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...")
# Hugging Face Spaces expects binding to 0.0.0.0 and listening on port 7860
app.run(host='0.0.0.0', port=7860, debug=True) # debug=True for local testing, disable for production |