fix(image): honor provider proxy for URL downloads

This commit is contained in:
chengyongru
2026-07-27 10:06:19 +08:00
committed by Xubin Ren
parent cc3dbbe804
commit d73794bc68
4 changed files with 211 additions and 42 deletions
+44 -14
View File
@@ -103,19 +103,19 @@ class CodexStreamingCompleteThenErrorResponse(FakeResponse):
@pytest.fixture(autouse=True)
def generated_image_downloads(monkeypatch) -> list[str]:
def generated_image_downloads(monkeypatch) -> list[tuple[str, str | None]]:
"""Keep provider response parsing tests independent from outbound HTTP."""
urls: list[str] = []
downloads: list[tuple[str, str | None]] = []
async def download(url: str) -> str:
urls.append(url)
async def download(url: str, *, proxy: str | None = None) -> str:
downloads.append((url, proxy))
return PNG_DATA_URL
monkeypatch.setattr(
"nanobot.providers.image_generation._download_image_data_url",
download,
)
return urls
return downloads
@pytest.mark.asyncio
@@ -294,19 +294,21 @@ async def test_aihubmix_image_edit_payload_uses_reference_images(tmp_path: Path)
@pytest.mark.asyncio
async def test_aihubmix_image_generation_downloads_url_response(
generated_image_downloads: list[str],
generated_image_downloads: list[tuple[str, str | None]],
) -> None:
fake = FakeClient(FakeResponse({"data": [{"url": "https://cdn.example/image.png"}]}))
fake.get_response = FakeResponse({}, content=PNG_BYTES)
proxy = "http://127.0.0.1:23458"
client = AIHubMixImageGenerationClient(
api_key="sk-ahm-test",
proxy=proxy,
client=fake, # type: ignore[arg-type]
)
response = await client.generate(prompt="draw", model="gpt-image-2-free")
assert response.images[0].startswith("data:image/png;base64,")
assert generated_image_downloads == ["https://cdn.example/image.png"]
assert generated_image_downloads == [("https://cdn.example/image.png", proxy)]
@pytest.mark.asyncio
@@ -836,18 +838,22 @@ async def test_openai_b64_json_response_uses_detected_mime() -> None:
@pytest.mark.asyncio
async def test_openai_url_download_fallback(generated_image_downloads: list[str]) -> None:
async def test_openai_url_download_fallback(
generated_image_downloads: list[tuple[str, str | None]],
) -> None:
fake = FakeClient(FakeResponse({"data": [{"url": "https://cdn.example/image.png"}]}))
fake.get_response = FakeResponse({}, content=PNG_BYTES)
proxy = "http://127.0.0.1:23458"
client = OpenAIImageGenerationClient(
api_key="sk-openai-test",
proxy=proxy,
client=fake, # type: ignore[arg-type]
)
response = await client.generate(prompt="draw", model="dall-e-3")
assert response.images[0].startswith("data:image/png;base64,")
assert generated_image_downloads == ["https://cdn.example/image.png"]
assert generated_image_downloads == [("https://cdn.example/image.png", proxy)]
@pytest.mark.asyncio
@@ -1211,14 +1217,16 @@ async def test_custom_generate_maps_one_k_to_openai_dimension() -> None:
@pytest.mark.asyncio
async def test_custom_generate_extra_body_can_override_defaults(
generated_image_downloads: list[str],
generated_image_downloads: list[tuple[str, str | None]],
) -> None:
fake = FakeClient(FakeResponse({"data": [{"url": "https://images.example/cat.png"}]}))
fake.get_response = FakeResponse({}, content=PNG_BYTES)
proxy = "http://127.0.0.1:23458"
client = CustomImageGenerationClient(
api_key="sk-custom-test",
api_base="https://custom.example/v1",
extra_body={"response_format": "url", "size": "2K"},
proxy=proxy,
client=fake, # type: ignore[arg-type]
)
@@ -1229,7 +1237,7 @@ async def test_custom_generate_extra_body_can_override_defaults(
)
assert response.images == [PNG_DATA_URL]
assert generated_image_downloads == ["https://images.example/cat.png"]
assert generated_image_downloads == [("https://images.example/cat.png", proxy)]
body = fake.calls[0]["json"]
assert body["response_format"] == "url"
assert body["size"] == "2K"
@@ -1636,19 +1644,21 @@ async def test_zhipu_image_generation_with_explicit_size() -> None:
@pytest.mark.asyncio
async def test_zhipu_image_generation_downloads_url_response(
generated_image_downloads: list[str],
generated_image_downloads: list[tuple[str, str | None]],
) -> None:
fake = FakeClient(FakeResponse({"data": [{"url": "https://cdn.example/image.png"}]}))
fake.get_response = FakeResponse({}, content=PNG_BYTES)
proxy = "http://127.0.0.1:23458"
client = ZhipuImageGenerationClient(
api_key="sk-zhipu-test",
proxy=proxy,
client=fake, # type: ignore[arg-type]
)
response = await client.generate(prompt="draw", model="glm-image")
assert response.images[0].startswith("data:image/png;base64,")
assert generated_image_downloads == ["https://cdn.example/image.png"]
assert generated_image_downloads == [("https://cdn.example/image.png", proxy)]
@pytest.mark.asyncio
@@ -1728,7 +1738,9 @@ def _modelscope_fast_poll(monkeypatch) -> None:
@pytest.mark.asyncio
async def test_modelscope_image_generation_submit_and_poll() -> None:
async def test_modelscope_image_generation_submit_and_poll(
generated_image_downloads: list[tuple[str, str | None]],
) -> None:
submit = FakeResponse({"task_id": "abc123"})
poll_responses = [
FakeResponse({"task_status": "PENDING"}),
@@ -1738,9 +1750,11 @@ async def test_modelscope_image_generation_submit_and_poll() -> None:
}),
]
fake = ModelScopeFakeClient(submit, poll_responses)
proxy = "http://127.0.0.1:23458"
client = ModelScopeImageGenerationClient(
api_key="ms-token",
api_base="https://api-inference.modelscope.cn/v1",
proxy=proxy,
client=fake, # type: ignore[arg-type]
)
@@ -1750,6 +1764,7 @@ async def test_modelscope_image_generation_submit_and_poll() -> None:
)
assert response.images[0].startswith("data:image/png;base64,")
assert generated_image_downloads == [("https://cdn.example/image.png", proxy)]
# Verify POST request
post_call = fake.calls[0]
@@ -1919,3 +1934,18 @@ async def test_modelscope_image_generation_poll_timeout(monkeypatch) -> None:
# Should have polled up to the (patched) attempt limit.
assert len(fake.get_calls) == 3
def test_image_provider_http_client_kwargs_include_explicit_proxy() -> None:
proxy = "http://127.0.0.1:23458"
client = AIHubMixImageGenerationClient(
api_key="sk-ahm-test",
proxy=proxy,
)
assert client._http_client_kwargs() == {
"timeout": client.timeout,
"proxy": proxy,
"trust_env": False,
}