sync: 181 file da Baida98/AI@a636f462 (2026-08-25 13:51 UTC) [deploy-all]

#86
by Baida07 - opened
agents/unified_loop_tools.py CHANGED
@@ -11,6 +11,7 @@ Python MRO garantisce che self.xxx funzioni per attr definite su UnifiedAgentLoo
11
  """
12
  from __future__ import annotations
13
  import asyncio
 
14
  import os
15
  import re
16
  from typing import Any
@@ -370,11 +371,27 @@ class DirectToolsMixin:
370
  f"https://image.pollinations.ai/prompt/{quote(_img_prompt, safe='')}"
371
  f"?width=512&height=512&seed={_img_seed}&nologo=true&enhance=true"
372
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
373
  return (
374
  "[DIRECT_TERMINAL]\n"
375
  f"![Immagine generata]({img_url})\n\n"
376
- "E2E_IMAGE_OK: immagine generata e visualizzata qui sopra. "
377
  f"[Apri o scarica l’immagine]({img_url}).\n\n"
 
378
  f"Prompt usato: {_img_prompt[:200]}\n"
379
  "Dimensioni: 512x512 px"
380
  )
 
11
  """
12
  from __future__ import annotations
13
  import asyncio
14
+ import hashlib
15
  import os
16
  import re
17
  from typing import Any
 
371
  f"https://image.pollinations.ai/prompt/{quote(_img_prompt, safe='')}"
372
  f"?width=512&height=512&seed={_img_seed}&nologo=true&enhance=true"
373
  )
374
+ # L'URL viene materializzato dal client nel VFS come data URI JPEG
375
+ # durante il commit atomico del task. Il nome è derivato dal prompt
376
+ # normalizzato: è sicuro, ripetibile e non contiene input utente.
377
+ _artifact_id = hashlib.sha256(_img_prompt.encode("utf-8")).hexdigest()[:12]
378
+ _artifact_path = f"generated-image-{_artifact_id}.jpg"
379
+ if on_step:
380
+ await _maybe_await(on_step({
381
+ "action": "file_written",
382
+ "status": "done",
383
+ "path": _artifact_path,
384
+ "source_url": img_url,
385
+ "mime_type": "image/jpeg",
386
+ "title": "Immagine salvata nel workspace",
387
+ "explanation": f"Salvo {_artifact_path} nel VFS…",
388
+ }))
389
  return (
390
  "[DIRECT_TERMINAL]\n"
391
  f"![Immagine generata]({img_url})\n\n"
392
+ "E2E_IMAGE_OK: immagine generata, visualizzata e salvata nel workspace. "
393
  f"[Apri o scarica l’immagine]({img_url}).\n\n"
394
+ f"File VFS: `{_artifact_path}`\n"
395
  f"Prompt usato: {_img_prompt[:200]}\n"
396
  "Dimensioni: 512x512 px"
397
  )
api/agent.py CHANGED
@@ -1222,11 +1222,21 @@ async def stream_agent_task(task_id: str, request: Request, resume: int = 0, rol
1222
  step_data.get('output', '')[:500])
1223
  _vfs_op = 'delete' if 'delete' in _action else 'write'
1224
  _vfs_evt: dict = {'taskId': task_id, 'file': str(_vfs_file)[:500], 'op': _vfs_op}
1225
- # SYNC-1: includi content nel SSE event per file_written (≤60KB)
1226
- # Frontend scrive direttamente nel VFS locale senza fetch aggiuntivo
 
 
1227
  if _action == 'file_written' and step_data.get('content'):
1228
  _vfs_evt['content'] = str(step_data['content'])[:60_000]
1229
  _vfs_written_paths.add(str(_vfs_file)[:500])
 
 
 
 
 
 
 
 
1230
  _sse('vfs_update', _vfs_evt)
1231
 
1232
  # S363-UI: thought event — emitted when planner completes
 
1222
  step_data.get('output', '')[:500])
1223
  _vfs_op = 'delete' if 'delete' in _action else 'write'
1224
  _vfs_evt: dict = {'taskId': task_id, 'file': str(_vfs_file)[:500], 'op': _vfs_op}
1225
+ # SYNC-1: includi content nel SSE event per file_written (≤60KB).
1226
+ # Per immagini dirette il backend non serializza byte nell'SSE:
1227
+ # inoltra soltanto l'URL HTTPS generato internamente, che il client
1228
+ # materializza come data URI nel proprio VFS prima del commit atomico.
1229
  if _action == 'file_written' and step_data.get('content'):
1230
  _vfs_evt['content'] = str(step_data['content'])[:60_000]
1231
  _vfs_written_paths.add(str(_vfs_file)[:500])
1232
+ elif _action == 'file_written':
1233
+ _source_url = str(step_data.get('source_url') or '')
1234
+ _mime_type = str(step_data.get('mime_type') or '')
1235
+ if (_source_url.startswith('https://image.pollinations.ai/') and
1236
+ _mime_type in {'image/jpeg', 'image/png', 'image/webp'}):
1237
+ _vfs_evt['sourceUrl'] = _source_url[:2_000]
1238
+ _vfs_evt['mimeType'] = _mime_type
1239
+ _vfs_written_paths.add(str(_vfs_file)[:500])
1240
  _sse('vfs_update', _vfs_evt)
1241
 
1242
  # S363-UI: thought event — emitted when planner completes
tests/test_direct_file_conversion.py CHANGED
@@ -76,15 +76,27 @@ def test_direct_image_returns_renderable_terminal_markdown_without_llm():
76
  def _max_tokens_for_goal(self, _goal):
77
  return 4096
78
 
 
 
 
 
 
79
  output, called, succeeded, errors = asyncio.run(
80
  Harness()._run_direct_tools(
81
- "Genera un’immagine quadrata di un aeroplanino arancione su fondo blu notte."
 
82
  )
83
  )
84
 
85
  assert output.startswith("[DIRECT_TERMINAL]\n![Immagine generata](https://image.pollinations.ai/prompt/")
86
  assert "E2E_IMAGE_OK" in output
87
  assert "aeroplanino%20arancione" in output
 
 
 
 
 
 
88
  assert called == 1
89
  assert succeeded == 1
90
  assert errors == 0
 
76
  def _max_tokens_for_goal(self, _goal):
77
  return 4096
78
 
79
+ events = []
80
+
81
+ async def on_step(event):
82
+ events.append(event)
83
+
84
  output, called, succeeded, errors = asyncio.run(
85
  Harness()._run_direct_tools(
86
+ "Genera un’immagine quadrata di un aeroplanino arancione su fondo blu notte.",
87
+ on_step=on_step,
88
  )
89
  )
90
 
91
  assert output.startswith("[DIRECT_TERMINAL]\n![Immagine generata](https://image.pollinations.ai/prompt/")
92
  assert "E2E_IMAGE_OK" in output
93
  assert "aeroplanino%20arancione" in output
94
+ assert "File VFS: `generated-image-" in output
95
+ image_event = next(event for event in events if event.get("action") == "file_written")
96
+ assert image_event["path"].startswith("generated-image-")
97
+ assert image_event["path"].endswith(".jpg")
98
+ assert image_event["source_url"].startswith("https://image.pollinations.ai/prompt/")
99
+ assert image_event["mime_type"] == "image/jpeg"
100
  assert called == 1
101
  assert succeeded == 1
102
  assert errors == 0