from __future__ import annotations import base64 from typing import TYPE_CHECKING, Any from yuxi.utils.logging_config import logger if TYPE_CHECKING: from .client import UrbitClient async def analyze_image( client: UrbitClient, image_url: str, prompt: str = "Describe this image in detail.", *, vision_api_url: str | None = None, vision_api_key: str | None = None, ) -> str | None: try: from .media import download_media image_data, content_type = await download_media(client, image_url) if not image_data: logger.warning(f"[Urbit] Vision: failed to download image from {image_url}") return None logger.info(f"[Urbit] Vision: downloaded {len(image_data)} bytes ({content_type})") return await _invoke_vision_pipeline( image_data, content_type, prompt, vision_api_url=vision_api_url, vision_api_key=vision_api_key, ) except Exception as e: logger.warning(f"[Urbit] Vision analysis failed: {e}") return None async def _invoke_vision_pipeline( image_data: bytes, content_type: str, prompt: str, *, vision_api_url: str | None = None, vision_api_key: str | None = None, ) -> str | None: if not vision_api_url: vision_api_url = "https://api.openai.com/v1/chat/completions" base64_image = base64.b64encode(image_data).decode("utf-8") data_url = f"data:{content_type};base64,{base64_image}" payload = { "model": "gpt-4o", "messages": [ { "role": "user", "content": [ {"type": "text", "text": prompt}, {"type": "image_url", "image_url": {"url": data_url}}, ], } ], "max_tokens": 300, } headers: dict[str, str] = {"Content-Type": "application/json"} if vision_api_key: headers["Authorization"] = f"Bearer {vision_api_key}" import httpx async with httpx.AsyncClient(timeout=httpx.Timeout(60.0)) as http: r = await http.post(vision_api_url, json=payload, headers=headers) r.raise_for_status() data = r.json() choice = data.get("choices", [{}])[0] message = choice.get("message", {}) result = message.get("content", "") if result: logger.info(f"[Urbit] Vision: analysis complete ({len(result)} chars)") return result or None async def build_vision_context( image_urls: list[str], prompt: str = "What do you see in these images?", ) -> dict[str, Any]: return { "images": [{"url": url} for url in image_urls], "prompt": prompt, "source": "urbit_vision", }