Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions tests/integration/providers/test_fal_ai_image_wire.py
Original file line number Diff line number Diff line change
Expand Up @@ -345,3 +345,54 @@ def respond(request: Request) -> Reply:
}
]
assert [(request.method, request.target) for request in wire.drain()] == [("POST", "/fal-ai/flux/dev")]


def test_fal_gpt_image_25_edit_sends_multipart_n_as_integer_num_images(gateway: Gateway) -> None:
def respond(request: Request) -> Reply:
assert request.method == "POST"
assert request.headers["authorization"] == "Key synthetic-fal-key"
assert request.target == "/openai/gpt-image-2.5/flare/edit"
assert request.headers["content-type"] == "application/json"
assert _JSON_OBJECT.validate_json(request.body) == {
"prompt": _PROMPT,
"image_urls": ["data:image/png;base64," + base64.b64encode(_PNG_BYTES).decode()],
"num_images": 2,
}
return Reply(
body=_image_response(
(
(f"{wire_url}/files/edit-1.png", 1024, 1536),
(f"{wire_url}/files/edit-2.png", 1024, 1536),
),
_PROMPT,
)
)

with wire_server(respond) as wire, gateway.scenario() as scenario:
wire_url: Final = wire.url
model: Final = scenario.model(model=f"fal_ai/{_EDIT_MODEL}", api_base=wire.url, api_key="synthetic-fal-key")
response: Final = gateway.client.post(
"/v1/images/edits",
data={"model": model, "prompt": _PROMPT, "n": "2"},
files={"image": ("red_circle.png", _PNG_BYTES, "image/png")},
headers={"Authorization": f"Bearer {gateway.key}"},
)
assert response.status_code == 200, response.text
payload: Final = _JSON_OBJECT.validate_json(response.content)
assert payload["data"] == [
{
"url": f"{wire.url}/files/edit-1.png",
"b64_json": None,
"revised_prompt": None,
"provider_specific_fields": {"width": 1024, "height": 1536, "content_type": "image/png"},
},
{
"url": f"{wire.url}/files/edit-2.png",
"b64_json": None,
"revised_prompt": None,
"provider_specific_fields": {"width": 1024, "height": 1536, "content_type": "image/png"},
},
]
assert [(request.method, request.target) for request in wire.drain()] == [
("POST", "/openai/gpt-image-2.5/flare/edit")
]
83 changes: 83 additions & 0 deletions tests/integration/providers/test_image_gen_drop_params_wire.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import json
from typing import Final

import openai
import pytest
from integration._support.client import Gateway
from integration._support.wire import Reply, Request, wire_server
from pydantic import JsonValue, TypeAdapter

_JSON_OBJECT: Final = TypeAdapter(dict[str, JsonValue])
_PROMPT: Final = "a transparent lighthouse at dawn"


def test_image_generation_additional_drop_params_reaches_provider_body(gateway: Gateway) -> None:
Expand Down Expand Up @@ -47,3 +50,83 @@ def respond(request: Request) -> Reply:
assert response.status_code == 200, response.text
assert response.json()["data"][0]["b64_json"] == "aW1n"
assert [(request.method, request.target) for request in wire.drain()] == [("POST", "/images/generations")]


def _openai_client(gateway: Gateway, model: str, alias: str) -> openai.OpenAI:
base: Final = str(gateway.client.base_url)
match alias:
case "v1":
return openai.OpenAI(base_url=f"{base}/v1", api_key=gateway.key, max_retries=0)
case "unversioned":
return openai.OpenAI(base_url=base, api_key=gateway.key, max_retries=0)
case "azure_deployment":
return openai.AzureOpenAI(
azure_endpoint=base,
azure_deployment=model,
api_version="2025-04-01-preview",
api_key=gateway.key,
max_retries=0,
)
raise AssertionError(alias)


@pytest.mark.parametrize("alias", ("v1", "unversioned", "azure_deployment"))
def test_gpt_image_generation_forwards_every_sdk_field_once_with_integer_n(gateway: Gateway, alias: str) -> None:
def respond(request: Request) -> Reply:
assert request.method == "POST"
assert request.target == "/images/generations"
assert request.headers["authorization"] == "Bearer synthetic-openai-key"
assert _JSON_OBJECT.validate_json(request.body) == {
"model": "gpt-image-1",
"prompt": _PROMPT,
"n": 2,
"size": "1024x1536",
"quality": "high",
"background": "transparent",
"output_format": "webp",
"moderation": "low",
}
return Reply(
body=json.dumps(
{
"created": 1700000000,
"data": [{"b64_json": "aW1nLTE="}, {"b64_json": "aW1nLTI="}],
"background": "transparent",
"output_format": "webp",
"quality": "high",
"size": "1024x1536",
"usage": {
"input_tokens": 12,
"input_tokens_details": {"text_tokens": 12, "image_tokens": 0},
"output_tokens": 4160,
"total_tokens": 4172,
},
}
).encode()
)

with wire_server(respond) as wire, gateway.scenario() as scenario:
model: Final = scenario.model(
model="openai/gpt-image-1",
api_base=wire.url,
api_key="synthetic-openai-key",
)
client: Final = _openai_client(gateway, model, alias)
result: Final = client.images.generate(
model=model,
prompt=_PROMPT,
n=2,
size="1024x1536",
quality="high",
background="transparent",
output_format="webp",
moderation="low",
)
assert [image.b64_json for image in result.data] == ["aW1nLTE=", "aW1nLTI="]
assert result.usage is not None
assert (
result.usage.input_tokens,
result.usage.output_tokens,
result.usage.total_tokens,
) == (12, 4160, 4172)
assert [(request.method, request.target) for request in wire.drain()] == [("POST", "/images/generations")]
Loading
Loading