Spaces:
Sleeping
Sleeping
| """FastAPI application for ChargebackOps.""" | |
| from __future__ import annotations | |
| import logging | |
| import os | |
| from fastapi import HTTPException | |
| from fastapi.responses import JSONResponse | |
| try: | |
| from openenv.core.env_server.http_server import create_app | |
| except Exception as exc: # pragma: no cover | |
| raise ImportError( | |
| "openenv-core is required to run ChargebackOps. Install project dependencies first." | |
| ) from exc | |
| try: | |
| from ..runners.baseline_runner import run_baseline | |
| from ..core.episode_store import get_report, list_reports | |
| from ..runners.inference import run_inference | |
| from ..core.models import ( | |
| BaselineRunResult, | |
| ChargebackOpsAction, | |
| ChargebackOpsObservation, | |
| TasksResponse, | |
| TaskSummary, | |
| ) | |
| from ..scenarios.simulation import list_tasks | |
| from .chargeback_ops_environment import ChargebackOpsEnvironment | |
| from .demo_ui import build_demo | |
| except ImportError: # pragma: no cover | |
| from runners.baseline_runner import run_baseline | |
| from core.episode_store import get_report, list_reports | |
| from runners.inference import run_inference | |
| from core.models import ( | |
| BaselineRunResult, | |
| ChargebackOpsAction, | |
| ChargebackOpsObservation, | |
| TasksResponse, | |
| TaskSummary, | |
| ) | |
| from scenarios.simulation import list_tasks | |
| from server.chargeback_ops_environment import ChargebackOpsEnvironment | |
| from server.demo_ui import build_demo | |
| app = create_app( | |
| ChargebackOpsEnvironment, | |
| ChargebackOpsAction, | |
| ChargebackOpsObservation, | |
| env_name="chargeback_ops", | |
| max_concurrent_envs=8, | |
| ) | |
| try: | |
| import gradio as gr | |
| app = gr.mount_gradio_app(app, build_demo(), path="/demo") | |
| except Exception: | |
| logging.getLogger(__name__).warning("Gradio demo unavailable", exc_info=True) | |
| # Canonical Space card URL for README / judges (not the relative /demo path). | |
| _DEFAULT_DEMO_SPACE_URL = "https://huggingface.co/spaces/mitudrudutta/ChargeBackOps" | |
| def _canonical_demo_space_url() -> str: | |
| """Human-facing Hugging Face Space URL (Space card + embedded app).""" | |
| space_id = (os.environ.get("SPACE_ID") or "").strip() | |
| if space_id: | |
| return f"https://huggingface.co/spaces/{space_id}" | |
| override = (os.environ.get("DEMO_SPACE_URL") or "").strip() | |
| if override: | |
| return override | |
| return _DEFAULT_DEMO_SPACE_URL | |
| def _interactive_demo_url() -> str: | |
| """Same-origin Gradio mount; absolute URL when Hugging Face sets SPACE_HOST.""" | |
| host = (os.environ.get("SPACE_HOST") or "").strip() | |
| if host: | |
| return f"https://{host.rstrip('/')}/demo/" | |
| return "/demo" | |
| def root() -> JSONResponse: | |
| """Return a lightweight root response for HF Space and validator pings. | |
| ``demo_url`` is always the canonical Hugging Face Space page (shareable, | |
| stable, matches README badges). ``interactive_demo_url`` is the live | |
| Gradio app on this deployment (relative locally, absolute on Spaces). | |
| """ | |
| return JSONResponse( | |
| { | |
| "name": "ChargebackOps", | |
| "status": "ok", | |
| "docs_url": "/docs", | |
| "health_url": "/health", | |
| "tasks_url": "/tasks", | |
| "demo_url": _canonical_demo_space_url(), | |
| "interactive_demo_url": _interactive_demo_url(), | |
| } | |
| ) | |
| def tasks() -> TasksResponse: | |
| """List built-in tasks and the action schema.""" | |
| return TasksResponse( | |
| tasks=[ | |
| TaskSummary( | |
| task_id=task.task_id, | |
| title=task.title, | |
| difficulty=task.difficulty, | |
| objective=task.objective, | |
| description=task.description, | |
| max_steps=task.max_steps, | |
| case_count=len(task.cases), | |
| ) | |
| for task in list_tasks() | |
| ], | |
| action_schema=ChargebackOpsAction.model_json_schema(), | |
| ) | |
| def generate_tasks( | |
| seed: int = 42, | |
| easy: int = 2, | |
| medium: int = 2, | |
| hard: int = 2, | |
| ) -> list[dict]: | |
| """Generate parametric tasks from a seed for infinite scenario variety.""" | |
| try: | |
| from scenarios.case_generator import generate_task_suite | |
| except ImportError: # pragma: no cover | |
| from ..scenarios.case_generator import generate_task_suite | |
| suite = generate_task_suite( | |
| base_seed=seed, | |
| easy_count=easy, | |
| medium_count=medium, | |
| hard_count=hard, | |
| ) | |
| return [ | |
| { | |
| "task_id": t.task_id, | |
| "title": t.title, | |
| "difficulty": t.difficulty, | |
| "objective": t.objective, | |
| "case_count": len(t.cases), | |
| "max_steps": t.max_steps, | |
| } | |
| for t in suite | |
| ] | |
| def grader(episode_id: str | None = None): | |
| """Return a stored grade for a completed episode.""" | |
| report = get_report(episode_id) | |
| if report is None: | |
| raise HTTPException( | |
| status_code=404, | |
| detail="No completed episode report found. Finish an episode first or provide a valid episode_id.", | |
| ) | |
| return report.model_dump() | |
| def baseline( | |
| provider: str | None = None, | |
| model_name: str | None = None, | |
| ) -> BaselineRunResult: | |
| """Run the baseline inference policy across all tasks.""" | |
| if provider is None and model_name is None: | |
| return run_inference() | |
| return run_baseline(provider=provider, model_name=model_name) | |
| def results(): | |
| """Return all completed episode reports for inspection and replay.""" | |
| reports = list_reports() | |
| return [report.model_dump() for report in reports] | |
| def main(host: str = "0.0.0.0", port: int = 8000) -> None: | |
| """Local entry point for uvicorn.""" | |
| import uvicorn | |
| uvicorn.run(app, host=host, port=port) | |
| if __name__ == "__main__": # pragma: no cover | |
| main() | |