mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-10-31 02:02:22 +01:00
fix(images): preserve explicit medium quality requests
Use configured quality only for omitted arguments in generation, MCP and edit helpers. Keep explicit medium choices from being overwritten by a low or high default.
This commit is contained in:
@@ -33,7 +33,7 @@ async def list_tools() -> list[Tool]:
|
||||
"prompt": {"type": "string", "description": "Image description prompt"},
|
||||
"model": {"type": "string", "description": "Model name (auto-detects if omitted)"},
|
||||
"size": {"type": "string", "description": "Image size (default 1024x1024)"},
|
||||
"quality": {"type": "string", "description": "Quality: low, medium, high, auto (default medium)"},
|
||||
"quality": {"type": "string", "description": "Quality: low, medium, high, auto (default configured quality, otherwise medium)"},
|
||||
},
|
||||
"required": ["prompt"],
|
||||
},
|
||||
@@ -66,7 +66,7 @@ async def call_tool(name: str, arguments: dict) -> list[TextContent]:
|
||||
|
||||
if not model_spec:
|
||||
model_spec = _settings.get("image_model", "")
|
||||
if quality == "medium" and _settings.get("image_quality"):
|
||||
if "quality" not in arguments and _settings.get("image_quality"):
|
||||
quality = _settings["image_quality"]
|
||||
|
||||
# Auto-detect best available image model
|
||||
|
||||
@@ -1207,7 +1207,7 @@ async def do_generate_image(content: str, session_id: Optional[str] = None, owne
|
||||
Line 1: prompt describing the image
|
||||
Line 2: model name (optional, default auto-detects: prefers gpt-image-1.5 > gpt-image-1)
|
||||
Line 3: size (optional, defaults to 1024x1024)
|
||||
Line 4: quality (optional, defaults to medium — options: low, medium, high, auto)
|
||||
Line 4: quality (optional, defaults to configured quality or medium — low, medium, high, auto)
|
||||
"""
|
||||
import base64
|
||||
import httpx
|
||||
@@ -1225,13 +1225,13 @@ async def do_generate_image(content: str, session_id: Optional[str] = None, owne
|
||||
prompt = str(args.get('prompt') or '').strip()
|
||||
model_spec = str(args.get('model') or '').strip()
|
||||
size = str(args.get('size') or '1024x1024')
|
||||
quality = str(args.get('quality') or 'medium')
|
||||
quality = str(args.get('quality') or '')
|
||||
else:
|
||||
lines = content.strip().split("\n")
|
||||
prompt = lines[0].strip() if lines else ""
|
||||
model_spec = lines[1].strip() if len(lines) > 1 and lines[1].strip() else ""
|
||||
size = lines[2].strip() if len(lines) > 2 and lines[2].strip() else "1024x1024"
|
||||
quality = lines[3].strip() if len(lines) > 3 and lines[3].strip() else "medium"
|
||||
quality = lines[3].strip() if len(lines) > 3 and lines[3].strip() else ""
|
||||
|
||||
if not prompt:
|
||||
return {"error": "Image prompt is required (line 1)"}
|
||||
@@ -1249,8 +1249,8 @@ async def do_generate_image(content: str, session_id: Optional[str] = None, owne
|
||||
# Use admin-configured model/quality if not specified by the tool call
|
||||
if not model_spec:
|
||||
model_spec = _settings.get("image_model", "")
|
||||
if quality == "medium" and _settings.get("image_quality"):
|
||||
quality = _settings["image_quality"]
|
||||
if not quality:
|
||||
quality = _settings.get("image_quality") or "medium"
|
||||
|
||||
# Auto-detect best available image model if still not set
|
||||
if not model_spec:
|
||||
@@ -1482,7 +1482,7 @@ async def do_edit_image(
|
||||
session_id: Optional[str] = None,
|
||||
owner: Optional[str] = None,
|
||||
size: str = "auto",
|
||||
quality: str = "medium",
|
||||
quality: Optional[str] = None,
|
||||
progress_callback: Optional[Callable[[Dict[str, Any]], Awaitable[None]]] = None,
|
||||
incognito: bool = False,
|
||||
) -> Dict:
|
||||
@@ -1509,8 +1509,8 @@ async def do_edit_image(
|
||||
|
||||
if not model_spec:
|
||||
model_spec = _settings.get("image_model", "")
|
||||
if quality == "medium" and _settings.get("image_quality"):
|
||||
quality = _settings["image_quality"]
|
||||
if quality is None:
|
||||
quality = _settings.get("image_quality") or "medium"
|
||||
if not model_spec:
|
||||
return {"error": "No image model selected for image editing"}
|
||||
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
"""Explicit quality choices must take precedence over the configured default."""
|
||||
import asyncio
|
||||
import json
|
||||
|
||||
import pytest
|
||||
from PIL import Image
|
||||
|
||||
|
||||
@pytest.mark.parametrize("caller", ["json", "lines", "mcp", "edit"])
|
||||
@pytest.mark.parametrize("requested,configured,expected", [
|
||||
("medium", "high", "medium"),
|
||||
("medium", "low", "medium"),
|
||||
("low", "high", "low"),
|
||||
("high", "low", "high"),
|
||||
("auto", "high", "auto"),
|
||||
(None, "high", "high"),
|
||||
(None, None, "medium"),
|
||||
])
|
||||
def test_image_quality_precedence(monkeypatch, tmp_path, caller, requested, configured, expected):
|
||||
from src import ai_interaction, settings
|
||||
from mcp_servers.image_gen_server import call_tool
|
||||
|
||||
config = {"image_gen_enabled": True, "image_model": "gpt-image-1"}
|
||||
if configured is not None:
|
||||
config["image_quality"] = configured
|
||||
monkeypatch.setattr(settings, "load_settings", lambda: config)
|
||||
monkeypatch.setattr(ai_interaction, "_resolve_model", lambda *args, **kwargs:
|
||||
("http://fixture.invalid/v1/chat/completions", "gpt-image-1", {}))
|
||||
captured = []
|
||||
|
||||
class Response:
|
||||
status_code = 418
|
||||
text = "Fixture captured request"
|
||||
|
||||
def json(self):
|
||||
return {"error": {"message": self.text}}
|
||||
|
||||
class Client:
|
||||
def __init__(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
async def __aenter__(self):
|
||||
return self
|
||||
|
||||
async def __aexit__(self, *args):
|
||||
pass
|
||||
|
||||
async def post(self, url, **kwargs):
|
||||
captured.append(kwargs.get("json") or kwargs.get("data"))
|
||||
return Response()
|
||||
|
||||
monkeypatch.setattr("httpx.AsyncClient", Client)
|
||||
args = {"prompt": "Fixture image", "model": "gpt-image-1"}
|
||||
if requested is not None:
|
||||
args["quality"] = requested
|
||||
if caller == "json":
|
||||
operation = ai_interaction.do_generate_image(json.dumps(args), incognito=True)
|
||||
elif caller == "lines":
|
||||
content = "Fixture image\ngpt-image-1\n1024x1024"
|
||||
if requested is not None:
|
||||
content += "\n" + requested
|
||||
operation = ai_interaction.do_generate_image(content, incognito=True)
|
||||
elif caller == "mcp":
|
||||
operation = call_tool("generate_image", args)
|
||||
else:
|
||||
source = tmp_path / "fixture.png"
|
||||
Image.new("RGB", (2, 2), "white").save(source)
|
||||
kwargs = {} if requested is None else {"quality": requested}
|
||||
operation = ai_interaction.do_edit_image("Fixture image", str(source), size="1024x1024",
|
||||
incognito=True, **kwargs)
|
||||
result = asyncio.run(operation)
|
||||
assert "418" in str(result), result
|
||||
assert len(captured) == 1
|
||||
assert captured[0]["quality"] == expected
|
||||
Reference in New Issue
Block a user