backup: snapshot post-démo GHT 2026-05-19
Backup état complet après enregistrement vidéo démo de bout en bout. À utiliser comme point de référence pour la consolidation post-démo. Changements majeurs de la session 18-19 mai : - AIVA-URGENCE : page autonome avec preset URL + auto-focus chain - Workflow Demo_urgence_3_db : merge linux_db + steps AIVA + pause humaine NoMachine - Bypass LLM (static_result / static_text) dans replay_engine pour démos déterministes sans appel Ollama - Fix api_stream:3013 — replay_paused au premier polling /next - dag_execute : lift duration_ms vers top-level pour wait runtime - NPM bypass auth /aiva-urgence/ via location ^~ (proxy_host/10.conf hors git) - scripts/cancel-replays.sh — workaround Stop VWB qui ne purge pas la queue Anchors visuels (468) forcés dans le commit pour garantir restorabilité. DB workflows actuelle + ~12 .bak DB de la journée incluses. Sujets identifiés pour consolidation post-démo (TODO) : 1. Bug VWB recapture anchor ne régénère pas le PNG 2. Léa client accumule état mémoire (restart périodique requis) 3. Stop VWB ne purge pas la queue serveur (lien manquant vers /replay/cancel) 4. Bug coord client mss tronqué 2560x60 → mapping Y cassé 5. delay_before/delay_after ignorés au runtime (fix partiel duration_ms) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -18,8 +18,10 @@ from agent_v0.server_v1.replay_engine import (
|
||||
_resolve_runtime_vars,
|
||||
_handle_extract_text_action,
|
||||
_handle_t2a_decision_action,
|
||||
_handle_concat_text_vars_action,
|
||||
_edge_to_normalized_actions,
|
||||
_create_replay_state,
|
||||
SCROLL_PAUSE_MS,
|
||||
)
|
||||
from visual_workflow_builder.backend.services.learned_workflow_bridge import (
|
||||
VWB_ACTION_TO_CORE,
|
||||
@@ -92,7 +94,9 @@ def test_t2a_decision_in_allowed():
|
||||
|
||||
|
||||
def test_server_side_types():
|
||||
assert _SERVER_SIDE_ACTION_TYPES == {"extract_text", "t2a_decision"}
|
||||
# Set extensible : on vérifie au minimum les 2 types historiques.
|
||||
# extract_text_scroll ajoute `_concat_text_vars` (action serveur interne).
|
||||
assert {"extract_text", "t2a_decision"}.issubset(_SERVER_SIDE_ACTION_TYPES)
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
@@ -280,3 +284,165 @@ def test_export_workflow_with_t2a_chain():
|
||||
# Vérifier que le templating est bien transporté
|
||||
t2a_edge = next(e for e in core["edges"] if e["action"]["type"] == "t2a_decision")
|
||||
assert t2a_edge["action"]["parameters"]["input_template"] == "{{dpi}}"
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# extract_text_scroll — expansion + handler concat
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
def test_extract_text_scroll_in_allowed():
|
||||
assert "extract_text_scroll" in _ALLOWED_ACTION_TYPES
|
||||
assert "_concat_text_vars" in _ALLOWED_ACTION_TYPES
|
||||
|
||||
|
||||
def test_extract_text_scroll_concat_is_server_side():
|
||||
assert "_concat_text_vars" in _SERVER_SIDE_ACTION_TYPES
|
||||
|
||||
|
||||
def test_edge_to_action_extract_text_scroll_expands_to_six_steps():
|
||||
edge = _FakeEdge(_FakeAction(
|
||||
"extract_text_scroll",
|
||||
parameters={"variable_name": "t_full", "paragraph": True},
|
||||
))
|
||||
actions = _edge_to_normalized_actions(edge, params={})
|
||||
# 6 actions : OCR(top), Ctrl+End, wait, OCR(bottom), concat, Ctrl+Home
|
||||
assert len(actions) == 6
|
||||
types = [a["type"] for a in actions]
|
||||
assert types == [
|
||||
"extract_text",
|
||||
"key_combo",
|
||||
"wait",
|
||||
"extract_text",
|
||||
"_concat_text_vars",
|
||||
"key_combo",
|
||||
]
|
||||
# Sub-actions OCR utilisent des vars internes différentes
|
||||
assert actions[0]["parameters"]["output_var"] == "__t_full_top"
|
||||
assert actions[3]["parameters"]["output_var"] == "__t_full_bottom"
|
||||
# Ctrl+End / Ctrl+Home corrects
|
||||
assert actions[1]["keys"] == ["ctrl", "end"]
|
||||
assert actions[5]["keys"] == ["ctrl", "home"]
|
||||
# Wait = SCROLL_PAUSE_MS
|
||||
assert actions[2]["duration_ms"] == SCROLL_PAUSE_MS
|
||||
# Concat lit les bons noms et écrit dans la var finale
|
||||
concat_params = actions[4]["parameters"]
|
||||
assert concat_params["top_var"] == "__t_full_top"
|
||||
assert concat_params["bottom_var"] == "__t_full_bottom"
|
||||
assert concat_params["output_var"] == "t_full"
|
||||
assert concat_params["separator"] == "\n\n"
|
||||
# Tous les action_id sont uniques et toutes les actions héritent de l'edge
|
||||
action_ids = {a["action_id"] for a in actions}
|
||||
assert len(action_ids) == 6
|
||||
for a in actions:
|
||||
assert a["edge_id"] == "e1"
|
||||
assert a["from_node"] == "n1"
|
||||
|
||||
|
||||
def test_edge_to_action_extract_text_scroll_default_var_name():
|
||||
edge = _FakeEdge(_FakeAction("extract_text_scroll", parameters={}))
|
||||
actions = _edge_to_normalized_actions(edge, params={})
|
||||
# Default = extracted_text, donc internal vars = __extracted_text_top/bottom
|
||||
assert actions[0]["parameters"]["output_var"] == "__extracted_text_top"
|
||||
assert actions[4]["parameters"]["output_var"] == "extracted_text"
|
||||
|
||||
|
||||
def test_edge_to_action_extract_text_scroll_accepts_output_var_legacy():
|
||||
"""Compat : `output_var` accepté en plus de `variable_name`."""
|
||||
edge = _FakeEdge(_FakeAction(
|
||||
"extract_text_scroll",
|
||||
parameters={"output_var": "legacy_var"},
|
||||
))
|
||||
actions = _edge_to_normalized_actions(edge, params={})
|
||||
assert actions[4]["parameters"]["output_var"] == "legacy_var"
|
||||
|
||||
|
||||
def test_handle_concat_text_vars_merges_top_and_bottom():
|
||||
state = _create_replay_state("rep1", "wf", "sess", 3)
|
||||
state["variables"] = {
|
||||
"__t_full_top": "Lignes du haut",
|
||||
"__t_full_bottom": "Lignes du bas",
|
||||
"other": "intact",
|
||||
}
|
||||
action = {
|
||||
"type": "_concat_text_vars",
|
||||
"parameters": {
|
||||
"top_var": "__t_full_top",
|
||||
"bottom_var": "__t_full_bottom",
|
||||
"output_var": "t_full",
|
||||
"separator": "\n\n",
|
||||
},
|
||||
}
|
||||
ok = _handle_concat_text_vars_action(action, state)
|
||||
assert ok is True
|
||||
assert state["variables"]["t_full"] == "Lignes du haut\n\nLignes du bas"
|
||||
# Variables internes nettoyées
|
||||
assert "__t_full_top" not in state["variables"]
|
||||
assert "__t_full_bottom" not in state["variables"]
|
||||
# Autres variables préservées
|
||||
assert state["variables"]["other"] == "intact"
|
||||
|
||||
|
||||
def test_handle_concat_text_vars_handles_empty_top():
|
||||
state = _create_replay_state("rep1", "wf", "sess", 3)
|
||||
state["variables"] = {"__a_top": "", "__a_bottom": "Bas seul"}
|
||||
action = {
|
||||
"type": "_concat_text_vars",
|
||||
"parameters": {
|
||||
"top_var": "__a_top",
|
||||
"bottom_var": "__a_bottom",
|
||||
"output_var": "a",
|
||||
"separator": "\n\n",
|
||||
},
|
||||
}
|
||||
ok = _handle_concat_text_vars_action(action, state)
|
||||
assert ok is True
|
||||
# Pas de séparateur en début/fin si une var est vide
|
||||
assert state["variables"]["a"] == "Bas seul"
|
||||
|
||||
|
||||
def test_handle_concat_text_vars_handles_both_empty():
|
||||
state = _create_replay_state("rep1", "wf", "sess", 3)
|
||||
state["variables"] = {"__a_top": "", "__a_bottom": ""}
|
||||
action = {
|
||||
"type": "_concat_text_vars",
|
||||
"parameters": {
|
||||
"top_var": "__a_top",
|
||||
"bottom_var": "__a_bottom",
|
||||
"output_var": "a",
|
||||
},
|
||||
}
|
||||
ok = _handle_concat_text_vars_action(action, state)
|
||||
assert ok is False # rien d'utile produit
|
||||
assert state["variables"]["a"] == ""
|
||||
|
||||
|
||||
def test_handle_concat_text_vars_preserves_user_named_vars():
|
||||
"""Si top/bottom ne commencent pas par __ on ne les supprime pas."""
|
||||
state = _create_replay_state("rep1", "wf", "sess", 3)
|
||||
state["variables"] = {"user_top": "haut", "user_bottom": "bas"}
|
||||
action = {
|
||||
"type": "_concat_text_vars",
|
||||
"parameters": {
|
||||
"top_var": "user_top",
|
||||
"bottom_var": "user_bottom",
|
||||
"output_var": "merged",
|
||||
},
|
||||
}
|
||||
_handle_concat_text_vars_action(action, state)
|
||||
assert state["variables"]["user_top"] == "haut"
|
||||
assert state["variables"]["user_bottom"] == "bas"
|
||||
assert state["variables"]["merged"] == "haut\n\nbas"
|
||||
|
||||
|
||||
def test_vwb_extract_text_scroll_passthrough():
|
||||
assert VWB_ACTION_TO_CORE["extract_text_scroll"] == "extract_text_scroll"
|
||||
|
||||
|
||||
def test_vwb_params_extract_text_scroll_preserves_variable_name():
|
||||
p = _vwb_params_to_core("extract_text_scroll", {"variable_name": "t_full"})
|
||||
assert p == {"variable_name": "t_full"}
|
||||
|
||||
|
||||
def test_vwb_params_extract_text_scroll_legacy_output_var():
|
||||
p = _vwb_params_to_core("extract_text_scroll", {"output_var": "legacy"})
|
||||
assert p["variable_name"] == "legacy"
|
||||
|
||||
Reference in New Issue
Block a user