beswarm 0.2.36__tar.gz → 0.2.38__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.
- {beswarm-0.2.36 → beswarm-0.2.38}/PKG-INFO +1 -1
- beswarm-0.2.38/beswarm/agents/chatgroup.py +275 -0
- beswarm-0.2.36/beswarm/tools/worker.py → beswarm-0.2.38/beswarm/agents/planact.py +5 -33
- beswarm-0.2.38/beswarm/core.py +11 -0
- {beswarm-0.2.36/beswarm/tools → beswarm-0.2.38/beswarm}/taskmanager.py +3 -81
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/tools/__init__.py +32 -33
- beswarm-0.2.38/beswarm/tools/subtasks.py +77 -0
- beswarm-0.2.38/beswarm/tools/worker.py +41 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm.egg-info/PKG-INFO +1 -1
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm.egg-info/SOURCES.txt +5 -1
- {beswarm-0.2.36 → beswarm-0.2.38}/pyproject.toml +1 -1
- {beswarm-0.2.36 → beswarm-0.2.38}/MANIFEST.in +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/README.md +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/__init__.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/main.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/setup.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/src/aient/__init__.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/src/aient/core/__init__.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/src/aient/core/log_config.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/src/aient/core/models.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/src/aient/core/request.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/src/aient/core/response.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/src/aient/core/test/test_base_api.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/src/aient/core/test/test_geminimask.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/src/aient/core/test/test_image.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/src/aient/core/test/test_payload.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/src/aient/core/utils.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/src/aient/models/__init__.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/src/aient/models/audio.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/src/aient/models/base.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/src/aient/models/chatgpt.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/src/aient/models/claude.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/src/aient/models/duckduckgo.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/src/aient/models/gemini.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/src/aient/models/groq.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/src/aient/models/vertex.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/src/aient/plugins/__init__.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/src/aient/plugins/arXiv.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/src/aient/plugins/config.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/src/aient/plugins/excute_command.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/src/aient/plugins/get_time.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/src/aient/plugins/image.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/src/aient/plugins/list_directory.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/src/aient/plugins/read_file.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/src/aient/plugins/read_image.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/src/aient/plugins/readonly.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/src/aient/plugins/registry.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/src/aient/plugins/run_python.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/src/aient/plugins/websearch.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/src/aient/plugins/write_file.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/src/aient/utils/__init__.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/src/aient/utils/prompt.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/src/aient/utils/scripts.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/test/chatgpt.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/test/claude.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/test/test.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/test/test_API.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/test/test_Deepbricks.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/test/test_Web_crawler.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/test/test_aiwaves.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/test/test_aiwaves_arxiv.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/test/test_ask_gemini.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/test/test_class.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/test/test_claude.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/test/test_claude_zh_char.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/test/test_ddg_search.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/test/test_download_pdf.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/test/test_gemini.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/test/test_get_token_dict.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/test/test_google_search.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/test/test_jieba.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/test/test_json.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/test/test_logging.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/test/test_ollama.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/test/test_plugin.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/test/test_py_run.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/test/test_requests.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/test/test_search.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/test/test_tikitoken.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/test/test_token.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/test/test_url.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/test/test_whisper.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/test/test_wildcard.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/aient/test/test_yjh.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/bemcp/bemcp/__init__.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/bemcp/bemcp/decorator.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/bemcp/bemcp/main.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/bemcp/bemcp/utils.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/bemcp/test/client.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/bemcp/test/server.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/broker.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/prompt.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-language-pack/README.md +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-language-pack/arduino-tags.scm +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-language-pack/c-tags.scm +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-language-pack/chatito-tags.scm +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-language-pack/commonlisp-tags.scm +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-language-pack/cpp-tags.scm +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-language-pack/csharp-tags.scm +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-language-pack/d-tags.scm +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-language-pack/dart-tags.scm +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-language-pack/elisp-tags.scm +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-language-pack/elixir-tags.scm +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-language-pack/elm-tags.scm +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-language-pack/gleam-tags.scm +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-language-pack/go-tags.scm +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-language-pack/java-tags.scm +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-language-pack/javascript-tags.scm +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-language-pack/lua-tags.scm +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-language-pack/pony-tags.scm +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-language-pack/properties-tags.scm +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-language-pack/python-tags.scm +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-language-pack/r-tags.scm +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-language-pack/racket-tags.scm +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-language-pack/ruby-tags.scm +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-language-pack/rust-tags.scm +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-language-pack/solidity-tags.scm +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-language-pack/swift-tags.scm +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-language-pack/udev-tags.scm +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-languages/README.md +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-languages/c-tags.scm +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-languages/c_sharp-tags.scm +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-languages/cpp-tags.scm +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-languages/dart-tags.scm +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-languages/elisp-tags.scm +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-languages/elixir-tags.scm +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-languages/elm-tags.scm +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-languages/go-tags.scm +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-languages/hcl-tags.scm +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-languages/java-tags.scm +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-languages/javascript-tags.scm +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-languages/kotlin-tags.scm +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-languages/ocaml-tags.scm +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-languages/php-tags.scm +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-languages/python-tags.scm +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-languages/ql-tags.scm +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-languages/ruby-tags.scm +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-languages/rust-tags.scm +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-languages/scala-tags.scm +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/queries/tree-sitter-languages/typescript-tags.scm +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/tools/click.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/tools/completion.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/tools/edit_file.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/tools/planner.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/tools/repomap.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/tools/request_input.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/tools/screenshot.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/tools/search_arxiv.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/tools/search_web.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm/utils.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm.egg-info/dependency_links.txt +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm.egg-info/requires.txt +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/beswarm.egg-info/top_level.txt +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/setup.cfg +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/test/test_TaskManager.py +0 -0
- {beswarm-0.2.36 → beswarm-0.2.38}/test/test_broker.py +0 -0
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
"""
|
|
2
|
+
A simplified, declarative implementation of the Beswarm worker agent system,
|
|
3
|
+
built using the custom MessageBroker for a high-level pub/sub architecture.
|
|
4
|
+
"""
|
|
5
|
+
import os
|
|
6
|
+
import sys
|
|
7
|
+
import uuid
|
|
8
|
+
import json
|
|
9
|
+
import copy
|
|
10
|
+
import asyncio
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from datetime import datetime
|
|
13
|
+
from typing import List, Dict, Union
|
|
14
|
+
|
|
15
|
+
from ..broker import MessageBroker
|
|
16
|
+
from ..bemcp.bemcp import MCPManager
|
|
17
|
+
from ..utils import register_mcp_tools
|
|
18
|
+
from ..aient.src.aient.models import chatgpt
|
|
19
|
+
from ..aient.src.aient.plugins import get_function_call_list, registry
|
|
20
|
+
|
|
21
|
+
worker_system_prompt = """
|
|
22
|
+
你是{name}。帮助用户头脑风暴。请分析不同用户的观点,并给出你的观点。
|
|
23
|
+
你的回答必须是 @用户名字开头。后面接上你的回复。如果你觉得无需回答的时候,请直接回复<stop>。
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
class BaseAgent:
|
|
27
|
+
"""Base class for agents, handling common initialization and disposal."""
|
|
28
|
+
def __init__(self, name: str, tools_json: List, agent_config: Dict, work_dir: str, cache_messages: Union[bool, List[Dict]], broker: MessageBroker, listen_topic: str, publish_topic: str, status_topic: str):
|
|
29
|
+
|
|
30
|
+
self.id = str(uuid.uuid4())
|
|
31
|
+
self.name = name
|
|
32
|
+
self.tools_json = tools_json
|
|
33
|
+
self.work_dir = work_dir
|
|
34
|
+
self.cache_file = Path(work_dir) / ".beswarm" / "work_agent_conversation_history.json"
|
|
35
|
+
self.config = copy.deepcopy(agent_config)
|
|
36
|
+
self.cache_messages = cache_messages
|
|
37
|
+
if cache_messages and isinstance(cache_messages, bool) and cache_messages == True:
|
|
38
|
+
self.cache_messages = json.loads(self.cache_file.read_text(encoding="utf-8"))
|
|
39
|
+
self.broker = broker
|
|
40
|
+
self.listen_topic = listen_topic
|
|
41
|
+
self.error_topic = listen_topic + ".error"
|
|
42
|
+
self.publish_topic = publish_topic
|
|
43
|
+
self.status_topic = status_topic
|
|
44
|
+
self._subscription = self.broker.subscribe(self.handle_message, [self.listen_topic, self.error_topic])
|
|
45
|
+
|
|
46
|
+
async def handle_message(self, message: Dict):
|
|
47
|
+
"""Process incoming messages. Must be implemented by subclasses."""
|
|
48
|
+
raise NotImplementedError
|
|
49
|
+
|
|
50
|
+
def dispose(self):
|
|
51
|
+
"""Cancels the subscription and cleans up resources."""
|
|
52
|
+
if self._subscription:
|
|
53
|
+
self._subscription.dispose()
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class WorkerAgent(BaseAgent):
|
|
57
|
+
"""Executes instructions and publishes results to a message broker."""
|
|
58
|
+
def __init__(self, name: str, tools_json: List, agent_config: Dict, work_dir: str, cache_messages: Union[bool, List[Dict]], broker: MessageBroker, listen_topic: str, publish_topic: str, status_topic: str):
|
|
59
|
+
super().__init__(name, tools_json, agent_config, work_dir, cache_messages, broker, listen_topic, publish_topic, status_topic)
|
|
60
|
+
|
|
61
|
+
self.config["system_prompt"] = self.config["system_prompt"].format(name=self.name)
|
|
62
|
+
self.agent = chatgpt(**self.config)
|
|
63
|
+
|
|
64
|
+
async def handle_message(self, message: Dict):
|
|
65
|
+
"""Receives an instruction, executes it, and publishes the response."""
|
|
66
|
+
|
|
67
|
+
if message.get("id") == self.id:
|
|
68
|
+
return
|
|
69
|
+
|
|
70
|
+
instruction = message["result"]
|
|
71
|
+
response = await self.agent.ask_async(instruction)
|
|
72
|
+
|
|
73
|
+
if '<stop>' in response.strip():
|
|
74
|
+
if response.replace('<stop>', '').strip() != '':
|
|
75
|
+
self.broker.publish({"id": self.id, "status": "new_message", "result": f"{self.name}: {response.replace('<stop>', '')}\n\n"}, self.status_topic)
|
|
76
|
+
return
|
|
77
|
+
|
|
78
|
+
if response.strip() == '':
|
|
79
|
+
print("\n❌ 工作智能体回复为空,请重新生成指令。")
|
|
80
|
+
self.broker.publish(message, self.error_topic)
|
|
81
|
+
else:
|
|
82
|
+
self.broker.publish({"id": self.id, "status": "new_message", "result": f"{self.name}: {response}\n\n"}, self.status_topic)
|
|
83
|
+
self.broker.publish({
|
|
84
|
+
"id": self.id,
|
|
85
|
+
"result": f"{self.name}: {response}"
|
|
86
|
+
}, self.publish_topic)
|
|
87
|
+
|
|
88
|
+
class Tee:
|
|
89
|
+
def __init__(self, *files):
|
|
90
|
+
self.files = files
|
|
91
|
+
|
|
92
|
+
def write(self, obj):
|
|
93
|
+
for f in self.files:
|
|
94
|
+
f.write(obj)
|
|
95
|
+
f.flush()
|
|
96
|
+
|
|
97
|
+
def flush(self):
|
|
98
|
+
for f in self.files:
|
|
99
|
+
f.flush()
|
|
100
|
+
|
|
101
|
+
class ChatGroupWorker:
|
|
102
|
+
"""The 'glue' class that orchestrates agents via a MessageBroker."""
|
|
103
|
+
def __init__(self, tools: List[Union[str, Dict]], work_dir: str, cache_messages: Union[bool, List[Dict]] = None, broker: MessageBroker = None, mcp_manager: MCPManager = None, task_manager = None):
|
|
104
|
+
self.tools = tools
|
|
105
|
+
self.work_dir = Path(work_dir)
|
|
106
|
+
self.cache_messages = cache_messages
|
|
107
|
+
|
|
108
|
+
self.broker = broker
|
|
109
|
+
self.mcp_manager = mcp_manager
|
|
110
|
+
self.task_manager = task_manager
|
|
111
|
+
self.task_completion_event = asyncio.Event()
|
|
112
|
+
self.final_result = None
|
|
113
|
+
self._status_subscription = None
|
|
114
|
+
self.agents = []
|
|
115
|
+
self.setup()
|
|
116
|
+
|
|
117
|
+
self.channel = self.broker.request_channel()
|
|
118
|
+
self.WORKER_RESPONSE_TOPIC = self.channel + ".worker_responses"
|
|
119
|
+
self.TASK_STATUS_TOPIC =self.channel + ".task_status"
|
|
120
|
+
|
|
121
|
+
def setup(self):
|
|
122
|
+
cache_dir = self.work_dir / ".beswarm"
|
|
123
|
+
cache_dir.mkdir(parents=True, exist_ok=True)
|
|
124
|
+
self.task_manager.set_root_path(self.work_dir)
|
|
125
|
+
self.cache_file = cache_dir / "work_agent_conversation_history.json"
|
|
126
|
+
if not self.cache_file.exists():
|
|
127
|
+
self.cache_file.write_text("[]", encoding="utf-8")
|
|
128
|
+
|
|
129
|
+
DEBUG = os.getenv("DEBUG", "false").lower() in ("true", "1", "t", "yes")
|
|
130
|
+
if DEBUG:
|
|
131
|
+
log_file = open(cache_dir / "history.log", "a", encoding="utf-8")
|
|
132
|
+
log_file.write(f"========== {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} ==========\n")
|
|
133
|
+
original_stdout = sys.stdout
|
|
134
|
+
original_stderr = sys.stderr
|
|
135
|
+
sys.stdout = Tee(original_stdout, log_file)
|
|
136
|
+
sys.stderr = Tee(original_stderr, log_file)
|
|
137
|
+
|
|
138
|
+
async def _configure_tools(self):
|
|
139
|
+
mcp_list = [item for item in self.tools if isinstance(item, dict)]
|
|
140
|
+
if mcp_list:
|
|
141
|
+
for mcp_item in mcp_list:
|
|
142
|
+
mcp_name, mcp_config = list(mcp_item.items())[0]
|
|
143
|
+
await self.mcp_manager.add_server(mcp_name, mcp_config)
|
|
144
|
+
client = self.mcp_manager.clients.get(mcp_name)
|
|
145
|
+
await register_mcp_tools(client, registry)
|
|
146
|
+
all_mcp_tools = await self.mcp_manager.get_all_tools()
|
|
147
|
+
self.tools.extend([tool.name for tool in sum(all_mcp_tools.values(), [])])
|
|
148
|
+
self.tools = [item for item in self.tools if not isinstance(item, dict)]
|
|
149
|
+
if "task_complete" not in self.tools: self.tools.append("task_complete")
|
|
150
|
+
self.tools_json = [value for _, value in get_function_call_list(self.tools).items()]
|
|
151
|
+
|
|
152
|
+
def _task_status_subscriber(self, message: Dict):
|
|
153
|
+
"""Subscriber for task status changes."""
|
|
154
|
+
if message.get("status") == "finished":
|
|
155
|
+
self.final_result = message.get("result")
|
|
156
|
+
self.task_completion_event.set()
|
|
157
|
+
|
|
158
|
+
if message.get("status") == "error":
|
|
159
|
+
raise Exception(message.get("result"))
|
|
160
|
+
|
|
161
|
+
if message.get("status") == "new_message":
|
|
162
|
+
print(message.get("result"))
|
|
163
|
+
chat_window = open(self.work_dir / ".beswarm" / "chat_window.md", "a", encoding="utf-8")
|
|
164
|
+
chat_window.write("# " + message.get("result") + "\n")
|
|
165
|
+
|
|
166
|
+
def _setup_agents(self):
|
|
167
|
+
worker_agent_config = {
|
|
168
|
+
"api_key": os.getenv("API_KEY"), "api_url": os.getenv("BASE_URL"),
|
|
169
|
+
"engine": os.getenv("FAST_MODEL") or os.getenv("MODEL"),
|
|
170
|
+
"system_prompt": worker_system_prompt,
|
|
171
|
+
"print_log": True, "temperature": 0.5, "function_call_max_loop": 100
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
chat_agent1 = WorkerAgent(
|
|
175
|
+
name="数学家", tools_json=self.tools_json, agent_config=worker_agent_config, work_dir=self.work_dir, cache_messages=self.cache_messages,
|
|
176
|
+
broker=self.broker, listen_topic=self.WORKER_RESPONSE_TOPIC,
|
|
177
|
+
publish_topic=self.WORKER_RESPONSE_TOPIC, status_topic=self.TASK_STATUS_TOPIC
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
chat_agent2 = WorkerAgent(
|
|
181
|
+
name="哲学家", tools_json=self.tools_json, agent_config=worker_agent_config, work_dir=self.work_dir, cache_messages=self.cache_messages,
|
|
182
|
+
broker=self.broker, listen_topic=self.WORKER_RESPONSE_TOPIC,
|
|
183
|
+
publish_topic=self.WORKER_RESPONSE_TOPIC, status_topic=self.TASK_STATUS_TOPIC
|
|
184
|
+
)
|
|
185
|
+
self.agents = [chat_agent1, chat_agent2]
|
|
186
|
+
|
|
187
|
+
async def run(self):
|
|
188
|
+
"""Sets up subscriptions and starts the workflow, waiting for user input."""
|
|
189
|
+
os.chdir(self.work_dir.absolute())
|
|
190
|
+
await self._configure_tools()
|
|
191
|
+
|
|
192
|
+
self._setup_agents()
|
|
193
|
+
|
|
194
|
+
self._status_subscription = self.broker.subscribe(self._task_status_subscriber, self.TASK_STATUS_TOPIC)
|
|
195
|
+
|
|
196
|
+
print("开启群聊, 请输入内容开始对话, 输入 'exit' 或 'quit' 结束:")
|
|
197
|
+
loop = asyncio.get_running_loop()
|
|
198
|
+
|
|
199
|
+
async def user_input_loop():
|
|
200
|
+
while True:
|
|
201
|
+
try:
|
|
202
|
+
line = await loop.run_in_executor(None, sys.stdin.readline)
|
|
203
|
+
line = line.strip()
|
|
204
|
+
if not line:
|
|
205
|
+
continue
|
|
206
|
+
if line.lower() in ['exit', 'quit']:
|
|
207
|
+
self.task_completion_event.set()
|
|
208
|
+
break
|
|
209
|
+
|
|
210
|
+
self.broker.publish({"id": "user", "result": f"小蓝: {line}"}, self.WORKER_RESPONSE_TOPIC)
|
|
211
|
+
self.broker.publish({"id": "user", "status": "new_message", "result": f"小蓝: {line}\n\n"}, self.TASK_STATUS_TOPIC)
|
|
212
|
+
except asyncio.CancelledError:
|
|
213
|
+
break
|
|
214
|
+
|
|
215
|
+
input_task = asyncio.create_task(user_input_loop())
|
|
216
|
+
|
|
217
|
+
await self.task_completion_event.wait()
|
|
218
|
+
|
|
219
|
+
if not input_task.done():
|
|
220
|
+
input_task.cancel()
|
|
221
|
+
|
|
222
|
+
self.cleanup()
|
|
223
|
+
return self.final_result
|
|
224
|
+
|
|
225
|
+
async def run_for_web(self):
|
|
226
|
+
"""Sets up agents and subscriptions for web-based interaction, then yields messages."""
|
|
227
|
+
os.chdir(self.work_dir.absolute())
|
|
228
|
+
await self._configure_tools()
|
|
229
|
+
self._setup_agents()
|
|
230
|
+
|
|
231
|
+
self._status_subscription = self.broker.subscribe(self._task_status_subscriber, self.TASK_STATUS_TOPIC)
|
|
232
|
+
|
|
233
|
+
print("Web chat group is running. Waiting for messages...")
|
|
234
|
+
await self.task_completion_event.wait()
|
|
235
|
+
print("Web chat group finished.")
|
|
236
|
+
self.cleanup()
|
|
237
|
+
|
|
238
|
+
def cleanup(self):
|
|
239
|
+
"""Cleans up resources like agent subscriptions and MCP manager."""
|
|
240
|
+
print("Cleaning up resources...")
|
|
241
|
+
for agent in self.agents:
|
|
242
|
+
agent.dispose()
|
|
243
|
+
if self._status_subscription:
|
|
244
|
+
self._status_subscription.dispose()
|
|
245
|
+
|
|
246
|
+
# This needs to be async, so we run it in a new event loop if needed
|
|
247
|
+
try:
|
|
248
|
+
loop = asyncio.get_running_loop()
|
|
249
|
+
if loop.is_running():
|
|
250
|
+
loop.create_task(self.mcp_manager.cleanup())
|
|
251
|
+
else:
|
|
252
|
+
asyncio.run(self.mcp_manager.cleanup())
|
|
253
|
+
except RuntimeError: # No running loop
|
|
254
|
+
asyncio.run(self.mcp_manager.cleanup())
|
|
255
|
+
|
|
256
|
+
async def stream_run(self):
|
|
257
|
+
"""Runs the workflow and yields status messages."""
|
|
258
|
+
os.chdir(self.work_dir.absolute())
|
|
259
|
+
await self._configure_tools()
|
|
260
|
+
|
|
261
|
+
instruction_agent, worker_agent = self._setup_agents()
|
|
262
|
+
|
|
263
|
+
try:
|
|
264
|
+
async for message in self.broker.iter_topic(self.TASK_STATUS_TOPIC):
|
|
265
|
+
if message.get("status") == "new_message":
|
|
266
|
+
yield message.get("result")
|
|
267
|
+
elif message.get("status") == "finished":
|
|
268
|
+
yield message.get("result")
|
|
269
|
+
break
|
|
270
|
+
elif message.get("status") == "error":
|
|
271
|
+
raise Exception(message.get("result"))
|
|
272
|
+
finally:
|
|
273
|
+
instruction_agent.dispose()
|
|
274
|
+
worker_agent.dispose()
|
|
275
|
+
await self.mcp_manager.cleanup()
|
|
@@ -12,10 +12,9 @@ from typing import List, Dict, Union
|
|
|
12
12
|
|
|
13
13
|
from ..broker import MessageBroker
|
|
14
14
|
from ..aient.src.aient.models import chatgpt
|
|
15
|
-
from ..aient.src.aient.plugins import
|
|
15
|
+
from ..aient.src.aient.plugins import get_function_call_list, registry
|
|
16
16
|
from ..prompt import worker_system_prompt, instruction_system_prompt
|
|
17
17
|
from ..utils import extract_xml_content, get_current_screen_image_message, replace_xml_content, register_mcp_tools
|
|
18
|
-
from ..bemcp.bemcp import MCPManager
|
|
19
18
|
|
|
20
19
|
class BaseAgent:
|
|
21
20
|
"""Base class for agents, handling common initialization and disposal."""
|
|
@@ -198,12 +197,9 @@ class Tee:
|
|
|
198
197
|
for f in self.files:
|
|
199
198
|
f.flush()
|
|
200
199
|
|
|
201
|
-
broker = MessageBroker()
|
|
202
|
-
mcp_manager = MCPManager()
|
|
203
|
-
|
|
204
200
|
class BrokerWorker:
|
|
205
201
|
"""The 'glue' class that orchestrates agents via a MessageBroker."""
|
|
206
|
-
def __init__(self, goal: str, tools: List[Union[str, Dict]], work_dir: str, cache_messages: Union[bool, List[Dict]] = None, broker
|
|
202
|
+
def __init__(self, goal: str, tools: List[Union[str, Dict]], work_dir: str, cache_messages: Union[bool, List[Dict]] = None, broker = None, mcp_manager = None, task_manager = None):
|
|
207
203
|
self.goal = goal
|
|
208
204
|
self.tools = tools
|
|
209
205
|
self.work_dir = Path(work_dir)
|
|
@@ -211,6 +207,7 @@ class BrokerWorker:
|
|
|
211
207
|
|
|
212
208
|
self.broker = broker
|
|
213
209
|
self.mcp_manager = mcp_manager
|
|
210
|
+
self.task_manager = task_manager
|
|
214
211
|
self.task_completion_event = asyncio.Event()
|
|
215
212
|
self.final_result = None
|
|
216
213
|
self._status_subscription = None
|
|
@@ -224,7 +221,7 @@ class BrokerWorker:
|
|
|
224
221
|
def setup(self):
|
|
225
222
|
cache_dir = self.work_dir / ".beswarm"
|
|
226
223
|
cache_dir.mkdir(parents=True, exist_ok=True)
|
|
227
|
-
task_manager.set_root_path(self.work_dir)
|
|
224
|
+
self.task_manager.set_root_path(self.work_dir)
|
|
228
225
|
self.cache_file = cache_dir / "work_agent_conversation_history.json"
|
|
229
226
|
if not self.cache_file.exists():
|
|
230
227
|
self.cache_file.write_text("[]", encoding="utf-8")
|
|
@@ -339,29 +336,4 @@ class BrokerWorker:
|
|
|
339
336
|
finally:
|
|
340
337
|
instruction_agent.dispose()
|
|
341
338
|
worker_agent.dispose()
|
|
342
|
-
await self.mcp_manager.cleanup()
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
@register_tool()
|
|
346
|
-
async def worker(goal: str, tools: List[Union[str, Dict]], work_dir: str, cache_messages: Union[bool, List[Dict]] = None):
|
|
347
|
-
start_time = datetime.now()
|
|
348
|
-
worker_instance = BrokerWorker(goal, tools, work_dir, cache_messages, broker, mcp_manager)
|
|
349
|
-
result = await worker_instance.run()
|
|
350
|
-
end_time = datetime.now()
|
|
351
|
-
print(f"\n任务开始时间: {start_time.strftime('%Y-%m-%d %H:%M:%S')}")
|
|
352
|
-
print(f"任务结束时间: {end_time.strftime('%Y-%m-%d %H:%M:%S')}")
|
|
353
|
-
print(f"总用时: {end_time - start_time}")
|
|
354
|
-
return result
|
|
355
|
-
|
|
356
|
-
@register_tool()
|
|
357
|
-
async def worker_gen(goal: str, tools: List[Union[str, Dict]], work_dir: str, cache_messages: Union[bool, List[Dict]] = None):
|
|
358
|
-
start_time = datetime.now()
|
|
359
|
-
worker_instance = BrokerWorker(goal, tools, work_dir, cache_messages, broker, mcp_manager)
|
|
360
|
-
async for result in worker_instance.stream_run():
|
|
361
|
-
yield result
|
|
362
|
-
end_time = datetime.now()
|
|
363
|
-
print(f"\n任务开始时间: {start_time.strftime('%Y-%m-%d %H:%M:%S')}")
|
|
364
|
-
print(f"任务结束时间: {end_time.strftime('%Y-%m-%d %H:%M:%S')}")
|
|
365
|
-
print(f"总用时: {end_time - start_time}")
|
|
366
|
-
|
|
367
|
-
from .taskmanager import task_manager
|
|
339
|
+
await self.mcp_manager.cleanup()
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import ast
|
|
2
1
|
import json
|
|
3
2
|
import uuid
|
|
4
3
|
import asyncio
|
|
5
4
|
from enum import Enum
|
|
6
5
|
from pathlib import Path
|
|
7
6
|
|
|
8
|
-
from
|
|
7
|
+
from .aient.src.aient.plugins import registry
|
|
9
8
|
|
|
10
9
|
class TaskStatus(Enum):
|
|
11
10
|
"""任务状态枚举"""
|
|
@@ -91,7 +90,7 @@ class TaskManager:
|
|
|
91
90
|
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
91
|
for task_id in running_task_id_list:
|
|
93
92
|
tasks_params = self.tasks_cache[task_id]["args"]
|
|
94
|
-
task_id =
|
|
93
|
+
task_id = self.resume_task(task_id, registry.tools["worker"], tasks_params)
|
|
95
94
|
|
|
96
95
|
def resume_task(self, task_id, task_coro, args):
|
|
97
96
|
"""
|
|
@@ -223,85 +222,8 @@ class TaskManager:
|
|
|
223
222
|
# 如果任务ID不存在,则返回-1
|
|
224
223
|
return -1
|
|
225
224
|
|
|
226
|
-
|
|
227
|
-
task_manager = TaskManager()
|
|
228
|
-
|
|
229
|
-
worker_fun = registry.tools["worker"]
|
|
230
|
-
|
|
231
|
-
@register_tool()
|
|
232
|
-
def create_task(goal, tools, work_dir):
|
|
233
|
-
"""
|
|
234
|
-
启动一个子任务来自动完成指定的任务目标 (`goal`)。
|
|
235
|
-
|
|
236
|
-
这个子任务接收一个清晰的任务描述、一组可供调用的工具 (`tools`),以及一个工作目录 (`work_dir`)。
|
|
237
|
-
它会结合可用的工具,自主规划并逐步执行必要的操作,直到最终完成指定的任务目标。
|
|
238
|
-
核心功能是根据输入的目标,驱动整个任务执行流程。
|
|
239
|
-
子任务下上文为空,因此需要细致的背景信息。
|
|
240
|
-
|
|
241
|
-
Args:
|
|
242
|
-
goal (str): 需要完成的具体任务目标描述。子任务将围绕此目标进行工作。必须清晰、具体。必须包含背景信息,完成指标等。写清楚什么时候算任务完成,同时交代清楚任务的背景信息,这个背景信息可以是需要读取的文件等一切有助于完成任务的信息。
|
|
243
|
-
tools (list[str]): 一个包含可用工具函数对象的列表。子任务在执行任务时可能会调用这些工具来与环境交互(例如读写文件、执行命令等)。
|
|
244
|
-
work_dir (str): 工作目录的绝对路径。子任务将在此目录上下文中执行操作。子任务的工作目录位置在主任务的工作目录的子目录。子任务工作目录**禁止**设置为主任务目录本身。
|
|
245
|
-
|
|
246
|
-
Returns:
|
|
247
|
-
str: 当任务成功完成时,返回字符串 "任务已完成"。
|
|
248
|
-
"""
|
|
249
|
-
tasks_params = [
|
|
250
|
-
{"goal": goal, "tools": ast.literal_eval(tools), "work_dir": work_dir, "cache_messages": True}
|
|
251
|
-
]
|
|
252
|
-
task_ids = task_manager.create_tasks(worker_fun, tasks_params)
|
|
253
|
-
return task_ids
|
|
254
|
-
|
|
255
|
-
@register_tool()
|
|
256
|
-
def resume_task(task_id, goal):
|
|
257
|
-
"""
|
|
258
|
-
恢复一个子任务。
|
|
259
|
-
"""
|
|
260
|
-
if task_id not in task_manager.tasks_cache:
|
|
261
|
-
return f"任务 {task_id} 不存在"
|
|
262
|
-
tasks_params = task_manager.tasks_cache[task_id]["args"]
|
|
263
|
-
tasks_params["goal"] = goal
|
|
264
|
-
tasks_params["cache_messages"] = True
|
|
265
|
-
task_id = task_manager.resume_task(task_id, worker_fun, tasks_params)
|
|
266
|
-
return f"任务 {task_id} 已恢复"
|
|
267
|
-
|
|
268
|
-
@register_tool()
|
|
269
|
-
def get_all_tasks_status():
|
|
270
|
-
"""
|
|
271
|
-
获取所有任务的状态。
|
|
272
|
-
子任务状态会持久化到磁盘,因此即使历史记录为空,之前的子任务仍然存在。
|
|
273
|
-
|
|
274
|
-
Returns:
|
|
275
|
-
str: 所有任务的状态。每个任务的id,状态,结果。
|
|
276
|
-
"""
|
|
277
|
-
return task_manager.tasks_cache
|
|
278
|
-
|
|
279
|
-
@register_tool()
|
|
280
|
-
async def get_task_result():
|
|
281
|
-
"""
|
|
282
|
-
等待并获取子任务的执行结果。如果需要等待子任务完成,请使用这个工具。一旦有任务完成,会自动获取结果。如果调用时没有任务完成,会等待直到有任务完成。
|
|
283
|
-
|
|
284
|
-
Returns:
|
|
285
|
-
str: 子任务的执行结果。
|
|
286
|
-
"""
|
|
287
|
-
running_tasks_num = len([task_id for task_id, task in task_manager.tasks_cache.items() if task_id != "root_path" and task.get("status") == "RUNNING"])
|
|
288
|
-
if running_tasks_num == 0:
|
|
289
|
-
return "All tasks are finished."
|
|
290
|
-
task_id, status, result = await task_manager.get_next_result()
|
|
291
|
-
|
|
292
|
-
unfinished_tasks = [task_id for task_id, task in task_manager.tasks_cache.items() if task_id != "root_path" and task.get("status") != "DONE"]
|
|
293
|
-
text = "".join([
|
|
294
|
-
f"Task ID: {task_id}\n",
|
|
295
|
-
f"Status: {status.value}\n",
|
|
296
|
-
f"Result: {result}\n\n",
|
|
297
|
-
f"There are {len(unfinished_tasks)} unfinished tasks, unfinished task ids: {unfinished_tasks}" if unfinished_tasks else "All tasks are finished.",
|
|
298
|
-
])
|
|
299
|
-
|
|
300
|
-
return text
|
|
301
|
-
|
|
302
225
|
async def main():
|
|
303
226
|
manager = TaskManager()
|
|
304
|
-
from worker import worker
|
|
305
227
|
|
|
306
228
|
# --- 任务提交阶段 ---
|
|
307
229
|
print("--- 任务提交 ---")
|
|
@@ -313,7 +235,7 @@ async def main():
|
|
|
313
235
|
{"goal": 2},
|
|
314
236
|
{"goal": 4},
|
|
315
237
|
]
|
|
316
|
-
task_ids = manager.create_tasks(worker, tasks_to_run)
|
|
238
|
+
task_ids = manager.create_tasks(registry.tools["worker"], tasks_to_run)
|
|
317
239
|
print(f"\n主程序: {len(task_ids)} 个任务已提交,现在开始等待结果...\n")
|
|
318
240
|
|
|
319
241
|
# --- 结果处理阶段 ---
|
|
@@ -1,60 +1,59 @@
|
|
|
1
1
|
from .edit_file import edit_file
|
|
2
|
-
from .
|
|
3
|
-
from .
|
|
4
|
-
from .request_input import request_admin_input
|
|
5
|
-
|
|
2
|
+
from .search_web import search_web
|
|
3
|
+
from .completion import task_complete
|
|
6
4
|
from .search_arxiv import search_arxiv
|
|
7
5
|
from .repomap import get_code_repo_map
|
|
6
|
+
from .request_input import request_admin_input
|
|
7
|
+
from .screenshot import save_screenshot_to_file
|
|
8
|
+
from .worker import worker, worker_gen, chatgroup
|
|
8
9
|
from .click import find_and_click_element, scroll_screen
|
|
9
|
-
from .
|
|
10
|
-
from .taskmanager import create_task, resume_task, get_all_tasks_status, get_task_result
|
|
11
|
-
from .completion import task_complete
|
|
10
|
+
from .subtasks import create_task, resume_task, get_all_tasks_status, get_task_result
|
|
12
11
|
|
|
13
12
|
#显式导入 aient.plugins 中的所需内容
|
|
14
13
|
from ..aient.src.aient.plugins import (
|
|
15
|
-
excute_command,
|
|
16
14
|
get_time,
|
|
15
|
+
read_file,
|
|
16
|
+
read_image,
|
|
17
|
+
register_tool,
|
|
18
|
+
write_to_file,
|
|
19
|
+
excute_command,
|
|
17
20
|
generate_image,
|
|
18
21
|
list_directory,
|
|
19
|
-
|
|
22
|
+
get_url_content,
|
|
20
23
|
run_python_script,
|
|
24
|
+
set_readonly_path,
|
|
21
25
|
get_search_results,
|
|
22
|
-
write_to_file,
|
|
23
26
|
download_read_arxiv_pdf,
|
|
24
|
-
get_url_content,
|
|
25
|
-
read_image,
|
|
26
|
-
set_readonly_path,
|
|
27
|
-
register_tool,
|
|
28
27
|
)
|
|
29
28
|
|
|
30
29
|
__all__ = [
|
|
31
|
-
"edit_file",
|
|
32
30
|
"worker",
|
|
31
|
+
"get_time",
|
|
32
|
+
"edit_file",
|
|
33
|
+
"read_file",
|
|
34
|
+
"chatgroup",
|
|
33
35
|
"worker_gen",
|
|
36
|
+
"read_image",
|
|
37
|
+
"search_web",
|
|
38
|
+
"create_task",
|
|
39
|
+
"resume_task",
|
|
34
40
|
"search_arxiv",
|
|
35
|
-
"
|
|
36
|
-
|
|
41
|
+
"write_to_file",
|
|
42
|
+
"scroll_screen",
|
|
43
|
+
"register_tool",
|
|
44
|
+
"task_complete",
|
|
37
45
|
"excute_command",
|
|
38
|
-
"read_image",
|
|
39
|
-
"get_time",
|
|
40
46
|
"generate_image",
|
|
41
47
|
"list_directory",
|
|
42
|
-
"
|
|
43
|
-
"run_python_script",
|
|
44
|
-
"get_search_results",
|
|
45
|
-
"write_to_file",
|
|
46
|
-
"download_read_arxiv_pdf",
|
|
48
|
+
"get_task_result",
|
|
47
49
|
"get_url_content",
|
|
48
|
-
"find_and_click_element",
|
|
49
|
-
"scroll_screen",
|
|
50
|
-
"register_tool",
|
|
51
|
-
"search_web",
|
|
52
|
-
"save_screenshot_to_file",
|
|
53
50
|
"set_readonly_path",
|
|
51
|
+
"get_code_repo_map",
|
|
52
|
+
"run_python_script",
|
|
53
|
+
"get_search_results",
|
|
54
54
|
"request_admin_input",
|
|
55
|
-
"create_task",
|
|
56
|
-
"resume_task",
|
|
57
55
|
"get_all_tasks_status",
|
|
58
|
-
"
|
|
59
|
-
"
|
|
56
|
+
"find_and_click_element",
|
|
57
|
+
"download_read_arxiv_pdf",
|
|
58
|
+
"save_screenshot_to_file",
|
|
60
59
|
]
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
|
|
3
|
+
from ..core import task_manager
|
|
4
|
+
from ..aient.src.aient.plugins import register_tool, registry
|
|
5
|
+
|
|
6
|
+
worker_fun = registry.tools["worker"]
|
|
7
|
+
|
|
8
|
+
@register_tool()
|
|
9
|
+
def create_task(goal, tools, work_dir):
|
|
10
|
+
"""
|
|
11
|
+
启动一个子任务来自动完成指定的任务目标 (`goal`)。
|
|
12
|
+
|
|
13
|
+
这个子任务接收一个清晰的任务描述、一组可供调用的工具 (`tools`),以及一个工作目录 (`work_dir`)。
|
|
14
|
+
它会结合可用的工具,自主规划并逐步执行必要的操作,直到最终完成指定的任务目标。
|
|
15
|
+
核心功能是根据输入的目标,驱动整个任务执行流程。
|
|
16
|
+
子任务下上文为空,因此需要细致的背景信息。
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
goal (str): 需要完成的具体任务目标描述。子任务将围绕此目标进行工作。必须清晰、具体。必须包含背景信息,完成指标等。写清楚什么时候算任务完成,同时交代清楚任务的背景信息,这个背景信息可以是需要读取的文件等一切有助于完成任务的信息。
|
|
20
|
+
tools (list[str]): 一个包含可用工具函数对象的列表。子任务在执行任务时可能会调用这些工具来与环境交互(例如读写文件、执行命令等)。
|
|
21
|
+
work_dir (str): 工作目录的绝对路径。子任务将在此目录上下文中执行操作。子任务的工作目录位置在主任务的工作目录的子目录。子任务工作目录**禁止**设置为主任务目录本身。
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
str: 当任务成功完成时,返回字符串 "任务已完成"。
|
|
25
|
+
"""
|
|
26
|
+
tasks_params = [
|
|
27
|
+
{"goal": goal, "tools": ast.literal_eval(tools), "work_dir": work_dir, "cache_messages": True}
|
|
28
|
+
]
|
|
29
|
+
task_ids = task_manager.create_tasks(worker_fun, tasks_params)
|
|
30
|
+
return task_ids
|
|
31
|
+
|
|
32
|
+
@register_tool()
|
|
33
|
+
def resume_task(task_id, goal):
|
|
34
|
+
"""
|
|
35
|
+
恢复一个子任务。
|
|
36
|
+
"""
|
|
37
|
+
if task_id not in task_manager.tasks_cache:
|
|
38
|
+
return f"任务 {task_id} 不存在"
|
|
39
|
+
tasks_params = task_manager.tasks_cache[task_id]["args"]
|
|
40
|
+
tasks_params["goal"] = goal
|
|
41
|
+
tasks_params["cache_messages"] = True
|
|
42
|
+
task_id = task_manager.resume_task(task_id, worker_fun, tasks_params)
|
|
43
|
+
return f"任务 {task_id} 已恢复"
|
|
44
|
+
|
|
45
|
+
@register_tool()
|
|
46
|
+
def get_all_tasks_status():
|
|
47
|
+
"""
|
|
48
|
+
获取所有任务的状态。
|
|
49
|
+
子任务状态会持久化到磁盘,因此即使历史记录为空,之前的子任务仍然存在。
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
str: 所有任务的状态。每个任务的id,状态,结果。
|
|
53
|
+
"""
|
|
54
|
+
return task_manager.tasks_cache
|
|
55
|
+
|
|
56
|
+
@register_tool()
|
|
57
|
+
async def get_task_result():
|
|
58
|
+
"""
|
|
59
|
+
等待并获取子任务的执行结果。如果需要等待子任务完成,请使用这个工具。一旦有任务完成,会自动获取结果。如果调用时没有任务完成,会等待直到有任务完成。
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
str: 子任务的执行结果。
|
|
63
|
+
"""
|
|
64
|
+
running_tasks_num = len([task_id for task_id, task in task_manager.tasks_cache.items() if task_id != "root_path" and task.get("status") == "RUNNING"])
|
|
65
|
+
if running_tasks_num == 0:
|
|
66
|
+
return "All tasks are finished."
|
|
67
|
+
task_id, status, result = await task_manager.get_next_result()
|
|
68
|
+
|
|
69
|
+
unfinished_tasks = [task_id for task_id, task in task_manager.tasks_cache.items() if task_id != "root_path" and task.get("status") != "DONE"]
|
|
70
|
+
text = "".join([
|
|
71
|
+
f"Task ID: {task_id}\n",
|
|
72
|
+
f"Status: {status.value}\n",
|
|
73
|
+
f"Result: {result}\n\n",
|
|
74
|
+
f"There are {len(unfinished_tasks)} unfinished tasks, unfinished task ids: {unfinished_tasks}" if unfinished_tasks else "All tasks are finished.",
|
|
75
|
+
])
|
|
76
|
+
|
|
77
|
+
return text
|