From 45f590cf11a015271f4daa68416ae664a762927c Mon Sep 17 00:00:00 2001 From: Konstantin Fickel Date: Fri, 13 Feb 2026 20:10:48 +0100 Subject: [PATCH] chore: add pyrightconfig.json pointing to devenv venv Allows pyright to resolve third-party imports from .devenv/state/venv --- bulkgen/providers/image.py | 70 ++++++++++++++++++++++++++++++++++++++ pyrightconfig.json | 4 +++ 2 files changed, 74 insertions(+) create mode 100644 bulkgen/providers/image.py create mode 100644 pyrightconfig.json diff --git a/bulkgen/providers/image.py b/bulkgen/providers/image.py new file mode 100644 index 0000000..d8f03f4 --- /dev/null +++ b/bulkgen/providers/image.py @@ -0,0 +1,70 @@ +"""BlackForestLabs image generation provider.""" + +from __future__ import annotations + +import asyncio +import base64 +from pathlib import Path +from typing import override + +import httpx +from blackforest import BFLClient +from blackforest.types.general.client_config import ClientConfig + +from bulkgen.config import TargetConfig +from bulkgen.providers import Provider + +_BFL_SYNC_CONFIG = ClientConfig(sync=True, timeout=300) + + +def _encode_image_b64(path: Path) -> str: + """Read an image file and return its base64-encoded representation.""" + return base64.b64encode(path.read_bytes()).decode("ascii") + + +class ImageProvider(Provider): + """Generates images via the BlackForestLabs API.""" + + def __init__(self, api_key: str) -> None: + self._client = BFLClient(api_key=api_key) + + @override + async def generate( + self, + target_name: str, + target_config: TargetConfig, + resolved_prompt: str, + resolved_model: str, + project_dir: Path, + ) -> None: + output_path = project_dir / target_name + + inputs: dict[str, object] = {"prompt": resolved_prompt} + + if target_config.width is not None: + inputs["width"] = target_config.width + if target_config.height is not None: + inputs["height"] = target_config.height + + if target_config.reference_image is not None: + ref_path = project_dir / target_config.reference_image + inputs["image_prompt"] = _encode_image_b64(ref_path) + + for control_name in target_config.control_images: + ctrl_path = project_dir / control_name + inputs["control_image"] = _encode_image_b64(ctrl_path) + + result = await asyncio.to_thread( + self._client.generate, resolved_model, inputs, _BFL_SYNC_CONFIG + ) + + image_url: str | None = result.result.get("sample") # type: ignore[union-attr] + if not image_url: + msg = f"BFL API did not return an image URL for target '{target_name}'" + raise RuntimeError(msg) + + async with httpx.AsyncClient() as http: + response = await http.get(image_url) + response.raise_for_status() + + output_path.write_bytes(response.content) diff --git a/pyrightconfig.json b/pyrightconfig.json new file mode 100644 index 0000000..b6d2af9 --- /dev/null +++ b/pyrightconfig.json @@ -0,0 +1,4 @@ +{ + "venvPath": ".", + "venv": ".devenv/state/venv" +}