---
name: ask_llm_vision
kind: function
lang: py
domain: core
version: "1.0.0"
purity: impure
signature: "def ask_llm_vision(prompt: str, image_path: str = '', *, image_b64: str = '', media_type: str = '', model: str = 'claude-opus-4-8', system: str = '', max_tokens: int = 4096, echo: bool = False, token: str = '') -> dict"
description: "Pregunta multimodal (imagen + texto) al modelo via la API directa de Anthropic con el token OAuth de Claude Max. Construye un content block [imagen base64, texto] y devuelve dict {ok, text, model, error}. Wrapper sobre stream_anthropic_messages (grupo claude-direct, arranque 0, sin proceso claude). Util para describir/puntuar/clasificar imagenes (p.ej. evaluar una generacion ComfyUI)."
error_type: error_go_core
tags: ["claude-direct", "llm", "anthropic", "vision", "multimodal", "image", "oauth"]
uses_functions:
  - stream_anthropic_messages_py_core
uses_types: []
params:
  - name: prompt
    desc: "Pregunta o instruccion de texto sobre la imagen."
  - name: image_path
    desc: "Ruta a la imagen en disco; se lee y codifica a base64. El media_type se deduce de la extension si no se pasa. Ignorado si se pasa image_b64."
  - name: image_b64
    desc: "Alternativa a image_path: la imagen ya en base64 (sin prefijo data:). Requiere media_type explicito. keyword-only."
  - name: media_type
    desc: "Tipo MIME de la imagen (image/png, image/jpeg, image/webp, image/gif). Obligatorio con image_b64; deducido del path si se omite. keyword-only."
  - name: model
    desc: "Id del modelo Anthropic con vision. Default claude-opus-4-8. keyword-only."
  - name: system
    desc: "System prompt opcional (string vacio = ninguno). keyword-only."
  - name: max_tokens
    desc: "Maximo de tokens de salida. Default 4096. keyword-only."
  - name: echo
    desc: "Si True, vuelca el texto a stdout segun llega (streaming). keyword-only."
  - name: token
    desc: "Token OAuth; si vacio lo carga stream_anthropic_messages automaticamente. keyword-only."
output: "dict {ok, text, model, error}. En exito ok=True y text lleva la respuesta completa; en error ok=False, text vacio y error describe la causa. Nunca lanza excepcion."
file_path: python/functions/core/ask_llm_vision.py
---

# ask_llm_vision

Versión multimodal de [`ask_llm`](ask_llm.md): adjunta una imagen al prompt y devuelve la
respuesta del modelo. Usa la API directa de Anthropic con el token OAuth de Claude Max (sin
proceso `claude`, arranque 0), reutilizando [`stream_anthropic_messages`](stream_anthropic_messages.md),
que ya acepta content blocks multimodales. Cumple la regla `llm_invocation`: SIEMPRE claude-direct,
NUNCA `claude -p`.

## Ejemplo

```bash
# CLI (fn run): describe una imagen
fn run ask_llm_vision "describe esta imagen en una frase" --image ~/ComfyUI/output/demo_00001_.png

# Directo con el venv
python/.venv/bin/python3 python/functions/core/ask_llm_vision.py \
  "que defectos ves en esta cara?" --image /tmp/render.png --model claude-opus-4-8
```

```python
import sys, os
sys.path.insert(0, os.path.join(os.environ["HOME"], "fn_registry", "python", "functions"))
from core.ask_llm_vision import ask_llm_vision

res = ask_llm_vision(
    "Puntua de 0 a 10 el realismo de este retrato y justifica en una frase.",
    image_path="/tmp/portrait.png",
    model="claude-opus-4-8",
)
if res["ok"]:
    print(res["text"])
else:
    print("error:", res["error"])
```

## Cuando usarla

- Cuando necesites el juicio del modelo **sobre una imagen** (describir, puntuar, clasificar,
  comparar, detectar defectos). Caso típico: cerrar el bucle de scoring de una skill ComfyUI —
  generas un PNG y `ask_llm_vision` lo evalúa para alimentar `score_mean`.
- Si solo mandas texto, usa `ask_llm`. Si necesitas tools / loop agéntico, `run_claude_tool_loop_py_core`.
  Si necesitas los eventos crudos (deltas, tool_use), `stream_anthropic_messages_py_core`.

## Gotchas

- **Es impura y hace red**: una request HTTP a `api.anthropic.com` por llamada. Sujeta a los
  rate limits del plan (`HTTP 429` en ráfagas → espacia o reporta el error del dict).
- **media_type obligatorio con `image_b64`**: sin extensión de archivo no se puede deducir; pásalo
  explícito (`image/png`, `image/jpeg`, `image/webp`, `image/gif`).
- **No lanza excepción**: errores (imagen inexistente, fallo HTTP) salen como `{ok: False, error: ...}`,
  no como `raise`. Comprueba siempre `res["ok"]` antes de usar `res["text"]`.
- **Tamaño de la imagen**: imágenes muy grandes inflan los tokens de entrada (la API escala/limita
  internamente). Para puntuar en lote conviene reducir la resolución antes.
- **Modelo con visión**: usa un modelo multimodal (`claude-opus-4-8` por defecto). Un id inexistente
  da `404 not_found_error` propagado en `error`.
