vectorvein 0.3.1__py3-none-any.whl → 0.3.3__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.
Files changed (43) hide show
  1. vectorvein/api/client.py +81 -103
  2. vectorvein/api/exceptions.py +1 -3
  3. vectorvein/api/models.py +11 -11
  4. vectorvein/chat_clients/anthropic_client.py +157 -169
  5. vectorvein/chat_clients/base_client.py +257 -198
  6. vectorvein/chat_clients/openai_compatible_client.py +150 -161
  7. vectorvein/chat_clients/utils.py +44 -24
  8. vectorvein/server/token_server.py +1 -1
  9. vectorvein/settings/__init__.py +27 -27
  10. vectorvein/types/defaults.py +32 -16
  11. vectorvein/types/llm_parameters.py +40 -34
  12. vectorvein/types/settings.py +10 -10
  13. vectorvein/utilities/media_processing.py +1 -1
  14. vectorvein/utilities/rate_limiter.py +5 -6
  15. vectorvein/utilities/retry.py +6 -5
  16. vectorvein/workflow/graph/edge.py +3 -3
  17. vectorvein/workflow/graph/node.py +14 -26
  18. vectorvein/workflow/graph/port.py +40 -39
  19. vectorvein/workflow/graph/workflow.py +13 -25
  20. vectorvein/workflow/nodes/audio_generation.py +5 -7
  21. vectorvein/workflow/nodes/control_flows.py +7 -9
  22. vectorvein/workflow/nodes/file_processing.py +4 -6
  23. vectorvein/workflow/nodes/image_generation.py +20 -22
  24. vectorvein/workflow/nodes/llms.py +13 -15
  25. vectorvein/workflow/nodes/media_editing.py +26 -40
  26. vectorvein/workflow/nodes/media_processing.py +19 -21
  27. vectorvein/workflow/nodes/output.py +10 -12
  28. vectorvein/workflow/nodes/relational_db.py +3 -5
  29. vectorvein/workflow/nodes/text_processing.py +8 -10
  30. vectorvein/workflow/nodes/tools.py +8 -10
  31. vectorvein/workflow/nodes/triggers.py +1 -3
  32. vectorvein/workflow/nodes/vector_db.py +3 -5
  33. vectorvein/workflow/nodes/video_generation.py +4 -6
  34. vectorvein/workflow/nodes/web_crawlers.py +4 -6
  35. vectorvein/workflow/utils/analyse.py +5 -13
  36. vectorvein/workflow/utils/check.py +6 -16
  37. vectorvein/workflow/utils/json_to_code.py +6 -14
  38. vectorvein/workflow/utils/layout.py +3 -5
  39. {vectorvein-0.3.1.dist-info → vectorvein-0.3.3.dist-info}/METADATA +1 -1
  40. vectorvein-0.3.3.dist-info/RECORD +68 -0
  41. {vectorvein-0.3.1.dist-info → vectorvein-0.3.3.dist-info}/WHEEL +1 -1
  42. vectorvein-0.3.1.dist-info/RECORD +0 -68
  43. {vectorvein-0.3.1.dist-info → vectorvein-0.3.3.dist-info}/entry_points.txt +0 -0
@@ -1,5 +1,5 @@
1
1
  import uuid
2
- from typing import Dict, Any, Optional, List, Union, TYPE_CHECKING
2
+ from typing import Any, TYPE_CHECKING
3
3
 
4
4
  from .port import InputPort, OutputPort
5
5
 
@@ -8,7 +8,7 @@ if TYPE_CHECKING:
8
8
  from .port import PortType, Port
9
9
 
10
10
 
11
- class PortsDict(Dict[str, "Port"]):
11
+ class PortsDict(dict[str, "Port"]):
12
12
  """自定义字典类,用于管理节点的端口,并强制执行端口添加权限。"""
13
13
 
14
14
  def __init__(self, owner_node: "Node", *args, **kwargs):
@@ -22,13 +22,9 @@ class PortsDict(Dict[str, "Port"]):
22
22
  return
23
23
 
24
24
  if isinstance(value, OutputPort) and not self._owner_node.can_add_output_ports:
25
- raise ValueError(
26
- f"Node<{self._owner_node.id}> '{self._owner_node.type}' does not allow adding output ports"
27
- )
25
+ raise ValueError(f"Node<{self._owner_node.id}> '{self._owner_node.type}' does not allow adding output ports")
28
26
  elif isinstance(value, InputPort) and not self._owner_node.can_add_input_ports:
29
- raise ValueError(
30
- f"Node<{self._owner_node.id}> '{self._owner_node.type}' does not allow adding input ports"
31
- )
27
+ raise ValueError(f"Node<{self._owner_node.id}> '{self._owner_node.type}' does not allow adding input ports")
32
28
 
33
29
  super().__setitem__(key, value)
34
30
 
@@ -44,9 +40,9 @@ class Node:
44
40
  category: str,
45
41
  task_name: str,
46
42
  description: str = "",
47
- ports: Optional[Dict[str, "Port"]] = None,
48
- node_id: Optional[str] = None,
49
- position: Optional[Dict[str, float]] = None,
43
+ ports: dict[str, "Port"] | None = None,
44
+ node_id: str | None = None,
45
+ position: dict[str, float] | None = None,
50
46
  seleted_workflow_title: str = "",
51
47
  is_template: bool = False,
52
48
  initialized: bool = False,
@@ -67,7 +63,7 @@ class Node:
67
63
  self.ports[name] = port
68
64
  self.ports.finish_initialization()
69
65
 
70
- self.position: Dict[str, float] = position or {"x": 0, "y": 0}
66
+ self.position: dict[str, float] = position or {"x": 0, "y": 0}
71
67
  self.seleted_workflow_title: str = seleted_workflow_title
72
68
  self.is_template: bool = is_template
73
69
  self.initialized: bool = initialized
@@ -78,10 +74,10 @@ class Node:
78
74
  def add_port(
79
75
  self,
80
76
  name: str,
81
- port_type: Union["PortType", str],
77
+ port_type: "PortType | str",
82
78
  show: bool = False,
83
79
  value: Any = None,
84
- options: Optional[List[Any]] = None,
80
+ options: list[Any] | None = None,
85
81
  is_output: bool = False,
86
82
  **kwargs,
87
83
  ):
@@ -91,20 +87,16 @@ class Node:
91
87
 
92
88
  if self.has_port(name):
93
89
  raise ValueError(f"Node<{self.id}> '{self.type}' already has a port named '{name}'")
94
- self.ports[name] = OutputPort(
95
- name=name, port_type=port_type, show=show, value=value, options=options, **kwargs
96
- )
90
+ self.ports[name] = OutputPort(name=name, port_type=port_type, show=show, value=value, options=options, **kwargs)
97
91
  else:
98
92
  if not self.can_add_input_ports:
99
93
  raise ValueError(f"Node<{self.id}> '{self.type}' does not allow adding input ports")
100
94
 
101
95
  if self.has_port(name):
102
96
  raise ValueError(f"Node<{self.id}> '{self.type}' already has a port named '{name}'")
103
- self.ports[name] = InputPort(
104
- name=name, port_type=port_type, show=show, value=value, options=options, **kwargs
105
- )
97
+ self.ports[name] = InputPort(name=name, port_type=port_type, show=show, value=value, options=options, **kwargs)
106
98
 
107
- def to_dict(self) -> Dict[str, Any]:
99
+ def to_dict(self) -> dict[str, Any]:
108
100
  # 如果端口有条件表达式,且根据条件不能显示,则端口 show 要强制设置为 False
109
101
  for port in self.ports.values():
110
102
  port_show = port.show
@@ -121,11 +113,7 @@ class Node:
121
113
  "description": self.description,
122
114
  "seleted_workflow_title": self.seleted_workflow_title,
123
115
  "is_template": self.is_template,
124
- "template": {
125
- port_name: port.to_dict()
126
- for port_name, port in self.ports.items()
127
- if port_name not in ["debug", "seleted_workflow_title", "is_template"]
128
- },
116
+ "template": {port_name: port.to_dict() for port_name, port in self.ports.items() if port_name not in ["debug", "seleted_workflow_title", "is_template"]},
129
117
  },
130
118
  "category": self.category,
131
119
  "position": self.position,
@@ -1,5 +1,6 @@
1
1
  from enum import Enum
2
- from typing import Optional, Any, Dict, List, Union, Callable
2
+ from typing import Any
3
+ from collections.abc import Callable
3
4
 
4
5
 
5
6
  class PortType(Enum):
@@ -20,24 +21,24 @@ class Port:
20
21
  def __init__(
21
22
  self,
22
23
  name: str,
23
- port_type: Union[PortType, str],
24
+ port_type: "PortType | str",
24
25
  required: bool = True,
25
26
  show: bool = False,
26
27
  value: Any = None,
27
- options: Optional[List[Any]] = None,
28
- field_type: Optional[str] = None,
28
+ options: list[Any] | None = None,
29
+ field_type: str | None = None,
29
30
  is_output: bool = False,
30
- condition: Optional[str] = None,
31
- condition_python: Optional[Callable[[Dict[str, "Port"]], bool]] = None,
32
- max_length: Optional[int] = None,
33
- support_file_types: Optional[List[str]] = None,
34
- multiple: Optional[bool] = None,
35
- group: Optional[str] = None,
31
+ condition: str | None = None,
32
+ condition_python: Callable[[dict[str, "Port"]], bool] | None = None,
33
+ max_length: int | None = None,
34
+ support_file_types: list[str] | None = None,
35
+ multiple: bool | None = None,
36
+ group: str | None = None,
36
37
  group_collpased: bool = False,
37
38
  has_tooltip: bool = False,
38
- max: Optional[Union[int, float]] = None,
39
- min: Optional[Union[int, float]] = None,
40
- max_count: Optional[int] = None,
39
+ max: int | float | None = None,
40
+ min: int | float | None = None,
41
+ max_count: int | None = None,
41
42
  list: bool = False,
42
43
  ) -> None:
43
44
  self.name = name
@@ -72,7 +73,7 @@ class Port:
72
73
  else:
73
74
  return "str"
74
75
 
75
- def to_dict(self) -> Dict[str, Any]:
76
+ def to_dict(self) -> dict[str, Any]:
76
77
  return {
77
78
  "name": self.name,
78
79
  "display_name": self.name,
@@ -102,7 +103,7 @@ class Port:
102
103
  @value.setter
103
104
  def value(self, value: Any) -> None:
104
105
  if self.options:
105
- if value not in map(lambda x: x["value"], self.options):
106
+ if value not in (x["value"] for x in self.options):
106
107
  raise ValueError(f"Value `{value}` is not in Port `{self.name}` options {self.options}")
107
108
  self._value = value
108
109
 
@@ -117,23 +118,23 @@ class InputPort(Port):
117
118
  def __init__(
118
119
  self,
119
120
  name: str,
120
- port_type: Union[PortType, str],
121
+ port_type: PortType | str,
121
122
  required: bool = True,
122
123
  show: bool = False,
123
124
  value: Any = None,
124
- options: Optional[List[Any]] = None,
125
- field_type: Optional[str] = None,
126
- condition: Optional[str] = None,
127
- condition_python: Optional[Callable[[Dict[str, "Port"]], bool]] = None,
128
- max_length: Optional[int] = None,
129
- support_file_types: Optional[List[str]] = None,
130
- multiple: Optional[bool] = None,
131
- group: Optional[str] = None,
125
+ options: list[Any] | None = None,
126
+ field_type: str | None = None,
127
+ condition: str | None = None,
128
+ condition_python: Callable[[dict[str, "Port"]], bool] | None = None,
129
+ max_length: int | None = None,
130
+ support_file_types: list[str] | None = None,
131
+ multiple: bool | None = None,
132
+ group: str | None = None,
132
133
  group_collpased: bool = False,
133
134
  has_tooltip: bool = False,
134
- max: Optional[Union[int, float]] = None,
135
- min: Optional[Union[int, float]] = None,
136
- max_count: Optional[int] = None,
135
+ max: int | float | None = None,
136
+ min: int | float | None = None,
137
+ max_count: int | None = None,
137
138
  list: bool = False,
138
139
  ) -> None:
139
140
  super().__init__(
@@ -164,23 +165,23 @@ class OutputPort(Port):
164
165
  def __init__(
165
166
  self,
166
167
  name: str = "output",
167
- port_type: Union[PortType, str] = PortType.TEXT,
168
+ port_type: PortType | str = PortType.TEXT,
168
169
  required: bool = False,
169
170
  show: bool = False,
170
171
  value: Any = None,
171
- options: Optional[List[Any]] = None,
172
- field_type: Optional[str] = None,
173
- condition: Optional[str] = None,
174
- condition_python: Optional[Callable[[Dict[str, "Port"]], bool]] = None,
175
- max_length: Optional[int] = None,
176
- support_file_types: Optional[List[str]] = None,
177
- multiple: Optional[bool] = None,
178
- group: Optional[str] = None,
172
+ options: list[Any] | None = None,
173
+ field_type: str | None = None,
174
+ condition: str | None = None,
175
+ condition_python: Callable[[dict[str, "Port"]], bool] | None = None,
176
+ max_length: int | None = None,
177
+ support_file_types: list[str] | None = None,
178
+ multiple: bool | None = None,
179
+ group: str | None = None,
179
180
  group_collpased: bool = False,
180
181
  has_tooltip: bool = False,
181
- max: Optional[Union[int, float]] = None,
182
- min: Optional[Union[int, float]] = None,
183
- max_count: Optional[int] = None,
182
+ max: int | float | None = None,
183
+ min: int | float | None = None,
184
+ max_count: int | None = None,
184
185
  list: bool = False,
185
186
  ) -> None:
186
187
  super().__init__(
@@ -1,5 +1,5 @@
1
1
  import json
2
- from typing import List, Union, Dict, Any, Optional
2
+ from typing import Any
3
3
 
4
4
  from .node import Node
5
5
  from .edge import Edge
@@ -18,13 +18,13 @@ from ..utils.check import (
18
18
 
19
19
  class Workflow:
20
20
  def __init__(self) -> None:
21
- self.nodes: List[Node] = []
22
- self.edges: List[Edge] = []
21
+ self.nodes: list[Node] = []
22
+ self.edges: list[Edge] = []
23
23
 
24
24
  def add_node(self, node: Node):
25
25
  self.nodes.append(node)
26
26
 
27
- def add_nodes(self, nodes: List[Node]):
27
+ def add_nodes(self, nodes: list[Node]):
28
28
  self.nodes.extend(nodes)
29
29
 
30
30
  def add_edge(self, edge: Edge):
@@ -32,9 +32,9 @@ class Workflow:
32
32
 
33
33
  def connect(
34
34
  self,
35
- source_node: Union[str, Node],
35
+ source_node: str | Node,
36
36
  source_port: str,
37
- target_node: Union[str, Node],
37
+ target_node: str | Node,
38
38
  target_port: str,
39
39
  ):
40
40
  # 获取源节点ID
@@ -72,9 +72,7 @@ class Workflow:
72
72
  # 确保目标端口是InputPort而不是OutputPort
73
73
  target_port_obj = target_node_obj.ports[target_port]
74
74
  if isinstance(target_port_obj, OutputPort):
75
- raise ValueError(
76
- f"The target port {target_port} of node {target_node_id} is an output port. OutputPort cannot be a connection target."
77
- )
75
+ raise ValueError(f"The target port {target_port} of node {target_node_id} is an output port. OutputPort cannot be a connection target.")
78
76
 
79
77
  # 检查目标端口是否已有被连接的线
80
78
  for edge in self.edges:
@@ -161,7 +159,7 @@ class Workflow:
161
159
 
162
160
  return result
163
161
 
164
- def layout(self, options: Optional[Dict[str, Any]] = None) -> "Workflow":
162
+ def layout(self, options: dict[str, Any] | None = None) -> "Workflow":
165
163
  """对工作流中的节点进行自动布局,计算并更新每个节点的位置。
166
164
 
167
165
  此方法实现了一个简单的分层布局算法,将节点按照有向图的拓扑结构进行排列。
@@ -222,9 +220,7 @@ class Workflow:
222
220
  node_type=node_type,
223
221
  category=category,
224
222
  task_name=task_name,
225
- description=node_data["data"].get(
226
- "description", node_instance.description if hasattr(node_instance, "description") else ""
227
- ),
223
+ description=node_data["data"].get("description", node_instance.description if hasattr(node_instance, "description") else ""),
228
224
  node_id=node_data["id"],
229
225
  position=node_data.get("position", {"x": 0, "y": 0}),
230
226
  seleted_workflow_title=node_data["data"].get("seleted_workflow_title", ""),
@@ -278,9 +274,7 @@ class Workflow:
278
274
  is_output = port_data.get("is_output", False)
279
275
 
280
276
  # 检查节点是否允许添加该类型的端口
281
- if (is_output and not node.can_add_output_ports) or (
282
- not is_output and not node.can_add_input_ports
283
- ):
277
+ if (is_output and not node.can_add_output_ports) or (not is_output and not node.can_add_input_ports):
284
278
  # 如果不允许添加,跳过该端口
285
279
  continue
286
280
 
@@ -295,9 +289,7 @@ class Workflow:
295
289
  options=port_data.get("options"),
296
290
  field_type=port_data.get("type"),
297
291
  max_length=port_data.get("max_length"),
298
- support_file_types=port_data.get("support_file_types", "").split(", ")
299
- if port_data.get("support_file_types")
300
- else None,
292
+ support_file_types=port_data.get("support_file_types", "").split(", ") if port_data.get("support_file_types") else None,
301
293
  multiple=port_data.get("multiple"),
302
294
  group=port_data.get("group"),
303
295
  group_collpased=port_data.get("group_collpased", False),
@@ -316,9 +308,7 @@ class Workflow:
316
308
  options=port_data.get("options"),
317
309
  field_type=port_data.get("type"),
318
310
  max_length=port_data.get("max_length"),
319
- support_file_types=port_data.get("support_file_types", "").split(", ")
320
- if port_data.get("support_file_types")
321
- else None,
311
+ support_file_types=port_data.get("support_file_types", "").split(", ") if port_data.get("support_file_types") else None,
322
312
  multiple=port_data.get("multiple"),
323
313
  group=port_data.get("group"),
324
314
  group_collpased=port_data.get("group_collpased", False),
@@ -348,9 +338,7 @@ class Workflow:
348
338
  if target_node.has_port(target_port_name):
349
339
  target_port = target_node.ports[target_port_name]
350
340
  if isinstance(target_port, OutputPort):
351
- raise ValueError(
352
- f"The target port {target_port_name} of node {target_node_id} is an output port. OutputPort cannot be a connection target."
353
- )
341
+ raise ValueError(f"The target port {target_port_name} of node {target_node_id} is an output port. OutputPort cannot be a connection target.")
354
342
 
355
343
  edge = Edge(
356
344
  id=edge_data["id"],
@@ -1,11 +1,9 @@
1
- from typing import Optional
2
-
3
1
  from ..graph.node import Node
4
2
  from ..graph.port import PortType, InputPort, OutputPort
5
3
 
6
4
 
7
5
  class MinimaxMusicGeneration(Node):
8
- def __init__(self, id: Optional[str] = None):
6
+ def __init__(self, id: str | None = None):
9
7
  super().__init__(
10
8
  node_type="MinimaxMusicGeneration",
11
9
  category="audio_generation",
@@ -15,7 +13,7 @@ class MinimaxMusicGeneration(Node):
15
13
  "audio_file": InputPort(
16
14
  name="audio_file",
17
15
  port_type=PortType.FILE,
18
- value=list(),
16
+ value=[],
19
17
  support_file_types=[".wav", ".mp3"],
20
18
  multiple=True,
21
19
  ),
@@ -88,7 +86,7 @@ class MinimaxMusicGeneration(Node):
88
86
 
89
87
 
90
88
  class SoundEffects(Node):
91
- def __init__(self, id: Optional[str] = None):
89
+ def __init__(self, id: str | None = None):
92
90
  super().__init__(
93
91
  node_type="SoundEffects",
94
92
  category="audio_generation",
@@ -105,7 +103,7 @@ class SoundEffects(Node):
105
103
  "video": InputPort(
106
104
  name="video",
107
105
  port_type=PortType.FILE,
108
- value=list(),
106
+ value=[],
109
107
  support_file_types=[".mp4", ".mov", ".webm", ".m4v", ".gif"],
110
108
  multiple=True,
111
109
  ),
@@ -131,7 +129,7 @@ class SoundEffects(Node):
131
129
 
132
130
 
133
131
  class Tts(Node):
134
- def __init__(self, id: Optional[str] = None):
132
+ def __init__(self, id: str | None = None):
135
133
  super().__init__(
136
134
  node_type="Tts",
137
135
  category="audio_generation",
@@ -1,11 +1,9 @@
1
- from typing import Optional
2
-
3
1
  from ..graph.node import Node
4
2
  from ..graph.port import PortType, InputPort, OutputPort
5
3
 
6
4
 
7
5
  class Conditional(Node):
8
- def __init__(self, id: Optional[str] = None):
6
+ def __init__(self, id: str | None = None):
9
7
  super().__init__(
10
8
  node_type="Conditional",
11
9
  category="control_flows",
@@ -66,7 +64,7 @@ class Conditional(Node):
66
64
 
67
65
 
68
66
  class Empty(Node):
69
- def __init__(self, id: Optional[str] = None):
67
+ def __init__(self, id: str | None = None):
70
68
  super().__init__(
71
69
  node_type="Empty",
72
70
  category="control_flows",
@@ -84,7 +82,7 @@ class Empty(Node):
84
82
 
85
83
 
86
84
  class HumanFeedback(Node):
87
- def __init__(self, id: Optional[str] = None):
85
+ def __init__(self, id: str | None = None):
88
86
  super().__init__(
89
87
  node_type="HumanFeedback",
90
88
  category="control_flows",
@@ -107,7 +105,7 @@ class HumanFeedback(Node):
107
105
 
108
106
 
109
107
  class JsonProcess(Node):
110
- def __init__(self, id: Optional[str] = None):
108
+ def __init__(self, id: str | None = None):
111
109
  super().__init__(
112
110
  node_type="JsonProcess",
113
111
  category="control_flows",
@@ -140,7 +138,7 @@ class JsonProcess(Node):
140
138
  "keys": InputPort(
141
139
  name="keys",
142
140
  port_type=PortType.INPUT,
143
- value=list(),
141
+ value=[],
144
142
  ),
145
143
  "default_value": InputPort(
146
144
  name="default_value",
@@ -156,7 +154,7 @@ class JsonProcess(Node):
156
154
 
157
155
 
158
156
  class RandomChoice(Node):
159
- def __init__(self, id: Optional[str] = None):
157
+ def __init__(self, id: str | None = None):
160
158
  super().__init__(
161
159
  node_type="RandomChoice",
162
160
  category="control_flows",
@@ -166,7 +164,7 @@ class RandomChoice(Node):
166
164
  "input": InputPort(
167
165
  name="input",
168
166
  port_type=PortType.LIST,
169
- value=list(),
167
+ value=[],
170
168
  ),
171
169
  "output": OutputPort(),
172
170
  },
@@ -1,11 +1,9 @@
1
- from typing import Optional
2
-
3
1
  from ..graph.node import Node
4
2
  from ..graph.port import PortType, InputPort, OutputPort
5
3
 
6
4
 
7
5
  class FileLoader(Node):
8
- def __init__(self, id: Optional[str] = None):
6
+ def __init__(self, id: str | None = None):
9
7
  super().__init__(
10
8
  node_type="FileLoader",
11
9
  category="file_processing",
@@ -15,7 +13,7 @@ class FileLoader(Node):
15
13
  "files": InputPort(
16
14
  name="files",
17
15
  port_type=PortType.FILE,
18
- value=list(),
16
+ value=[],
19
17
  multiple=True,
20
18
  show=True,
21
19
  ),
@@ -67,7 +65,7 @@ class FileLoader(Node):
67
65
 
68
66
 
69
67
  class FileUpload(Node):
70
- def __init__(self, id: Optional[str] = None):
68
+ def __init__(self, id: str | None = None):
71
69
  super().__init__(
72
70
  node_type="FileUpload",
73
71
  category="file_processing",
@@ -77,7 +75,7 @@ class FileUpload(Node):
77
75
  "files": InputPort(
78
76
  name="files",
79
77
  port_type=PortType.FILE,
80
- value=list(),
78
+ value=[],
81
79
  support_file_types=["*/*"],
82
80
  multiple=True,
83
81
  show=True,