Trisha Tomy
Consolidated final Dockerfile and app.py configurations
6170d37
raw
history blame
5.45 kB
# 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