Spaces:
Sleeping
Sleeping
| """Step 6: turn each [IMAGE: ...] marker into a FLUX prompt and render it. | |
| Images are produced entirely by **remote HF Inference Provider calls** (FLUX.1-schnell) | |
| billed to the user's token — this Space has no GPU, so nothing is generated locally. | |
| The render tries auto provider routing first, then falls back across the providers that | |
| serve the model, so a single provider being unavailable for the user's token doesn't | |
| break the run. | |
| """ | |
| from __future__ import annotations | |
| from pathlib import Path | |
| from typing import List, Optional, Tuple | |
| from huggingface_hub import InferenceClient | |
| from . import config, llm | |
| _PROMPT_SYSTEM = ( | |
| "You are a prompt engineer for the FLUX text-to-image model. Given a short scene " | |
| "description for a blog illustration and the article topic, write ONE vivid, concrete " | |
| "image prompt (single line, <60 words). Describe subject, setting, composition, " | |
| "lighting and style. Prefer clean, editorial, photographic or tasteful illustrative " | |
| "styles suitable for a professional blog. No text/words in the image. Return only the prompt." | |
| ) | |
| def _flux_prompt(client: InferenceClient, topic: str, scene: str) -> str: | |
| try: | |
| p = llm.chat( | |
| client, | |
| config.MODEL_REASONING, | |
| _PROMPT_SYSTEM, | |
| f"Article topic: {topic}\nScene: {scene}\nWrite the FLUX prompt.", | |
| max_tokens=150, | |
| temperature=0.8, | |
| ) | |
| p = p.strip().strip('"') | |
| return p or scene | |
| except Exception: | |
| return f"{scene}, editorial photography, clean composition, natural lighting" | |
| def _render(hf_token: str, prompt: str) -> Tuple[Optional[object], Optional[str]]: | |
| """Generate one image via Inference Providers, trying auto then explicit providers. | |
| Returns (PIL image, None) on success or (None, error message) if every provider fails. | |
| """ | |
| # None => let the router auto-select; then try each known provider explicitly. | |
| attempts: List[Optional[str]] = [None] + config.IMAGE_PROVIDERS | |
| errors: List[str] = [] | |
| for provider in attempts: | |
| try: | |
| client = ( | |
| InferenceClient(token=hf_token, provider=provider) | |
| if provider | |
| else InferenceClient(token=hf_token) | |
| ) | |
| image = client.text_to_image(prompt=prompt, model=config.MODEL_IMAGE) | |
| return image, None | |
| except Exception as e: # noqa: BLE001 - try the next provider | |
| errors.append(f"{provider or 'auto'}: {e}") | |
| continue | |
| return None, " | ".join(errors[-3:]) | |
| def generate_images( | |
| client: InferenceClient, | |
| hf_token: str, | |
| topic: str, | |
| scenes: List[str], | |
| run_dir: Path, | |
| ) -> List[dict]: | |
| """Render one image per scene via inference calls. Returns [{scene, prompt, path|None, error?}].""" | |
| run_dir.mkdir(parents=True, exist_ok=True) | |
| out: List[dict] = [] | |
| for i, scene in enumerate(scenes): | |
| prompt = _flux_prompt(client, topic, scene) | |
| item = {"scene": scene, "prompt": prompt, "path": None} | |
| image, err = _render(hf_token, prompt) | |
| if image is not None: | |
| try: | |
| path = run_dir / f"image_{i + 1}.png" | |
| image.save(path) | |
| item["path"] = str(path) | |
| except Exception as e: # noqa: BLE001 | |
| item["error"] = f"save failed: {e}" | |
| else: | |
| item["error"] = err or "image generation failed" | |
| out.append(item) | |
| return out | |