Entradas con imágenes
dejar que el usuario adjunte imágenes a un turno para que el modelo pueda describir capturas, leer diagramas, hacer OCR a una foto, o razonar sobre una UI.
Objetivo: dejar que el usuario adjunte imágenes a un turno para que el modelo pueda describir capturas, leer diagramas, hacer OCR a una foto, o razonar sobre una UI.
Dificultad: media. Tiempo: 2–3 horas. Toca: internal/api/types.go, internal/provider/, commands.go, internal/ui/.
Lo que ya está en el repo
Los tipos de mensaje agnósticos al provider en internal/api/types.go solo conocen tres tipos de bloque: BlockText, BlockToolUse, BlockToolResult. Tanto el provider Anthropic como el OpenAI los traducen a/desde sus SDKs en internal/provider/anthropic.go e internal/provider/openai.go. La UI acepta texto plano del usuario y renderiza bloques de texto o de herramienta de vuelta.
Hoy no hay forma de enviar una imagen. Los dos backends lo soportan — Anthropic vía ImageBlockParam (fuente base64 o URL), OpenAI vía la forma de contenido de visión — el harness simplemente no lo expone.
Lo que vas a construir
Un camino para que el usuario adjunte una o más imágenes al siguiente mensaje, con los providers traduciéndolas correctamente.
Pasos sugeridos
-
Extiende el modelo de bloque. Añade
BlockImagea las constantesBlockTypey los campos que necesita:const BlockImage BlockType = "image" type Block struct { // ... campos existentes ... // BlockImage ImageSource string // "base64" o "url" ImageMediaType string // "image/png", "image/jpeg", "image/webp", "image/gif" ImageData string // payload base64 O la URL, según Source }No metas esto a la fuerza en
Text. Mantener los campos separados hace que la traducción en los providers sea obvia. -
Enseña a Anthropic a enviarla. En
anthropic.go, cuando recorres los bloques del mensaje para construir los params del SDK, mapeaBlockImageaanthropic.NewImageBlock(o al constructor base64/URL según tu versión del SDK). El media type es obligatorio; rechaza vacío/no soportado. -
Enseña a OpenAI a enviarla. La entrada de visión de OpenAI usa el array
contentcon objetos{"type": "image_url", "image_url": {"url": "..."}}. Para entradas base64, codifica comodata:<media-type>;base64,<payload>y pásalo por el mismo campo. Documenta el truco de que no todos los modelos OpenAI soportan visión — devuelve un error amigable si el modelo activo no. -
Añade los comandos
/attachy/clear-attach. El patrón de slash command está encommands.go./attach ~/Desktop/screenshot.pnglee el archivo, detecta el MIME type (http.DetectContentTypecon los primeros 512 bytes basta), lo codifica en base64 y prepara unBlockImagepara anteponerlo al siguiente mensaje del usuario./clear-attachdescarta lo preparado./attachsin argumentos lista lo que hay preparado. -
Conecta lo preparado en el envío. Cuando el usuario envía un turno, el bucle del agente antepone los bloques de imagen preparados al bloque de texto. Tras el envío, limpia el área de preparación.
-
Renderiza el adjunto en la TUI. No intentes dibujar la imagen — la mayoría de terminales no pueden. Renderiza un indicador de una línea:
[imagen: screenshot.png · png · 248KB]. El renderizador de transcripción (Ejercicio 4 si lo hiciste) también necesita conocer este bloque. -
Valida. Rechaza archivos de más de ~5 MB (el cap por imagen de Anthropic) con un error claro. Rechaza MIME types no soportados de entrada.
Aceptación
/attach demo.pngseguido dedescribe esta imagenenvía tanto la imagen como el texto al modelo en un único turno, y la respuesta hace referencia a lo que hay en la imagen.- El mismo flujo funciona tras
/provider openai gpt-4o(o cualquier modelo OpenAI con visión). - Un MIME no soportado o un archivo demasiado grande imprime un error claro antes del envío — sin viaje al SDK.
- La transcripción muestra
[imagen: …]en lugar del binario, y las herramientas que recorren el contenido del mensaje (compactación, summarize, save) no petan con el nuevo tipo de bloque.
Extra
- Drag-and-drop en la TUI: detecta cuando la entrada es un URI/path de archivo y autopreparar.
- Una herramienta
screenshotque capture la pantalla en macOS (screencapture -i -c+ lectura del clipboard) y prepare el resultado. - Formato URL:
/attach https://example.com/foo.png— Anthropic acepta URLs directamente; OpenAI también víaimage_url.url. Sálta el encode a base64 en esa ruta. - Imágenes de salida: algunos modelos pueden devolver imágenes generadas. Round-trippéa un bloque de imagen generada de vuelta a la transcripción y escríbela al disco.