import time import gradio as gr import requests # Default backend URL for Modal deployment DEFAULT_BACKEND_URL = "PASTE_YOUR_DEPLOYED_URL_HERE" def extract_text(receipt, api_uri, progress=gr.Progress(track_tqdm=True)): if receipt is None: return gr.update(value="Please upload a receipt image.", interactive=True) # Use user-provided API URI or default backend_url = ( api_uri.strip() if api_uri and api_uri.strip() else DEFAULT_BACKEND_URL ) parse_url = f"{backend_url}/parse" result_url = f"{backend_url}/result" try: mime_type = getattr(receipt, "type", "application/octet-stream") with open(receipt.name, "rb") as f: files = {"receipt": (receipt.name, f, mime_type)} resp = requests.post(parse_url, files=files) # Send image to backend if not resp.ok: return f"Error from backend: {resp.status_code} {resp.text}" data = resp.json() call_id = data.get("call_id") if not call_id: return "No call_id returned from backend." # Poll /result/{call_id} for i in range(60): # Poll for up to 60 seconds poll_resp = requests.get(f"{result_url}/{call_id}") if poll_resp.status_code == 202: progress((i + 1) / 60, desc="Processing...") time.sleep(1) continue if poll_resp.ok: try: result = poll_resp.json() if isinstance(result, dict): return result.get("result", str(result)) return str(result) except Exception: return poll_resp.text else: return f"Error polling result: {poll_resp.status_code} {poll_resp.text}" return "Timed out waiting for OCR result." except Exception as e: return f"Exception: {e}" # JavaScript to force dark theme on Gradio js_func = """ function refresh() { const url = new URL(window.location); if (url.searchParams.get('__theme') !== 'dark') { url.searchParams.set('__theme', 'dark'); window.location.href = url.href; } } """ def main(): with gr.Blocks( js=js_func, theme=gr.themes.Soft(primary_hue="orange", secondary_hue="gray") ) as demo: # Title and subtitle gr.HTML("""

Receipt OCR based on NuExtract-2.0

Upload a receipt image and extract text using NuExtract-2.0 hosted on Modal.

""") with gr.Group(): api_uri = gr.Textbox( label="Backend API URI", placeholder="https://your-backend-url.modal.run", info="Please enter your backend api url", lines=1, ) with gr.Row(): with gr.Column(scale=1, min_width=350): with gr.Group(): receipt = gr.File( label="Upload Receipt Image", file_types=["image"], type="filepath", ) image_preview = gr.Image( label="Preview", visible=False, show_label=True, height=250, width=250, elem_id="preview-img", ) with gr.Column(scale=2, min_width=400): with gr.Group(): result_box = gr.Textbox( label="OCR Result", lines=14, interactive=True, show_copy_button=True, elem_id="result-box", container=True, max_lines=20, placeholder="The extracted text will appear here...", ) extract_btn = gr.Button("Extract Text", variant="primary", size="lg") status = gr.Markdown("", visible=False) # Add examples section gr.Examples( examples=[["receipt.png"]], inputs=receipt, label="📄 Example Receipt", ) # Show preview of uploaded image def show_preview(file): if file is not None: return gr.update(value=file.name, visible=True) return gr.update(visible=False) receipt.change(fn=show_preview, inputs=receipt, outputs=image_preview) extract_btn.click( fn=extract_text, inputs=[receipt, api_uri], outputs=result_box ) gr.HTML(""" """) demo.launch(share=True) if __name__ == "__main__": main()