v4
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
"""Dona: reuse the existing, data-only dashboard renderer for Hermes replies.
|
||||
|
||||
This module never executes model-supplied HTML, JavaScript or instructions. Only
|
||||
`dona_dynamic.normalize_dashboard` can build cards; invalid content remains text.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from typing import Any
|
||||
|
||||
from services import dona_dynamic
|
||||
|
||||
|
||||
MAX_JSON = dona_dynamic.MAX_RESPONSE
|
||||
|
||||
|
||||
def is_monitor_request(prompt: str) -> bool:
|
||||
"""Route clearly operational Proxmox/Wazuh checks to existing dashboard API."""
|
||||
q = str(prompt or '').casefold()
|
||||
if not re.search(r'\b(proxmox|wazuh|cluster|clusters)\b', q):
|
||||
return False
|
||||
return bool(re.search(
|
||||
r'\b(v[eé]rifi(?:e|es|er|ez)|contr[oô]le(?:r|s|z)?|surveille(?:r|s|z)?|'
|
||||
r'montre|affiche|tableau|[eé]tat|statut|status|ressources?|'
|
||||
r'performances?|noeuds?|nœuds?|serveurs?|alertes?|supervision|'
|
||||
r'fonctionn(?:e|ement)|disponibilit[eé])\b', q))
|
||||
|
||||
|
||||
def _candidate_text(answer: Any, display: Any) -> str | None:
|
||||
if isinstance(display, dict):
|
||||
if isinstance(display.get('cards'), list):
|
||||
# Raw dashboard object should not be converted via repr().
|
||||
import json
|
||||
return json.dumps(display, ensure_ascii=False)
|
||||
if display.get('type') in (None, 'text'):
|
||||
raw = display.get('text')
|
||||
if isinstance(raw, str) and '"cards"' in raw:
|
||||
return raw
|
||||
if isinstance(answer, str) and '"cards"' in answer:
|
||||
return answer
|
||||
if isinstance(answer, dict):
|
||||
if isinstance(answer.get('cards'), list):
|
||||
import json
|
||||
return json.dumps(answer, ensure_ascii=False)
|
||||
for key in ('text', 'speech'):
|
||||
raw = answer.get(key)
|
||||
if isinstance(raw, str) and '"cards"' in raw:
|
||||
return raw
|
||||
return None
|
||||
|
||||
|
||||
def _clean_line_continuations(text: str) -> str:
|
||||
"""Repair two common harmless Hermes JSON formatting artefacts only.
|
||||
|
||||
Trailing backslash + real newline (copied JSON), and invalid ``\\~``
|
||||
escaping in approximate values. Other malformed content is left untouched.
|
||||
"""
|
||||
text = re.sub(r'\\[ \t]*\r?\n', '\n', text)
|
||||
return text.replace('\\~', '~')
|
||||
|
||||
|
||||
def as_board(answer: Any, display: Any, prompt: str) -> dict | None:
|
||||
"""Return a server-validated dashboard, or None for an ordinary answer."""
|
||||
raw = _candidate_text(answer, display)
|
||||
if not raw or len(raw) > MAX_JSON:
|
||||
return None
|
||||
try:
|
||||
extracted = dona_dynamic.extract_dashboard(_clean_line_continuations(raw))
|
||||
return dona_dynamic.normalize_dashboard(extracted, prompt=prompt, origin='hermes')
|
||||
except (ValueError, TypeError, KeyError):
|
||||
return None
|
||||
|
||||
|
||||
def convert(answer: Any, prompt: str, display: Any, speech: str):
|
||||
"""Convert ONLY actual card JSON. Preserve ordinary answers and images."""
|
||||
if isinstance(display, dict) and display.get('type') not in (None, 'text'):
|
||||
return display, speech
|
||||
board = as_board(answer, display, prompt)
|
||||
if board is None:
|
||||
return display, speech
|
||||
return {'type': 'dona_dynamic', 'dashboard': board}, 'Voici le tableau de bord : ' + board['title']
|
||||
Reference in New Issue
Block a user