vectorvein 0.2.48__py3-none-any.whl → 0.2.50__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- vectorvein/types/defaults.py +1 -1
- vectorvein/workflow/graph/workflow.py +21 -1
- vectorvein/workflow/utils/layout.py +114 -0
- {vectorvein-0.2.48.dist-info → vectorvein-0.2.50.dist-info}/METADATA +1 -1
- {vectorvein-0.2.48.dist-info → vectorvein-0.2.50.dist-info}/RECORD +7 -6
- {vectorvein-0.2.48.dist-info → vectorvein-0.2.50.dist-info}/WHEEL +0 -0
- {vectorvein-0.2.48.dist-info → vectorvein-0.2.50.dist-info}/entry_points.txt +0 -0
vectorvein/types/defaults.py
CHANGED
@@ -698,7 +698,7 @@ GEMINI_MODELS: Final[Dict[str, Dict[str, Any]]] = {
|
|
698
698
|
"id": "gemini-2.0-flash-thinking-exp-01-21",
|
699
699
|
"context_length": 1048576,
|
700
700
|
"max_output_tokens": 8192,
|
701
|
-
"function_call_available":
|
701
|
+
"function_call_available": False,
|
702
702
|
"response_format_available": True,
|
703
703
|
"native_multimodal": False,
|
704
704
|
},
|
@@ -1,9 +1,10 @@
|
|
1
1
|
import json
|
2
|
-
from typing import List, Union, TypedDict
|
2
|
+
from typing import List, Union, TypedDict, Dict, Any, Optional
|
3
3
|
|
4
4
|
from .node import Node
|
5
5
|
from .edge import Edge
|
6
6
|
from .port import InputPort
|
7
|
+
from ..utils.layout import layout
|
7
8
|
|
8
9
|
|
9
10
|
class UIWarning(TypedDict, total=False):
|
@@ -283,3 +284,22 @@ class Workflow:
|
|
283
284
|
result["ui_warnings"] = ui_check
|
284
285
|
|
285
286
|
return result
|
287
|
+
|
288
|
+
def layout(self, options: Optional[Dict[str, Any]] = None) -> "Workflow":
|
289
|
+
"""对工作流中的节点进行自动布局,计算并更新每个节点的位置。
|
290
|
+
|
291
|
+
此方法实现了一个简单的分层布局算法,将节点按照有向图的拓扑结构进行排列。
|
292
|
+
|
293
|
+
Args:
|
294
|
+
options: 布局选项,包括:
|
295
|
+
- direction: 布局方向 ('TB', 'BT', 'LR', 'RL'),默认 'LR'
|
296
|
+
- node_spacing: 同一层级节点间的间距,默认 500
|
297
|
+
- layer_spacing: 不同层级间的间距,默认 400
|
298
|
+
- margin_x: 图形左右边距,默认 20
|
299
|
+
- margin_y: 图形上下边距,默认 20
|
300
|
+
|
301
|
+
Returns:
|
302
|
+
布局后的工作流对象
|
303
|
+
"""
|
304
|
+
layout(self.nodes, self.edges, options)
|
305
|
+
return self
|
@@ -0,0 +1,114 @@
|
|
1
|
+
from typing import Optional, Dict, Any, List, TYPE_CHECKING
|
2
|
+
|
3
|
+
|
4
|
+
if TYPE_CHECKING:
|
5
|
+
from vectorvein.workflow.graph.node import Node
|
6
|
+
from vectorvein.workflow.graph.edge import Edge
|
7
|
+
|
8
|
+
|
9
|
+
def layout(nodes: List["Node"], edges: List["Edge"], options: Optional[Dict[str, Any]] = None):
|
10
|
+
"""对工作流中的节点进行自动布局,计算并更新每个节点的位置。
|
11
|
+
|
12
|
+
此方法实现了一个简单的分层布局算法,将节点按照有向图的拓扑结构进行排列。
|
13
|
+
|
14
|
+
Args:
|
15
|
+
options: 布局选项,包括:
|
16
|
+
- direction: 布局方向 ('TB', 'BT', 'LR', 'RL'),默认 'TB'
|
17
|
+
- node_spacing: 同一层级节点间的间距,默认 150
|
18
|
+
- layer_spacing: 不同层级间的间距,默认 100
|
19
|
+
- margin_x: 图形左右边距,默认 20
|
20
|
+
- margin_y: 图形上下边距,默认 20
|
21
|
+
|
22
|
+
Returns:
|
23
|
+
布局后的工作流对象
|
24
|
+
"""
|
25
|
+
# 设置默认选项
|
26
|
+
default_options = {
|
27
|
+
"direction": "LR", # 从上到下的布局
|
28
|
+
"node_spacing": 400, # 同一层级节点间的间距
|
29
|
+
"layer_spacing": 500, # 不同层级间的间距
|
30
|
+
"margin_x": 20, # 图形左右边距
|
31
|
+
"margin_y": 20, # 图形上下边距
|
32
|
+
}
|
33
|
+
|
34
|
+
# 合并用户提供的选项
|
35
|
+
if options:
|
36
|
+
default_options.update(options)
|
37
|
+
|
38
|
+
# 构建邻接表
|
39
|
+
adjacency = {node.id: [] for node in nodes}
|
40
|
+
in_degree = {node.id: 0 for node in nodes}
|
41
|
+
|
42
|
+
for edge in edges:
|
43
|
+
if edge.source in adjacency:
|
44
|
+
adjacency[edge.source].append(edge.target)
|
45
|
+
in_degree[edge.target] = in_degree.get(edge.target, 0) + 1
|
46
|
+
|
47
|
+
# 找出所有入度为0的节点(根节点)
|
48
|
+
roots = [node_id for node_id, degree in in_degree.items() if degree == 0]
|
49
|
+
|
50
|
+
# 如果没有根节点,选择第一个节点作为起点
|
51
|
+
if not roots and nodes:
|
52
|
+
roots = [nodes[0].id]
|
53
|
+
|
54
|
+
# 按层级排列节点
|
55
|
+
layers = []
|
56
|
+
visited = set()
|
57
|
+
|
58
|
+
current_layer = roots
|
59
|
+
while current_layer:
|
60
|
+
layers.append(current_layer)
|
61
|
+
next_layer = []
|
62
|
+
for node_id in current_layer:
|
63
|
+
visited.add(node_id)
|
64
|
+
for neighbor in adjacency.get(node_id, []):
|
65
|
+
if neighbor not in visited and all(
|
66
|
+
parent in visited for parent in [e.source for e in edges if e.target == neighbor]
|
67
|
+
):
|
68
|
+
next_layer.append(neighbor)
|
69
|
+
current_layer = next_layer
|
70
|
+
|
71
|
+
# 还有未访问的节点(可能是孤立节点或环的一部分)
|
72
|
+
remaining = [node.id for node in nodes if node.id not in visited]
|
73
|
+
if remaining:
|
74
|
+
layers.append(remaining)
|
75
|
+
|
76
|
+
# 根据层级信息设置节点位置
|
77
|
+
layer_spacing = default_options["layer_spacing"]
|
78
|
+
node_spacing = default_options["node_spacing"]
|
79
|
+
margin_x = default_options["margin_x"]
|
80
|
+
margin_y = default_options["margin_y"]
|
81
|
+
|
82
|
+
# 布局方向
|
83
|
+
is_vertical = default_options["direction"] in ["TB", "BT"]
|
84
|
+
is_reversed = default_options["direction"] in ["BT", "RL"]
|
85
|
+
|
86
|
+
for layer_idx, layer in enumerate(layers):
|
87
|
+
for node_idx, node_id in enumerate(layer):
|
88
|
+
# 根据布局方向计算位置
|
89
|
+
if is_vertical:
|
90
|
+
# 垂直布局 (TB 或 BT)
|
91
|
+
x = node_idx * node_spacing + margin_x
|
92
|
+
y = layer_idx * layer_spacing + margin_y
|
93
|
+
if is_reversed: # BT 布局需要反转 y 坐标
|
94
|
+
y = (len(layers) - 1 - layer_idx) * layer_spacing + margin_y
|
95
|
+
else:
|
96
|
+
# 水平布局 (LR 或 RL)
|
97
|
+
x = layer_idx * layer_spacing + margin_x
|
98
|
+
y = node_idx * node_spacing + margin_y
|
99
|
+
if is_reversed: # RL 布局需要反转 x 坐标
|
100
|
+
x = (len(layers) - 1 - layer_idx) * layer_spacing + margin_x
|
101
|
+
|
102
|
+
# 找到节点对象并设置位置
|
103
|
+
for node in nodes:
|
104
|
+
if node.id == node_id:
|
105
|
+
# 确保节点有 position 属性
|
106
|
+
if not hasattr(node, "position"):
|
107
|
+
node.position = {"x": x, "y": y}
|
108
|
+
else:
|
109
|
+
# 如果已经有 position 属性,更新它
|
110
|
+
if isinstance(node.position, dict):
|
111
|
+
node.position.update({"x": x, "y": y})
|
112
|
+
else:
|
113
|
+
node.position = {"x": x, "y": y}
|
114
|
+
break
|
@@ -1,6 +1,6 @@
|
|
1
|
-
vectorvein-0.2.
|
2
|
-
vectorvein-0.2.
|
3
|
-
vectorvein-0.2.
|
1
|
+
vectorvein-0.2.50.dist-info/METADATA,sha256=ltuWwgnho44gDn6eGM8ED7_dtnefHd1KZ89yMM4bqrY,4570
|
2
|
+
vectorvein-0.2.50.dist-info/WHEEL,sha256=thaaA2w1JzcGC48WYufAs8nrYZjJm8LqNfnXFOFyCC4,90
|
3
|
+
vectorvein-0.2.50.dist-info/entry_points.txt,sha256=6OYgBcLyFCUgeqLgnvMyOJxPCWzgy7se4rLPKtNonMs,34
|
4
4
|
vectorvein/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
5
|
vectorvein/api/__init__.py,sha256=lfY-XA46fgD2iIZTU0VYP8i07AwA03Egj4Qua0vUKrQ,738
|
6
6
|
vectorvein/api/client.py,sha256=xF-leKDQzVyyy9FnIRaz0k4eElYW1XbbzeRLcpnyk90,33047
|
@@ -32,7 +32,7 @@ vectorvein/server/token_server.py,sha256=36F9PKSNOX8ZtYBXY_l-76GQTpUSmQ2Y8EMy1H7
|
|
32
32
|
vectorvein/settings/__init__.py,sha256=3Kw3hbvqcIQepAR6Q2m2UXbBnwyJTUm8yAz-aHmbUTg,11163
|
33
33
|
vectorvein/settings/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
34
34
|
vectorvein/types/__init__.py,sha256=ypg8c8AwF49FrFBMqmgH_eIBH4LFf0KN4kjqQa7zrvM,3376
|
35
|
-
vectorvein/types/defaults.py,sha256=
|
35
|
+
vectorvein/types/defaults.py,sha256=MpDvT9vNwIqwHFKXTlBq3FY02PLr4BncCdvNhfbxM60,27563
|
36
36
|
vectorvein/types/enums.py,sha256=LplSVkXLBK-t8TWtJKj_f7ktWTd6CSHWRLb67XKMm54,1716
|
37
37
|
vectorvein/types/exception.py,sha256=KtnqZ-1DstHm95SZAyZdHhkGq1bJ4A9Aw3Zfdu-VIFo,130
|
38
38
|
vectorvein/types/llm_parameters.py,sha256=2rF-CQsWcHqTzI2r5x55gSsFm7LW_iCxQxZTEnk0yF8,7843
|
@@ -44,7 +44,7 @@ vectorvein/utilities/retry.py,sha256=6KFS9R2HdhqM3_9jkjD4F36ZSpEx2YNFGOVlpOsUetM
|
|
44
44
|
vectorvein/workflow/graph/edge.py,sha256=1ckyyjCue_PLm7P1ItUfKOy6AKkemOpZ9m1WJ8UXIHQ,1072
|
45
45
|
vectorvein/workflow/graph/node.py,sha256=U3LAq01wOaDANVUuYWc2BhIq-Pl2-33FirJZkkNL_wA,5329
|
46
46
|
vectorvein/workflow/graph/port.py,sha256=_QpHCBGAu657VhYAh0Wzjri3ZZ8-WYJp99J465mqmwo,6492
|
47
|
-
vectorvein/workflow/graph/workflow.py,sha256=
|
47
|
+
vectorvein/workflow/graph/workflow.py,sha256=rXLIKQiOFjyP3YwYf0deHUcRNBCHJcTGj2omM4a9SpE,12070
|
48
48
|
vectorvein/workflow/nodes/__init__.py,sha256=dWrWtL3q0Vsn-MLgJ7gNgLCrwZ5BrqjrN2QFPNeBMuc,3240
|
49
49
|
vectorvein/workflow/nodes/audio_generation.py,sha256=ZRFZ_ycMTSJ2LKmekctagQdJYTl-3q4TNOIKETpS9AM,5870
|
50
50
|
vectorvein/workflow/nodes/control_flows.py,sha256=l8CjFQlsGV3fNGM6SVzS1Kz361K1xDv1fGT7acuDXuU,6613
|
@@ -62,4 +62,5 @@ vectorvein/workflow/nodes/vector_db.py,sha256=t6I17q6iR3yQreiDHpRrksMdWDPIvgqJs0
|
|
62
62
|
vectorvein/workflow/nodes/video_generation.py,sha256=qmdg-t_idpxq1veukd-jv_ChICMOoInKxprV9Z4Vi2w,4118
|
63
63
|
vectorvein/workflow/nodes/web_crawlers.py,sha256=BhJBX1AZH7-22Gu95Ox4qJqmH5DU-m4dbUb5N5DTA-M,5559
|
64
64
|
vectorvein/workflow/utils/json_to_code.py,sha256=F7dhDy8kGc8ndOeihGLRLGFGlquoxVlb02ENtxnQ0C8,5914
|
65
|
-
vectorvein
|
65
|
+
vectorvein/workflow/utils/layout.py,sha256=j0bRD3uaXu40xCS6U6BGahBI8FrHa5MiF55GbTrZ1LM,4565
|
66
|
+
vectorvein-0.2.50.dist-info/RECORD,,
|
File without changes
|
File without changes
|