Descripción de imagen
Pega una image URL pública y obtén un caption estilo alt-text para accesibilidad
Showcase — Descripción de imagen
Pega una image URL pública y obtén un caption estilo alt-text para accesibilidad más una descripción corta. Es la llamada multimodal más simple: un solo mensaje cuyo contenido es una parte de texto con la instrucción y una parte de imagen, razonadas juntas.
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— pasa la image URL directo al modelo.backend/ai_gemini.py— descarga la imagen y la pasa como bytes inline.frontend/app/page.tsx— caja de URL + caption.
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 image-caption.zip
cd image-caption
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 1 (OpenAI): image captioning from a URL.
Paste a public image URL and get an accessibility-style caption plus a short
description. One multimodal message: an instruction text part and an image part.
"""
from openai import OpenAI
_client = OpenAI()
_MODEL = "gpt-5.4-nano"
_PROMPT = (
"Write a concise one-sentence alt-text caption for this image, then a "
"2-3 sentence description of what's in it. Label them 'Alt text:' and "
"'Description:'."
)
def run(image_url: str) -> str:
url = image_url.strip()
if not url.startswith("http"):
return "Paste a public image URL (starting with http:// or 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 1 (Gemini): image captioning from a URL.
Same captioning prompt; Gemini takes the image as downloaded bytes.
"""
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 = (
"Write a concise one-sentence alt-text caption for this image, then a "
"2-3 sentence description of what's in it. Label them 'Alt text:' and "
"'Description:'."
)
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 (starting with http:// or 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],
)
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