Skip to content

Commit 2028018

Browse files
committed
feat: simplify image response with MARKDOWN_EMBED_IMAGES config
Remove Open WebUI integration (OPENWEBUI_BASE_URL, OPENWEBUI_API_KEY, SAVE_IMAGES_LOCALLY). Images are now always stored locally. New approach for Open WebUI: Set MARKDOWN_EMBED_IMAGES=true to return base64 data URIs in markdown responses. Open WebUI's ENABLE_CHAT_RESPONSE_BASE64_IMAGE_URL_CONVERSION will automatically convert these to file URLs in the user's context. Changes: - Add MARKDOWN_EMBED_IMAGES config (default: false) - Make markdown the default response_format - Remove openwebui_service.py and related logic - Simplify storage_service.py
1 parent 0992562 commit 2028018

10 files changed

Lines changed: 119 additions & 360 deletions

File tree

.env.example

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,22 @@ GEMINI_API_KEY=
2626
# Path to store generated images
2727
STORAGE_PATH=./generated_images
2828

29-
# Save images locally (default: true)
30-
# Set to false if using Open WebUI integration exclusively
31-
SAVE_IMAGES_LOCALLY=true
32-
33-
# IMPORTANT: URL where THIS API serves images (not Open WebUI or other services!)
34-
# This should be the URL where this image-generation API is accessible.
29+
# URL where THIS API serves images
3530
# Examples:
3631
# - Local: http://localhost:8000
3732
# - Docker: http://openapi-image-gen:8000 (use container name)
3833
# - External: https://images.example.com
3934
IMAGE_BASE_URL=http://localhost:8000
4035

36+
# -----------------------------------------------------------------------------
37+
# Response Configuration
38+
# -----------------------------------------------------------------------------
39+
# Embed images as base64 data URI in markdown responses (default: false)
40+
# When true: Returns ![image](data:image/png;base64,...)
41+
# When false: Returns ![image](http://...)
42+
# Set to true for Open WebUI integration with ENABLE_CHAT_RESPONSE_BASE64_IMAGE_URL_CONVERSION
43+
MARKDOWN_EMBED_IMAGES=false
44+
4145
# -----------------------------------------------------------------------------
4246
# Security (Optional)
4347
# -----------------------------------------------------------------------------
@@ -57,18 +61,6 @@ FILTER_IMAGE_MODELS=true
5761
# Cache TTL for model list in seconds (default: 3600 = 1 hour)
5862
MODEL_CACHE_TTL=3600
5963

60-
# -----------------------------------------------------------------------------
61-
# Open WebUI Integration (Optional)
62-
# -----------------------------------------------------------------------------
63-
# When configured, images are uploaded to Open WebUI's file storage.
64-
# This ensures images are accessible from anywhere (internal/external).
65-
66-
# Open WebUI instance URL (e.g., https://chat.mydomain.com)
67-
OPENWEBUI_BASE_URL=
68-
69-
# API key from Open WebUI (Settings > Account > API Keys)
70-
OPENWEBUI_API_KEY=
71-
7264
# -----------------------------------------------------------------------------
7365
# Server Configuration
7466
# -----------------------------------------------------------------------------

CONFIGURATION.md

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,6 @@ Complete configuration reference for the Image Generation API.
3737
- Default: `./generated_images`
3838
- Docker: Use volume mount (e.g., `/app/generated_images`)
3939

40-
**`SAVE_IMAGES_LOCALLY`**
41-
- Whether to save images to local storage
42-
- Default: `true`
43-
- Set to `false` if using Open WebUI integration exclusively
44-
- When `false`, images are only uploaded to Open WebUI (no local backup)
45-
4640
**`IMAGE_BASE_URL`**
4741
- Public base URL for serving images
4842
- Default: `http://localhost:8000`
@@ -56,19 +50,14 @@ Complete configuration reference for the Image Generation API.
5650
- Example: `gpt-image-1`, `dall-e-3`
5751
- Optional - if not set, first available model is used
5852

59-
### Open WebUI Integration
60-
61-
**`OPENWEBUI_BASE_URL`**
62-
- Open WebUI instance URL
63-
- Example: `https://chat.mydomain.com`
64-
- When set (with API key), images are uploaded to Open WebUI's file storage
65-
66-
**`OPENWEBUI_API_KEY`**
67-
- API key from Open WebUI
68-
- Get from: Open WebUI Settings > Account > API Keys
69-
- Required together with `OPENWEBUI_BASE_URL`
53+
### Response Configuration
7054

71-
When both are configured, images are automatically uploaded to Open WebUI. This ensures images work from anywhere - no more URL accessibility issues with Docker or external access.
55+
**`MARKDOWN_EMBED_IMAGES`**
56+
- Embed images as base64 data URI in markdown responses
57+
- Default: `false`
58+
- When `true`: Returns `![image](data:image/png;base64,...)`
59+
- When `false`: Returns `![image](http://...)`
60+
- Set to `true` for Open WebUI integration with `ENABLE_CHAT_RESPONSE_BASE64_IMAGE_URL_CONVERSION`
7261

7362
### Security
7463

app/api/routes/edit.py

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -217,18 +217,50 @@ async def edit_image_json(
217217
if not urls:
218218
raise HTTPException(status_code=500, detail="No images generated")
219219

220-
return ImageResponse(
221-
image_url=urls[0],
222-
prompt=request.prompt,
223-
model=model,
224-
provider=request.provider,
225-
metadata={
226-
"all_urls": urls if len(urls) > 1 else None,
227-
"n": len(urls),
228-
"edit": True,
229-
"source_image": request.image_url,
230-
},
231-
)
220+
# Return markdown format (same as generate endpoint)
221+
if settings.MARKDOWN_EMBED_IMAGES:
222+
# Return markdown with embedded base64 data URI
223+
image_filename = urls[0].split("/")[-1]
224+
image_path = Path(settings.STORAGE_PATH) / image_filename
225+
226+
if not image_path.exists():
227+
raise HTTPException(status_code=500, detail="Edited image file not found")
228+
229+
with open(image_path, "rb") as f:
230+
image_data = base64.b64encode(f.read()).decode("utf-8")
231+
232+
ext = image_path.suffix.lower()
233+
mime_types = {".png": "image/png", ".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".webp": "image/webp"}
234+
mime_type = mime_types.get(ext, "image/png")
235+
236+
markdown = f"![Edited image](data:{mime_type};base64,{image_data})"
237+
return ImageResponse(
238+
markdown=markdown,
239+
prompt=request.prompt,
240+
model=model,
241+
provider=request.provider,
242+
metadata={
243+
"n": len(urls),
244+
"edit": True,
245+
"source_image": request.image_url,
246+
},
247+
)
248+
else:
249+
# Return markdown with image URL
250+
markdown = f"![Edited image]({urls[0]})"
251+
return ImageResponse(
252+
markdown=markdown,
253+
image_url=urls[0],
254+
prompt=request.prompt,
255+
model=model,
256+
provider=request.provider,
257+
metadata={
258+
"all_urls": urls if len(urls) > 1 else None,
259+
"n": len(urls),
260+
"edit": True,
261+
"source_image": request.image_url,
262+
},
263+
)
232264

233265

234266
def _get_service(provider: str):

app/api/routes/generate.py

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -99,20 +99,49 @@ async def generate_image(request: ImageRequest, _: None = Depends(verify_token))
9999
)
100100

101101
if request.response_format == "markdown":
102-
# Return ready-to-use markdown with image URL
103-
markdown = f"![Generated image]({urls[0]})"
104-
return ImageResponse(
105-
markdown=markdown,
106-
image_url=urls[0],
107-
prompt=request.prompt,
108-
model=model,
109-
provider=request.provider,
110-
metadata={
111-
"aspect_ratio": request.aspect_ratio,
112-
"quality": request.quality,
113-
"n": len(urls),
114-
},
115-
)
102+
if settings.MARKDOWN_EMBED_IMAGES:
103+
# Return markdown with embedded base64 data URI
104+
# Open WebUI's ENABLE_CHAT_RESPONSE_BASE64_IMAGE_URL_CONVERSION will convert this
105+
image_filename = urls[0].split("/")[-1]
106+
image_path = Path(settings.STORAGE_PATH) / image_filename
107+
108+
if not image_path.exists():
109+
raise HTTPException(status_code=500, detail="Generated image file not found")
110+
111+
with open(image_path, "rb") as f:
112+
image_data = base64.b64encode(f.read()).decode("utf-8")
113+
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")
117+
118+
markdown = f"![Generated image](data:{mime_type};base64,{image_data})"
119+
return ImageResponse(
120+
markdown=markdown,
121+
prompt=request.prompt,
122+
model=model,
123+
provider=request.provider,
124+
metadata={
125+
"aspect_ratio": request.aspect_ratio,
126+
"quality": request.quality,
127+
"n": len(urls),
128+
},
129+
)
130+
else:
131+
# Return markdown with image URL
132+
markdown = f"![Generated image]({urls[0]})"
133+
return ImageResponse(
134+
markdown=markdown,
135+
image_url=urls[0],
136+
prompt=request.prompt,
137+
model=model,
138+
provider=request.provider,
139+
metadata={
140+
"aspect_ratio": request.aspect_ratio,
141+
"quality": request.quality,
142+
"n": len(urls),
143+
},
144+
)
116145

117146
return ImageResponse(
118147
image_url=urls[0],

app/core/config.py

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@ class Settings(BaseSettings):
4343
# Storage Configuration
4444
STORAGE_PATH: str = "./generated_images"
4545
IMAGE_BASE_URL: str = "http://localhost:8000" # URL where this API serves images
46-
SAVE_IMAGES_LOCALLY: bool = True # Set to false if using Open WebUI exclusively
46+
47+
# Response Configuration
48+
MARKDOWN_EMBED_IMAGES: bool = False # Embed images as base64 data URI in markdown responses
4749

4850
# Security (Optional)
4951
API_BEARER_TOKEN: str | None = None
@@ -53,10 +55,6 @@ class Settings(BaseSettings):
5355
FILTER_IMAGE_MODELS: bool = True # Only return image generation models from LiteLLM
5456
DEFAULT_MODEL: str | None = None # Default model for image generation
5557

56-
# Open WebUI Integration (optional)
57-
OPENWEBUI_BASE_URL: str | None = None # e.g. https://chat.mydomain.com
58-
OPENWEBUI_API_KEY: str | None = None # API key from Open WebUI settings
59-
6058
# Server Configuration
6159
HOST: str = "0.0.0.0"
6260
PORT: int = 8000
@@ -76,10 +74,5 @@ def gemini_available(self) -> bool:
7674
"""Check if Gemini direct access is available."""
7775
return bool(self.GEMINI_API_KEY)
7876

79-
@property
80-
def openwebui_available(self) -> bool:
81-
"""Check if Open WebUI integration is configured."""
82-
return bool(self.OPENWEBUI_BASE_URL and self.OPENWEBUI_API_KEY)
83-
8477

8578
settings = Settings()

app/schemas/requests.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,12 @@ class ImageRequest(BaseModel):
5555
)
5656

5757
response_format: Literal["url", "base64", "markdown"] = Field(
58-
default="url",
58+
default="markdown",
5959
description=(
6060
"Response format for generated images. "
61-
"'url': Returns image URL (for web apps). "
62-
"'base64': Returns base64 encoded data (for offline/serverless). "
63-
"'markdown': Returns ready-to-use markdown with image URL (for chat/LLM integrations)."
61+
"'markdown': Returns markdown with image (default, best for LLM/chat integrations). "
62+
"'url': Returns image URL only. "
63+
"'base64': Returns base64 encoded data in JSON."
6464
),
6565
)
6666

app/services/openwebui_service.py

Lines changed: 0 additions & 86 deletions
This file was deleted.

0 commit comments

Comments
 (0)