Skip to content

Commit ad80069

Browse files
committed
feat: add OPENWEBUI_MODE for OpenWebUI tool integration
- Add OPENWEBUI_MODE config that returns ["data:mime;base64,..."] format - OpenWebUI extracts this as a file and displays the image correctly - Change default response_format from "markdown" to "url" - Keep MARKDOWN_EMBED_IMAGES for backwards compatibility - Update CONFIGURATION.md with response format reference table
1 parent 9b05fb5 commit ad80069

5 files changed

Lines changed: 86 additions & 16 deletions

File tree

CONFIGURATION.md

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ Complete configuration reference for the Image Generation API.
4646
**`SAVE_IMAGES_LOCALLY`**
4747
- Save images to local disk
4848
- Default: `true`
49-
- Set to `false` when using `MARKDOWN_EMBED_IMAGES=true` with Open WebUI
49+
- Set to `false` when using `OPENWEBUI_MODE=true` or `MARKDOWN_EMBED_IMAGES=true`
5050
- When `false`, images are temporarily written for base64 conversion then deleted
5151

5252
### Model Defaults
@@ -58,12 +58,24 @@ Complete configuration reference for the Image Generation API.
5858

5959
### Response Configuration
6060

61+
**`OPENWEBUI_MODE`**
62+
- Enable Open WebUI tool integration mode
63+
- Default: `false`
64+
- When `true`:
65+
- Returns `["data:image/png;base64,..."]` (list with data URI)
66+
- OpenWebUI extracts this as a file and displays the image
67+
- Overrides `response_format` parameter and `MARKDOWN_EMBED_IMAGES`
68+
- Works with OpenWebUI's tool response handling
69+
- When `false`:
70+
- Normal API behavior based on `response_format` parameter
71+
- Requires: OpenWebUI with `ENABLE_CHAT_RESPONSE_BASE64_IMAGE_URL_CONVERSION=true`
72+
6173
**`MARKDOWN_EMBED_IMAGES`**
6274
- Embed images as base64 data URI in markdown responses
6375
- Default: `false`
64-
- When `true`: Returns `![image](data:image/png;base64,...)`
65-
- When `false`: Returns `![image](http://...)`
66-
- Set to `true` for Open WebUI integration with `ENABLE_CHAT_RESPONSE_BASE64_IMAGE_URL_CONVERSION`
76+
- When `true` and `response_format="markdown"`: Returns `{"markdown": "![image](data:image/png;base64,...)"}`
77+
- When `false` and `response_format="markdown"`: Returns `{"markdown": "![image](http://...)"}`
78+
- Note: Ignored when `OPENWEBUI_MODE=true`
6779

6880
### Security
6981

@@ -341,3 +353,13 @@ Use after LiteLLM configuration changes.
341353
- Verify `STORAGE_PATH` has write permissions
342354
- Check `BASE_URL` matches your deployment
343355
- For Docker, ensure volume is mounted correctly
356+
357+
## Response Format Reference
358+
359+
| `OPENWEBUI_MODE` | `MARKDOWN_EMBED_IMAGES` | `response_format` | Response |
360+
|------------------|-------------------------|-------------------|----------|
361+
| `true` | (ignored) | (ignored) | `["data:image/png;base64,..."]` |
362+
| `false` | `false` | `url` (default) | `{"image_url": "http://..."}` |
363+
| `false` | `false` | `base64` | `{"image_base64": "...", "mime_type": "..."}` |
364+
| `false` | `false` | `markdown` | `{"markdown": "![img](http://...)"}` |
365+
| `false` | `true` | `markdown` | `{"markdown": "![img](data:...;base64,...)"}` |

app/api/routes/edit.py

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -165,19 +165,20 @@ async def edit_image(
165165

166166
@router.post(
167167
"/json",
168-
response_model=ImageResponse,
168+
response_model=None,
169169
operation_id="edit_image_json",
170170
summary="Edit image (JSON)",
171171
description=(
172172
"Edit an existing image using a JSON request body. "
173173
"This endpoint is designed for LLM tool use - provide the image_url "
174-
"from a previous generate_image response. Uses prompt-based editing."
174+
"from a previous generate_image response. Uses prompt-based editing. "
175+
"Response format depends on OPENWEBUI_MODE setting."
175176
),
176177
)
177178
async def edit_image_json(
178179
request: ImageEditRequest,
179180
_: None = Depends(verify_token),
180-
) -> ImageResponse:
181+
):
181182
"""
182183
Edit an image using JSON request body (LLM-friendly endpoint).
183184
"""
@@ -218,7 +219,29 @@ async def edit_image_json(
218219
if not urls:
219220
raise HTTPException(status_code=500, detail="No images generated")
220221

221-
# Return markdown format (same as generate endpoint)
222+
# OpenWebUI mode: return list with data URI for tool integration
223+
if settings.OPENWEBUI_MODE:
224+
image_filename = urls[0].split("/")[-1]
225+
image_path = Path(settings.STORAGE_PATH) / image_filename
226+
227+
if not image_path.exists():
228+
raise HTTPException(status_code=500, detail="Edited image file not found")
229+
230+
with open(image_path, "rb") as f:
231+
image_data = base64.b64encode(f.read()).decode("utf-8")
232+
233+
# Clean up local file if not saving locally
234+
if not settings.SAVE_IMAGES_LOCALLY:
235+
with contextlib.suppress(Exception):
236+
image_path.unlink()
237+
238+
ext = image_path.suffix.lower()
239+
mime_types = {".png": "image/png", ".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".webp": "image/webp"}
240+
mime_type = mime_types.get(ext, "image/png")
241+
242+
return [f"data:{mime_type};base64,{image_data}"]
243+
244+
# Return based on MARKDOWN_EMBED_IMAGES setting
222245
if settings.MARKDOWN_EMBED_IMAGES:
223246
# Return markdown with embedded base64 data URI
224247
image_filename = urls[0].split("/")[-1]

app/api/routes/generate.py

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,17 @@
2323

2424
@router.post(
2525
"",
26-
response_model=ImageResponse,
26+
response_model=None,
2727
operation_id="generate_image",
2828
summary="Generate image",
2929
description=(
3030
"Generate an image from a text prompt. "
3131
"Uses LiteLLM proxy by default for cost tracking, with fallback to direct API calls. "
32-
"Supports OpenAI DALL-E and Google Gemini models."
32+
"Supports OpenAI DALL-E and Google Gemini models. "
33+
"Response format depends on OPENWEBUI_MODE and response_format parameter."
3334
),
3435
)
35-
async def generate_image(request: ImageRequest, _: None = Depends(verify_token)) -> ImageResponse:
36+
async def generate_image(request: ImageRequest, _: None = Depends(verify_token)):
3637
"""
3738
Generate image with standard JSON response.
3839
"""
@@ -69,6 +70,28 @@ async def generate_image(request: ImageRequest, _: None = Depends(verify_token))
6970
if not urls:
7071
raise HTTPException(status_code=500, detail="No images generated")
7172

73+
# OpenWebUI mode: return list with data URI for tool integration
74+
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+
return [f"data:{mime_type};base64,{image_data}"]
94+
7295
# Handle response format
7396
if request.response_format == "base64":
7497
# Extract filename from URL and read file

app/core/config.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,10 @@ 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 when using MARKDOWN_EMBED_IMAGES with Open WebUI
46+
SAVE_IMAGES_LOCALLY: bool = True # Set to false when using OPENWEBUI_MODE or MARKDOWN_EMBED_IMAGES
4747

4848
# Response Configuration
49+
OPENWEBUI_MODE: bool = False # Return ["data:mime;base64,..."] for OpenWebUI tool integration
4950
MARKDOWN_EMBED_IMAGES: bool = False # Embed images as base64 data URI in markdown responses
5051

5152
# Security (Optional)

app/schemas/requests.py

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

5757
response_format: Literal["url", "base64", "markdown"] = Field(
58-
default="markdown",
58+
default="url",
5959
description=(
6060
"Response format for generated images. "
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."
61+
"'url': Returns image URL (default). "
62+
"'base64': Returns base64 encoded data in JSON. "
63+
"'markdown': Returns markdown with image. "
64+
"Note: Ignored when OPENWEBUI_MODE=true."
6465
),
6566
)
6667

0 commit comments

Comments
 (0)