Spaces:
Running
Running
| import httpx | |
| import logging | |
| from config.settings import get_settings | |
| logger = logging.getLogger(__name__) | |
| settings = get_settings() | |
| JDOODLE_LANGUAGE_MAP = { | |
| "python": {"language": "python3", "versionIndex": "4"}, | |
| "javascript": {"language": "nodejs", "versionIndex": "4"}, | |
| "typescript": {"language": "typescript", "versionIndex": "1"}, | |
| "java": {"language": "java", "versionIndex": "4"}, | |
| "c++": {"language": "cpp17", "versionIndex": "1"}, | |
| "c": {"language": "c", "versionIndex": "5"}, | |
| "go": {"language": "go", "versionIndex": "4"}, | |
| "rust": {"language": "rust", "versionIndex": "4"}, | |
| "ruby": {"language": "ruby", "versionIndex": "4"}, | |
| "php": {"language": "php", "versionIndex": "4"}, | |
| } | |
| async def run_code_jdoodle(language: str, code: str) -> str: | |
| """Run code via JDoodle API. Returns output string.""" | |
| lang_config = JDOODLE_LANGUAGE_MAP.get(language.lower()) | |
| if not lang_config: | |
| raise ValueError(f"Language '{language}' not supported") | |
| async with httpx.AsyncClient(timeout=30.0) as client: | |
| response = await client.post( | |
| "https://api.jdoodle.com/v1/execute", | |
| json={ | |
| "clientId": settings.jdoodle_client_id, | |
| "clientSecret": settings.jdoodle_client_secret, | |
| "script": code, | |
| "language": lang_config["language"], | |
| "versionIndex": lang_config["versionIndex"], | |
| } | |
| ) | |
| if response.status_code != 200: | |
| logger.error(f"JDoodle error {response.status_code}: {response.text}") | |
| raise Exception(f"Code execution service error: {response.status_code}") | |
| result = response.json() | |
| # JDoodle returns error info in statusCode field | |
| if result.get("statusCode") == 400: | |
| raise Exception("JDoodle: Invalid request") | |
| if result.get("statusCode") == 429: | |
| raise Exception("Daily code execution limit reached. Try again tomorrow.") | |
| return result.get("output", "No output") | |
| async def run_code_piston(language: str, version: str, code: str) -> str: | |
| """Run code via Piston API (fallback). Returns output string.""" | |
| async with httpx.AsyncClient(timeout=30.0) as client: | |
| response = await client.post( | |
| f"{settings.piston_api_url}/execute", | |
| json={ | |
| "language": language, | |
| "version": version, | |
| "files": [{"content": code}], | |
| } | |
| ) | |
| if response.status_code != 200: | |
| raise Exception(f"Piston error: {response.status_code}") | |
| result = response.json() | |
| run = result.get("run", {}) | |
| return run.get("output") or run.get("stderr") or "No output" | |
| async def run_code(language: str, code: str, version: str = "") -> str: | |
| """Run code with JDoodle primary, Piston fallback.""" | |
| try: | |
| return await run_code_jdoodle(language, code) | |
| except Exception as e: | |
| logger.warning(f"JDoodle failed ({e}), falling back to Piston") | |
| try: | |
| return await run_code_piston(language, version, code) | |
| except Exception as e2: | |
| raise Exception(f"Both code runners failed. JDoodle: {e} | Piston: {e2}") | |