feat(dashboard): launch supervised competence tests
This commit is contained in:
@@ -2515,6 +2515,151 @@ def dashboard_promote_competence(competence_id):
|
||||
}), status
|
||||
|
||||
|
||||
def _dashboard_streaming_json_request(
|
||||
method: str,
|
||||
path: str,
|
||||
*,
|
||||
payload: dict | None = None,
|
||||
timeout: int = 10,
|
||||
):
|
||||
"""Proxy JSON minimal vers le streaming server, avec Bearer token."""
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
|
||||
base_url = os.environ.get('RPA_STREAMING_URL', 'http://localhost:5005').rstrip('/')
|
||||
url = f"{base_url}{path}"
|
||||
headers = {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
token = os.environ.get('RPA_API_TOKEN', '')
|
||||
if token:
|
||||
headers['Authorization'] = f'Bearer {token}'
|
||||
|
||||
data_bytes = None
|
||||
if payload is not None:
|
||||
data_bytes = json.dumps(payload).encode('utf-8')
|
||||
|
||||
try:
|
||||
req = urllib.request.Request(url, data=data_bytes, headers=headers, method=method)
|
||||
with urllib.request.urlopen(req, timeout=timeout) as response:
|
||||
raw = response.read().decode('utf-8')
|
||||
try:
|
||||
data = json.loads(raw) if raw else {}
|
||||
except json.JSONDecodeError:
|
||||
data = {'raw': raw}
|
||||
return jsonify(data), response.status
|
||||
except urllib.error.HTTPError as exc:
|
||||
raw = exc.read().decode('utf-8')
|
||||
try:
|
||||
detail = json.loads(raw) if raw else {'error': str(exc)}
|
||||
except json.JSONDecodeError:
|
||||
detail = {'error': raw or str(exc)}
|
||||
return jsonify(detail), exc.code
|
||||
except urllib.error.URLError as exc:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': f'Serveur streaming (5005) inaccessible : {exc}',
|
||||
'hint': 'Vérifiez que le service streaming est démarré et que l’agent Windows est connecté.',
|
||||
}), 502
|
||||
|
||||
|
||||
@app.route('/api/v1/lea/competences/<competence_id>/replay', methods=['POST'])
|
||||
def dashboard_replay_competence(competence_id):
|
||||
"""Start a supervised competence replay through the dashboard."""
|
||||
from urllib.parse import quote
|
||||
|
||||
payload = request.get_json(silent=True) or {}
|
||||
replay_payload = {
|
||||
'supervised': bool(payload.get('supervised', True)),
|
||||
'start_replay': bool(payload.get('start_replay', True)),
|
||||
'session_id': str(payload.get('session_id') or ''),
|
||||
}
|
||||
machine_id = str(payload.get('machine_id') or '').strip()
|
||||
if machine_id:
|
||||
replay_payload['machine_id'] = machine_id
|
||||
|
||||
encoded_id = quote(competence_id, safe='')
|
||||
return _dashboard_streaming_json_request(
|
||||
'POST',
|
||||
f'/api/v1/lea/competences/{encoded_id}/replay',
|
||||
payload=replay_payload,
|
||||
timeout=30,
|
||||
)
|
||||
|
||||
|
||||
@app.route('/api/v1/lea/replays/<replay_id>', methods=['GET'])
|
||||
def dashboard_replay_status(replay_id):
|
||||
"""Return replay status from the streaming server."""
|
||||
from urllib.parse import quote
|
||||
|
||||
encoded_id = quote(replay_id, safe='')
|
||||
return _dashboard_streaming_json_request(
|
||||
'GET',
|
||||
f'/api/v1/traces/stream/replay/{encoded_id}',
|
||||
timeout=5,
|
||||
)
|
||||
|
||||
|
||||
@app.route('/api/v1/lea/replays/<replay_id>/resume', methods=['POST'])
|
||||
def dashboard_resume_replay(replay_id):
|
||||
"""Resume a supervised replay from the dashboard modal."""
|
||||
from urllib.parse import quote
|
||||
|
||||
payload = request.get_json(silent=True) or {}
|
||||
encoded_id = quote(replay_id, safe='')
|
||||
return _dashboard_streaming_json_request(
|
||||
'POST',
|
||||
f'/api/v1/traces/stream/replay/{encoded_id}/resume',
|
||||
payload={
|
||||
'acknowledged_check_ids': payload.get('acknowledged_check_ids') or [],
|
||||
},
|
||||
timeout=10,
|
||||
)
|
||||
|
||||
|
||||
@app.route('/api/v1/lea/competences/<competence_id>/verdict', methods=['POST'])
|
||||
def dashboard_submit_competence_verdict(competence_id):
|
||||
"""Persist a supervised competence verdict from the dashboard."""
|
||||
try:
|
||||
from core.competences.verdicts import (
|
||||
CompetenceVerdictError,
|
||||
store_competence_verdict,
|
||||
)
|
||||
payload = request.get_json(silent=True) or {}
|
||||
verdict = store_competence_verdict(competence_id, payload)
|
||||
except KeyError:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': f"Competence '{competence_id}' introuvable",
|
||||
'write_back_enabled': False,
|
||||
'yaml_write': False,
|
||||
}), 404
|
||||
except CompetenceVerdictError as exc:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': str(exc),
|
||||
'write_back_enabled': False,
|
||||
'yaml_write': False,
|
||||
}), 400
|
||||
except Exception as exc:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': str(exc),
|
||||
'write_back_enabled': False,
|
||||
'yaml_write': False,
|
||||
}), 500
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'competence_id': competence_id,
|
||||
'verdict': verdict,
|
||||
'duplicate': verdict['duplicate'],
|
||||
'write_back_enabled': False,
|
||||
'yaml_write': False,
|
||||
}), 200 if verdict['duplicate'] else 201
|
||||
|
||||
|
||||
def _kb_faiss_stats() -> dict:
|
||||
"""Statistiques de l'index FAISS."""
|
||||
faiss_index_path = DATA_PATH / "faiss_index" / "main.index"
|
||||
|
||||
Reference in New Issue
Block a user