Spaces:
Running
Running
sync: 181 file da Baida98/AI@3bcaeb21 (2026-08-25 11:21 UTC) [deploy-all]
#78
by Baida07 - opened
- agents/unified_loop_tools.py +10 -1
- tests/test_direct_file_conversion.py +4 -1
- tools/registry.py +32 -24
agents/unified_loop_tools.py
CHANGED
|
@@ -360,7 +360,16 @@ class DirectToolsMixin:
|
|
| 360 |
if _sc is not None:
|
| 361 |
return _sc
|
| 362 |
_t0 = asyncio.get_event_loop().time()
|
| 363 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 364 |
try:
|
| 365 |
from api.state import record_timing as _rtc; _rtc("tool_call", (asyncio.get_event_loop().time() - _t0) * 1000)
|
| 366 |
except Exception as _e: _logger.debug('[timing/record_timing] %s', _e)
|
|
|
|
| 360 |
if _sc is not None:
|
| 361 |
return _sc
|
| 362 |
_t0 = asyncio.get_event_loop().time()
|
| 363 |
+
# Nel percorso diretto privilegiamo l'URL keyless e renderizzabile
|
| 364 |
+
# subito: l'endpoint vision interno può richiedere un cold-start
|
| 365 |
+
# più lungo del timeout interattivo, facendo ricadere il task nel
|
| 366 |
+
# fallback LLM pur avendo un fallback immagine disponibile.
|
| 367 |
+
r = await asyncio.wait_for(
|
| 368 |
+
TOOL_REGISTRY["generate_image"]["_fn"](
|
| 369 |
+
prompt=_img_prompt[:600], prefer_internal=False,
|
| 370 |
+
),
|
| 371 |
+
timeout=12,
|
| 372 |
+
)
|
| 373 |
try:
|
| 374 |
from api.state import record_timing as _rtc; _rtc("tool_call", (asyncio.get_event_loop().time() - _t0) * 1000)
|
| 375 |
except Exception as _e: _logger.debug('[timing/record_timing] %s', _e)
|
tests/test_direct_file_conversion.py
CHANGED
|
@@ -12,7 +12,10 @@ def _install_tool_stubs(writes, image_url="https://example.test/generated.png"):
|
|
| 12 |
# Il registry reale usa il contratto `ok` per le operazioni filesystem.
|
| 13 |
return {"ok": True, "path": path}
|
| 14 |
|
| 15 |
-
async def generate_image(prompt, width=512, height=512):
|
|
|
|
|
|
|
|
|
|
| 16 |
return {"url": image_url, "prompt": prompt, "width": width, "height": height}
|
| 17 |
|
| 18 |
registry.TOOL_REGISTRY = {
|
|
|
|
| 12 |
# Il registry reale usa il contratto `ok` per le operazioni filesystem.
|
| 13 |
return {"ok": True, "path": path}
|
| 14 |
|
| 15 |
+
async def generate_image(prompt, width=512, height=512, prefer_internal=True):
|
| 16 |
+
# Il percorso interattivo deve ottenere un URL renderizzabile subito,
|
| 17 |
+
# senza attendere il cold-start del generatore interno.
|
| 18 |
+
assert prefer_internal is False
|
| 19 |
return {"url": image_url, "prompt": prompt, "width": width, "height": height}
|
| 20 |
|
| 21 |
registry.TOOL_REGISTRY = {
|
tools/registry.py
CHANGED
|
@@ -438,7 +438,12 @@ async def _run_python(code: str) -> dict:
|
|
| 438 |
|
| 439 |
|
| 440 |
|
| 441 |
-
async def _generate_image(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 442 |
"""
|
| 443 |
Genera immagine AI: FLUX.1-schnell via backend /api/vision/generate (HF Space, gratuito),
|
| 444 |
fallback Pollinations AI se FLUX non disponibile.
|
|
@@ -446,29 +451,32 @@ async def _generate_image(prompt: str, width: int = 512, height: int = 512) -> d
|
|
| 446 |
import urllib.parse
|
| 447 |
|
| 448 |
# V001: Prova prima il backend interno FLUX (stesso processo, localhost)
|
| 449 |
-
|
| 450 |
-
|
| 451 |
-
|
| 452 |
-
|
| 453 |
-
|
| 454 |
-
|
| 455 |
-
|
| 456 |
-
|
| 457 |
-
|
| 458 |
-
|
| 459 |
-
|
| 460 |
-
|
| 461 |
-
|
| 462 |
-
|
| 463 |
-
|
| 464 |
-
|
| 465 |
-
|
| 466 |
-
|
| 467 |
-
|
| 468 |
-
|
| 469 |
-
|
| 470 |
-
|
| 471 |
-
|
|
|
|
|
|
|
|
|
|
| 472 |
|
| 473 |
# Fallback: Pollinations AI (gratuito, nessuna API key)
|
| 474 |
encoded = urllib.parse.quote(prompt.strip(), safe="")
|
|
|
|
| 438 |
|
| 439 |
|
| 440 |
|
| 441 |
+
async def _generate_image(
|
| 442 |
+
prompt: str,
|
| 443 |
+
width: int = 512,
|
| 444 |
+
height: int = 512,
|
| 445 |
+
prefer_internal: bool = True,
|
| 446 |
+
) -> dict:
|
| 447 |
"""
|
| 448 |
Genera immagine AI: FLUX.1-schnell via backend /api/vision/generate (HF Space, gratuito),
|
| 449 |
fallback Pollinations AI se FLUX non disponibile.
|
|
|
|
| 451 |
import urllib.parse
|
| 452 |
|
| 453 |
# V001: Prova prima il backend interno FLUX (stesso processo, localhost)
|
| 454 |
+
# soltanto per chiamanti che possono attendere il cold-start. Il direct tool
|
| 455 |
+
# della chat privilegia invece il fallback URL immediato.
|
| 456 |
+
if prefer_internal:
|
| 457 |
+
_base_url = os.environ.get("BACKEND_BASE_URL", "http://localhost:7860")
|
| 458 |
+
_token = os.environ.get("INTERNAL_TOKEN", "")
|
| 459 |
+
try:
|
| 460 |
+
async with httpx.AsyncClient(timeout=30.0) as c:
|
| 461 |
+
_r = await c.post(
|
| 462 |
+
f"{_base_url}/api/vision/generate",
|
| 463 |
+
json={"prompt": prompt.strip()[:600], "width": width, "height": height},
|
| 464 |
+
headers={**({"X-Internal-Token": _token} if _token else {})},
|
| 465 |
+
)
|
| 466 |
+
if _r.status_code == 200:
|
| 467 |
+
_data = _r.json()
|
| 468 |
+
if _data.get("ok") and _data.get("image_url"):
|
| 469 |
+
return {
|
| 470 |
+
"url": _data["image_url"],
|
| 471 |
+
"prompt": prompt,
|
| 472 |
+
"width": width,
|
| 473 |
+
"height": height,
|
| 474 |
+
"ready": True,
|
| 475 |
+
"source": "flux",
|
| 476 |
+
"note": "Immagine generata con FLUX.1-schnell via backend.",
|
| 477 |
+
}
|
| 478 |
+
except Exception:
|
| 479 |
+
pass # FLUX non raggiungibile → Pollinations fallback
|
| 480 |
|
| 481 |
# Fallback: Pollinations AI (gratuito, nessuna API key)
|
| 482 |
encoded = urllib.parse.quote(prompt.strip(), safe="")
|