MargeCode / app.py
ancerlop's picture
Fix Gradio Error
bd4e55e verified
raw
history blame
6.82 kB
from flask import Flask, request, jsonify
import subprocess
import os
import tempfile
import uuid
import time
import logging
from flask_cors import CORS
import gradio as gr
import requests
app = Flask(__name__)
CORS(app) # Habilitar CORS para todas las rutas
# Configuraci贸n de logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Directorio temporal para los archivos de c贸digo
TEMP_DIR = os.path.join(tempfile.gettempdir(), '42coderunner')
os.makedirs(TEMP_DIR, exist_ok=True)
# Tiempo m谩ximo de ejecuci贸n (en segundos)
MAX_EXECUTION_TIME = 10 # Aumentamos un poco para Gradio
# URL base de la API (para la interfaz Gradio)
# Detecta si estamos en Hugging Face Spaces
API_BASE_URL = os.environ.get("SPACE_HOST")
if API_BASE_URL:
# En Hugging Face, usa la URL p煤blica del espacio
# Aseg煤rate de que el protocolo sea https
API_BASE_URL = f"https://{API_BASE_URL}"
else:
# Localmente, usa localhost
API_BASE_URL = "http://localhost:5000"
API_EXECUTE_URL = f"{API_BASE_URL}/api/execute"
# --- Funciones de la API Flask ---
@app.route('/api/execute', methods=['POST'])
def execute_code():
try:
# Obtener el c贸digo C del request
data = request.get_json()
if not data or 'code' not in data:
return jsonify({'success': False, 'error': 'No se proporcion贸 c贸digo'}), 400
code = data['code']
# Crear un ID 煤nico para este trabajo
job_id = str(uuid.uuid4())
# Crear archivos temporales para el c贸digo y la salida
code_file = os.path.join(TEMP_DIR, f"{job_id}.c")
executable = os.path.join(TEMP_DIR, f"{job_id}.exe")
# Guardar el c贸digo en un archivo temporal
with open(code_file, 'w') as f:
f.write(code)
# Compilar el c贸digo
logger.info(f"Compilando c贸digo para job {job_id}")
compile_process = subprocess.run(
['gcc', code_file, '-o', executable],
capture_output=True,
text=True
)
# Verificar si la compilaci贸n fue exitosa
if compile_process.returncode != 0:
return jsonify({
'success': False,
'error': compile_process.stderr
})
# Ejecutar el c贸digo compilado
logger.info(f"Ejecutando c贸digo para job {job_id}")
try:
start_time = time.time()
run_process = subprocess.run(
[executable],
capture_output=True,
text=True,
timeout=MAX_EXECUTION_TIME
)
execution_time = time.time() - start_time
# Preparar la respuesta
result = {
'success': run_process.returncode == 0,
'output': run_process.stdout,
'error': run_process.stderr,
'execution_time': execution_time
}
except subprocess.TimeoutExpired:
result = {
'success': False,
'error': f'La ejecuci贸n excedi贸 el tiempo l铆mite de {MAX_EXECUTION_TIME} segundos'
}
# Limpiar archivos temporales
try:
os.remove(code_file)
if os.path.exists(executable):
os.remove(executable)
except Exception as e:
logger.error(f"Error al limpiar archivos temporales: {e}")
return jsonify(result)
except Exception as e:
logger.error(f"Error inesperado: {e}")
return jsonify({'success': False, 'error': f'Error interno del servidor: {str(e)}'}), 500
@app.route('/api/health', methods=['GET'])
def health_check():
"""Basic health check endpoint."""
logger.info("Health check requested.")
# A simple health check is enough for Hugging Face
# The previous checks (compiler, temp dir) might fail in the HF environment
return jsonify({'status': 'ok', 'timestamp': time.time()})
# --- Interfaz Gradio ---
def run_c_code(c_code):
"""Funci贸n que se ejecuta cuando el usuario env铆a c贸digo C a trav茅s de Gradio."""
logger.info(f"Solicitud Gradio recibida. URL API: {API_EXECUTE_URL}")
try:
response = requests.post(API_EXECUTE_URL, json={'code': c_code}, timeout=MAX_EXECUTION_TIME + 5) # Timeout un poco mayor para la request
response.raise_for_status() # Lanza excepci贸n para errores HTTP
data = response.json()
if data.get('success'):
output = f"--- Salida ({data.get('execution_time', 0):.4f}s) ---\n{data.get('output', '')}"
if data.get('error'): # Mostrar stderr aunque la ejecuci贸n sea exitosa (warnings, etc.)
output += f"\n\n--- Errores (stderr) ---\n{data['error']}"
return output
else:
return f"--- Error ---\n{data.get('error', 'Error desconocido')}"
except requests.exceptions.RequestException as e:
logger.error(f"Error de red al llamar a la API: {e}")
return f"Error de red al conectar con el backend: {e}"
except Exception as e:
logger.error(f"Error inesperado en la interfaz Gradio: {e}")
return f"Error inesperado en la interfaz: {str(e)}"
# Ejemplo de c贸digo C para la interfaz
EXAMPLE_CODE = """#include <stdio.h>
int main() {
printf("隆Hola desde 42CodeRunner!\n");
return 0;
}
"""
# Crear la interfaz Gradio
iface = gr.Interface(
fn=run_c_code,
inputs=gr.Code(language="c", label="C贸digo C", value=EXAMPLE_CODE),
outputs=gr.Textbox(label="Resultado de la Ejecuci贸n", lines=15),
title="馃強u200d鈾傦笍 42CodeRunner",
description="Escribe tu c贸digo C, haz clic en 'Submit' y mira la magia suceder. Ejecutado de forma segura en el backend.",
allow_flagging='never'
)
# --- Integration and Execution ---
# Create a main Gradio Blocks app to host both the UI and the API
with gr.Blocks(title="馃強u200d鈾傦笍 42CodeRunner") as demo:
# Render the defined interface within the Blocks
iface.render()
# Mount the Flask app (containing the API) onto the Gradio Blocks app under /api
# This makes the Flask routes available at paths like /api/execute
demo = gr.mount_wsgi_app(demo, app, path="/api")
# --- Ejecuci贸n de la App ---
if __name__ == '__main__':
# Use Gradio's default port or environment variable
port = int(os.environ.get('PORT', 7860))
# Launch the combined Gradio Blocks app
# Allow connection from network (needed for Docker/Spaces)
demo.launch(server_name='0.0.0.0', server_port=port)