Spaces:
Running
Running
sync: HF InferenceClient image generation fix
#51
by Baida07 - opened
- api/vision.py +32 -52
- tests/test_vision_hf_only.py +10 -5
api/vision.py
CHANGED
|
@@ -18,7 +18,7 @@ Fallback chain analyze_image:
|
|
| 18 |
3. HF BLIP VQA + captioning (richiede solo HF_TOKEN)
|
| 19 |
|
| 20 |
Image generation and editing use exclusively Hugging Face Inference API:
|
| 21 |
-
-
|
| 22 |
- FLUX.1-Kontext-dev for prompt-guided image editing
|
| 23 |
"""
|
| 24 |
import asyncio, base64, io, os, httpx, logging
|
|
@@ -35,11 +35,11 @@ _HF_API = "https://router.huggingface.co/hf-inference"
|
|
| 35 |
_USER_AGENT = "Mozilla/5.0 (compatible; AgenteAI/3.0)"
|
| 36 |
|
| 37 |
_MODEL_MAP: dict[str, str] = {
|
| 38 |
-
"FLUX.1-schnell": "
|
| 39 |
-
"FLUX.1-dev": "
|
| 40 |
-
"sdxl": "stabilityai/stable-diffusion-
|
| 41 |
-
"flux": "
|
| 42 |
-
"flux-schnell": "
|
| 43 |
}
|
| 44 |
|
| 45 |
_EDIT_MODEL = "timbrooks/instruct-pix2pix"
|
|
@@ -93,53 +93,33 @@ async def generate_image(req: GenerateImageRequest):
|
|
| 93 |
- HF restituisce raw bytes PNG β non JSON.
|
| 94 |
- steps ottimali FLUX.1-schnell: 4 (veloce) β 8 (qualitΓ ).
|
| 95 |
"""
|
| 96 |
-
model_id = _MODEL_MAP.get(req.model, "
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
|
|
|
| 111 |
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
except Exception:
|
| 123 |
-
wait = 20
|
| 124 |
-
_logger.info("HF model loading, waiting %.0fsβ¦", wait)
|
| 125 |
-
await asyncio.sleep(wait)
|
| 126 |
-
continue
|
| 127 |
-
|
| 128 |
-
try:
|
| 129 |
-
err = r.json().get("error", r.text[:200])
|
| 130 |
-
except Exception:
|
| 131 |
-
err = r.text[:200]
|
| 132 |
-
return {
|
| 133 |
-
"ok": False, "error": f"HF API {r.status_code}: {err}",
|
| 134 |
-
"hint": "Aggiungi HF_TOKEN nelle variabili d'ambiente per piΓΉ richieste/ora.",
|
| 135 |
-
}
|
| 136 |
-
|
| 137 |
-
except httpx.TimeoutException:
|
| 138 |
-
return {"ok": False, "error": "Timeout 90s β modello in cold-start. Riprova tra 30s."}
|
| 139 |
-
except Exception as e:
|
| 140 |
-
return {"ok": False, "error": str(e)[:300]}
|
| 141 |
-
|
| 142 |
-
return {"ok": False, "error": "Impossibile generare dopo 2 tentativi."}
|
| 143 |
|
| 144 |
|
| 145 |
# βββ /edit ββββββββββββββββββββββββββββββββββββββββββοΏ½οΏ½βββββββββββββββββββββββββ
|
|
|
|
| 18 |
3. HF BLIP VQA + captioning (richiede solo HF_TOKEN)
|
| 19 |
|
| 20 |
Image generation and editing use exclusively Hugging Face Inference API:
|
| 21 |
+
- Stable Diffusion 3 Medium for text-to-image generation via HF Inference Providers
|
| 22 |
- FLUX.1-Kontext-dev for prompt-guided image editing
|
| 23 |
"""
|
| 24 |
import asyncio, base64, io, os, httpx, logging
|
|
|
|
| 35 |
_USER_AGENT = "Mozilla/5.0 (compatible; AgenteAI/3.0)"
|
| 36 |
|
| 37 |
_MODEL_MAP: dict[str, str] = {
|
| 38 |
+
"FLUX.1-schnell": "stabilityai/stable-diffusion-3-medium-diffusers",
|
| 39 |
+
"FLUX.1-dev": "stabilityai/stable-diffusion-3-medium-diffusers",
|
| 40 |
+
"sdxl": "stabilityai/stable-diffusion-3-medium-diffusers",
|
| 41 |
+
"flux": "stabilityai/stable-diffusion-3-medium-diffusers",
|
| 42 |
+
"flux-schnell": "stabilityai/stable-diffusion-3-medium-diffusers",
|
| 43 |
}
|
| 44 |
|
| 45 |
_EDIT_MODEL = "timbrooks/instruct-pix2pix"
|
|
|
|
| 93 |
- HF restituisce raw bytes PNG β non JSON.
|
| 94 |
- steps ottimali FLUX.1-schnell: 4 (veloce) β 8 (qualitΓ ).
|
| 95 |
"""
|
| 96 |
+
model_id = _MODEL_MAP.get(req.model, "stabilityai/stable-diffusion-3-medium-diffusers")
|
| 97 |
+
prompt = req.prompt.strip()[:400]
|
| 98 |
+
steps = min(max(req.steps, 1), 8)
|
| 99 |
+
width = min(max(req.width, 256), 1024)
|
| 100 |
+
height = min(max(req.height, 256), 1024)
|
| 101 |
+
|
| 102 |
+
def _run_generation():
|
| 103 |
+
client = InferenceClient(token=os.getenv("HF_TOKEN"), provider="auto", timeout=90)
|
| 104 |
+
return client.text_to_image(
|
| 105 |
+
prompt=prompt,
|
| 106 |
+
model=model_id,
|
| 107 |
+
negative_prompt=req.negative_prompt[:200] if req.negative_prompt else None,
|
| 108 |
+
num_inference_steps=steps,
|
| 109 |
+
width=width,
|
| 110 |
+
height=height,
|
| 111 |
+
)
|
| 112 |
|
| 113 |
+
try:
|
| 114 |
+
generated = await asyncio.to_thread(_run_generation)
|
| 115 |
+
output = io.BytesIO()
|
| 116 |
+
generated.save(output, format="PNG")
|
| 117 |
+
return {"ok": True, "image_b64": base64.b64encode(output.getvalue()).decode(), "mime": "image/png", "model": model_id, "prompt": req.prompt[:100]}
|
| 118 |
+
except TimeoutError:
|
| 119 |
+
return {"ok": False, "error": "Timeout 90s β modello HF in cold-start. Riprova tra 30s."}
|
| 120 |
+
except Exception as e:
|
| 121 |
+
_logger.warning("HF image generation failed: %s", type(e).__name__)
|
| 122 |
+
return {"ok": False, "error": f"HF image generation unavailable: {str(e)[:300]}"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 123 |
|
| 124 |
|
| 125 |
# βββ /edit ββββββββββββββββββββββββββββββββββββββββββοΏ½οΏ½βββββββββββββββββββββββββ
|
tests/test_vision_hf_only.py
CHANGED
|
@@ -63,16 +63,21 @@ def test_analyze_uses_hf_vqa_without_openai(monkeypatch):
|
|
| 63 |
|
| 64 |
|
| 65 |
def test_generate_uses_huggingface(monkeypatch):
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 69 |
|
| 70 |
result = asyncio.run(vision.generate_image(vision.GenerateImageRequest(prompt="un paesaggio")))
|
| 71 |
|
| 72 |
assert result["ok"] is True
|
| 73 |
assert result["mime"] == "image/png"
|
| 74 |
-
assert result["image_b64"]
|
| 75 |
-
assert "router.huggingface.co/hf-inference" in FakeClient.calls[0][0]
|
| 76 |
|
| 77 |
|
| 78 |
def test_edit_uses_inference_client_hf(monkeypatch):
|
|
|
|
| 63 |
|
| 64 |
|
| 65 |
def test_generate_uses_huggingface(monkeypatch):
|
| 66 |
+
class FakeInferenceClient:
|
| 67 |
+
def __init__(self, **kwargs):
|
| 68 |
+
self.kwargs = kwargs
|
| 69 |
+
|
| 70 |
+
def text_to_image(self, **kwargs):
|
| 71 |
+
assert kwargs["model"] == "stabilityai/stable-diffusion-3-medium-diffusers"
|
| 72 |
+
return Image.new("RGB", (1, 1), (0, 120, 255))
|
| 73 |
+
|
| 74 |
+
monkeypatch.setattr(vision, "InferenceClient", FakeInferenceClient)
|
| 75 |
|
| 76 |
result = asyncio.run(vision.generate_image(vision.GenerateImageRequest(prompt="un paesaggio")))
|
| 77 |
|
| 78 |
assert result["ok"] is True
|
| 79 |
assert result["mime"] == "image/png"
|
| 80 |
+
assert len(base64.b64decode(result["image_b64"])) > 0
|
|
|
|
| 81 |
|
| 82 |
|
| 83 |
def test_edit_uses_inference_client_hf(monkeypatch):
|