diff --git a/surfsense_backend/app/agents/chat/multi_agent_chat/subagents/builtins/deliverables/tools/generate_image.py b/surfsense_backend/app/agents/chat/multi_agent_chat/subagents/builtins/deliverables/tools/generate_image.py index 736c508ff..ae7e33428 100644 --- a/surfsense_backend/app/agents/chat/multi_agent_chat/subagents/builtins/deliverables/tools/generate_image.py +++ b/surfsense_backend/app/agents/chat/multi_agent_chat/subagents/builtins/deliverables/tools/generate_image.py @@ -240,24 +240,23 @@ def create_generate_image_tool( error="No images were generated", ) + # Update all image URLs in response_dict to be absolute (for the serving endpoint) + from urllib.parse import urlparse + for image in images: + if image.get("url"): + raw_url: str = image["url"] + if raw_url.startswith("/") and provider_base_url: + parsed = urlparse(provider_base_url) + origin = f"{parsed.scheme}://{parsed.netloc}" + image["url"] = f"{origin}{raw_url}" # Update the stored dict! + first_image = images[0] revised_prompt = first_image.get("revised_prompt", prompt) # b64_json (e.g. gpt-image-1) is served via our backend endpoint so # megabytes of base64 don't bloat the LLM context. - # Some OpenAI-compatible backends (e.g. Xinference) return a relative - # URL like /files/image.png. Browsers can't resolve these, so we - # prepend the provider's base origin when the URL starts with "/". if first_image.get("url"): - raw_url: str = first_image["url"] - if raw_url.startswith("/") and provider_base_url: - from urllib.parse import urlparse - - parsed = urlparse(provider_base_url) - origin = f"{parsed.scheme}://{parsed.netloc}" - image_url = f"{origin}{raw_url}" - else: - image_url = raw_url + image_url = first_image["url"] elif first_image.get("b64_json"): backend_url = config.BACKEND_URL or "http://localhost:8000" image_url = ( diff --git a/surfsense_backend/app/routes/image_generation_routes.py b/surfsense_backend/app/routes/image_generation_routes.py index cc3e51ed5..5bd058cb1 100644 --- a/surfsense_backend/app/routes/image_generation_routes.py +++ b/surfsense_backend/app/routes/image_generation_routes.py @@ -213,7 +213,7 @@ async def _execute_image_generation( ) # Store response - image_gen.response_data = ( + response_dict = ( response.model_dump() if hasattr(response, "model_dump") else dict(response) ) if not image_gen.model and hasattr(response, "_hidden_params"): @@ -221,6 +221,20 @@ async def _execute_image_generation( if isinstance(hidden, dict) and hidden.get("model"): image_gen.model = hidden["model"] + # Fix relative URLs in response data (for the serving endpoint) + from urllib.parse import urlparse + images = response_dict.get("data", []) + provider_base_url = resolved_kwargs.get("api_base") + for image in images: + if image.get("url"): + raw_url: str = image["url"] + if raw_url.startswith("/") and provider_base_url: + parsed = urlparse(provider_base_url) + origin = f"{parsed.scheme}://{parsed.netloc}" + image["url"] = f"{origin}{raw_url}" + + image_gen.response_data = response_dict + # ============================================================================= # Image Generation Execution + Results CRUD diff --git a/surfsense_web/components/new-chat/model-selector.tsx b/surfsense_web/components/new-chat/model-selector.tsx index c10bfd862..e4ae427aa 100644 --- a/surfsense_web/components/new-chat/model-selector.tsx +++ b/surfsense_web/components/new-chat/model-selector.tsx @@ -272,6 +272,7 @@ export function ModelSelector({ type="button" variant="ghost" size="sm" + aria-label="Select chat model" className={cn( "h-8 min-w-0 gap-2 rounded-md px-3 text-muted-foreground transition-colors", "select-none",