sync: 190 file da Baida98/AI@fe52a247 (2026-08-26 16:35 UTC) [deploy-all]

#121
by Baida07 - opened
Files changed (1) hide show
  1. api/agent.py +49 -0
api/agent.py CHANGED
@@ -1404,6 +1404,55 @@ async def stream_agent_task(task_id: str, request: Request, resume: int = 0, rol
1404
  allow_tools=not bool(task.get('forbid_tools', False)),
1405
  allow_local_csv_conversion=bool(task.get('allow_local_csv_conversion', False)),
1406
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1407
  _agent_tasks[task_id]['status'] = 'SUCCESS'
1408
  asyncio.create_task(sb_update_status(task_id, 'SUCCESS')).add_done_callback(_log_task_exc)
1409
  # ARCH-K2.2: pubblica lifecycle event via Kernel
 
1404
  allow_tools=not bool(task.get('forbid_tools', False)),
1405
  allow_local_csv_conversion=bool(task.get('allow_local_csv_conversion', False)),
1406
  )
1407
+
1408
+ # ARTIFACT-CONTRACT-FALLBACK: alcuni provider restituiscono codice HTML
1409
+ # nella risposta finale dopo aver narrato una scrittura, senza produrre
1410
+ # il tool event `file_written`. Non committiamo mai una falsa positività:
1411
+ # se il goal è UI, proviamo solo a materializzare un documento HTML
1412
+ # completo già presente nell'output, usando l'executor VFS autenticato.
1413
+ # Se non troviamo un documento completo o la scrittura fallisce, il task
1414
+ # resta senza artifact e il frontend non riceve alcun sync inventato.
1415
+ if _artifact_goal and not _vfs_written_paths and getattr(loop, 'executor', None):
1416
+ _artifact_output = str(result.get('output', result) if isinstance(result, dict) else result)
1417
+ _html_match = re.search(r'(?is)(<!doctype\s+html\b.*?</html>)', _artifact_output)
1418
+ if not _html_match:
1419
+ _html_match = re.search(r'(?is)(<html(?:\s[^>]*)?>.*?</html>)', _artifact_output)
1420
+ if _html_match:
1421
+ _artifact_content = _html_match.group(1).strip()
1422
+ _artifact_path = 'index.html'
1423
+ _path_match = re.search(r'(?i)(?:/|\b)([\w.-]+\.html)\b', _artifact_output)
1424
+ if _path_match:
1425
+ _artifact_path = _path_match.group(1)
1426
+ try:
1427
+ _write_result = await asyncio.wait_for(
1428
+ loop.executor.run_tool('write_file', {
1429
+ 'path': _artifact_path,
1430
+ 'content': _artifact_content,
1431
+ }),
1432
+ timeout=25.0,
1433
+ )
1434
+ _write_ok = not (isinstance(_write_result, dict) and _write_result.get('error'))
1435
+ if _write_ok:
1436
+ _vfs_written_paths.add(_artifact_path)
1437
+ await step_cb({
1438
+ 'action': 'file_written', 'status': 'done',
1439
+ 'path': _artifact_path, 'content': _artifact_content,
1440
+ 'result': f'Artifact materializzato: {_artifact_path}',
1441
+ })
1442
+ _read_result = await asyncio.wait_for(
1443
+ loop.executor.run_tool('read_file', {'path': _artifact_path}),
1444
+ timeout=15.0,
1445
+ )
1446
+ _read_ok = not (isinstance(_read_result, dict) and _read_result.get('error'))
1447
+ await step_cb({
1448
+ 'action': 'read_file',
1449
+ 'status': 'done' if _read_ok else 'error',
1450
+ 'path': _artifact_path,
1451
+ 'result': f'Verifica artifact: {_artifact_path}' if _read_ok else str(_read_result)[:300],
1452
+ })
1453
+ except Exception as _artifact_exc:
1454
+ _logger.warning('[agent] artifact fallback failed: %s', type(_artifact_exc).__name__)
1455
+
1456
  _agent_tasks[task_id]['status'] = 'SUCCESS'
1457
  asyncio.create_task(sb_update_status(task_id, 'SUCCESS')).add_done_callback(_log_task_exc)
1458
  # ARCH-K2.2: pubblica lifecycle event via Kernel