fix(workflow): conserve machine_id au round-trip to_dict/from_dict
Les workflows rechargés du disque retombaient sur machine_id='default' : to_dict ne sérialisait pas l'attribut d'instance _machine_id et from_dict ne le reposait pas (il dormait dans metadata['machine_id']). to_dict le sérialise si présent (pas de 'default' parasite) ; from_dict le restaure depuis le champ explicite ou metadata (rétrocompat des workflows déjà sur disque). Test de non-régression round-trip. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1250,12 +1250,16 @@ class Workflow:
|
||||
}
|
||||
if self.chain_config:
|
||||
result["chain_config"] = self.chain_config.to_dict() if hasattr(self.chain_config, 'to_dict') else self.chain_config
|
||||
# machine_id : attribut d'instance posé au runtime (pas un champ dataclass)
|
||||
machine_id = getattr(self, "_machine_id", None)
|
||||
if machine_id:
|
||||
result["machine_id"] = machine_id
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, data: Dict[str, Any]) -> 'Workflow':
|
||||
"""Désérialiser depuis JSON"""
|
||||
return cls(
|
||||
wf = cls(
|
||||
workflow_id=data["workflow_id"],
|
||||
name=data.get("name", data["workflow_id"]),
|
||||
description=data.get("description", ""),
|
||||
@@ -1277,7 +1281,13 @@ class Workflow:
|
||||
references=data.get("references", []),
|
||||
chain_config=data.get("chain_config")
|
||||
)
|
||||
|
||||
# Reposer machine_id (attribut d'instance) : priorité au champ explicite,
|
||||
# sinon depuis metadata['machine_id'] (rétrocompat des workflows déjà sur disque)
|
||||
machine_id = data.get("machine_id") or (wf.metadata or {}).get("machine_id")
|
||||
if machine_id:
|
||||
wf._machine_id = machine_id
|
||||
return wf
|
||||
|
||||
def to_json(self) -> str:
|
||||
"""Sérialiser en JSON string"""
|
||||
return json.dumps(self.to_dict(), indent=2)
|
||||
|
||||
44
tests/unit/test_workflow_graph_machine_id.py
Normal file
44
tests/unit/test_workflow_graph_machine_id.py
Normal file
@@ -0,0 +1,44 @@
|
||||
"""
|
||||
Test de non-régression : conservation du machine_id au round-trip to_dict/from_dict.
|
||||
|
||||
Bug : les workflows listés via /api/v1/traces/stream/workflows étaient tous
|
||||
attribués à machine_id="default" alors que les sessions portaient le bon
|
||||
machine_id (lea-*). Cause : to_dict ne sérialisait pas l'attribut d'instance
|
||||
`_machine_id` et from_dict ne le reposait pas (il dormait dans
|
||||
metadata['machine_id']). list_workflows tombait alors sur le fallback "default".
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from core.models.workflow_graph import Workflow
|
||||
|
||||
|
||||
def _make_minimal_workflow(machine_id: str) -> Workflow:
|
||||
"""Construit un workflow minimal portant un machine_id dans ses métadonnées."""
|
||||
now = datetime.now().isoformat()
|
||||
return Workflow.from_dict({
|
||||
"workflow_id": "wf-test",
|
||||
"name": "wf-test",
|
||||
"nodes": [],
|
||||
"edges": [],
|
||||
"safety_rules": {},
|
||||
"stats": {},
|
||||
"learning": {},
|
||||
"entry_nodes": [],
|
||||
"end_nodes": [],
|
||||
"created_at": now,
|
||||
"updated_at": now,
|
||||
"metadata": {"machine_id": machine_id},
|
||||
})
|
||||
|
||||
|
||||
def test_machine_id_preserved_after_to_dict_from_dict_round_trip():
|
||||
"""Un workflow doit conserver son machine_id après un round-trip de (dé)sérialisation."""
|
||||
wf = _make_minimal_workflow("lea-poste-3")
|
||||
# Simule l'étiquetage runtime fait par le stream_processor
|
||||
wf._machine_id = "lea-poste-3"
|
||||
|
||||
restored = Workflow.from_dict(wf.to_dict())
|
||||
|
||||
# Invariant : le machine_id survit au round-trip (comme le fait list_workflows)
|
||||
assert getattr(restored, "_machine_id", "default") == "lea-poste-3"
|
||||
Reference in New Issue
Block a user