feat(vwb): nom par défaut explicite pour workflows importés de Léa (B2)
Avant : tous les workflows importés s'appelaient « Unnamed Workflow »
→ la liste devenait illisible dès qu'il y en avait plusieurs.
Après : génération d'un nom explicite par _derive_default_name :
1. Premier `template.window.title_pattern` utile dans les nodes
(filtrage de "Unknown" / "unknown_window"), avec extraction de
l'app derrière le séparateur Windows « – » / « - »
(ex: « Sans titre – Bloc-notes » → « Bloc-notes »).
2. Premier `template.window.process_name` non-null
(ex: « explorer.exe »).
3. Fallback : 8 premiers caractères du workflow_id, après
nettoyage des préfixes techniques ("workflow_sess_", ...).
Le nom final inclut toujours la date de l'import :
« Léa Bloc-notes — 2026-04-16 08:41 »
« Léa explorer.exe — 2026-04-16 08:41 »
« Léa 20260404 — 2026-04-16 08:41 » (fallback)
Ne se déclenche que si le nom entrant est vide,
« Unnamed Workflow » ou « Workflow importé » (insensible à la
casse). Le paramètre `name` explicite de la requête reste
prioritaire. L'utilisateur peut renommer via le bouton éditer.
Pas de modification du schema workflow (champ `name` existant).
Tests manuels sur données réelles :
- notepad_enriched.json (tous nodes "Unknown") → fallback id OK
- Bloc-notes, Explorateur et Recherche (2) → « Léa Rechercher »
- workflow construit avec title 'Sans titre – Bloc-notes'
→ « Léa Bloc-notes » OK
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -40,6 +40,71 @@ if _ROOT not in sys.path:
|
|||||||
STREAMING_SERVER_URL = "http://localhost:5005"
|
STREAMING_SERVER_URL = "http://localhost:5005"
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Helpers — nom par défaut à l'import
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def _derive_default_name(core_dict: Dict[str, Any]) -> str:
|
||||||
|
"""
|
||||||
|
Génère un nom par défaut explicite pour un workflow appris importé,
|
||||||
|
quand son champ `name` est vide ou vaut « Unnamed Workflow ».
|
||||||
|
|
||||||
|
Stratégie, par ordre de priorité :
|
||||||
|
1. Premier `template.window.title_pattern` exploitable dans les nodes
|
||||||
|
(après filtrage de "Unknown"/"unknown_window") ; on extrait le nom
|
||||||
|
de l'app derrière un séparateur « – » / « - » typique de Windows
|
||||||
|
(« Sans titre – Bloc-notes » → « Bloc-notes »).
|
||||||
|
2. Premier `template.window.process_name` non-null.
|
||||||
|
3. Fallback : 8 premiers caractères de `workflow_id`.
|
||||||
|
|
||||||
|
La date de l'import (YYYY-MM-DD HH:MM) est toujours ajoutée en suffixe.
|
||||||
|
L'utilisateur peut renommer ensuite dans le VWB.
|
||||||
|
"""
|
||||||
|
from datetime import datetime as _dt
|
||||||
|
|
||||||
|
def _extract_app(title: str) -> Optional[str]:
|
||||||
|
if not title:
|
||||||
|
return None
|
||||||
|
t = title.strip()
|
||||||
|
if not t or t.lower() in {"unknown", "unknown_window"}:
|
||||||
|
return None
|
||||||
|
# Séparateurs Windows classiques : « – » (em dash), « — », « - »
|
||||||
|
for sep in (" – ", " — ", " - "):
|
||||||
|
if sep in t:
|
||||||
|
# Le nom de l'app est généralement la partie droite
|
||||||
|
right = t.rsplit(sep, 1)[-1].strip()
|
||||||
|
if right:
|
||||||
|
return right
|
||||||
|
# Pas de séparateur → renvoyer le titre brut (ex : "Rechercher")
|
||||||
|
return t
|
||||||
|
|
||||||
|
app_name: Optional[str] = None
|
||||||
|
for node in (core_dict.get("nodes") or []):
|
||||||
|
window = ((node.get("template") or {}).get("window") or {})
|
||||||
|
app_name = _extract_app(window.get("title_pattern") or "")
|
||||||
|
if app_name:
|
||||||
|
break
|
||||||
|
proc = window.get("process_name")
|
||||||
|
if proc:
|
||||||
|
app_name = str(proc).strip()
|
||||||
|
break
|
||||||
|
|
||||||
|
timestamp = _dt.now().strftime("%Y-%m-%d %H:%M")
|
||||||
|
|
||||||
|
if app_name:
|
||||||
|
return f"Léa {app_name} — {timestamp}"
|
||||||
|
|
||||||
|
wf_id = core_dict.get("workflow_id") or ""
|
||||||
|
# Nettoyer les préfixes techniques courants (workflow_, sess_) pour garder
|
||||||
|
# un identifiant lisible de 8 caractères.
|
||||||
|
for prefix in ("workflow_sess_", "workflow_", "sess_", "session_"):
|
||||||
|
if wf_id.startswith(prefix):
|
||||||
|
wf_id = wf_id[len(prefix):]
|
||||||
|
break
|
||||||
|
suffix = wf_id[:8] if wf_id else "?"
|
||||||
|
return f"Léa {suffix} — {timestamp}"
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# GET /api/v3/learned-workflows
|
# GET /api/v3/learned-workflows
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
@@ -209,7 +274,14 @@ def import_learned_workflow(workflow_id: str):
|
|||||||
|
|
||||||
wf_meta, steps_list, warnings = convert_learned_to_vwb_steps(core_dict)
|
wf_meta, steps_list, warnings = convert_learned_to_vwb_steps(core_dict)
|
||||||
|
|
||||||
# Surcharger le nom si fourni
|
# B2 — nom par défaut explicite pour les workflows arrivant en
|
||||||
|
# "Unnamed Workflow" depuis Léa. N'affecte pas les workflows déjà
|
||||||
|
# nommés manuellement. L'humain peut renommer ensuite dans le VWB.
|
||||||
|
current_name = (wf_meta.get("name") or "").strip()
|
||||||
|
if current_name.lower() in {"", "unnamed workflow", "workflow importé"}:
|
||||||
|
wf_meta["name"] = _derive_default_name(core_dict)
|
||||||
|
|
||||||
|
# Surcharger le nom si fourni explicitement dans la requête
|
||||||
if data.get("name"):
|
if data.get("name"):
|
||||||
wf_meta["name"] = data["name"]
|
wf_meta["name"] = data["name"]
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user