From 446b659c543d3c966bb4dc670f3d322857f4a7cd Mon Sep 17 00:00:00 2001 From: aran-yogesh Date: Sun, 22 Feb 2026 18:57:49 -0800 Subject: [PATCH] refactor: keep image URLs in text and centralize image auth --- .../agent/middleware/check_message_queue.py | 52 ++++++++----------- apps/agent/agent/utils/multimodal.py | 26 +++++----- apps/agent/agent/webapp.py | 18 ++----- 3 files changed, 40 insertions(+), 56 deletions(-) diff --git a/apps/agent/agent/middleware/check_message_queue.py b/apps/agent/agent/middleware/check_message_queue.py index 89cc49a3..66178f83 100644 --- a/apps/agent/agent/middleware/check_message_queue.py +++ b/apps/agent/agent/middleware/check_message_queue.py @@ -27,6 +27,28 @@ class LinearNotifyState(AgentState): linear_messages_sent_count: int +async def _build_blocks_from_payload( + payload: dict[str, Any], +) -> list[dict[str, Any]]: + text = payload.get("text", "") + image_urls = payload.get("image_urls", []) or [] + blocks: list[dict[str, Any]] = [] + if text: + blocks.append({"type": "text", "text": text}) + + if not image_urls: + return blocks + linear_api_key = os.environ.get("LINEAR_API_KEY", "") + async with httpx.AsyncClient() as client: + for image_url in image_urls: + image_block = await fetch_image_block( + image_url, client, linear_api_key=linear_api_key + ) + if image_block: + blocks.append(image_block) + return blocks + + @before_model(state_schema=LinearNotifyState) async def check_message_queue_before_model( # noqa: PLR0911 state: LinearNotifyState, # noqa: ARG001 @@ -84,36 +106,6 @@ async def check_message_queue_before_model( # noqa: PLR0911 thread_id, ) - async def _build_blocks_from_payload( - payload: dict[str, Any], - ) -> list[dict[str, Any]]: - text = payload.get("text", "") - image_urls = payload.get("image_urls", []) or [] - blocks: list[dict[str, Any]] = [] - if text: - blocks.append({"type": "text", "text": text}) - - if not image_urls: - return blocks - linear_api_key = os.environ.get("LINEAR_API_KEY", "") - async with httpx.AsyncClient() as client: - for image_url in image_urls: - headers = None - if "uploads.linear.app" in image_url: - if linear_api_key: - headers = {"Authorization": linear_api_key} - else: - logger.warning( - "LINEAR_API_KEY not set; cannot authenticate image fetch for %s", - image_url, - ) - image_block = await fetch_image_block( - image_url, client, headers=headers - ) - if image_block: - blocks.append(image_block) - return blocks - content_blocks: list[dict[str, Any]] = [] for msg in queued_messages: content = msg.get("content") diff --git a/apps/agent/agent/utils/multimodal.py b/apps/agent/agent/utils/multimodal.py index a8cb05cb..4443cc78 100644 --- a/apps/agent/agent/utils/multimodal.py +++ b/apps/agent/agent/utils/multimodal.py @@ -5,6 +5,7 @@ from __future__ import annotations import base64 import logging import mimetypes +import os import re from typing import Any @@ -35,26 +36,27 @@ def extract_image_urls(text: str) -> list[str]: return deduped -def strip_image_urls(text: str, image_urls: list[str]) -> str: - """Remove markdown image links and direct image URLs from text.""" - if not text or not image_urls: - return text - - logger.debug("Stripping %d image URL(s) from text", len(image_urls)) - text = IMAGE_MARKDOWN_RE.sub("", text) - for url in image_urls: - text = text.replace(url, "") - return text - async def fetch_image_block( image_url: str, client: httpx.AsyncClient, - headers: dict[str, str] | None = None, + *, + linear_api_key: str | None = None, ) -> dict[str, Any] | None: """Fetch image bytes and build an image content block.""" try: logger.debug("Fetching image from %s", image_url) + headers = None + if "uploads.linear.app" in image_url: + if linear_api_key is None: + linear_api_key = os.environ.get("LINEAR_API_KEY", "") + if linear_api_key: + headers = {"Authorization": linear_api_key} + else: + logger.warning( + "LINEAR_API_KEY not set; cannot authenticate image fetch for %s", + image_url, + ) response = await client.get(image_url, headers=headers) response.raise_for_status() content_type = response.headers.get("Content-Type", "").split(";")[0].strip() diff --git a/apps/agent/agent/webapp.py b/apps/agent/agent/webapp.py index c3b308c5..1b6c0270 100644 --- a/apps/agent/agent/webapp.py +++ b/apps/agent/agent/webapp.py @@ -15,7 +15,7 @@ from langgraph_sdk import get_client # Local import for encryption from .encryption import encrypt_token -from .utils.multimodal import extract_image_urls, fetch_image_block, strip_image_urls +from .utils.multimodal import extract_image_urls, fetch_image_block logger = logging.getLogger(__name__) @@ -581,7 +581,6 @@ async def process_linear_issue( # noqa: PLR0912, PLR0915 description_image_urls = extract_image_urls(description) if description_image_urls: image_urls.extend(description_image_urls) - description = strip_image_urls(description, description_image_urls) logger.debug( "Found %d image URL(s) in issue description", len(description_image_urls), @@ -643,7 +642,6 @@ async def process_linear_issue( # noqa: PLR0912, PLR0915 body_image_urls = extract_image_urls(body) if body_image_urls: image_urls.extend(body_image_urls) - body = strip_image_urls(body, body_image_urls) logger.debug( "Found %d image URL(s) in comment by %s", len(body_image_urls), @@ -661,7 +659,6 @@ async def process_linear_issue( # noqa: PLR0912, PLR0915 trigger_image_urls = extract_image_urls(trigger_body) if trigger_image_urls: image_urls.extend(trigger_image_urls) - trigger_body = strip_image_urls(trigger_body, trigger_image_urls) logger.debug( "Found %d image URL(s) in triggering comment by %s", len(trigger_image_urls), @@ -696,16 +693,9 @@ async def process_linear_issue( # noqa: PLR0912, PLR0915 async with httpx.AsyncClient() as client: for image_url in image_urls: - headers = None - if "uploads.linear.app" in image_url: - if LINEAR_API_KEY: - headers = {"Authorization": LINEAR_API_KEY} - else: - logger.warning( - "LINEAR_API_KEY not set; cannot authenticate image fetch for %s", - image_url, - ) - image_block = await fetch_image_block(image_url, client, headers=headers) + image_block = await fetch_image_block( + image_url, client, linear_api_key=LINEAR_API_KEY + ) if image_block: content_blocks.append(image_block) logger.info("Built %d content block(s) for prompt", len(content_blocks))