Spaces:
Running on Zero
Running on Zero
Download app.py from devYoda/scanlens-models: direct link, hf CLI and curl.
- Browser
- Download file 3.12 kB
-
https://huggingface.co/spaces/devYoda/scanlens-models/resolve/main/app.py
- Command line
-
hf download hf://spaces/devYoda/scanlens-models/app.py
-
curl -L -o app.py https://huggingface.co/spaces/devYoda/scanlens-models/resolve/main/app.py
3.12 kB
| """Hugging Face Space entry point: Gradio on ZeroGPU hardware. | |
| ZeroGPU (the free GPU option on Spaces) only runs Gradio Spaces, so this serves the same tests as | |
| server.py through a Gradio API instead of plain HTTP. The Next.js app calls it with @gradio/client | |
| when MODEL_SPACE is set; see docs/DEPLOYMENT.md. | |
| analyze(image, test_id, secret) -> {"status": ..., "body": ...}, the same as POST /tests/<test_id> | |
| tests(secret) -> {"status": ..., "body": ...}, the same as GET /tests | |
| """ | |
| import hmac | |
| import os | |
| # ZeroGPU emulates CUDA outside @spaces.GPU functions, so models are placed on "cuda" at startup. | |
| os.environ.setdefault("DEVICE", "cuda") | |
| import gradio as gr | |
| import spaces | |
| from PIL import Image, UnidentifiedImageError | |
| import server | |
| # Models load once at startup, before any request; loading inside a GPU call would repeat it every time. | |
| server._preload() | |
| def _allowed(secret): | |
| return not server.SECRET or hmac.compare_digest(secret or "", server.SECRET) | |
| def _run(test_id, image): | |
| return server.run_matching(image) if test_id == "auto" else server.run_test(test_id, image) | |
| def analyze(image, test_id, secret): | |
| if not _allowed(secret): | |
| return {"status": 401, "body": {"error": "Missing or wrong secret."}} | |
| if test_id != "auto" and test_id not in server.TESTS: | |
| return {"status": 404, "body": {"error": "Unknown test."}} | |
| if not image or not 0 < os.path.getsize(image) <= server.MAX_BYTES: | |
| return {"status": 400, "body": {"error": "Send one image of up to 25 MB."}} | |
| try: | |
| img = Image.open(image).convert("RGB") | |
| except UnidentifiedImageError: | |
| return {"status": 400, "body": {"error": "The file isn't an image this server can read."}} | |
| try: | |
| status, body = _run(test_id, img) | |
| except Exception as e: # includes running out of daily ZeroGPU quota | |
| return {"status": 500, "body": {"error": f"The model failed: {e}"}} | |
| return {"status": status, "body": body} | |
| def tests(secret): | |
| if not _allowed(secret): | |
| return {"status": 401, "body": {"error": "Missing or wrong secret."}} | |
| return {"status": 200, "body": server.list_tests()} | |
| with gr.Blocks(title="ScanLens model server") as demo: | |
| gr.Markdown("# ScanLens model server\nUsed by the ScanLens app. Requests need the server secret.") | |
| # A file, not gr.Image, so the scan reaches the models byte for byte without being re-encoded. | |
| # No file_types: the app uploads a blob without a file name, and analyze() checks it is an image. | |
| image = gr.File(label="Scan image", type="filepath") | |
| test_id = gr.Dropdown(["auto", *server.TESTS], value="auto", label="Test") | |
| secret = gr.Textbox(label="Secret", type="password") | |
| with gr.Row(): | |
| run_button = gr.Button("Analyse", variant="primary") | |
| tests_button = gr.Button("List tests") | |
| out = gr.JSON(label="Result") | |
| run_button.click(analyze, [image, test_id, secret], out, api_name="analyze") | |
| tests_button.click(tests, [secret], out, api_name="tests") | |
| if __name__ == "__main__": | |
| demo.queue().launch(max_file_size="25mb") | |