vectorvein 0.2.31__tar.gz → 0.2.33__tar.gz

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 (64) hide show
  1. {vectorvein-0.2.31 → vectorvein-0.2.33}/PKG-INFO +1 -1
  2. {vectorvein-0.2.31 → vectorvein-0.2.33}/pyproject.toml +1 -1
  3. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/chat_clients/utils.py +14 -1
  4. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/workflow/graph/node.py +8 -0
  5. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/workflow/nodes/audio_generation.py +4 -0
  6. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/workflow/nodes/control_flows.py +1 -0
  7. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/workflow/nodes/image_generation.py +21 -1
  8. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/workflow/nodes/llms.py +13 -0
  9. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/workflow/nodes/media_editing.py +6 -0
  10. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/workflow/nodes/media_processing.py +19 -0
  11. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/workflow/nodes/text_processing.py +9 -12
  12. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/workflow/nodes/web_crawlers.py +4 -0
  13. {vectorvein-0.2.31 → vectorvein-0.2.33}/README.md +0 -0
  14. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/__init__.py +0 -0
  15. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/api/__init__.py +0 -0
  16. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/api/client.py +0 -0
  17. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/api/exceptions.py +0 -0
  18. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/api/models.py +0 -0
  19. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/chat_clients/__init__.py +0 -0
  20. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/chat_clients/anthropic_client.py +0 -0
  21. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/chat_clients/baichuan_client.py +0 -0
  22. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/chat_clients/base_client.py +0 -0
  23. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/chat_clients/deepseek_client.py +0 -0
  24. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/chat_clients/ernie_client.py +0 -0
  25. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/chat_clients/gemini_client.py +0 -0
  26. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/chat_clients/groq_client.py +0 -0
  27. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/chat_clients/local_client.py +0 -0
  28. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/chat_clients/minimax_client.py +0 -0
  29. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/chat_clients/mistral_client.py +0 -0
  30. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/chat_clients/moonshot_client.py +0 -0
  31. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/chat_clients/openai_client.py +0 -0
  32. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/chat_clients/openai_compatible_client.py +0 -0
  33. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/chat_clients/py.typed +0 -0
  34. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/chat_clients/qwen_client.py +0 -0
  35. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/chat_clients/stepfun_client.py +0 -0
  36. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/chat_clients/xai_client.py +0 -0
  37. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/chat_clients/yi_client.py +0 -0
  38. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/chat_clients/zhipuai_client.py +0 -0
  39. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/py.typed +0 -0
  40. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/server/token_server.py +0 -0
  41. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/settings/__init__.py +0 -0
  42. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/settings/py.typed +0 -0
  43. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/types/__init__.py +0 -0
  44. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/types/defaults.py +0 -0
  45. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/types/enums.py +0 -0
  46. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/types/exception.py +0 -0
  47. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/types/llm_parameters.py +0 -0
  48. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/types/py.typed +0 -0
  49. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/types/settings.py +0 -0
  50. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/utilities/media_processing.py +0 -0
  51. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/utilities/rate_limiter.py +0 -0
  52. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/utilities/retry.py +0 -0
  53. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/workflow/graph/edge.py +0 -0
  54. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/workflow/graph/port.py +0 -0
  55. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/workflow/graph/workflow.py +0 -0
  56. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/workflow/nodes/__init__.py +0 -0
  57. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/workflow/nodes/file_processing.py +0 -0
  58. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/workflow/nodes/output.py +0 -0
  59. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/workflow/nodes/relational_db.py +0 -0
  60. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/workflow/nodes/tools.py +0 -0
  61. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/workflow/nodes/triggers.py +0 -0
  62. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/workflow/nodes/vector_db.py +0 -0
  63. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/workflow/nodes/video_generation.py +0 -0
  64. {vectorvein-0.2.31 → vectorvein-0.2.33}/src/vectorvein/workflow/utils/json_to_code.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vectorvein
3
- Version: 0.2.31
3
+ Version: 0.2.33
4
4
  Summary: VectorVein Python SDK
5
5
  Author-Email: Anderson <andersonby@163.com>
6
6
  License: MIT
@@ -17,7 +17,7 @@ description = "VectorVein Python SDK"
17
17
  name = "vectorvein"
18
18
  readme = "README.md"
19
19
  requires-python = ">=3.10"
20
- version = "0.2.31"
20
+ version = "0.2.33"
21
21
 
22
22
  [project.license]
23
23
  text = "MIT"
@@ -595,7 +595,20 @@ def transform_from_openai_message(message: ChatCompletionMessageParam, backend:
595
595
  )
596
596
  return {"role": role, "content": formatted_content}
597
597
  else:
598
- return {"role": role, "content": content}
598
+ if tool_calls:
599
+ formatted_content = [{"type": "text", "text": content}] if content else []
600
+ for tool_call in tool_calls:
601
+ formatted_content.append(
602
+ {
603
+ "type": "tool_use",
604
+ "id": tool_call["id"],
605
+ "name": tool_call["function"]["name"],
606
+ "input": json.loads(tool_call["function"]["arguments"]),
607
+ }
608
+ )
609
+ return {"role": role, "content": formatted_content}
610
+ else:
611
+ return {"role": role, "content": content}
599
612
  else:
600
613
  return message # 对于其他后端,保持原样
601
614
 
@@ -17,6 +17,8 @@ class Node:
17
17
  seleted_workflow_title: str = "",
18
18
  is_template: bool = False,
19
19
  initialized: bool = False,
20
+ can_add_input_ports: bool = False,
21
+ can_add_output_ports: bool = False,
20
22
  ) -> None:
21
23
  self.id: str = node_id or str(uuid.uuid4())
22
24
  self.type: str = node_type
@@ -31,6 +33,8 @@ class Node:
31
33
  self.ignored: bool = False
32
34
  self.lock: bool = False
33
35
  self.shadow: bool = False
36
+ self.can_add_input_ports: bool = can_add_input_ports
37
+ self.can_add_output_ports: bool = can_add_output_ports
34
38
 
35
39
  def add_port(
36
40
  self,
@@ -43,10 +47,14 @@ class Node:
43
47
  **kwargs,
44
48
  ):
45
49
  if is_output:
50
+ if not self.can_add_output_ports:
51
+ raise ValueError(f"Node<{self.id}> '{self.type}' does not allow adding output ports")
46
52
  self.ports[name] = OutputPort(
47
53
  name=name, port_type=port_type, show=show, value=value, options=options, **kwargs
48
54
  )
49
55
  else:
56
+ if not self.can_add_input_ports:
57
+ raise ValueError(f"Node<{self.id}> '{self.type}' does not allow adding input ports")
50
58
  self.ports[name] = InputPort(
51
59
  name=name, port_type=port_type, show=show, value=value, options=options, **kwargs
52
60
  )
@@ -17,6 +17,7 @@ class MinimaxMusicGeneration(Node):
17
17
  port_type=PortType.FILE,
18
18
  value=list(),
19
19
  support_file_types=[".wav", ".mp3"],
20
+ multiple=True,
20
21
  ),
21
22
  "purpose": InputPort(
22
23
  name="purpose",
@@ -99,12 +100,14 @@ class SoundEffects(Node):
99
100
  port_type=PortType.TEXTAREA,
100
101
  value="",
101
102
  max_length=50,
103
+ multiple=True,
102
104
  ),
103
105
  "video": InputPort(
104
106
  name="video",
105
107
  port_type=PortType.FILE,
106
108
  value=list(),
107
109
  support_file_types=[".mp4", ".mov", ".webm", ".m4v", ".gif"],
110
+ multiple=True,
108
111
  ),
109
112
  "length": InputPort(
110
113
  name="length",
@@ -139,6 +142,7 @@ class Tts(Node):
139
142
  name="text",
140
143
  port_type=PortType.TEXTAREA,
141
144
  value="",
145
+ multiple=True,
142
146
  ),
143
147
  "output_type": OutputPort(
144
148
  name="output_type",
@@ -149,6 +149,7 @@ class JsonProcess(Node):
149
149
  ),
150
150
  "output": OutputPort(),
151
151
  },
152
+ can_add_output_ports=True,
152
153
  )
153
154
 
154
155
 
@@ -40,22 +40,26 @@ class BackgroundGeneration(Node):
40
40
  port_type=PortType.FILE,
41
41
  value=list(),
42
42
  support_file_types=[".jpg", ".jpeg", ".png", ".webp"],
43
+ multiple=True,
43
44
  ),
44
45
  "ref_prompt": InputPort(
45
46
  name="ref_prompt",
46
47
  port_type=PortType.TEXTAREA,
47
48
  value="",
48
49
  max_length=200,
50
+ multiple=True,
49
51
  ),
50
52
  "image_title": InputPort(
51
53
  name="title",
52
54
  port_type=PortType.INPUT,
53
55
  value="",
56
+ multiple=True,
54
57
  ),
55
58
  "image_sub_title": InputPort(
56
59
  name="sub_title",
57
60
  port_type=PortType.INPUT,
58
61
  value="",
62
+ multiple=True,
59
63
  ),
60
64
  "n": InputPort(
61
65
  name="n",
@@ -109,6 +113,7 @@ class DallE(Node):
109
113
  name="prompt",
110
114
  port_type=PortType.TEXTAREA,
111
115
  value="",
116
+ multiple=True,
112
117
  ),
113
118
  "model": InputPort(
114
119
  name="model",
@@ -173,6 +178,7 @@ class Flux1(Node):
173
178
  name="prompt",
174
179
  port_type=PortType.TEXTAREA,
175
180
  value="",
181
+ multiple=True,
176
182
  ),
177
183
  "model": InputPort(
178
184
  name="model",
@@ -184,6 +190,7 @@ class Flux1(Node):
184
190
  {"value": "FLUX.1 [pro]", "label": "FLUX.1 [pro]"},
185
191
  {"value": "FLUX.1 [pro] ultra", "label": "FLUX.1 [pro] ultra"},
186
192
  ],
193
+ multiple=True,
187
194
  ),
188
195
  "width": InputPort(
189
196
  name="width",
@@ -258,6 +265,7 @@ class Inpainting(Node):
258
265
  port_type=PortType.FILE,
259
266
  value=list(),
260
267
  support_file_types=[".jpg", ".jpeg", ".png", ".webp"],
268
+ multiple=True,
261
269
  ),
262
270
  "inpainting_method": InputPort(
263
271
  name="inpainting_method",
@@ -274,6 +282,7 @@ class Inpainting(Node):
274
282
  value=list(),
275
283
  support_file_types=[".jpg", ".jpeg", ".png", ".webp"],
276
284
  condition="return fieldsData.inpainting_method.value === 'custom'",
285
+ multiple=True,
277
286
  ),
278
287
  "prompt": InputPort(
279
288
  name="prompt",
@@ -320,11 +329,13 @@ class Kolors(Node):
320
329
  name="prompt",
321
330
  port_type=PortType.TEXTAREA,
322
331
  value="",
332
+ multiple=True,
323
333
  ),
324
334
  "negative_prompt": InputPort(
325
335
  name="negative_prompt",
326
336
  port_type=PortType.TEXTAREA,
327
337
  value="",
338
+ multiple=True,
328
339
  ),
329
340
  "width": InputPort(
330
341
  name="width",
@@ -390,11 +401,13 @@ class Pulid(Node):
390
401
  port_type=PortType.FILE,
391
402
  value=list(),
392
403
  support_file_types=[".jpg", ".jpeg", ".png", ".webp"],
404
+ multiple=True,
393
405
  ),
394
406
  "prompt": InputPort(
395
407
  name="prompt",
396
408
  port_type=PortType.TEXTAREA,
397
409
  value="",
410
+ multiple=True,
398
411
  ),
399
412
  "negative_prompt": InputPort(
400
413
  name="negative_prompt",
@@ -499,7 +512,7 @@ class Recraft(Node):
499
512
  port_type=PortType.FILE,
500
513
  value=list(),
501
514
  support_file_types=[".jpg", ".jpeg", ".png", ".webp"],
502
- multiple=False,
515
+ multiple=True,
503
516
  condition="return fieldsData.generation_type.value === 'image_to_vector'",
504
517
  ),
505
518
  "prompt": InputPort(
@@ -507,6 +520,7 @@ class Recraft(Node):
507
520
  port_type=PortType.TEXTAREA,
508
521
  value="",
509
522
  condition="return fieldsData.generation_type.value === 'text_to_image'",
523
+ multiple=True,
510
524
  ),
511
525
  "base_style": InputPort(
512
526
  name="base_style",
@@ -519,6 +533,7 @@ class Recraft(Node):
519
533
  {"value": "vector_illustration", "label": "vector_illustration"},
520
534
  ],
521
535
  condition="return fieldsData.generation_type.value === 'text_to_image'",
536
+ multiple=True,
522
537
  ),
523
538
  "substyle_realistic_image": InputPort(
524
539
  name="substyle_realistic_image",
@@ -535,6 +550,7 @@ class Recraft(Node):
535
550
  {"value": "motion_blur", "label": "motion_blur"},
536
551
  ],
537
552
  condition="return fieldsData.generation_type.value === 'text_to_image' && fieldsData.base_style.value === 'realistic_image'",
553
+ multiple=True,
538
554
  ),
539
555
  "substyle_digital_illustration": InputPort(
540
556
  name="substyle_digital_illustration",
@@ -553,6 +569,7 @@ class Recraft(Node):
553
569
  {"value": "2d_art_poster_2", "label": "2d_art_poster_2"},
554
570
  ],
555
571
  condition="return fieldsData.generation_type.value === 'text_to_image' && fieldsData.base_style.value === 'digital_illustration'",
572
+ multiple=True,
556
573
  ),
557
574
  "substyle_vector_illustration": InputPort(
558
575
  name="substyle_vector_illustration",
@@ -566,6 +583,7 @@ class Recraft(Node):
566
583
  {"value": "linocut", "label": "linocut"},
567
584
  ],
568
585
  condition="return fieldsData.generation_type.value === 'text_to_image' && fieldsData.base_style.value === 'vector_illustration'",
586
+ multiple=True,
569
587
  ),
570
588
  "size": InputPort(
571
589
  name="size",
@@ -647,11 +665,13 @@ class StableDiffusion(Node):
647
665
  name="prompt",
648
666
  port_type=PortType.TEXTAREA,
649
667
  value="",
668
+ multiple=True,
650
669
  ),
651
670
  "negative_prompt": InputPort(
652
671
  name="negative_prompt",
653
672
  port_type=PortType.TEXTAREA,
654
673
  value="",
674
+ multiple=True,
655
675
  ),
656
676
  "model": InputPort(
657
677
  name="model",
@@ -16,6 +16,7 @@ class AliyunQwen(Node):
16
16
  name="prompt",
17
17
  port_type=PortType.TEXT,
18
18
  value="",
19
+ multiple=True,
19
20
  ),
20
21
  "llm_model": InputPort(
21
22
  name="llm_model",
@@ -77,6 +78,7 @@ class Baichuan(Node):
77
78
  name="prompt",
78
79
  port_type=PortType.TEXTAREA,
79
80
  value="",
81
+ multiple=True,
80
82
  ),
81
83
  "llm_model": InputPort(
82
84
  name="llm_model",
@@ -165,6 +167,7 @@ class BaiduWenxin(Node):
165
167
  name="prompt",
166
168
  port_type=PortType.TEXTAREA,
167
169
  value="",
170
+ multiple=True,
168
171
  ),
169
172
  "llm_model": InputPort(
170
173
  name="llm_model",
@@ -204,6 +207,7 @@ class ChatGLM(Node):
204
207
  name="prompt",
205
208
  port_type=PortType.TEXTAREA,
206
209
  value="",
210
+ multiple=True,
207
211
  ),
208
212
  "llm_model": InputPort(
209
213
  name="llm_model",
@@ -286,6 +290,7 @@ class Claude(Node):
286
290
  name="prompt",
287
291
  port_type=PortType.TEXTAREA,
288
292
  value="",
293
+ multiple=True,
289
294
  ),
290
295
  "llm_model": InputPort(
291
296
  name="llm_model",
@@ -333,6 +338,7 @@ class Deepseek(Node):
333
338
  name="prompt",
334
339
  port_type=PortType.TEXTAREA,
335
340
  value="",
341
+ multiple=True,
336
342
  ),
337
343
  "llm_model": InputPort(
338
344
  name="llm_model",
@@ -423,6 +429,7 @@ class Gemini(Node):
423
429
  name="prompt",
424
430
  port_type=PortType.TEXTAREA,
425
431
  value="",
432
+ multiple=True,
426
433
  ),
427
434
  "llm_model": InputPort(
428
435
  name="llm_model",
@@ -519,6 +526,7 @@ class LingYiWanWu(Node):
519
526
  name="prompt",
520
527
  port_type=PortType.TEXTAREA,
521
528
  value="",
529
+ multiple=True,
522
530
  ),
523
531
  "llm_model": InputPort(
524
532
  name="llm_model",
@@ -563,6 +571,7 @@ class MiniMax(Node):
563
571
  name="prompt",
564
572
  port_type=PortType.TEXTAREA,
565
573
  value="",
574
+ multiple=True,
566
575
  ),
567
576
  "llm_model": InputPort(
568
577
  name="llm_model",
@@ -648,6 +657,7 @@ class Moonshot(Node):
648
657
  name="prompt",
649
658
  port_type=PortType.TEXTAREA,
650
659
  value="",
660
+ multiple=True,
651
661
  ),
652
662
  "llm_model": InputPort(
653
663
  name="llm_model",
@@ -734,6 +744,7 @@ class OpenAI(Node):
734
744
  name="prompt",
735
745
  port_type=PortType.TEXTAREA,
736
746
  value="",
747
+ multiple=True,
737
748
  ),
738
749
  "llm_model": InputPort(
739
750
  name="llm_model",
@@ -824,6 +835,7 @@ class XAi(Node):
824
835
  name="prompt",
825
836
  port_type=PortType.TEXTAREA,
826
837
  value="",
838
+ multiple=True,
827
839
  ),
828
840
  "llm_model": InputPort(
829
841
  name="llm_model",
@@ -908,6 +920,7 @@ class CustomModel(Node):
908
920
  name="prompt",
909
921
  port_type=PortType.TEXTAREA,
910
922
  value="",
923
+ multiple=True,
911
924
  ),
912
925
  "model_family": InputPort(
913
926
  name="model_family",
@@ -17,6 +17,7 @@ class AudioEditing(Node):
17
17
  port_type=PortType.FILE,
18
18
  value=list(),
19
19
  support_file_types=[".mp3", ".wav", ".ogg", ".m4a"],
20
+ multiple=True,
20
21
  ),
21
22
  "audio_processing_logic": InputPort(
22
23
  name="audio_processing_logic",
@@ -221,6 +222,7 @@ class ImageEditing(Node):
221
222
  port_type=PortType.FILE,
222
223
  value=list(),
223
224
  support_file_types=[".jpg", ".jpeg", ".png", ".webp"],
225
+ multiple=True,
224
226
  ),
225
227
  "crop": InputPort(
226
228
  name="crop",
@@ -362,6 +364,7 @@ class ImageSegmentation(Node):
362
364
  port_type=PortType.FILE,
363
365
  value=list(),
364
366
  support_file_types=[".jpg", ".jpeg", ".png", ".webp"],
367
+ multiple=True,
365
368
  ),
366
369
  "selection_method": InputPort(
367
370
  name="selection_method",
@@ -422,6 +425,7 @@ class ImageWatermark(Node):
422
425
  port_type=PortType.FILE,
423
426
  value=list(),
424
427
  support_file_types=[".jpg", ".jpeg", ".png", ".webp"],
428
+ multiple=True,
425
429
  ),
426
430
  "image_or_text": InputPort(
427
431
  name="image_or_text",
@@ -554,6 +558,7 @@ class VideoEditing(Node):
554
558
  port_type=PortType.FILE,
555
559
  value=list(),
556
560
  support_file_types=["video/*"],
561
+ multiple=True,
557
562
  ),
558
563
  "video_processing_logic": InputPort(
559
564
  name="video_processing_logic",
@@ -631,6 +636,7 @@ class VideoScreenshot(Node):
631
636
  port_type=PortType.FILE,
632
637
  value=list(),
633
638
  support_file_types=["video/*"],
639
+ multiple=True,
634
640
  ),
635
641
  "screenshot_method": InputPort(
636
642
  name="screenshot_method",
@@ -16,6 +16,7 @@ class ClaudeVision(Node):
16
16
  name="text_prompt",
17
17
  port_type=PortType.TEXTAREA,
18
18
  value="",
19
+ multiple=True,
19
20
  ),
20
21
  "llm_model": InputPort(
21
22
  name="llm_model",
@@ -28,6 +29,12 @@ class ClaudeVision(Node):
28
29
  {"value": "claude-3-haiku", "label": "claude-3-haiku"},
29
30
  ],
30
31
  ),
32
+ "multiple_input": InputPort(
33
+ name="multiple_input",
34
+ port_type=PortType.CHECKBOX,
35
+ value=False,
36
+ has_tooltip=True,
37
+ ),
31
38
  "images_or_urls": InputPort(
32
39
  name="images_or_urls",
33
40
  port_type=PortType.RADIO,
@@ -130,6 +137,12 @@ class GeminiVision(Node):
130
137
  {"value": "gemini-exp-1206", "label": "gemini-exp-1206"},
131
138
  ],
132
139
  ),
140
+ "multiple_input": InputPort(
141
+ name="multiple_input",
142
+ port_type=PortType.CHECKBOX,
143
+ value=False,
144
+ has_tooltip=True,
145
+ ),
133
146
  "images_or_urls": InputPort(
134
147
  name="images_or_urls",
135
148
  port_type=PortType.RADIO,
@@ -404,6 +417,12 @@ class QwenVision(Node):
404
417
  {"value": "qwen-vl-plus", "label": "qwen-vl-plus"},
405
418
  ],
406
419
  ),
420
+ "multiple_input": InputPort(
421
+ name="multiple_input",
422
+ port_type=PortType.CHECKBOX,
423
+ value=False,
424
+ has_tooltip=True,
425
+ ),
407
426
  "images_or_urls": InputPort(
408
427
  name="images_or_urls",
409
428
  port_type=PortType.RADIO,
@@ -16,6 +16,7 @@ class TextInOut(Node):
16
16
  name="text",
17
17
  port_type=PortType.TEXTAREA,
18
18
  value="",
19
+ multiple=True,
19
20
  ),
20
21
  "input_type": InputPort(
21
22
  name="input_type",
@@ -26,10 +27,7 @@ class TextInOut(Node):
26
27
  {"value": "number", "label": "number"},
27
28
  ],
28
29
  ),
29
- "output": OutputPort(
30
- name="output",
31
- port_type=PortType.TEXT,
32
- ),
30
+ "output": OutputPort(),
33
31
  },
34
32
  )
35
33
 
@@ -46,16 +44,14 @@ class TextReplace(Node):
46
44
  name="text",
47
45
  port_type=PortType.TEXTAREA,
48
46
  value="",
47
+ multiple=True,
49
48
  ),
50
49
  "replace_items": InputPort(
51
50
  name="replace_items",
52
51
  port_type=PortType.INPUT,
53
52
  value=[],
54
53
  ),
55
- "output": OutputPort(
56
- name="output",
57
- port_type=PortType.TEXT,
58
- ),
54
+ "output": OutputPort(),
59
55
  },
60
56
  )
61
57
 
@@ -101,10 +97,7 @@ class TextSplitters(Node):
101
97
  value="\\n",
102
98
  condition="return fieldsData.split_method.value == 'delimiter'",
103
99
  ),
104
- "output": OutputPort(
105
- name="output",
106
- port_type=PortType.TEXT,
107
- ),
100
+ "output": OutputPort(),
108
101
  },
109
102
  )
110
103
 
@@ -121,6 +114,7 @@ class TextTruncation(Node):
121
114
  name="text",
122
115
  port_type=PortType.TEXTAREA,
123
116
  value="",
117
+ multiple=True,
124
118
  ),
125
119
  "truncate_method": InputPort(
126
120
  name="truncate_method",
@@ -212,9 +206,11 @@ class TemplateCompose(Node):
212
206
  name="template",
213
207
  port_type=PortType.TEXTAREA,
214
208
  value="",
209
+ multiple=True,
215
210
  ),
216
211
  "output": OutputPort(),
217
212
  },
213
+ can_add_input_ports=True,
218
214
  )
219
215
 
220
216
 
@@ -230,6 +226,7 @@ class RegexExtract(Node):
230
226
  name="text",
231
227
  port_type=PortType.TEXTAREA,
232
228
  value="",
229
+ multiple=True,
233
230
  ),
234
231
  "pattern": InputPort(
235
232
  name="pattern",
@@ -16,6 +16,7 @@ class TextCrawler(Node):
16
16
  name="url",
17
17
  port_type=PortType.INPUT,
18
18
  value="",
19
+ multiple=True,
19
20
  ),
20
21
  "output_type": InputPort(
21
22
  name="output_type",
@@ -53,6 +54,7 @@ class BilibiliCrawler(Node):
53
54
  name="url_or_bvid",
54
55
  port_type=PortType.INPUT,
55
56
  value="",
57
+ multiple=True,
56
58
  ),
57
59
  "download_video": InputPort(
58
60
  name="download_video",
@@ -93,6 +95,7 @@ class DouyinCrawler(Node):
93
95
  name="url",
94
96
  port_type=PortType.INPUT,
95
97
  value="",
98
+ multiple=True,
96
99
  ),
97
100
  "output_title": OutputPort(
98
101
  name="output_title",
@@ -119,6 +122,7 @@ class YoutubeCrawler(Node):
119
122
  name="url_or_video_id",
120
123
  port_type=PortType.INPUT,
121
124
  value="",
125
+ multiple=True,
122
126
  ),
123
127
  "get_comments": InputPort(
124
128
  name="get_comments",
File without changes