vectorvein 0.1.81__tar.gz → 0.1.83__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 (56) hide show
  1. {vectorvein-0.1.81 → vectorvein-0.1.83}/PKG-INFO +1 -1
  2. {vectorvein-0.1.81 → vectorvein-0.1.83}/pyproject.toml +1 -1
  3. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/chat_clients/openai_compatible_client.py +120 -12
  4. {vectorvein-0.1.81 → vectorvein-0.1.83}/README.md +0 -0
  5. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/__init__.py +0 -0
  6. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/chat_clients/__init__.py +0 -0
  7. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/chat_clients/anthropic_client.py +0 -0
  8. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/chat_clients/baichuan_client.py +0 -0
  9. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/chat_clients/base_client.py +0 -0
  10. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/chat_clients/deepseek_client.py +0 -0
  11. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/chat_clients/gemini_client.py +0 -0
  12. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/chat_clients/groq_client.py +0 -0
  13. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/chat_clients/local_client.py +0 -0
  14. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/chat_clients/minimax_client.py +0 -0
  15. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/chat_clients/mistral_client.py +0 -0
  16. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/chat_clients/moonshot_client.py +0 -0
  17. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/chat_clients/openai_client.py +0 -0
  18. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/chat_clients/py.typed +0 -0
  19. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/chat_clients/qwen_client.py +0 -0
  20. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/chat_clients/stepfun_client.py +0 -0
  21. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/chat_clients/utils.py +0 -0
  22. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/chat_clients/xai_client.py +0 -0
  23. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/chat_clients/yi_client.py +0 -0
  24. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/chat_clients/zhipuai_client.py +0 -0
  25. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/py.typed +0 -0
  26. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/server/token_server.py +0 -0
  27. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/settings/__init__.py +0 -0
  28. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/settings/py.typed +0 -0
  29. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/types/defaults.py +0 -0
  30. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/types/enums.py +0 -0
  31. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/types/exception.py +0 -0
  32. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/types/llm_parameters.py +0 -0
  33. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/types/py.typed +0 -0
  34. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/utilities/media_processing.py +0 -0
  35. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/utilities/retry.py +0 -0
  36. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/workflow/graph/edge.py +0 -0
  37. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/workflow/graph/node.py +0 -0
  38. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/workflow/graph/port.py +0 -0
  39. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/workflow/graph/workflow.py +0 -0
  40. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/workflow/nodes/__init__.py +0 -0
  41. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/workflow/nodes/audio_generation.py +0 -0
  42. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/workflow/nodes/control_flows.py +0 -0
  43. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/workflow/nodes/file_processing.py +0 -0
  44. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/workflow/nodes/image_generation.py +0 -0
  45. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/workflow/nodes/llms.py +0 -0
  46. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/workflow/nodes/media_editing.py +0 -0
  47. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/workflow/nodes/media_processing.py +0 -0
  48. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/workflow/nodes/output.py +0 -0
  49. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/workflow/nodes/relational_db.py +0 -0
  50. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/workflow/nodes/text_processing.py +0 -0
  51. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/workflow/nodes/tools.py +0 -0
  52. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/workflow/nodes/triggers.py +0 -0
  53. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/workflow/nodes/vector_db.py +0 -0
  54. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/workflow/nodes/video_generation.py +0 -0
  55. {vectorvein-0.1.81 → vectorvein-0.1.83}/src/vectorvein/workflow/nodes/web_crawlers.py +0 -0
  56. {vectorvein-0.1.81 → vectorvein-0.1.83}/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.1.81
3
+ Version: 0.1.83
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.1.81"
20
+ version = "0.1.83"
21
21
 
22
22
  [project.license]
23
23
  text = "MIT"
@@ -3,6 +3,7 @@
3
3
  import json
4
4
  from functools import cached_property
5
5
  from typing import overload, Generator, AsyncGenerator, Any, Literal, Iterable
6
+ import re
6
7
 
7
8
  import httpx
8
9
  from openai import OpenAI, AsyncOpenAI, AzureOpenAI, AsyncAzureOpenAI
@@ -229,6 +230,11 @@ class OpenAICompatibleChatClient(BaseChatClient):
229
230
  full_content = ""
230
231
  result = {}
231
232
  usage = None
233
+ buffer = ""
234
+ in_reasoning = False
235
+ current_reasoning = []
236
+ current_content = []
237
+
232
238
  for chunk in stream_response:
233
239
  if chunk.usage and chunk.usage.total_tokens:
234
240
  usage = Usage(
@@ -239,14 +245,11 @@ class OpenAICompatibleChatClient(BaseChatClient):
239
245
  completion_tokens_details=chunk.usage.completion_tokens_details,
240
246
  )
241
247
 
242
- if len(chunk.choices) == 0:
243
- if usage:
244
- yield ChatCompletionDeltaMessage(usage=usage)
245
- continue
246
- if not chunk.choices[0].delta:
248
+ if len(chunk.choices) == 0 or not chunk.choices[0].delta:
247
249
  if usage:
248
250
  yield ChatCompletionDeltaMessage(usage=usage)
249
251
  continue
252
+
250
253
  if self.model_setting.function_call_available:
251
254
  if chunk.choices[0].delta.tool_calls:
252
255
  for index, tool_call in enumerate(chunk.choices[0].delta.tool_calls):
@@ -254,16 +257,61 @@ class OpenAICompatibleChatClient(BaseChatClient):
254
257
  yield ChatCompletionDeltaMessage(**chunk.choices[0].delta.model_dump(), usage=usage)
255
258
  else:
256
259
  message = chunk.choices[0].delta.model_dump()
257
- full_content += message["content"] if message["content"] else ""
260
+ delta_content = message.get("content", "")
261
+ buffer += delta_content or ""
262
+
263
+ while True:
264
+ if not in_reasoning:
265
+ start_pos = buffer.find("<think>")
266
+ if start_pos != -1:
267
+ current_content.append(buffer[:start_pos])
268
+ buffer = buffer[start_pos + 7 :]
269
+ in_reasoning = True
270
+ else:
271
+ current_content.append(buffer)
272
+ buffer = ""
273
+ break
274
+ else:
275
+ end_pos = buffer.find("</think>")
276
+ if end_pos != -1:
277
+ current_reasoning.append(buffer[:end_pos])
278
+ buffer = buffer[end_pos + 8 :]
279
+ in_reasoning = False
280
+ else:
281
+ current_reasoning.append(buffer)
282
+ buffer = ""
283
+ break
284
+
285
+ message["content"] = "".join(current_content).strip()
286
+ if current_reasoning:
287
+ message["reasoning_content"] = "".join(current_reasoning).strip()
288
+ current_content.clear()
289
+ current_reasoning.clear()
290
+
258
291
  if tools:
292
+ full_content += message["content"]
259
293
  tool_call_data = ToolCallContentProcessor(full_content).tool_calls
260
294
  if tool_call_data:
261
295
  message["tool_calls"] = tool_call_data["tool_calls"]
296
+
262
297
  if full_content in ("<", "<|", "<|▶", "<|▶|") or full_content.startswith("<|▶|>"):
263
298
  message["content"] = ""
264
299
  result = message
265
300
  continue
301
+
266
302
  yield ChatCompletionDeltaMessage(**message, usage=usage)
303
+
304
+ if buffer:
305
+ if in_reasoning:
306
+ current_reasoning.append(buffer)
307
+ else:
308
+ current_content.append(buffer)
309
+ final_message = {
310
+ "content": "".join(current_content).strip(),
311
+ "reasoning_content": "".join(current_reasoning).strip() if current_reasoning else None,
312
+ }
313
+ yield ChatCompletionDeltaMessage(**final_message, usage=usage)
314
+
267
315
  if result:
268
316
  yield ChatCompletionDeltaMessage(**result, usage=usage)
269
317
 
@@ -286,6 +334,13 @@ class OpenAICompatibleChatClient(BaseChatClient):
286
334
  "reasoning_content": getattr(response.choices[0].message, "reasoning_content", None),
287
335
  "usage": response.usage.model_dump() if response.usage else None,
288
336
  }
337
+
338
+ if not result["reasoning_content"] and result["content"]:
339
+ think_match = re.search(r"<think>(.*?)</think>", result["content"], re.DOTALL)
340
+ if think_match:
341
+ result["reasoning_content"] = think_match.group(1).strip()
342
+ result["content"] = result["content"].replace(think_match.group(0), "", 1).strip()
343
+
289
344
  if tools:
290
345
  if self.model_setting.function_call_available and response.choices[0].message.tool_calls:
291
346
  result["tool_calls"] = [
@@ -501,6 +556,11 @@ class AsyncOpenAICompatibleChatClient(BaseAsyncChatClient):
501
556
  full_content = ""
502
557
  result = {}
503
558
  usage = None
559
+ buffer = ""
560
+ in_reasoning = False
561
+ current_reasoning = []
562
+ current_content = []
563
+
504
564
  async for chunk in stream_response:
505
565
  if chunk.usage and chunk.usage.total_tokens:
506
566
  usage = Usage(
@@ -511,11 +571,7 @@ class AsyncOpenAICompatibleChatClient(BaseAsyncChatClient):
511
571
  prompt_tokens_details=chunk.usage.prompt_tokens_details,
512
572
  )
513
573
 
514
- if len(chunk.choices) == 0:
515
- if usage:
516
- yield ChatCompletionDeltaMessage(usage=usage)
517
- continue
518
- if not chunk.choices[0].delta:
574
+ if len(chunk.choices) == 0 or not chunk.choices[0].delta:
519
575
  if usage:
520
576
  yield ChatCompletionDeltaMessage(usage=usage)
521
577
  continue
@@ -527,16 +583,61 @@ class AsyncOpenAICompatibleChatClient(BaseAsyncChatClient):
527
583
  yield ChatCompletionDeltaMessage(**chunk.choices[0].delta.model_dump(), usage=usage)
528
584
  else:
529
585
  message = chunk.choices[0].delta.model_dump()
530
- full_content += message["content"] if message["content"] else ""
586
+ delta_content = message.get("content", "")
587
+ buffer += delta_content or ""
588
+
589
+ while True:
590
+ if not in_reasoning:
591
+ start_pos = buffer.find("<think>")
592
+ if start_pos != -1:
593
+ current_content.append(buffer[:start_pos])
594
+ buffer = buffer[start_pos + 7 :]
595
+ in_reasoning = True
596
+ else:
597
+ current_content.append(buffer)
598
+ buffer = ""
599
+ break
600
+ else:
601
+ end_pos = buffer.find("</think>")
602
+ if end_pos != -1:
603
+ current_reasoning.append(buffer[:end_pos])
604
+ buffer = buffer[end_pos + 8 :]
605
+ in_reasoning = False
606
+ else:
607
+ current_reasoning.append(buffer)
608
+ buffer = ""
609
+ break
610
+
611
+ message["content"] = "".join(current_content).strip()
612
+ if current_reasoning:
613
+ message["reasoning_content"] = "".join(current_reasoning).strip()
614
+ current_content.clear()
615
+ current_reasoning.clear()
616
+
531
617
  if tools:
618
+ full_content += message["content"]
532
619
  tool_call_data = ToolCallContentProcessor(full_content).tool_calls
533
620
  if tool_call_data:
534
621
  message["tool_calls"] = tool_call_data["tool_calls"]
622
+
535
623
  if full_content in ("<", "<|", "<|▶", "<|▶|") or full_content.startswith("<|▶|>"):
536
624
  message["content"] = ""
537
625
  result = message
538
626
  continue
627
+
539
628
  yield ChatCompletionDeltaMessage(**message, usage=usage)
629
+
630
+ if buffer:
631
+ if in_reasoning:
632
+ current_reasoning.append(buffer)
633
+ else:
634
+ current_content.append(buffer)
635
+ final_message = {
636
+ "content": "".join(current_content).strip(),
637
+ "reasoning_content": "".join(current_reasoning).strip() if current_reasoning else None,
638
+ }
639
+ yield ChatCompletionDeltaMessage(**final_message, usage=usage)
640
+
540
641
  if result:
541
642
  yield ChatCompletionDeltaMessage(**result, usage=usage)
542
643
 
@@ -558,6 +659,13 @@ class AsyncOpenAICompatibleChatClient(BaseAsyncChatClient):
558
659
  "reasoning_content": getattr(response.choices[0].message, "reasoning_content", None),
559
660
  "usage": response.usage.model_dump() if response.usage else None,
560
661
  }
662
+
663
+ if not result["reasoning_content"] and result["content"]:
664
+ think_match = re.search(r"<think>(.*?)</think>", result["content"], re.DOTALL)
665
+ if think_match:
666
+ result["reasoning_content"] = think_match.group(1).strip()
667
+ result["content"] = result["content"].replace(think_match.group(0), "", 1).strip()
668
+
561
669
  if tools:
562
670
  if self.model_setting.function_call_available and response.choices[0].message.tool_calls:
563
671
  result["tool_calls"] = [
File without changes