mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-10-31 10:12:21 +01:00
Merge pull request #6915 from redpersongpt/codex/image-quality-explicit-default
fix(images): preserve explicit medium quality requests
This commit is contained in:
@@ -33,7 +33,7 @@ async def list_tools() -> list[Tool]:
|
|||||||
"prompt": {"type": "string", "description": "Image description prompt"},
|
"prompt": {"type": "string", "description": "Image description prompt"},
|
||||||
"model": {"type": "string", "description": "Model name (auto-detects if omitted)"},
|
"model": {"type": "string", "description": "Model name (auto-detects if omitted)"},
|
||||||
"size": {"type": "string", "description": "Image size (default 1024x1024)"},
|
"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"],
|
"required": ["prompt"],
|
||||||
},
|
},
|
||||||
@@ -66,7 +66,7 @@ async def call_tool(name: str, arguments: dict) -> list[TextContent]:
|
|||||||
|
|
||||||
if not model_spec:
|
if not model_spec:
|
||||||
model_spec = _settings.get("image_model", "")
|
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"]
|
quality = _settings["image_quality"]
|
||||||
|
|
||||||
# Auto-detect best available image model
|
# 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 1: prompt describing the image
|
||||||
Line 2: model name (optional, default auto-detects: prefers gpt-image-1.5 > gpt-image-1)
|
Line 2: model name (optional, default auto-detects: prefers gpt-image-1.5 > gpt-image-1)
|
||||||
Line 3: size (optional, defaults to 1024x1024)
|
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 base64
|
||||||
import httpx
|
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()
|
prompt = str(args.get('prompt') or '').strip()
|
||||||
model_spec = str(args.get('model') or '').strip()
|
model_spec = str(args.get('model') or '').strip()
|
||||||
size = str(args.get('size') or '1024x1024')
|
size = str(args.get('size') or '1024x1024')
|
||||||
quality = str(args.get('quality') or 'medium')
|
quality = str(args.get('quality') or '')
|
||||||
else:
|
else:
|
||||||
lines = content.strip().split("\n")
|
lines = content.strip().split("\n")
|
||||||
prompt = lines[0].strip() if lines else ""
|
prompt = lines[0].strip() if lines else ""
|
||||||
model_spec = lines[1].strip() if len(lines) > 1 and lines[1].strip() 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"
|
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:
|
if not prompt:
|
||||||
return {"error": "Image prompt is required (line 1)"}
|
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
|
# Use admin-configured model/quality if not specified by the tool call
|
||||||
if not model_spec:
|
if not model_spec:
|
||||||
model_spec = _settings.get("image_model", "")
|
model_spec = _settings.get("image_model", "")
|
||||||
if quality == "medium" and _settings.get("image_quality"):
|
if not quality:
|
||||||
quality = _settings["image_quality"]
|
quality = _settings.get("image_quality") or "medium"
|
||||||
|
|
||||||
# Auto-detect best available image model if still not set
|
# Auto-detect best available image model if still not set
|
||||||
if not model_spec:
|
if not model_spec:
|
||||||
@@ -1482,7 +1482,7 @@ async def do_edit_image(
|
|||||||
session_id: Optional[str] = None,
|
session_id: Optional[str] = None,
|
||||||
owner: Optional[str] = None,
|
owner: Optional[str] = None,
|
||||||
size: str = "auto",
|
size: str = "auto",
|
||||||
quality: str = "medium",
|
quality: Optional[str] = None,
|
||||||
progress_callback: Optional[Callable[[Dict[str, Any]], Awaitable[None]]] = None,
|
progress_callback: Optional[Callable[[Dict[str, Any]], Awaitable[None]]] = None,
|
||||||
incognito: bool = False,
|
incognito: bool = False,
|
||||||
) -> Dict:
|
) -> Dict:
|
||||||
@@ -1509,8 +1509,8 @@ async def do_edit_image(
|
|||||||
|
|
||||||
if not model_spec:
|
if not model_spec:
|
||||||
model_spec = _settings.get("image_model", "")
|
model_spec = _settings.get("image_model", "")
|
||||||
if quality == "medium" and _settings.get("image_quality"):
|
if quality is None:
|
||||||
quality = _settings["image_quality"]
|
quality = _settings.get("image_quality") or "medium"
|
||||||
if not model_spec:
|
if not model_spec:
|
||||||
return {"error": "No image model selected for image editing"}
|
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