feat(analytics): normalise API + contrat explicite get_next_action (Lot A)
Contrat get_next_action() — suppression du None ambigu :
{"status": "selected", "edge": ..., ...}
{"status": "terminal"}
{"status": "blocked", "reason": "no_valid_edge" | ...}
ExecutionLoop dispatche proprement : blocked -> PAUSED + _pause_requested,
terminal -> succès légitime. Rétrocompat défensive (None legacy -> blocked).
Analytics API normalisée (kwargs-only) :
on_execution_complete(duration_ms, status, steps_total|completed|failed)
on_step_complete(duration_ms, ...)
on_recovery_attempt(duration_ms, ...)
Découverte critique : les anciens appels utilisaient des méthodes et champs
inexistants (ExecutionMetrics.duration, metrics_collector.record_execution).
Le code n'avait jamais tourné au runtime — zéro analytics remontée.
L'exception était avalée par le try/except englobant.
58 tests (18 analytics + 11 contrat + 20 ExecutionLoop + 12 edge_scorer
non-régression). Migration complète, pas de pont legacy.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -227,12 +227,10 @@ class VisualWorkflowExecutor:
|
||||
self.analytics_integration.on_execution_complete(
|
||||
execution_id=execution_id,
|
||||
workflow_id=workflow_graph.workflow_id,
|
||||
started_at=result.start_time,
|
||||
completed_at=result.end_time,
|
||||
duration=result._calculate_duration() / 1000.0, # en secondes
|
||||
status='success',
|
||||
duration_ms=float(result._calculate_duration() or 0.0),
|
||||
status='completed',
|
||||
steps_completed=len(workflow_graph.nodes),
|
||||
steps_failed=0
|
||||
steps_failed=0,
|
||||
)
|
||||
|
||||
# Collecter les métriques Analytics pour l'UI
|
||||
@@ -265,13 +263,11 @@ class VisualWorkflowExecutor:
|
||||
self.analytics_integration.on_execution_complete(
|
||||
execution_id=execution_id,
|
||||
workflow_id=visual_workflow.workflow_id,
|
||||
started_at=result.start_time,
|
||||
completed_at=result.end_time,
|
||||
duration=result._calculate_duration() / 1000.0 if result._calculate_duration() else 0,
|
||||
duration_ms=float(result._calculate_duration() or 0.0),
|
||||
status='failed',
|
||||
error_message=str(e),
|
||||
steps_completed=0,
|
||||
steps_failed=1
|
||||
steps_failed=1,
|
||||
)
|
||||
|
||||
# Enregistrer l'échec dans le système d'apprentissage
|
||||
@@ -312,7 +308,8 @@ class VisualWorkflowExecutor:
|
||||
if result.success:
|
||||
self._log(execution_id, 'info', f'Workflow exécuté avec succès')
|
||||
|
||||
# Notifier Analytics pour chaque étape
|
||||
# Notifier Analytics pour chaque étape (contrat normalisé Lot A :
|
||||
# duration_ms en millisecondes, plus de "duration" en secondes)
|
||||
for i, step_result in enumerate(result.step_results):
|
||||
if self.analytics_integration:
|
||||
self.analytics_integration.on_step_complete(
|
||||
@@ -322,8 +319,8 @@ class VisualWorkflowExecutor:
|
||||
action_type=step_result.action_type,
|
||||
started_at=step_result.start_time,
|
||||
completed_at=step_result.end_time,
|
||||
duration=step_result.duration_seconds,
|
||||
success=step_result.success
|
||||
duration_ms=float(step_result.duration_seconds or 0.0) * 1000.0,
|
||||
success=step_result.success,
|
||||
)
|
||||
|
||||
# Notifier la progression
|
||||
@@ -383,8 +380,8 @@ class VisualWorkflowExecutor:
|
||||
action_type=getattr(node, 'action_type', 'unknown'),
|
||||
started_at=step_start_time,
|
||||
completed_at=step_end_time,
|
||||
duration=step_duration,
|
||||
success=True
|
||||
duration_ms=float(step_duration or 0.0) * 1000.0,
|
||||
success=True,
|
||||
)
|
||||
|
||||
progress = (i + 1) / total_nodes * 100
|
||||
|
||||
Reference in New Issue
Block a user