Upload images to fal.ai CDN instead of embedding base64 data URIs
Large base64 data URIs in the JSON payload were causing fal.ai to hang at IN_PROGRESS indefinitely. Now we upload images to fal.media CDN first and pass the resulting URLs in the image_urls array. Also removes debug logging from the previous commit. https://claude.ai/code/session_01VtRzQMdLschx9awGyTGGH7
This commit is contained in:
@@ -57,8 +57,6 @@ def _submit_request(endpoint, payload, timeout, api_key):
|
||||
f"fal.ai submit failed ({resp.status_code}): {resp.text}"
|
||||
)
|
||||
queue_data = resp.json()
|
||||
print(f"[NBP] Queue response keys: {list(queue_data.keys())}")
|
||||
print(f"[NBP] Queue response: {queue_data}")
|
||||
|
||||
request_id = queue_data.get("request_id")
|
||||
if not request_id:
|
||||
@@ -73,19 +71,13 @@ def _submit_request(endpoint, payload, timeout, api_key):
|
||||
"response_url",
|
||||
f"https://queue.fal.run/{endpoint}/requests/{request_id}",
|
||||
)
|
||||
print(f"[NBP] status_url: {status_url}")
|
||||
print(f"[NBP] result_url: {result_url}")
|
||||
|
||||
deadline = time.time() + timeout
|
||||
poll_count = 0
|
||||
while time.time() < deadline:
|
||||
status_resp = requests.get(status_url, headers=headers, timeout=30)
|
||||
status_resp.raise_for_status()
|
||||
status_data = status_resp.json()
|
||||
status = status_data.get("status")
|
||||
poll_count += 1
|
||||
if poll_count <= 5 or poll_count % 15 == 0:
|
||||
print(f"[NBP] Poll #{poll_count}: {status_data}")
|
||||
|
||||
if status == "COMPLETED":
|
||||
result_resp = requests.get(result_url, headers=headers, timeout=30)
|
||||
@@ -132,6 +124,31 @@ def _pil_to_data_uri(img, fmt="png"):
|
||||
return f"data:{mime};base64,{encoded}"
|
||||
|
||||
|
||||
def _upload_to_fal(img, fmt, api_key):
|
||||
"""Upload a PIL image to fal.ai CDN and return the access URL."""
|
||||
buf = io.BytesIO()
|
||||
img.save(buf, format=fmt.upper() if fmt != "jpeg" else "JPEG")
|
||||
mime = f"image/{fmt}" if fmt != "jpeg" else "image/jpeg"
|
||||
resp = requests.post(
|
||||
"https://fal.media/files/upload",
|
||||
headers={
|
||||
"Authorization": f"Bearer {api_key}",
|
||||
"Content-Type": mime,
|
||||
"X-Fal-File-Name": f"input.{fmt}",
|
||||
},
|
||||
data=buf.getvalue(),
|
||||
timeout=60,
|
||||
)
|
||||
if not resp.ok:
|
||||
raise RuntimeError(
|
||||
f"fal.ai image upload failed ({resp.status_code}): {resp.text}"
|
||||
)
|
||||
url = resp.json().get("access_url")
|
||||
if not url:
|
||||
raise RuntimeError(f"No access_url in upload response: {resp.json()}")
|
||||
return url
|
||||
|
||||
|
||||
class NanoBananaProTextToImage:
|
||||
"""Generate images from text prompts using Nano Banana Pro via fal.ai."""
|
||||
|
||||
@@ -264,12 +281,12 @@ class NanoBananaProImageEdit:
|
||||
):
|
||||
api_key = _resolve_api_key(fal_api_key)
|
||||
|
||||
# Build image_urls array (primary + optional reference images)
|
||||
image_urls = [_pil_to_data_uri(_tensor_to_pil(image), output_format)]
|
||||
# Upload images to fal.ai CDN and build image_urls array
|
||||
image_urls = [_upload_to_fal(_tensor_to_pil(image), output_format, api_key)]
|
||||
if image_2 is not None:
|
||||
image_urls.append(_pil_to_data_uri(_tensor_to_pil(image_2), output_format))
|
||||
image_urls.append(_upload_to_fal(_tensor_to_pil(image_2), output_format, api_key))
|
||||
if image_3 is not None:
|
||||
image_urls.append(_pil_to_data_uri(_tensor_to_pil(image_3), output_format))
|
||||
image_urls.append(_upload_to_fal(_tensor_to_pil(image_3), output_format, api_key))
|
||||
|
||||
payload = {
|
||||
"prompt": prompt,
|
||||
|
||||
Reference in New Issue
Block a user