diff --git a/src/tool_implementations.py b/src/tool_implementations.py index aa3d17f6b..ed65f4e2d 100644 --- a/src/tool_implementations.py +++ b/src/tool_implementations.py @@ -53,6 +53,8 @@ from src.tools.search import do_search_chats # noqa: F401 from src.tools.notes import do_manage_notes # noqa: F401 # Calendar domain extracted to src/tools/calendar.py (slice 1, #4082/#4071). from src.tools.calendar import do_manage_calendar # noqa: F401 +# Image domain extracted to src/tools/image.py (slice 1, #4082/#4071). +from src.tools.image import do_edit_image # noqa: F401 logger = logging.getLogger(__name__) @@ -145,35 +147,6 @@ def _internal_headers(owner: Optional[str] = None) -> Dict[str, str]: return headers -# ── Gallery tools ── - -async def do_edit_image(content: str, owner: Optional[str] = None) -> Dict: - """Edit a gallery image (upscale, rembg, inpaint, harmonize).""" - import httpx - try: - args = _parse_tool_args(content) - except ValueError: - return {"error": "Invalid JSON arguments", "exit_code": 1} - image_id = args.get("image_id", "") - action = args.get("action", "") - if not image_id or not action: - return {"error": "image_id and action are required", "exit_code": 1} - payload = {"image_id": image_id} - if args.get("prompt"): - payload["prompt"] = args["prompt"] - if args.get("scale"): - payload["scale"] = args["scale"] - try: - async with httpx.AsyncClient(timeout=120) as client: - resp = await client.post(f"{_INTERNAL_BASE}/api/gallery/{action}", json=payload) - data = resp.json() - if data.get("success") or data.get("id"): - return {"output": f"Image edited ({action}). New image ID: {data.get('id', '?')}", "exit_code": 0} - return {"error": data.get("error", f"{action} failed"), "exit_code": 1} - except Exception as e: - return {"error": str(e), "exit_code": 1} - - # ── Research tools ── async def do_manage_research(content: str, owner: Optional[str] = None) -> Dict: diff --git a/src/tools/__init__.py b/src/tools/__init__.py index 217a92af3..ea8bb9881 100644 --- a/src/tools/__init__.py +++ b/src/tools/__init__.py @@ -24,3 +24,4 @@ from src.tools.cookbook import ( # noqa: F401 from src.tools.search import do_search_chats # noqa: F401 from src.tools.notes import do_manage_notes # noqa: F401 from src.tools.calendar import do_manage_calendar # noqa: F401 +from src.tools.image import do_edit_image # noqa: F401 diff --git a/src/tools/image.py b/src/tools/image.py new file mode 100644 index 000000000..25e879ca2 --- /dev/null +++ b/src/tools/image.py @@ -0,0 +1,39 @@ +"""Image-domain tool implementations. + +Extracted from tool_implementations.py as part of slice 1 (#4082/#4071). +Holds the edit_image (gallery) tool. +``src.tool_implementations`` re-exports these for backward compatibility. +``_INTERNAL_BASE`` still lives in tool_implementations.py and is pulled back +function-locally here. +""" +from typing import Dict, Optional + +from src.tools._common import _parse_tool_args + + +async def do_edit_image(content: str, owner: Optional[str] = None) -> Dict: + """Edit a gallery image (upscale, rembg, inpaint, harmonize).""" + import httpx + from src.tool_implementations import _INTERNAL_BASE # shared constant, still lives in the facade + try: + args = _parse_tool_args(content) + except ValueError: + return {"error": "Invalid JSON arguments", "exit_code": 1} + image_id = args.get("image_id", "") + action = args.get("action", "") + if not image_id or not action: + return {"error": "image_id and action are required", "exit_code": 1} + payload = {"image_id": image_id} + if args.get("prompt"): + payload["prompt"] = args["prompt"] + if args.get("scale"): + payload["scale"] = args["scale"] + try: + async with httpx.AsyncClient(timeout=120) as client: + resp = await client.post(f"{_INTERNAL_BASE}/api/gallery/{action}", json=payload) + data = resp.json() + if data.get("success") or data.get("id"): + return {"output": f"Image edited ({action}). New image ID: {data.get('id', '?')}", "exit_code": 0} + return {"error": data.get("error", f"{action} failed"), "exit_code": 1} + except Exception as e: + return {"error": str(e), "exit_code": 1}