From a04cbfc6a0406663aa5e2155119a8e2a1fe2ea0f Mon Sep 17 00:00:00 2001 From: Vitaliy Date: Fri, 19 Jun 2026 01:11:59 -0400 Subject: [PATCH 1/3] fix: forward non_default_params in image_edit fallback handler Added support for forwarding provider-specific parameters in image edit requests. --- litellm/images/main.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/litellm/images/main.py b/litellm/images/main.py index 8b108ded4c9..f910df12dbb 100644 --- a/litellm/images/main.py +++ b/litellm/images/main.py @@ -963,6 +963,9 @@ def image_edit( client=kwargs.get("client"), aimage_edit=_is_async, ) + # Forward provider-specific params (e.g., image_config for OpenRouter) so they + # reach the provider the same way bedrock/stability/black_forest_labs paths do. + image_edit_request_params.update(non_default_params) # Call the handler with _is_async flag instead of directly calling the async handler return base_llm_http_handler.image_edit_handler( model=model, From 79b472c1c0dcadbc63ce7099723d3e0d99ac0d04 Mon Sep 17 00:00:00 2001 From: Vitaliy Date: Sat, 20 Jun 2026 00:32:05 -0400 Subject: [PATCH 2/3] Implement tests for non-default params in image_edit Add unit tests to verify forwarding of non-default parameters in image_edit fallback handler. --- .../test_image_edit_non_default_params.py | 141 ++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 tests/image_gen_tests/test_image_edit_non_default_params.py diff --git a/tests/image_gen_tests/test_image_edit_non_default_params.py b/tests/image_gen_tests/test_image_edit_non_default_params.py new file mode 100644 index 00000000000..c852418ff09 --- /dev/null +++ b/tests/image_gen_tests/test_image_edit_non_default_params.py @@ -0,0 +1,141 @@ +""" +Unit tests for PR #30814: forward non_default_params in image_edit fallback handler. + +Before the fix, provider-specific kwargs (e.g. image_config) were silently +dropped in the fallback branch of litellm.image_edit(). The bedrock, stability, +and black_forest_labs branches already called + image_edit_request_params.update(non_default_params) +but the else/fallback branch did not. + +These tests verify that any kwarg not in litellm's standard param list is +forwarded to base_llm_http_handler.image_edit_handler via +image_edit_optional_request_params. +""" +import sys +import os + +sys.path.insert(0, os.path.abspath("../..")) + +from unittest.mock import MagicMock, patch + +import pytest + +import litellm +from litellm.images.main import image_edit +from litellm.types.utils import ImageResponse, ImageObject + + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + +def _fake_image_response() -> ImageResponse: + return ImageResponse( + created=1700000000, + data=[ImageObject(url="https://example.com/edited.png")], + ) + + +def _make_logging_mock() -> MagicMock: + """Minimal stand-in for the LiteLLMLoggingObj passed via kwargs.""" + m = MagicMock() + m.update_from_kwargs.return_value = None + return m + + +def _make_provider_config_mock() -> MagicMock: + """Stand-in for BaseImageEditConfig.""" + cfg = MagicMock() + cfg.get_supported_openai_params.return_value = [] + return cfg + + +def _make_request_utils_mock() -> MagicMock: + utils = MagicMock() + utils.get_requested_image_edit_optional_param.return_value = MagicMock() + # Base params dict -- non_default_params are merged on top of this. + utils.get_optional_params_image_edit.return_value = {} + return utils + + +# --------------------------------------------------------------------------- +# Tests +# --------------------------------------------------------------------------- + +class TestFallbackHandlerForwardsNonDefaultParams: + """ + The fallback branch (anything that is not bedrock / stability / + black_forest_labs) must call + image_edit_request_params.update(non_default_params) + before delegating to base_llm_http_handler.image_edit_handler. + """ + + def _run(self, **extra_kwargs): + """ + Call image_edit() with all external dependencies mocked, returning the + captured call-kwargs of base_llm_http_handler.image_edit_handler. + """ + mock_handler = MagicMock(return_value=_fake_image_response()) + + with ( + patch( + "litellm.images.main.base_llm_http_handler.image_edit_handler", + mock_handler, + ), + patch( + "litellm.images.main.ProviderConfigManager.get_provider_image_edit_config", + return_value=_make_provider_config_mock(), + ), + patch( + "litellm.images.main._get_ImageEditRequestUtils", + return_value=_make_request_utils_mock(), + ), + ): + image_edit( + model="gpt-image-1", + image=b"fake-image-bytes", + prompt="add a sunset background", + litellm_logging_obj=_make_logging_mock(), + **extra_kwargs, + ) + + assert mock_handler.called, "image_edit_handler was never invoked" + return mock_handler.call_args.kwargs + + # ------------------------------------------------------------------ + + def test_image_config_forwarded(self): + """image_config (a non-default param) must reach the handler.""" + call_kw = self._run(image_config={"quality": "high", "background": "transparent"}) + optional = call_kw.get("image_edit_optional_request_params", {}) + + assert "image_config" in optional, ( + f"image_config was dropped. image_edit_optional_request_params={optional}" + ) + assert optional["image_config"] == {"quality": "high", "background": "transparent"} + + def test_multiple_non_default_params_forwarded(self): + """All non-default params are forwarded, not just image_config.""" + call_kw = self._run( + image_config={"quality": "hd"}, + custom_vendor_param="foo", + ) + optional = call_kw.get("image_edit_optional_request_params", {}) + + assert "image_config" in optional, f"image_config missing: {optional}" + assert "custom_vendor_param" in optional, f"custom_vendor_param missing: {optional}" + + def test_standard_params_not_in_non_default(self): + """ + Standard openai/litellm params (e.g. n, size) are not in non_default_params + and must not be re-injected by the update() call. + """ + call_kw = self._run(image_config={"quality": "standard"}) + optional = call_kw.get("image_edit_optional_request_params", {}) + # image_config is the non-default param -- it must be present. + assert "image_config" in optional + + def test_no_non_default_params_still_calls_handler(self): + """Sanity check: the fallback path works with no extra kwargs too.""" + call_kw = self._run() + assert "image_edit_optional_request_params" in call_kw From f3eee26a3fec6244789536c1b41b9e3b86b29428 Mon Sep 17 00:00:00 2001 From: Vitaliy Date: Tue, 23 Jun 2026 12:18:41 -0400 Subject: [PATCH 3/3] test: add unit test for fallback path non_default_params forwarding This test ensures that non-default parameters are correctly forwarded to the fallback image_edit_handler, addressing a regression issue. --- tests/image_gen_tests/test_image_edit.py | 69 ++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 tests/image_gen_tests/test_image_edit.py diff --git a/tests/image_gen_tests/test_image_edit.py b/tests/image_gen_tests/test_image_edit.py new file mode 100644 index 00000000000..70473f1fa07 --- /dev/null +++ b/tests/image_gen_tests/test_image_edit.py @@ -0,0 +1,69 @@ +""" +Tests for the image_edit() function in litellm/images/main.py +""" + +import io +import os +import sys +from unittest.mock import MagicMock, patch + +sys.path.insert(0, os.path.abspath("../..")) + +import litellm + + +def test_image_edit_fallback_forwards_non_default_params(): + """ + Regression test: non_default_params must reach the fallback image_edit_handler + path (i.e. providers other than bedrock / stability / black_forest_labs). + + Before gh-30814 the fallback path was missing:: + + image_edit_request_params.update(non_default_params) + + so provider-specific kwargs such as ``image_config`` were silently dropped + before the call to base_llm_http_handler.image_edit_handler. + """ + captured = {} + + def _capture_handler(**kwargs): + captured.update(kwargs) + return litellm.ImageResponse(data=[]) + + mock_image = io.BytesIO(b"\x89PNGfake") + mock_image.name = "test.png" + + mock_provider_config = MagicMock() + mock_utils = MagicMock() + mock_utils.get_requested_image_edit_optional_param.return_value = MagicMock() + mock_utils.get_optional_params_image_edit.return_value = {} + + with ( + patch( + "litellm.images.main.base_llm_http_handler.image_edit_handler", + side_effect=_capture_handler, + ), + patch( + "litellm.utils.ProviderConfigManager.get_provider_image_edit_config", + return_value=mock_provider_config, + ), + patch( + "litellm.images.main._get_ImageEditRequestUtils", + return_value=mock_utils, + ), + ): + litellm.image_edit( + model="openrouter/gpt-image-1", + image=mock_image, + prompt="a cute sea otter", + image_config={"style": "vivid"}, + api_key="fake-key", + ) + + forwarded = captured.get("image_edit_optional_request_params", {}) + assert "image_config" in forwarded, ( + f"Provider-specific kwargs not forwarded to fallback handler. " + f"Got image_edit_optional_request_params={forwarded!r}. " + f"Expected 'image_config' to be present (regression: gh-30814)." + ) + assert forwarded["image_config"] == {"style": "vivid"}