beswarm 0.2.34__tar.gz → 0.2.36__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.

Potentially problematic release.


This version of beswarm might be problematic. Click here for more details.

Files changed (153) hide show
  1. {beswarm-0.2.34 → beswarm-0.2.36}/PKG-INFO +1 -1
  2. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/setup.py +1 -1
  3. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/src/aient/core/request.py +26 -9
  4. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/src/aient/core/response.py +58 -106
  5. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/src/aient/models/chatgpt.py +4 -3
  6. beswarm-0.2.36/beswarm/broker.py +235 -0
  7. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/tools/click.py +1 -0
  8. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/tools/search_web.py +1 -3
  9. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/tools/taskmanager.py +12 -4
  10. beswarm-0.2.36/beswarm/tools/worker.py +367 -0
  11. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm.egg-info/PKG-INFO +1 -1
  12. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm.egg-info/SOURCES.txt +3 -1
  13. {beswarm-0.2.34 → beswarm-0.2.36}/pyproject.toml +1 -1
  14. beswarm-0.2.36/test/test_broker.py +195 -0
  15. beswarm-0.2.34/beswarm/tools/worker.py +0 -455
  16. {beswarm-0.2.34 → beswarm-0.2.36}/MANIFEST.in +0 -0
  17. {beswarm-0.2.34 → beswarm-0.2.36}/README.md +0 -0
  18. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/__init__.py +0 -0
  19. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/main.py +0 -0
  20. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/src/aient/__init__.py +0 -0
  21. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/src/aient/core/__init__.py +0 -0
  22. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/src/aient/core/log_config.py +0 -0
  23. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/src/aient/core/models.py +0 -0
  24. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/src/aient/core/test/test_base_api.py +0 -0
  25. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/src/aient/core/test/test_geminimask.py +0 -0
  26. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/src/aient/core/test/test_image.py +0 -0
  27. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/src/aient/core/test/test_payload.py +0 -0
  28. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/src/aient/core/utils.py +0 -0
  29. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/src/aient/models/__init__.py +0 -0
  30. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/src/aient/models/audio.py +0 -0
  31. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/src/aient/models/base.py +0 -0
  32. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/src/aient/models/claude.py +0 -0
  33. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/src/aient/models/duckduckgo.py +0 -0
  34. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/src/aient/models/gemini.py +0 -0
  35. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/src/aient/models/groq.py +0 -0
  36. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/src/aient/models/vertex.py +0 -0
  37. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/src/aient/plugins/__init__.py +0 -0
  38. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/src/aient/plugins/arXiv.py +0 -0
  39. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/src/aient/plugins/config.py +0 -0
  40. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/src/aient/plugins/excute_command.py +0 -0
  41. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/src/aient/plugins/get_time.py +0 -0
  42. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/src/aient/plugins/image.py +0 -0
  43. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/src/aient/plugins/list_directory.py +0 -0
  44. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/src/aient/plugins/read_file.py +0 -0
  45. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/src/aient/plugins/read_image.py +0 -0
  46. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/src/aient/plugins/readonly.py +0 -0
  47. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/src/aient/plugins/registry.py +0 -0
  48. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/src/aient/plugins/run_python.py +0 -0
  49. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/src/aient/plugins/websearch.py +0 -0
  50. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/src/aient/plugins/write_file.py +0 -0
  51. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/src/aient/utils/__init__.py +0 -0
  52. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/src/aient/utils/prompt.py +0 -0
  53. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/src/aient/utils/scripts.py +0 -0
  54. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/test/chatgpt.py +0 -0
  55. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/test/claude.py +0 -0
  56. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/test/test.py +0 -0
  57. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/test/test_API.py +0 -0
  58. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/test/test_Deepbricks.py +0 -0
  59. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/test/test_Web_crawler.py +0 -0
  60. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/test/test_aiwaves.py +0 -0
  61. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/test/test_aiwaves_arxiv.py +0 -0
  62. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/test/test_ask_gemini.py +0 -0
  63. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/test/test_class.py +0 -0
  64. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/test/test_claude.py +0 -0
  65. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/test/test_claude_zh_char.py +0 -0
  66. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/test/test_ddg_search.py +0 -0
  67. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/test/test_download_pdf.py +0 -0
  68. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/test/test_gemini.py +0 -0
  69. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/test/test_get_token_dict.py +0 -0
  70. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/test/test_google_search.py +0 -0
  71. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/test/test_jieba.py +0 -0
  72. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/test/test_json.py +0 -0
  73. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/test/test_logging.py +0 -0
  74. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/test/test_ollama.py +0 -0
  75. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/test/test_plugin.py +0 -0
  76. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/test/test_py_run.py +0 -0
  77. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/test/test_requests.py +0 -0
  78. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/test/test_search.py +0 -0
  79. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/test/test_tikitoken.py +0 -0
  80. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/test/test_token.py +0 -0
  81. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/test/test_url.py +0 -0
  82. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/test/test_whisper.py +0 -0
  83. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/test/test_wildcard.py +0 -0
  84. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/aient/test/test_yjh.py +0 -0
  85. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/bemcp/bemcp/__init__.py +0 -0
  86. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/bemcp/bemcp/decorator.py +0 -0
  87. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/bemcp/bemcp/main.py +0 -0
  88. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/bemcp/bemcp/utils.py +0 -0
  89. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/bemcp/test/client.py +0 -0
  90. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/bemcp/test/server.py +0 -0
  91. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/prompt.py +0 -0
  92. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-language-pack/README.md +0 -0
  93. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-language-pack/arduino-tags.scm +0 -0
  94. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-language-pack/c-tags.scm +0 -0
  95. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-language-pack/chatito-tags.scm +0 -0
  96. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-language-pack/commonlisp-tags.scm +0 -0
  97. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-language-pack/cpp-tags.scm +0 -0
  98. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-language-pack/csharp-tags.scm +0 -0
  99. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-language-pack/d-tags.scm +0 -0
  100. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-language-pack/dart-tags.scm +0 -0
  101. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-language-pack/elisp-tags.scm +0 -0
  102. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-language-pack/elixir-tags.scm +0 -0
  103. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-language-pack/elm-tags.scm +0 -0
  104. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-language-pack/gleam-tags.scm +0 -0
  105. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-language-pack/go-tags.scm +0 -0
  106. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-language-pack/java-tags.scm +0 -0
  107. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-language-pack/javascript-tags.scm +0 -0
  108. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-language-pack/lua-tags.scm +0 -0
  109. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-language-pack/pony-tags.scm +0 -0
  110. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-language-pack/properties-tags.scm +0 -0
  111. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-language-pack/python-tags.scm +0 -0
  112. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-language-pack/r-tags.scm +0 -0
  113. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-language-pack/racket-tags.scm +0 -0
  114. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-language-pack/ruby-tags.scm +0 -0
  115. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-language-pack/rust-tags.scm +0 -0
  116. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-language-pack/solidity-tags.scm +0 -0
  117. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-language-pack/swift-tags.scm +0 -0
  118. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-language-pack/udev-tags.scm +0 -0
  119. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-languages/README.md +0 -0
  120. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-languages/c-tags.scm +0 -0
  121. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-languages/c_sharp-tags.scm +0 -0
  122. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-languages/cpp-tags.scm +0 -0
  123. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-languages/dart-tags.scm +0 -0
  124. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-languages/elisp-tags.scm +0 -0
  125. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-languages/elixir-tags.scm +0 -0
  126. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-languages/elm-tags.scm +0 -0
  127. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-languages/go-tags.scm +0 -0
  128. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-languages/hcl-tags.scm +0 -0
  129. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-languages/java-tags.scm +0 -0
  130. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-languages/javascript-tags.scm +0 -0
  131. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-languages/kotlin-tags.scm +0 -0
  132. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-languages/ocaml-tags.scm +0 -0
  133. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-languages/php-tags.scm +0 -0
  134. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-languages/python-tags.scm +0 -0
  135. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-languages/ql-tags.scm +0 -0
  136. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-languages/ruby-tags.scm +0 -0
  137. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-languages/rust-tags.scm +0 -0
  138. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-languages/scala-tags.scm +0 -0
  139. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/queries/tree-sitter-languages/typescript-tags.scm +0 -0
  140. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/tools/__init__.py +0 -0
  141. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/tools/completion.py +0 -0
  142. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/tools/edit_file.py +0 -0
  143. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/tools/planner.py +0 -0
  144. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/tools/repomap.py +0 -0
  145. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/tools/request_input.py +0 -0
  146. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/tools/screenshot.py +0 -0
  147. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/tools/search_arxiv.py +0 -0
  148. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm/utils.py +0 -0
  149. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm.egg-info/dependency_links.txt +0 -0
  150. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm.egg-info/requires.txt +0 -0
  151. {beswarm-0.2.34 → beswarm-0.2.36}/beswarm.egg-info/top_level.txt +0 -0
  152. {beswarm-0.2.34 → beswarm-0.2.36}/setup.cfg +0 -0
  153. {beswarm-0.2.34 → beswarm-0.2.36}/test/test_TaskManager.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: beswarm
3
- Version: 0.2.34
3
+ Version: 0.2.36
4
4
  Summary: MAS
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -4,7 +4,7 @@ from setuptools import setup, find_packages
4
4
 
5
5
  setup(
6
6
  name="aient",
7
- version="1.1.52",
7
+ version="1.1.54",
8
8
  description="Aient: The Awakening of Agent.",
9
9
  long_description=Path.open(Path("README.md"), encoding="utf-8").read(),
10
10
  long_description_content_type="text/markdown",
@@ -74,9 +74,8 @@ async def get_gemini_payload(request, engine, provider, api_key=None):
74
74
  content.append(image_message)
75
75
  elif msg.content:
76
76
  content = [{"text": msg.content}]
77
- tool_calls = msg.tool_calls
78
77
  elif msg.content is None:
79
- continue
78
+ tool_calls = msg.tool_calls
80
79
 
81
80
  if tool_calls:
82
81
  tool_call = tool_calls[0]
@@ -110,7 +109,7 @@ async def get_gemini_payload(request, engine, provider, api_key=None):
110
109
  }]
111
110
  }
112
111
  )
113
- elif msg.role != "system":
112
+ elif msg.role != "system" and content:
114
113
  messages.append({"role": msg.role, "parts": content})
115
114
  elif msg.role == "system":
116
115
  content[0]["text"] = re.sub(r"_+", "_", content[0]["text"])
@@ -409,8 +408,9 @@ async def get_vertex_gemini_payload(request, engine, provider, api_key=None):
409
408
  elif item.type == "image_url" and provider.get("image", True):
410
409
  image_message = await get_image_message(item.image_url.url, engine)
411
410
  content.append(image_message)
412
- else:
411
+ elif msg.content:
413
412
  content = [{"text": msg.content}]
413
+ elif msg.content is None:
414
414
  tool_calls = msg.tool_calls
415
415
 
416
416
  if tool_calls:
@@ -445,7 +445,7 @@ async def get_vertex_gemini_payload(request, engine, provider, api_key=None):
445
445
  }]
446
446
  }
447
447
  )
448
- elif msg.role != "system":
448
+ elif msg.role != "system" and content:
449
449
  messages.append({"role": msg.role, "parts": content})
450
450
  elif msg.role == "system":
451
451
  system_prompt = system_prompt + "\n\n" + content[0]["text"]
@@ -1354,7 +1354,8 @@ async def get_openrouter_payload(request, engine, provider, api_key=None):
1354
1354
 
1355
1355
  messages = []
1356
1356
  for msg in request.messages:
1357
- name = None
1357
+ tool_calls = None
1358
+ tool_call_id = None
1358
1359
  if isinstance(msg.content, list):
1359
1360
  content = []
1360
1361
  for item in msg.content:
@@ -1366,9 +1367,25 @@ async def get_openrouter_payload(request, engine, provider, api_key=None):
1366
1367
  content.append(image_message)
1367
1368
  else:
1368
1369
  content = msg.content
1369
- name = msg.name
1370
- if name:
1371
- messages.append({"role": msg.role, "name": name, "content": content})
1370
+ tool_calls = msg.tool_calls
1371
+ tool_call_id = msg.tool_call_id
1372
+
1373
+ if tool_calls:
1374
+ tool_calls_list = []
1375
+ for tool_call in tool_calls:
1376
+ tool_calls_list.append({
1377
+ "id": tool_call.id,
1378
+ "type": tool_call.type,
1379
+ "function": {
1380
+ "name": tool_call.function.name,
1381
+ "arguments": tool_call.function.arguments
1382
+ }
1383
+ })
1384
+ if provider.get("tools"):
1385
+ messages.append({"role": msg.role, "tool_calls": tool_calls_list})
1386
+ elif tool_call_id:
1387
+ if provider.get("tools"):
1388
+ messages.append({"role": msg.role, "tool_call_id": tool_call_id, "content": content})
1372
1389
  else:
1373
1390
  # print("content", content)
1374
1391
  if isinstance(content, list):
@@ -20,6 +20,35 @@ async def check_response(response, error_log):
20
20
  return {"error": f"{error_log} HTTP Error", "status_code": response.status_code, "details": error_json}
21
21
  return None
22
22
 
23
+ def gemini_json_poccess(response_str):
24
+ promptTokenCount = 0
25
+ candidatesTokenCount = 0
26
+ totalTokenCount = 0
27
+ image_base64 = None
28
+
29
+ response_json = json.loads(response_str)
30
+ json_data = safe_get(response_json, "candidates", 0, "content", default=None)
31
+ finishReason = safe_get(response_json, "candidates", 0 , "finishReason", default=None)
32
+ if finishReason:
33
+ promptTokenCount = safe_get(response_json, "usageMetadata", "promptTokenCount", default=0)
34
+ candidatesTokenCount = safe_get(response_json, "usageMetadata", "candidatesTokenCount", default=0)
35
+ totalTokenCount = safe_get(response_json, "usageMetadata", "totalTokenCount", default=0)
36
+
37
+ content = safe_get(json_data, "parts", 0, "text", default="")
38
+ b64_json = safe_get(json_data, "parts", 0, "inlineData", "data", default="")
39
+ if b64_json:
40
+ image_base64 = b64_json
41
+
42
+ is_thinking = safe_get(json_data, "parts", 0, "thought", default=False)
43
+
44
+ function_call_name = safe_get(json_data, "functionCall", "name", default=None)
45
+ function_full_response = safe_get(json_data, "functionCall", "args", default="")
46
+ function_full_response = json.dumps(function_full_response) if function_full_response else None
47
+
48
+ blockReason = safe_get(json_data, 0, "promptFeedback", "blockReason", default=None)
49
+
50
+ return is_thinking, content, image_base64, function_call_name, function_full_response, finishReason, blockReason, promptTokenCount, candidatesTokenCount, totalTokenCount
51
+
23
52
  async def fetch_gemini_response_stream(client, url, headers, payload, model):
24
53
  timestamp = int(datetime.timestamp(datetime.now()))
25
54
  async with client.stream('POST', url, headers=headers, json=payload) as response:
@@ -28,131 +57,54 @@ async def fetch_gemini_response_stream(client, url, headers, payload, model):
28
57
  yield error_message
29
58
  return
30
59
  buffer = ""
31
- cache_buffer = ""
32
- revicing_function_call = False
33
- function_full_response = "{"
34
- need_function_call = False
35
- is_finish = False
36
60
  promptTokenCount = 0
37
61
  candidatesTokenCount = 0
38
62
  totalTokenCount = 0
39
63
  parts_json = ""
40
- image_base64 = ""
41
- # line_index = 0
42
- # last_text_line = 0
43
- # if "thinking" in model:
44
- # is_thinking = True
45
- # else:
46
- # is_thinking = False
47
64
  async for chunk in response.aiter_text():
48
65
  buffer += chunk
49
- cache_buffer += chunk
50
66
 
51
67
  while "\n" in buffer:
52
68
  line, buffer = buffer.split("\n", 1)
53
- # line_index += 1
54
69
  if line.startswith("data: "):
55
- json_line = line.lstrip("data: ").strip()
56
- response_json = json.loads(json_line)
57
- json_data = safe_get(response_json, "candidates", 0, "content", default=None)
58
- finishReason = safe_get(response_json, "candidates", 0 , "finishReason", default=None)
59
- if finishReason:
60
- promptTokenCount = safe_get(response_json, "usageMetadata", "promptTokenCount", default=0)
61
- candidatesTokenCount = safe_get(response_json, "usageMetadata", "candidatesTokenCount", default=0)
62
- totalTokenCount = safe_get(response_json, "usageMetadata", "totalTokenCount", default=0)
63
-
64
- content = safe_get(json_data, "parts", 0, "text", default="")
65
- b64_json = safe_get(json_data, "parts", 0, "inlineData", "data", default="")
66
- if b64_json:
67
- image_base64 = b64_json
68
-
69
- is_thinking = safe_get(json_data, "parts", 0, "thought", default=False)
70
- if is_thinking:
71
- sse_string = await generate_sse_response(timestamp, model, reasoning_content=content)
72
- yield sse_string
73
- elif not image_base64 and content:
74
- sse_string = await generate_sse_response(timestamp, model, content=content)
75
- yield sse_string
76
-
77
- continue
78
-
79
- # https://ai.google.dev/api/generate-content?hl=zh-cn#FinishReason
80
- if line and '\"finishReason\": \"' in line:
81
- if "stop" not in line.lower():
82
- logger.error(f"finishReason: {line}")
83
- is_finish = True
84
- if is_finish and '\"promptTokenCount\": ' in line:
85
- json_data = parse_json_safely( "{" + line + "}")
86
- promptTokenCount = json_data.get('promptTokenCount', 0)
87
- if is_finish and '\"candidatesTokenCount\": ' in line:
88
- json_data = parse_json_safely( "{" + line + "}")
89
- candidatesTokenCount = json_data.get('candidatesTokenCount', 0)
90
- if is_finish and '\"totalTokenCount\": ' in line:
91
- json_data = parse_json_safely( "{" + line + "}")
92
- totalTokenCount = json_data.get('totalTokenCount', 0)
93
-
94
- if (line and '"parts": [' in line or parts_json != "") and is_finish == False:
70
+ parts_json = line.lstrip("data: ").strip()
71
+ else:
95
72
  parts_json += line
96
- if parts_json != "" and line and '],' == line.strip():
97
- # tmp_parts_json = "{" + parts_json.split("} ] },")[0].strip().rstrip("}], ").replace("\n", "\\n").lstrip("{") + "}]}"
98
- tmp_parts_json = "{" + parts_json.split("} ] },")[0].strip().rstrip("}], ").replace("\n", "\\n").lstrip("{")
99
- if "inlineData" in tmp_parts_json:
100
- tmp_parts_json = tmp_parts_json + "}}]}"
101
- else:
102
- tmp_parts_json = tmp_parts_json + "}]}"
73
+ parts_json = parts_json.lstrip("[,")
103
74
  try:
104
- json_data = json.loads(tmp_parts_json)
105
-
106
- content = safe_get(json_data, "parts", 0, "text", default="")
107
- b64_json = safe_get(json_data, "parts", 0, "inlineData", "data", default="")
108
- if b64_json:
109
- image_base64 = b64_json
110
-
111
- is_thinking = safe_get(json_data, "parts", 0, "thought", default=False)
112
- if is_thinking:
113
- sse_string = await generate_sse_response(timestamp, model, reasoning_content=content)
114
- yield sse_string
115
- elif not image_base64 and content:
116
- sse_string = await generate_sse_response(timestamp, model, content=content)
117
- yield sse_string
75
+ json.loads(parts_json)
118
76
  except json.JSONDecodeError:
119
- logger.error(f"无法解析JSON: {parts_json}")
120
- parts_json = ""
121
-
122
- if line and ('\"functionCall\": {' in line or revicing_function_call):
123
- revicing_function_call = True
124
- need_function_call = True
125
- if ']' in line:
126
- revicing_function_call = False
127
77
  continue
128
78
 
129
- function_full_response += line
79
+ # https://ai.google.dev/api/generate-content?hl=zh-cn#FinishReason
80
+ is_thinking, content, image_base64, function_call_name, function_full_response, finishReason, blockReason, promptTokenCount, candidatesTokenCount, totalTokenCount = gemini_json_poccess(parts_json)
130
81
 
131
- if image_base64:
132
- yield await generate_no_stream_response(timestamp, model, content=content, tools_id=None, function_call_name=None, function_call_content=None, role=None, total_tokens=totalTokenCount, prompt_tokens=promptTokenCount, completion_tokens=candidatesTokenCount, image_base64=image_base64)
133
- return
82
+ if is_thinking:
83
+ sse_string = await generate_sse_response(timestamp, model, reasoning_content=content)
84
+ yield sse_string
85
+ elif not image_base64 and content:
86
+ sse_string = await generate_sse_response(timestamp, model, content=content)
87
+ yield sse_string
134
88
 
135
- if need_function_call:
136
- function_call = json.loads(function_full_response)
137
- function_call_name = function_call["functionCall"]["name"]
138
- sse_string = await generate_sse_response(timestamp, model, content=None, tools_id="chatcmpl-9inWv0yEtgn873CxMBzHeCeiHctTV", function_call_name=function_call_name)
139
- yield sse_string
140
- function_full_response = json.dumps(function_call["functionCall"]["args"])
141
- sse_string = await generate_sse_response(timestamp, model, content=None, tools_id="chatcmpl-9inWv0yEtgn873CxMBzHeCeiHctTV", function_call_name=None, function_call_content=function_full_response)
142
- yield sse_string
89
+ if image_base64:
90
+ yield await generate_no_stream_response(timestamp, model, content=content, tools_id=None, function_call_name=None, function_call_content=None, role=None, total_tokens=totalTokenCount, prompt_tokens=promptTokenCount, completion_tokens=candidatesTokenCount, image_base64=image_base64)
143
91
 
144
- cache_buffer_json = {}
145
- try:
146
- cache_buffer_json = json.loads(cache_buffer)
147
- except json.JSONDecodeError:
148
- cache_buffer_json = {}
92
+ if function_call_name:
93
+ sse_string = await generate_sse_response(timestamp, model, content=None, tools_id="chatcmpl-9inWv0yEtgn873CxMBzHeCeiHctTV", function_call_name=function_call_name)
94
+ yield sse_string
95
+ if function_full_response:
96
+ sse_string = await generate_sse_response(timestamp, model, content=None, tools_id="chatcmpl-9inWv0yEtgn873CxMBzHeCeiHctTV", function_call_name=None, function_call_content=function_full_response)
97
+ yield sse_string
149
98
 
150
- if cache_buffer == "[]" or safe_get(cache_buffer_json, 0, "promptFeedback", "blockReason") == "PROHIBITED_CONTENT":
151
- sse_string = await generate_sse_response(timestamp, model, stop="PROHIBITED_CONTENT")
152
- yield sse_string
153
- else:
154
- sse_string = await generate_sse_response(timestamp, model, stop="stop")
155
- yield sse_string
99
+ if parts_json == "[]" or blockReason == "PROHIBITED_CONTENT":
100
+ sse_string = await generate_sse_response(timestamp, model, stop="PROHIBITED_CONTENT")
101
+ yield sse_string
102
+ elif finishReason:
103
+ sse_string = await generate_sse_response(timestamp, model, stop="stop")
104
+ yield sse_string
105
+ break
106
+
107
+ parts_json = ""
156
108
 
157
109
  sse_string = await generate_sse_response(timestamp, model, None, None, None, None, None, totalTokenCount, promptTokenCount, candidatesTokenCount)
158
110
  yield sse_string
@@ -187,7 +187,8 @@ class chatgpt(BaseLLM):
187
187
  # print(json.dumps(replaced_text, indent=4, ensure_ascii=False))
188
188
  while message_index < conversation_len:
189
189
  if self.conversation[convo_id][message_index]["role"] == self.conversation[convo_id][message_index + 1]["role"]:
190
- if self.conversation[convo_id][message_index].get("content") and self.conversation[convo_id][message_index + 1].get("content"):
190
+ if self.conversation[convo_id][message_index].get("content") and self.conversation[convo_id][message_index + 1].get("content") \
191
+ and self.conversation[convo_id][message_index].get("content") != self.conversation[convo_id][message_index + 1].get("content"):
191
192
  if type(self.conversation[convo_id][message_index + 1]["content"]) == str \
192
193
  and type(self.conversation[convo_id][message_index]["content"]) == list:
193
194
  self.conversation[convo_id][message_index + 1]["content"] = [{"type": "text", "text": self.conversation[convo_id][message_index + 1]["content"]}]
@@ -754,8 +755,8 @@ class chatgpt(BaseLLM):
754
755
 
755
756
  # 打印日志
756
757
  if self.print_log:
757
- print("api_url", kwargs.get('api_url', self.api_url.chat_url) == url)
758
- print("api_url", kwargs.get('api_url', self.api_url.chat_url))
758
+ # print("api_url", kwargs.get('api_url', self.api_url.chat_url) == url)
759
+ # print("api_url", kwargs.get('api_url', self.api_url.chat_url))
759
760
  print("api_url", url)
760
761
  # print("headers", headers)
761
762
  print("api_key", kwargs.get('api_key', self.api_key))
@@ -0,0 +1,235 @@
1
+ """
2
+ 使用 Reaktiv 模拟消息队列 (发布/订阅)
3
+
4
+ 本模块提供了一个 MessageBroker 类,它利用 Reaktiv 的核心原语(Signal, Computed, Effect)
5
+ 来构建一个功能类似消息队列的、内存中的发布/订阅系统。
6
+ """
7
+ import asyncio
8
+ from typing import Callable, Any, List, Union, Tuple
9
+
10
+ from reaktiv import Signal, Effect, Computed, untracked, to_async_iter
11
+
12
+ class Subscription:
13
+ """封装一个或多个 Effect,提供统一的暂停、恢复和取消订阅的接口。"""
14
+
15
+ def __init__(
16
+ self,
17
+ broker: "MessageBroker",
18
+ callback: Callable[[Any], None],
19
+ effects_with_topics: List[Tuple[Effect, str]],
20
+ ):
21
+ self._broker = broker
22
+ self._callback = callback
23
+ self._effects_with_topics = effects_with_topics
24
+ self._effects = [e for e, t in effects_with_topics]
25
+ self.is_paused = Signal(False)
26
+
27
+ def pause(self):
28
+ """暂停订阅,将不再处理新消息。"""
29
+ self.is_paused.set(True)
30
+ if self._broker.debug:
31
+ print(f"Subscription paused.")
32
+
33
+ def resume(self):
34
+ """恢复订阅,将继续处理新消息。"""
35
+ self.is_paused.set(False)
36
+ if self._broker.debug:
37
+ print(f"Subscription resumed.")
38
+
39
+ def dispose(self):
40
+ """永久取消订阅并清理资源。"""
41
+ for effect, topic in self._effects_with_topics:
42
+ effect.dispose()
43
+ # 从代理的注册表中移除
44
+ if (
45
+ topic in self._broker._effects_registry
46
+ and self._callback in self._broker._effects_registry[topic]
47
+ ):
48
+ del self._broker._effects_registry[topic][self._callback]
49
+ if not self._broker._effects_registry[topic]:
50
+ del self._broker._effects_registry[topic]
51
+ if self._broker.debug:
52
+ print(f"Subscription disposed.")
53
+
54
+
55
+ class MessageBroker:
56
+ """一个简单的消息代理,使用 Reaktiv Signal 和 Computed 模拟消息队列和派生主题。"""
57
+
58
+ def __init__(self, debug: bool = False):
59
+ # 现在 _topics 可以存储 Signal (原始主题) 或 Computed (派生主题)。
60
+ self._topics: dict[str, Union[Signal[List[Any]], Computed[List[Any]]]] = {}
61
+ # 新增: 注册表来跟踪 (主题, 回调) -> Effect 的映射
62
+ self._effects_registry: dict[str, dict[Callable, Effect]] = {}
63
+ self.debug = debug
64
+ self._channel_counters: dict[str, int] = {}
65
+ # print("消息代理已启动。")
66
+
67
+ def request_channel(self, prefix: str = "channel") -> str:
68
+ """
69
+ 申请一个新的、唯一的频道名称。
70
+
71
+ 此方法为每个前缀维护一个独立的计数器。
72
+ 返回一个基于前缀和该前缀当前计数值的唯一字符串,例如 'channel0', 'worker_0', 'channel1'。
73
+ 它不直接创建主题或任何关联的 Signal;这将在首次发布或订阅到返回的主题名称时发生。
74
+
75
+ Args:
76
+ prefix: 频道名称的前缀。默认为 'channel'。
77
+
78
+ Returns:
79
+ 一个基于前缀的唯一主题/频道名称字符串。
80
+ """
81
+ if prefix not in self._channel_counters:
82
+ self._channel_counters[prefix] = 0
83
+
84
+ channel_name = f"{prefix}{self._channel_counters[prefix]}"
85
+ self._channel_counters[prefix] += 1
86
+ return channel_name
87
+
88
+ def publish(self, message: Any, topic: Union[str, List[str]] = "default"):
89
+ """
90
+ 向一个或多个主题发布一条新消息。
91
+ """
92
+ topics_to_publish = [topic] if isinstance(topic, str) else topic
93
+
94
+ for t in topics_to_publish:
95
+ # 只能向原始主题发布
96
+ topic_signal = self._topics.get(t)
97
+ if not isinstance(topic_signal, Signal):
98
+ print(f"警告:主题 '{t}' 不存在或不是一个可发布的原始主题。正在创建...")
99
+ topic_signal = Signal([])
100
+ self._topics[t] = topic_signal
101
+
102
+ # 通过 update 方法追加新消息来触发更新。
103
+ # 必须创建一个新列表才能让 Reaktiv 检测到变化。
104
+ topic_signal.update(lambda messages: messages + [message])
105
+ if self.debug:
106
+ print(f"新消息发布到 '{t}': \"{message}\"")
107
+
108
+ def subscribe(self, callback: Callable[[Any], None], topic: Union[str, List[str]] = "default") -> Subscription:
109
+ """
110
+ 订阅一个或多个主题。每当有新消息发布时,回调函数将被调用。
111
+ 此方法是幂等的:重复订阅同一个回调到同一个主题不会产生副作用。
112
+
113
+ Args:
114
+ callback: 处理消息的回调函数。
115
+ topic: 要订阅的主题,可以是单个字符串或字符串列表。
116
+
117
+ Returns:
118
+ 一个 Subscription 实例,用于管理订阅的生命周期(暂停、恢复、取消)。
119
+ """
120
+ topics_to_subscribe = [topic] if isinstance(topic, str) else topic
121
+ created_effects_with_topics = []
122
+
123
+ # 创建一个 Subscription 实例来管理所有相关的 effects
124
+ # 它需要提前创建,以便 effect_factory 可以访问它的 is_paused 信号
125
+ subscription = Subscription(self, callback, created_effects_with_topics)
126
+
127
+ for t in topics_to_subscribe:
128
+ # 检查此回调是否已订阅该主题
129
+ if t in self._effects_registry and callback in self._effects_registry.get(t, {}):
130
+ print(f"警告:订阅者 '{callback.__name__}' 已经订阅了 '{t}' 主题。跳过。")
131
+ continue
132
+
133
+ if t not in self._topics:
134
+ # 如果订阅一个不存在的主题,也为它创建一个 Signal
135
+ self._topics[t] = Signal([])
136
+
137
+ # 使用一个工厂函数来为每个主题创建独立的闭包,
138
+ # 确保每个订阅都有自己的 'last_processed_index'。
139
+ def effect_factory(current_topic: str):
140
+ last_processed_index = 0
141
+
142
+ def process_new_messages():
143
+ nonlocal last_processed_index
144
+ all_messages = self._topics[current_topic]()
145
+
146
+ # 如果暂停了,只更新索引以跳过消息,不进行处理
147
+ if untracked(subscription.is_paused):
148
+ last_processed_index = len(all_messages)
149
+ return
150
+
151
+ new_messages = all_messages[last_processed_index:]
152
+
153
+ if new_messages:
154
+ if self.debug:
155
+ print(f" -> 订阅者 '{callback.__name__}' 在 '{current_topic}' 主题上收到 {len(new_messages)} 条新消息。")
156
+ for msg in new_messages:
157
+ try:
158
+ if asyncio.iscoroutinefunction(callback):
159
+ asyncio.create_task(callback(msg))
160
+ else:
161
+ callback(msg)
162
+ except Exception as e:
163
+ print(f" !! 在订阅者 '{callback.__name__}' 中发生错误: {e}")
164
+
165
+ last_processed_index = len(all_messages)
166
+ return process_new_messages
167
+
168
+ if self.debug:
169
+ print(f"订阅者 '{callback.__name__}' 已订阅 '{t}' 主题。")
170
+ effect = Effect(effect_factory(t))
171
+
172
+ # 注册新的 effect
173
+ if t not in self._effects_registry:
174
+ self._effects_registry[t] = {}
175
+ self._effects_registry[t][callback] = effect
176
+
177
+ created_effects_with_topics.append((effect, t))
178
+
179
+ return subscription
180
+
181
+ def create_derived_topic(self, new_topic_name: str, source_topic: str, transform_fn: Callable[[List[Any]], List[Any]]):
182
+ """
183
+ 创建一个派生主题。
184
+
185
+ 这个新主题的内容是一个 Computed 信号,它会根据源主题的内容和转换函数自动更新。
186
+
187
+ Args:
188
+ new_topic_name: 派生主题的名称。
189
+ source_topic: 源主题的名称。
190
+ transform_fn: 一个函数,接收源主题的消息列表并返回新的消息列表。
191
+ """
192
+ if new_topic_name in self._topics:
193
+ print(f"警告:主题 '{new_topic_name}' 已存在。")
194
+ return
195
+
196
+ source_signal = self._topics.get(source_topic)
197
+ if not isinstance(source_signal, (Signal, Computed)):
198
+ print(f"错误:源主题 '{source_topic}' 不存在。")
199
+ return
200
+
201
+ # 创建一个 Computed 信号作为派生主题
202
+ derived_signal = Computed(
203
+ lambda: transform_fn(source_signal())
204
+ )
205
+
206
+ self._topics[new_topic_name] = derived_signal
207
+ if self.debug:
208
+ print(f"已从 '{source_topic}' 创建派生主题 '{new_topic_name}'。")
209
+
210
+ async def iter_topic(self, topic: str):
211
+ """
212
+ 返回一个异步迭代器,用于通过 async for 循环消费主题消息。
213
+
214
+ Args:
215
+ topic: 要订阅的主题名称。
216
+
217
+ Yields:
218
+ 主题中的新消息。
219
+ """
220
+ if topic not in self._topics:
221
+ # 如果主题不存在,创建一个,以防万一
222
+ self._topics[topic] = Signal([])
223
+
224
+ topic_signal = self._topics[topic]
225
+ last_yielded_index = 0
226
+
227
+ # to_async_iter 会在每次 topic_signal 更新时产生一个新的消息列表
228
+ async for all_messages in to_async_iter(topic_signal):
229
+ new_messages = all_messages[last_yielded_index:]
230
+ for msg in new_messages:
231
+ # 过滤掉内部的 'init' 消息
232
+ if msg != "init":
233
+ yield msg
234
+
235
+ last_yielded_index = len(all_messages)
@@ -223,6 +223,7 @@ Returns:
223
223
 
224
224
  # 工作agent初始化
225
225
  click_agent = chatgpt(**click_agent_config)
226
+ # https://developers.googleblog.com/en/conversational-image-segmentation-gemini-2-5/
226
227
  prompt = f"Give the segmentation masks for the {target_element}. Output a JSON list of segmentation masks where each entry contains the 2D bounding box in \"box_2d\" (format: ymin, xmin, ymax, xmax) and the mask in \"mask\". Only output the one that meets the criteria the most."
227
228
 
228
229
  print("正在截取当前屏幕...")
@@ -2,9 +2,7 @@ import re
2
2
  import os
3
3
  import json
4
4
  import httpx
5
- from urllib.parse import quote_plus
6
5
  import threading
7
- import time
8
6
 
9
7
  from ..aient.src.aient.plugins import register_tool, get_url_content # Assuming a similar plugin structure
10
8
 
@@ -364,7 +362,7 @@ if __name__ == '__main__':
364
362
  # search_query = "美国"
365
363
  # search_query = "machine learning models for higher heating value prediction using proximate vs ultimate analysis"
366
364
  # search_query = "patent driver cognitive load monitoring micro-expression thermal imaging fusion"
367
- search_query = "Self-supervised learning for seismology"
365
+ search_query = "deep learning models for siRNA activity"
368
366
  print(f"Performing web search for: '{search_query}'")
369
367
  results = await search_web(search_query) # results is a list of URLs
370
368
 
@@ -34,6 +34,7 @@ class TaskManager:
34
34
  self.task_cache_file.touch(exist_ok=True)
35
35
  self.read_tasks_cache()
36
36
  self.set_task_cache("root_path", str(self.root_path))
37
+ self.resume_all_running_task()
37
38
 
38
39
  def set_task_cache(self, *keys_and_value):
39
40
  """
@@ -86,6 +87,12 @@ class TaskManager:
86
87
  self.set_task_cache(task_id, "status", TaskStatus.RUNNING.value)
87
88
  return task_ids
88
89
 
90
+ def resume_all_running_task(self):
91
+ running_task_id_list = [task_id for task_id, task in self.tasks_cache.items() if task_id != "root_path" and task.get("status") == "RUNNING"]
92
+ for task_id in running_task_id_list:
93
+ tasks_params = self.tasks_cache[task_id]["args"]
94
+ task_id = task_manager.resume_task(task_id, worker_fun, tasks_params)
95
+
89
96
  def resume_task(self, task_id, task_coro, args):
90
97
  """
91
98
  恢复一个任务。
@@ -95,7 +102,7 @@ class TaskManager:
95
102
  return TaskStatus.NOT_FOUND
96
103
 
97
104
  coro = task_coro(**args)
98
- task_id = self.create_task(coro)
105
+ task_id = self.create_task(coro, task_id)
99
106
  self.set_task_cache(task_id, "args", args)
100
107
  self.set_task_cache(task_id, "status", TaskStatus.RUNNING.value)
101
108
  print(f"任务已恢复: ID={task_id}, Name={task_id}")
@@ -103,7 +110,7 @@ class TaskManager:
103
110
  print(f"self.tasks_cache: {json.dumps(self.tasks_cache, ensure_ascii=False, indent=4)}")
104
111
  return task_id
105
112
 
106
- def create_task(self, coro):
113
+ def create_task(self, coro, task_id=None):
107
114
  """
108
115
  创建并注册一个新任务。
109
116
 
@@ -114,7 +121,8 @@ class TaskManager:
114
121
  Returns:
115
122
  str: 任务的唯一ID。
116
123
  """
117
- task_id = str(uuid.uuid4())
124
+ if task_id == None:
125
+ task_id = str(uuid.uuid4())
118
126
  task_name = f"Task-{task_id[:8]}"
119
127
 
120
128
  # 使用 asyncio.create_task() 创建任务
@@ -233,7 +241,7 @@ def create_task(goal, tools, work_dir):
233
241
  Args:
234
242
  goal (str): 需要完成的具体任务目标描述。子任务将围绕此目标进行工作。必须清晰、具体。必须包含背景信息,完成指标等。写清楚什么时候算任务完成,同时交代清楚任务的背景信息,这个背景信息可以是需要读取的文件等一切有助于完成任务的信息。
235
243
  tools (list[str]): 一个包含可用工具函数对象的列表。子任务在执行任务时可能会调用这些工具来与环境交互(例如读写文件、执行命令等)。
236
- work_dir (str): 工作目录的绝对路径。子任务将在此目录上下文中执行操作。子任务的工作目录位置在主任务的工作目录的子目录。
244
+ work_dir (str): 工作目录的绝对路径。子任务将在此目录上下文中执行操作。子任务的工作目录位置在主任务的工作目录的子目录。子任务工作目录**禁止**设置为主任务目录本身。
237
245
 
238
246
  Returns:
239
247
  str: 当任务成功完成时,返回字符串 "任务已完成"。