Skip to content

Commit fd902ce

Browse files
authored
Vertex group rag variables expose (#32)
* fix vertex group variables expose * fix ut * fix ut for 3.9/3.12 version * fix while group according comments
1 parent f1b9270 commit fd902ce

File tree

10 files changed

+416
-104
lines changed

10 files changed

+416
-104
lines changed
Lines changed: 301 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
1+
#!/usr/bin/env python3
2+
"""
3+
测试VertexGroup exposed_variables修复功能的专项测试
4+
本测试文件专门验证修复后的VertexGroup exposed_variables功能
5+
"""
6+
7+
import os
8+
import sys
9+
10+
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
11+
12+
from vertex_flow.workflow.constants import LOCAL_VAR, SOURCE_SCOPE, SOURCE_VAR, SUBGRAPH_SOURCE
13+
from vertex_flow.workflow.vertex.function_vertex import FunctionVertex
14+
from vertex_flow.workflow.vertex.vertex_group import VertexGroup
15+
16+
17+
def test_exposed_variables_flattened_output():
18+
"""测试exposed_variables配置时返回扁平化输出"""
19+
print("=== 测试exposed_variables扁平化输出 ===")
20+
21+
def task_a(inputs):
22+
value = inputs.get("input_value", 0)
23+
return {"result_a": value * 2, "internal_data": "secret"}
24+
25+
def task_b(inputs):
26+
result_a = inputs.get("result_a", 0)
27+
return {"result_b": result_a + 10, "debug_info": "processed"}
28+
29+
vertex_a = FunctionVertex(
30+
id="vertex_a",
31+
name="Vertex A",
32+
task=task_a,
33+
variables=[{SOURCE_SCOPE: SUBGRAPH_SOURCE, SOURCE_VAR: "input_value", LOCAL_VAR: "input_value"}],
34+
)
35+
36+
vertex_b = FunctionVertex(
37+
id="vertex_b",
38+
name="Vertex B",
39+
task=task_b,
40+
variables=[{SOURCE_SCOPE: "vertex_a", SOURCE_VAR: "result_a", LOCAL_VAR: "result_a"}],
41+
)
42+
43+
vertex_group = VertexGroup(
44+
id="test_group",
45+
name="Test Group",
46+
subgraph_vertices=[vertex_a, vertex_b],
47+
variables=[{SOURCE_SCOPE: SUBGRAPH_SOURCE, SOURCE_VAR: "input_value", LOCAL_VAR: "input_value"}],
48+
exposed_variables=[
49+
{SOURCE_SCOPE: "vertex_a", SOURCE_VAR: "result_a", LOCAL_VAR: "exposed_a"},
50+
{SOURCE_SCOPE: "vertex_b", SOURCE_VAR: "result_b", LOCAL_VAR: "exposed_b"},
51+
],
52+
)
53+
54+
result = vertex_group.execute(inputs={"input_value": 5})
55+
print(f"VertexGroup执行结果: {result}")
56+
57+
# 验证扁平化输出
58+
assert "exposed_a" in result, "应该包含暴露的变量exposed_a"
59+
assert "exposed_b" in result, "应该包含暴露的变量exposed_b"
60+
assert result["exposed_a"] == 10, f"期望exposed_a为10,实际为{result['exposed_a']}"
61+
assert result["exposed_b"] == 20, f"期望exposed_b为20,实际为{result['exposed_b']}"
62+
63+
# 验证不包含未暴露的变量
64+
assert "vertex_a" not in result, "不应该包含子图顶点的嵌套输出"
65+
assert "vertex_b" not in result, "不应该包含子图顶点的嵌套输出"
66+
assert "internal_data" not in result, "不应该包含未暴露的内部变量"
67+
assert "debug_info" not in result, "不应该包含未暴露的调试信息"
68+
69+
print("✓ exposed_variables扁平化输出测试通过")
70+
71+
72+
def test_no_exposed_variables_backward_compatibility():
73+
"""测试没有exposed_variables时的向后兼容性"""
74+
print("\n=== 测试向后兼容性(无exposed_variables) ===")
75+
76+
def task_simple(inputs):
77+
value = inputs.get("input_value", 0)
78+
return {"result": value * 3, "status": "completed"}
79+
80+
vertex_simple = FunctionVertex(
81+
id="vertex_simple",
82+
name="Simple Vertex",
83+
task=task_simple,
84+
variables=[{SOURCE_SCOPE: SUBGRAPH_SOURCE, SOURCE_VAR: "input_value", LOCAL_VAR: "input_value"}],
85+
)
86+
87+
# 不配置exposed_variables
88+
vertex_group = VertexGroup(
89+
id="backward_group",
90+
name="Backward Compatibility Group",
91+
subgraph_vertices=[vertex_simple],
92+
variables=[{SOURCE_SCOPE: SUBGRAPH_SOURCE, SOURCE_VAR: "input_value", LOCAL_VAR: "input_value"}],
93+
# 故意不设置exposed_variables
94+
)
95+
96+
result = vertex_group.execute(inputs={"input_value": 4})
97+
print(f"VertexGroup执行结果: {result}")
98+
99+
# 验证向后兼容性:应该返回子图顶点的输出
100+
assert "vertex_simple" in result, "向后兼容:应该包含子图顶点的输出"
101+
assert result["vertex_simple"]["result"] == 12, f"期望结果为12,实际为{result['vertex_simple']['result']}"
102+
assert result["vertex_simple"]["status"] == "completed", "应该包含所有子图顶点的输出"
103+
104+
print("✓ 向后兼容性测试通过")
105+
106+
107+
def test_exposed_variables_edge_cases():
108+
"""测试exposed_variables的边界情况"""
109+
print("\n=== 测试exposed_variables边界情况 ===")
110+
111+
def task_with_none(inputs):
112+
value = inputs.get("input_value", 0)
113+
return {"result": value if value > 0 else None, "always_present": "exists"}
114+
115+
def task_with_empty(inputs):
116+
return {"empty_dict": {}, "empty_list": [], "zero_value": 0}
117+
118+
vertex_none = FunctionVertex(
119+
id="vertex_none",
120+
name="Vertex with None",
121+
task=task_with_none,
122+
variables=[{SOURCE_SCOPE: SUBGRAPH_SOURCE, SOURCE_VAR: "input_value", LOCAL_VAR: "input_value"}],
123+
)
124+
125+
vertex_empty = FunctionVertex(
126+
id="vertex_empty",
127+
name="Vertex with Empty Values",
128+
task=task_with_empty,
129+
)
130+
131+
vertex_group = VertexGroup(
132+
id="edge_case_group",
133+
name="Edge Case Group",
134+
subgraph_vertices=[vertex_none, vertex_empty],
135+
variables=[{SOURCE_SCOPE: SUBGRAPH_SOURCE, SOURCE_VAR: "input_value", LOCAL_VAR: "input_value"}],
136+
exposed_variables=[
137+
{SOURCE_SCOPE: "vertex_none", SOURCE_VAR: "result", LOCAL_VAR: "nullable_result"},
138+
{SOURCE_SCOPE: "vertex_none", SOURCE_VAR: "always_present", LOCAL_VAR: "always_there"},
139+
{SOURCE_SCOPE: "vertex_empty", SOURCE_VAR: "empty_dict", LOCAL_VAR: "empty_dict_exposed"},
140+
{SOURCE_SCOPE: "vertex_empty", SOURCE_VAR: "zero_value", LOCAL_VAR: "zero_exposed"},
141+
],
142+
)
143+
144+
# 测试正值情况
145+
result_positive = vertex_group.execute(inputs={"input_value": 5})
146+
print(f"正值输入结果: {result_positive}")
147+
148+
# 验证存在的变量
149+
if "nullable_result" in result_positive:
150+
assert result_positive["nullable_result"] == 5, "正值应该被正确暴露"
151+
if "always_there" in result_positive:
152+
assert result_positive["always_there"] == "exists", "非空值应该被正确暴露"
153+
if "empty_dict_exposed" in result_positive:
154+
assert result_positive["empty_dict_exposed"] == {}, "空字典应该被正确暴露"
155+
if "zero_exposed" in result_positive:
156+
assert result_positive["zero_exposed"] == 0, "零值应该被正确暴露"
157+
158+
# 测试零值情况
159+
result_zero = vertex_group.execute(inputs={"input_value": 0})
160+
print(f"零值输入结果: {result_zero}")
161+
162+
# 验证存在的变量
163+
if "nullable_result" in result_zero:
164+
assert result_zero["nullable_result"] is None, "None值应该被正确暴露"
165+
if "always_there" in result_zero:
166+
assert result_zero["always_there"] == "exists", "非空值应该被正确暴露"
167+
168+
print("✓ 边界情况测试通过")
169+
170+
171+
def test_exposed_variables_name_conflicts():
172+
"""测试exposed_variables名称冲突处理"""
173+
print("\n=== 测试exposed_variables名称冲突 ===")
174+
175+
def task_conflict_a(inputs):
176+
return {"same_name": "from_a", "unique_a": "value_a"}
177+
178+
def task_conflict_b(inputs):
179+
return {"same_name": "from_b", "unique_b": "value_b"}
180+
181+
vertex_a = FunctionVertex(id="conflict_a", name="Conflict A", task=task_conflict_a)
182+
vertex_b = FunctionVertex(id="conflict_b", name="Conflict B", task=task_conflict_b)
183+
184+
vertex_group = VertexGroup(
185+
id="conflict_group",
186+
name="Conflict Group",
187+
subgraph_vertices=[vertex_a, vertex_b],
188+
exposed_variables=[
189+
{SOURCE_SCOPE: "conflict_a", SOURCE_VAR: "same_name", LOCAL_VAR: "exposed_a"},
190+
{SOURCE_SCOPE: "conflict_b", SOURCE_VAR: "same_name", LOCAL_VAR: "exposed_b"},
191+
{SOURCE_SCOPE: "conflict_a", SOURCE_VAR: "unique_a", LOCAL_VAR: "unique_from_a"},
192+
{SOURCE_SCOPE: "conflict_b", SOURCE_VAR: "unique_b", LOCAL_VAR: "unique_from_b"},
193+
],
194+
)
195+
196+
result = vertex_group.execute(inputs={})
197+
print(f"名称冲突处理结果: {result}")
198+
199+
# 验证不同的暴露名称能正确区分来源
200+
assert result["exposed_a"] == "from_a", "来自vertex_a的值应该正确暴露"
201+
assert result["exposed_b"] == "from_b", "来自vertex_b的值应该正确暴露"
202+
assert result["unique_from_a"] == "value_a", "unique_a应该正确暴露"
203+
assert result["unique_from_b"] == "value_b", "unique_b应该正确暴露"
204+
205+
print("✓ 名称冲突处理测试通过")
206+
207+
208+
def test_exposed_variables_complex_data_types():
209+
"""测试exposed_variables处理复杂数据类型"""
210+
print("\n=== 测试复杂数据类型暴露 ===")
211+
212+
def task_complex(inputs):
213+
return {
214+
"nested_dict": {"level1": {"level2": "deep_value"}},
215+
"list_data": [1, 2, {"nested": "in_list"}],
216+
"mixed_types": {"string": "text", "number": 42, "boolean": True, "null": None, "list": ["a", "b", "c"]},
217+
}
218+
219+
vertex_complex = FunctionVertex(id="complex_vertex", name="Complex Data Vertex", task=task_complex)
220+
221+
vertex_group = VertexGroup(
222+
id="complex_group",
223+
name="Complex Data Group",
224+
subgraph_vertices=[vertex_complex],
225+
exposed_variables=[
226+
{SOURCE_SCOPE: "complex_vertex", SOURCE_VAR: "nested_dict", LOCAL_VAR: "exposed_nested"},
227+
{SOURCE_SCOPE: "complex_vertex", SOURCE_VAR: "list_data", LOCAL_VAR: "exposed_list"},
228+
{SOURCE_SCOPE: "complex_vertex", SOURCE_VAR: "mixed_types", LOCAL_VAR: "exposed_mixed"},
229+
],
230+
)
231+
232+
result = vertex_group.execute(inputs={})
233+
print(f"复杂数据类型结果: {result}")
234+
235+
# 验证复杂数据类型的正确暴露
236+
assert result["exposed_nested"]["level1"]["level2"] == "deep_value", "嵌套字典应该正确暴露"
237+
assert result["exposed_list"][2]["nested"] == "in_list", "列表中的嵌套数据应该正确暴露"
238+
assert result["exposed_mixed"]["string"] == "text", "混合类型中的字符串应该正确暴露"
239+
assert result["exposed_mixed"]["number"] == 42, "混合类型中的数字应该正确暴露"
240+
assert result["exposed_mixed"]["boolean"] is True, "混合类型中的布尔值应该正确暴露"
241+
assert result["exposed_mixed"]["null"] is None, "混合类型中的None值应该正确暴露"
242+
assert result["exposed_mixed"]["list"] == ["a", "b", "c"], "混合类型中的列表应该正确暴露"
243+
244+
print("✓ 复杂数据类型测试通过")
245+
246+
247+
def test_exposed_variables_missing_source():
248+
"""测试exposed_variables引用不存在的源变量"""
249+
print("\n=== 测试引用不存在的源变量 ===")
250+
251+
def task_normal(inputs):
252+
return {"existing_var": "exists"}
253+
254+
vertex_normal = FunctionVertex(id="normal_vertex", name="Normal Vertex", task=task_normal)
255+
256+
vertex_group = VertexGroup(
257+
id="missing_source_group",
258+
name="Missing Source Group",
259+
subgraph_vertices=[vertex_normal],
260+
exposed_variables=[
261+
{SOURCE_SCOPE: "normal_vertex", SOURCE_VAR: "existing_var", LOCAL_VAR: "exposed_existing"},
262+
{SOURCE_SCOPE: "normal_vertex", SOURCE_VAR: "non_existing_var", LOCAL_VAR: "exposed_missing"},
263+
{SOURCE_SCOPE: "non_existing_vertex", SOURCE_VAR: "any_var", LOCAL_VAR: "exposed_from_missing_vertex"},
264+
],
265+
)
266+
267+
result = vertex_group.execute(inputs={})
268+
print(f"缺失源变量处理结果: {result}")
269+
270+
# 验证存在的变量正常暴露
271+
assert "exposed_existing" in result, "存在的变量应该被正确暴露"
272+
assert result["exposed_existing"] == "exists", "存在的变量值应该正确"
273+
274+
# 验证不存在的变量不会导致错误,但也不会出现在结果中
275+
# 根据实际实现,不存在的变量会被忽略
276+
assert "exposed_missing" not in result, "不存在的变量不应该出现在结果中"
277+
assert "exposed_from_missing_vertex" not in result, "来自不存在顶点的变量不应该出现在结果中"
278+
print(f"结果中的键: {list(result.keys())}")
279+
280+
print("✓ 缺失源变量处理测试通过")
281+
282+
283+
def main():
284+
"""主测试函数"""
285+
try:
286+
test_exposed_variables_flattened_output()
287+
test_no_exposed_variables_backward_compatibility()
288+
test_exposed_variables_edge_cases()
289+
test_exposed_variables_name_conflicts()
290+
test_exposed_variables_complex_data_types()
291+
test_exposed_variables_missing_source()
292+
print("\n🎉 所有VertexGroup exposed_variables修复测试通过!")
293+
except Exception as e:
294+
print(f"\n❌ 测试失败: {e}")
295+
import traceback
296+
297+
traceback.print_exc()
298+
299+
300+
if __name__ == "__main__":
301+
main()

vertex_flow/tests/test_vertex_group_variables.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,10 @@ def test_function(inputs):
4848
result = vertex_group.execute(inputs={"input_value": 5})
4949
print(f"VertexGroup执行结果: {result}")
5050

51-
# 验证结果 - 根据当前实现,VertexGroup返回子图顶点的输出
52-
assert "test_vertex" in result, "应该包含子图顶点的输出"
53-
assert result["test_vertex"]["result"] == 10, f"期望结果为10,实际为{result['test_vertex']['result']}"
51+
# 验证结果 - 修复后VertexGroup在配置exposed_variables时返回扁平化结构
52+
assert "exposed_result" in result, "应该包含暴露的变量exposed_result"
53+
assert result["exposed_result"] == 10, f"期望结果为10,实际为{result['exposed_result']}"
54+
assert "test_vertex" not in result, "配置exposed_variables时不应该返回子图顶点的嵌套输出"
5455
assert "status" not in result, "未暴露的变量status不应该出现在结果中"
5556

5657
print("✓ VertexGroup基本功能测试通过")

0 commit comments

Comments
 (0)