Spaces:
Running
Running
| """BugLens entrypoint — a custom frontend served on Gradio's Server (FastAPI). | |
| `gradio.Server` is a FastAPI subclass with Gradio's API engine (queue, SSE, | |
| concurrency) layered on top. We use it to serve the custom `buglens-ui` React | |
| prototype as the frontend (the Off-Brand path) while exposing the analysis as | |
| a backend endpoint. | |
| Example screenshots are always served from the built-in mock reports (so the | |
| app is fully explorable without a GPU). Uploaded screenshots are analyzed for | |
| real by POSTing to the Modal backend when BUGLENS_BACKEND=modal. | |
| """ | |
| from __future__ import annotations | |
| import base64 | |
| import binascii | |
| import mimetypes | |
| from pathlib import Path | |
| from fastapi import HTTPException | |
| from fastapi.responses import FileResponse | |
| from fastapi.staticfiles import StaticFiles | |
| from gradio import Server | |
| from pydantic import BaseModel | |
| from buglens import backend | |
| from buglens.config import load_settings | |
| from buglens.examples import get_example, list_examples | |
| from buglens.render import ( | |
| regression_tests_to_csv, | |
| to_github_issue, | |
| to_jira_markdown, | |
| ) | |
| from buglens.schema import BugReport | |
| UI_DIR = Path(__file__).parent / "buglens-ui" | |
| INDEX_FILE = UI_DIR / "BugLens.html" | |
| mimetypes.add_type("text/babel", ".jsx") | |
| server = Server(title="BugLens") | |
| def analyze(example_id: str | None = None) -> BugReport: | |
| """Return a built-in example report for gallery clicks and exports.""" | |
| report = get_example(example_id) if example_id else None | |
| if report is None: | |
| raise HTTPException(status_code=404, detail="Unknown example id.") | |
| return report | |
| def api_examples() -> list[dict]: | |
| """Gallery metadata for the example screenshots.""" | |
| return list_examples() | |
| def api_analyze(id: str | None = None) -> dict: | |
| """Return a built-in example's report payload (gallery clicks).""" | |
| return analyze(id).to_ui_payload() | |
| class AnalyzeUpload(BaseModel): | |
| """JSON body for analyzing an uploaded screenshot.""" | |
| image_b64: str | |
| note: str = "" | |
| def api_analyze_upload(payload: AnalyzeUpload) -> dict: | |
| """Analyze an uploaded screenshot via the configured inference backend.""" | |
| settings = load_settings() | |
| if settings.backend.value != "modal": | |
| raise HTTPException( | |
| status_code=501, | |
| detail=( | |
| "Live screenshot analysis needs the Modal backend " | |
| "(set BUGLENS_BACKEND=modal). Pick an example to preview the app." | |
| ), | |
| ) | |
| try: | |
| image_bytes = base64.b64decode(payload.image_b64, validate=True) | |
| except (ValueError, binascii.Error) as exc: | |
| raise HTTPException(status_code=400, detail="Invalid image data.") from exc | |
| try: | |
| return backend.analyze_via_modal(settings.modal_endpoint, image_bytes, payload.note) | |
| except backend.BackendError as exc: | |
| raise HTTPException(status_code=502, detail=str(exc)) from exc | |
| def api_export(fmt: str, id: str | None = None): | |
| """Return a text export (jira | github | csv) for an example.""" | |
| report = analyze(id) | |
| if fmt == "jira": | |
| return {"format": "jira", "text": to_jira_markdown(report)} | |
| if fmt == "github": | |
| return {"format": "github", "text": to_github_issue(report)} | |
| if fmt == "csv": | |
| return {"format": "csv", "text": regression_tests_to_csv(report)} | |
| raise HTTPException(status_code=404, detail="Unknown export format.") | |
| def index() -> FileResponse: | |
| """Serve the custom BugLens frontend.""" | |
| return FileResponse(INDEX_FILE) | |
| # Frontend assets (the jsx modules) under a dedicated prefix so we never shadow | |
| # Gradio's own /gradio_api/* routes or our /api/* endpoints. | |
| server.mount("/static", StaticFiles(directory=UI_DIR), name="static") | |
| def main() -> None: | |
| """Launch the BugLens server.""" | |
| server.launch(server_name="0.0.0.0", server_port=7860) | |
| if __name__ == "__main__": | |
| main() | |