fix(vwb): propage l'image d'ancre aux substeps compound à l'import (SP-1/U-B)
Les actions compound passaient par _convert_compound_substep qui ne lisait jamais l'image d'ancre du parent -> substeps anchor_id NULL, "Ancre requise" sans image dans le VWB. On pose desormais l'ancre du parent (meme fallback que la branche action simple) sur le 1er substep cliquable uniquement. Test: test_learned_workflow_bridge.py (TDD, RED->GREEN). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -166,11 +166,30 @@ def convert_learned_to_vwb_steps(
|
|||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# Image d'ancre du parent : à poser sur le 1er substep cliquable.
|
||||||
|
# Les actions simples l'obtiennent déjà (branche `else` plus bas) ;
|
||||||
|
# les compound ne la propageaient pas → substeps anchor_id NULL.
|
||||||
|
compound_anchor_b64 = (
|
||||||
|
target.get("anchor_image_base64")
|
||||||
|
or target.get("screenshot")
|
||||||
|
or (target.get("context_hints") or {}).get("anchor_image_base64")
|
||||||
|
)
|
||||||
|
compound_anchor_attached = False
|
||||||
|
|
||||||
for sub_idx, sub in enumerate(sub_steps):
|
for sub_idx, sub in enumerate(sub_steps):
|
||||||
sub_type = sub.get("type", "unknown")
|
sub_type = sub.get("type", "unknown")
|
||||||
sub_vwb_type, sub_params = _convert_compound_substep(
|
sub_vwb_type, sub_params = _convert_compound_substep(
|
||||||
sub_type, sub, target
|
sub_type, sub, target
|
||||||
)
|
)
|
||||||
|
# Poser l'ancre du parent sur le 1er substep cliquable uniquement
|
||||||
|
# (éviter de la dupliquer sur les N substeps).
|
||||||
|
if (
|
||||||
|
compound_anchor_b64
|
||||||
|
and not compound_anchor_attached
|
||||||
|
and sub_vwb_type in ("click_anchor", "double_click_anchor", "right_click_anchor")
|
||||||
|
):
|
||||||
|
sub_params["_anchor_image_base64"] = compound_anchor_b64
|
||||||
|
compound_anchor_attached = True
|
||||||
label = _build_step_label(sub_vwb_type, sub_params, from_name, to_name)
|
label = _build_step_label(sub_vwb_type, sub_params, from_name, to_name)
|
||||||
steps.append({
|
steps.append({
|
||||||
"action_type": sub_vwb_type,
|
"action_type": sub_vwb_type,
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Test TDD — propagation de l'image d'ancre aux substeps *compound* (SP-1 / U-B).
|
||||||
|
|
||||||
|
Bug : `convert_learned_to_vwb_steps` ne propage `anchor_image_base64` qu'aux
|
||||||
|
actions simples (branche L226-233). Les substeps des actions *compound*
|
||||||
|
(majoritaires côté Léa) passent par `_convert_compound_substep` qui ne lit
|
||||||
|
jamais l'ancre → `anchor_id NULL` → "Ancre requise" sans image dans le VWB.
|
||||||
|
|
||||||
|
Cible du fix : poser l'image d'ancre du parent sur le 1er substep cliquable.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||||||
|
|
||||||
|
from services.learned_workflow_bridge import convert_learned_to_vwb_steps
|
||||||
|
|
||||||
|
_CLICK_TYPES = ("click_anchor", "double_click_anchor", "right_click_anchor")
|
||||||
|
|
||||||
|
|
||||||
|
def _compound_workflow_with_anchor(b64: str) -> dict:
|
||||||
|
"""Workflow core minimal : un edge compound dont la cible porte une image
|
||||||
|
d'ancre dans `context_hints`, avec un substep cliquable suivi d'une saisie."""
|
||||||
|
return {
|
||||||
|
"workflow_id": "wf_test_compound_anchor",
|
||||||
|
"name": "Test compound anchor",
|
||||||
|
"entry_nodes": ["n1"],
|
||||||
|
"nodes": [
|
||||||
|
{"node_id": "n1", "name": "Start"},
|
||||||
|
{"node_id": "n2", "name": "End"},
|
||||||
|
],
|
||||||
|
"edges": [
|
||||||
|
{
|
||||||
|
"edge_id": "e1",
|
||||||
|
"from_node": "n1",
|
||||||
|
"to_node": "n2",
|
||||||
|
"action": {
|
||||||
|
"type": "compound",
|
||||||
|
"target": {
|
||||||
|
"by_text": "Fichier",
|
||||||
|
"context_hints": {"anchor_image_base64": b64},
|
||||||
|
},
|
||||||
|
"parameters": {
|
||||||
|
"steps": [
|
||||||
|
{"type": "mouse_click", "pos": [0.5, 0.5], "button": "left"},
|
||||||
|
{"type": "text_input", "text": "bonjour"},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_compound_substep_inherits_anchor_image():
|
||||||
|
"""Le 1er substep cliquable d'un compound doit hériter de l'image d'ancre du parent."""
|
||||||
|
b64 = "ZmFrZV9hbmNob3I=" # "fake_anchor" encodé base64
|
||||||
|
_meta, steps, _warnings = convert_learned_to_vwb_steps(
|
||||||
|
_compound_workflow_with_anchor(b64)
|
||||||
|
)
|
||||||
|
|
||||||
|
clickable = [s for s in steps if s["action_type"] in _CLICK_TYPES]
|
||||||
|
assert clickable, "le compound aurait dû produire au moins un step cliquable"
|
||||||
|
assert clickable[0]["parameters"].get("_anchor_image_base64") == b64, (
|
||||||
|
"le 1er substep cliquable doit hériter de l'image d'ancre du parent "
|
||||||
|
"(target.context_hints.anchor_image_base64)"
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user