1- import base64
2- import contextlib
31import logging
4- from pathlib import Path
52
63from fastapi import APIRouter , Depends , HTTPException
74from fastapi .responses import HTMLResponse , StreamingResponse
85
6+ from app .api .dependencies import get_service
97from app .core .config import settings
108from app .core .security import verify_token
119from app .schemas .requests import ImageRequest
1210from app .schemas .responses import ImageResponse
13- from app .services .gemini_service import get_gemini_service
14- from app .services .litellm_service import get_litellm_service
1511from app .services .model_registry import model_registry
16- from app .services . openai_service import get_openai_service
12+ from app .utils . image import build_openwebui_html_response , read_image_as_base64
1713from app .utils .sse import generate_with_progress
1814
1915logger = logging .getLogger (__name__ )
@@ -49,7 +45,7 @@ async def generate_image(request: ImageRequest, _: None = Depends(verify_token))
4945
5046 # Get service based on provider
5147 try :
52- service = _get_service (request .provider )
48+ service = get_service (request .provider )
5349 except ValueError as e :
5450 raise HTTPException (status_code = 400 , detail = str (e )) from None
5551
@@ -72,48 +68,18 @@ async def generate_image(request: ImageRequest, _: None = Depends(verify_token))
7268
7369 # OpenWebUI mode: return HTML with embedded image for iframe display
7470 if settings .OPENWEBUI_MODE :
75- image_filename = urls [0 ].split ("/" )[- 1 ]
76- image_path = Path (settings .STORAGE_PATH ) / image_filename
77-
78- if not image_path .exists ():
79- raise HTTPException (status_code = 500 , detail = "Generated image file not found" )
80-
81- with open (image_path , "rb" ) as f :
82- image_data = base64 .b64encode (f .read ()).decode ("utf-8" )
83-
84- # Clean up local file if not saving locally
85- if not settings .SAVE_IMAGES_LOCALLY :
86- with contextlib .suppress (Exception ):
87- image_path .unlink ()
88-
89- ext = image_path .suffix .lower ()
90- mime_types = {".png" : "image/png" , ".jpg" : "image/jpeg" , ".jpeg" : "image/jpeg" , ".webp" : "image/webp" }
91- mime_type = mime_types .get (ext , "image/png" )
92-
93- html = f'<img src="data:{ mime_type } ;base64,{ image_data } " style="max-width:100%; height:auto;">'
94- return HTMLResponse (content = html , headers = {"Content-Disposition" : "inline" })
71+ try :
72+ image_data , mime_type = await read_image_as_base64 (urls [0 ])
73+ except FileNotFoundError :
74+ raise HTTPException (status_code = 500 , detail = "Generated image file not found" ) from None
75+ return build_openwebui_html_response (image_data , mime_type )
9576
9677 # Handle response format
9778 if request .response_format == "base64" :
98- # Extract filename from URL and read file
99- image_filename = urls [0 ].split ("/" )[- 1 ]
100- image_path = Path (settings .STORAGE_PATH ) / image_filename
101-
102- if not image_path .exists ():
103- raise HTTPException (status_code = 500 , detail = "Generated image file not found" )
104-
105- with open (image_path , "rb" ) as f :
106- image_data = base64 .b64encode (f .read ()).decode ("utf-8" )
107-
108- # Clean up local file if not saving locally
109- if not settings .SAVE_IMAGES_LOCALLY :
110- with contextlib .suppress (Exception ):
111- image_path .unlink ()
112-
113- # Determine mime type from extension
114- ext = image_path .suffix .lower ()
115- mime_types = {".png" : "image/png" , ".jpg" : "image/jpeg" , ".jpeg" : "image/jpeg" , ".webp" : "image/webp" }
116- mime_type = mime_types .get (ext , "image/png" )
79+ try :
80+ image_data , mime_type = await read_image_as_base64 (urls [0 ])
81+ except FileNotFoundError :
82+ raise HTTPException (status_code = 500 , detail = "Generated image file not found" ) from None
11783
11884 return ImageResponse (
11985 image_base64 = image_data ,
@@ -130,25 +96,12 @@ async def generate_image(request: ImageRequest, _: None = Depends(verify_token))
13096
13197 if request .response_format == "markdown" :
13298 if settings .MARKDOWN_EMBED_IMAGES :
133- # Return markdown with embedded base64 data URI
134- # Open WebUI's ENABLE_CHAT_RESPONSE_BASE64_IMAGE_URL_CONVERSION will convert this
135- image_filename = urls [0 ].split ("/" )[- 1 ]
136- image_path = Path (settings .STORAGE_PATH ) / image_filename
137-
138- if not image_path .exists ():
139- raise HTTPException (status_code = 500 , detail = "Generated image file not found" )
140-
141- with open (image_path , "rb" ) as f :
142- image_data = base64 .b64encode (f .read ()).decode ("utf-8" )
143-
144- # Clean up local file if not saving locally
145- if not settings .SAVE_IMAGES_LOCALLY :
146- with contextlib .suppress (Exception ):
147- image_path .unlink ()
148-
149- ext = image_path .suffix .lower ()
150- mime_types = {".png" : "image/png" , ".jpg" : "image/jpeg" , ".jpeg" : "image/jpeg" , ".webp" : "image/webp" }
151- mime_type = mime_types .get (ext , "image/png" )
99+ try :
100+ image_data , mime_type = await read_image_as_base64 (urls [0 ])
101+ except FileNotFoundError :
102+ raise HTTPException (
103+ status_code = 500 , detail = "Generated image file not found"
104+ ) from None
152105
153106 markdown = f""
154107 return ImageResponse (
@@ -214,7 +167,7 @@ async def generate_image_stream(request: ImageRequest, _: None = Depends(verify_
214167
215168 # Get service based on provider
216169 try :
217- service = _get_service (request .provider )
170+ service = get_service (request .provider )
218171 except ValueError as e :
219172 raise HTTPException (status_code = 400 , detail = str (e )) from None
220173
@@ -260,7 +213,7 @@ async def generate_image_preview(request: ImageRequest, _: None = Depends(verify
260213
261214 # Get service based on provider
262215 try :
263- service = _get_service (request .provider )
216+ service = get_service (request .provider )
264217 except ValueError as e :
265218 raise HTTPException (status_code = 400 , detail = str (e )) from None
266219
@@ -345,26 +298,6 @@ async def generate_image_preview(request: ImageRequest, _: None = Depends(verify
345298 return HTMLResponse (content = html_content , headers = {"Content-Disposition" : "inline" })
346299
347300
348- def _get_service (provider : str ):
349- """
350- Get service instance based on provider.
351- """
352- if provider == "litellm" :
353- if not settings .litellm_available :
354- raise ValueError ("LiteLLM not configured. Set LITELLM_BASE_URL" )
355- return get_litellm_service ()
356- elif provider == "openai" :
357- if not settings .openai_available :
358- raise ValueError ("OpenAI not configured. Set OPENAI_API_KEY" )
359- return get_openai_service ()
360- elif provider == "gemini" :
361- if not settings .gemini_available :
362- raise ValueError ("Gemini not configured. Set GEMINI_API_KEY" )
363- return get_gemini_service ()
364- else :
365- raise ValueError (f"Unknown provider: { provider } " )
366-
367-
368301def _get_default_model (provider : str ) -> str :
369302 """
370303 Get default model for provider based on available models.
0 commit comments