-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_subgraph.py
More file actions
86 lines (65 loc) · 2.48 KB
/
Copy pathtest_subgraph.py
File metadata and controls
86 lines (65 loc) · 2.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
"""SubgraphNode coverage.
The conformance adapter routes around `SubgraphNode` via a function-node
wrapper (so it can record outer-graph execution order in the parent trace).
These tests exercise `SubgraphNode.run` directly, plus the
`builder.add_subgraph_node` path that constructs one internally.
"""
from typing import Annotated, Any
from pydantic import Field
from openarmature.graph import (
END,
FieldNameMatching,
GraphBuilder,
State,
SubgraphNode,
append,
)
class Inner(State):
msg: str = ""
trace: Annotated[list[str], append] = Field(default_factory=list)
class Outer(State):
msg: str = ""
trace: Annotated[list[str], append] = Field(default_factory=lambda: ["outer-start"])
async def test_subgraph_node_run_projects_via_field_name_matching() -> None:
async def x(_s: Any) -> dict[str, Any]:
return {"msg": "hello", "trace": ["x"]}
inner = GraphBuilder(Inner).add_node("x", x).add_edge("x", END).set_entry("x").compile()
sub = SubgraphNode[Outer, Inner](name="sub", compiled=inner, projection=FieldNameMatching[Outer, Inner]())
result = await sub.run(Outer())
# Field-name matching projects shared fields back; subgraph runs from its own defaults.
assert dict(result) == {"msg": "hello", "trace": ["x"]}
async def test_outer_graph_composes_subgraph_via_add_subgraph_node() -> None:
async def x(_s: Any) -> dict[str, Any]:
return {"msg": "from-x", "trace": ["x"]}
async def y(_s: Any) -> dict[str, Any]:
return {"msg": "from-y", "trace": ["y"]}
inner = (
GraphBuilder(Inner)
.add_node("x", x)
.add_node("y", y)
.add_edge("x", "y")
.add_edge("y", END)
.set_entry("x")
.compile()
)
async def outer_a(_s: Any) -> dict[str, Any]:
return {"trace": ["outer_a"]}
async def outer_b(_s: Any) -> dict[str, Any]:
return {"trace": ["outer_b"]}
outer = (
GraphBuilder(Outer)
.add_node("outer_a", outer_a)
.add_subgraph_node("outer_sub", inner)
.add_node("outer_b", outer_b)
.add_edge("outer_a", "outer_sub")
.add_edge("outer_sub", "outer_b")
.add_edge("outer_b", END)
.set_entry("outer_a")
.compile()
)
final = await outer.invoke(Outer())
# `msg` merges via parent's last_write_wins; `trace` via parent's append reducer.
assert final.model_dump() == {
"msg": "from-y",
"trace": ["outer-start", "outer_a", "x", "y", "outer_b"],
}