Parser de recibos
Vision se encuentra con salida estructurada. Pega la URL de una foto de un recibo
Showcase — Parser de recibos
Vision se encuentra con salida estructurada. Pega la URL de una foto de un recibo o factura y recibe de vuelta JSON limpio — comercio, fecha, partidas, total — el tipo de registro que insertas directo en una base de datos. El modelo lee los pixeles; el prompt fija la forma (Gemini además usa JSON response mode).
Córrelo
bash bootstrap-secrets.sh # reads ../../../../.env, writes secrets/
docker compose up --build # default: PROVIDER=openai
Abre http://localhost:3000. Gemini: PROVIDER=gemini docker compose up --build.
Qué hay aquí
backend/ai_openai.py/backend/ai_gemini.py— un prompt de extracción de JSON sobre la imagen del recibo.frontend/app/page.tsx— caja de URL + JSON extraído.
Detenlo
docker compose down
Ejecútalo en tu máquina
Descarga el proyecto como ZIP y córrelo con Docker. Levanta un backend FastAPI y un frontend Next.js en localhost:3000.
unzip receipt-parser.zip
cd receipt-parser
bash bootstrap-secrets.sh # one-time: pulls API keys into ./secrets
docker compose up --build # default provider: openai
# or: PROVIDER=gemini docker compose up --build
Escribe algo, elige un proveedor y ejecuta el mismo código de Código contra la API real. Requiere iniciar sesión.
Los mismos módulos que ejecuta el botón Run. El proyecto completo (frontend, Dockerfile, compose) está en el ZIP, pestaña README.
backend/ai_openai.py
"""Showcase 3 (OpenAI): reading a receipt into structured JSON.
Vision meets structured output (week 4). Paste a photo URL of a receipt or
invoice and get back clean JSON — merchant, date, line items, total — the kind
of thing you'd insert straight into a database. The model reads the pixels; the
prompt pins the shape.
"""
from openai import OpenAI
_client = OpenAI()
_MODEL = "gpt-5.4-nano"
_PROMPT = (
"Read this receipt or invoice image and extract it as JSON with exactly these "
"keys: merchant (string), date (string or null), items (list of objects with "
"name and price), total (number or null). Output ONLY the JSON, no prose. Use "
"null for anything you can't read."
)
def run(image_url: str) -> str:
url = image_url.strip()
if not url.startswith("http"):
return "Paste a public image URL of a receipt or invoice (http/https)."
response = _client.responses.create(
model=_MODEL,
input=[{
"role": "user",
"content": [
{"type": "input_text", "text": _PROMPT},
{"type": "input_image", "image_url": url},
],
}],
)
return response.output_text
backend/ai_gemini.py
"""Showcase 3 (Gemini): reading a receipt into structured JSON.
Same extraction schema; Gemini takes the image as bytes and is asked for JSON.
"""
import os
import urllib.request
from google import genai
from google.genai import types
_client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])
_MODEL = "gemini-3.1-flash-lite"
_PROMPT = (
"Read this receipt or invoice image and extract it as JSON with exactly these "
"keys: merchant (string), date (string or null), items (list of objects with "
"name and price), total (number or null). Output ONLY the JSON, no prose. Use "
"null for anything you can't read."
)
def _mime(url: str) -> str:
u = url.lower()
if u.endswith(".png"):
return "image/png"
if u.endswith(".webp"):
return "image/webp"
return "image/jpeg"
def run(image_url: str) -> str:
url = image_url.strip()
if not url.startswith("http"):
return "Paste a public image URL of a receipt or invoice (http/https)."
image_bytes = urllib.request.urlopen(urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})).read()
response = _client.models.generate_content(
model=_MODEL,
contents=[types.Part.from_bytes(data=image_bytes, mime_type=_mime(url)), _PROMPT],
config=types.GenerateContentConfig(response_mime_type="application/json"),
)
return response.text or ""
Archivos del proyecto
.gitignoreREADME.es.mdREADME.mdbackend/Dockerfilebackend/ai_gemini.pybackend/ai_openai.pybackend/main.pybackend/requirements.txtbootstrap-secrets.shdocker-compose.ymlfrontend/Dockerfilefrontend/app/layout.tsxfrontend/app/page.tsxfrontend/next.config.tsfrontend/package.jsonfrontend/tsconfig.json