camel-ai 0.2.73a4__py3-none-any.whl → 0.2.80a2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- camel/__init__.py +1 -1
- camel/agents/_utils.py +38 -0
- camel/agents/chat_agent.py +2217 -519
- camel/agents/mcp_agent.py +30 -27
- camel/configs/__init__.py +15 -0
- camel/configs/aihubmix_config.py +88 -0
- camel/configs/amd_config.py +70 -0
- camel/configs/cometapi_config.py +104 -0
- camel/configs/minimax_config.py +93 -0
- camel/configs/nebius_config.py +103 -0
- camel/data_collectors/alpaca_collector.py +15 -6
- camel/datasets/base_generator.py +39 -10
- camel/environments/single_step.py +28 -3
- camel/environments/tic_tac_toe.py +1 -1
- camel/interpreters/__init__.py +2 -0
- camel/interpreters/docker/Dockerfile +3 -12
- camel/interpreters/e2b_interpreter.py +34 -1
- camel/interpreters/microsandbox_interpreter.py +395 -0
- camel/loaders/__init__.py +11 -2
- camel/loaders/chunkr_reader.py +9 -0
- camel/memories/agent_memories.py +48 -4
- camel/memories/base.py +26 -0
- camel/memories/blocks/chat_history_block.py +122 -4
- camel/memories/context_creators/score_based.py +25 -384
- camel/memories/records.py +88 -8
- camel/messages/base.py +153 -34
- camel/models/__init__.py +10 -0
- camel/models/aihubmix_model.py +83 -0
- camel/models/aiml_model.py +1 -16
- camel/models/amd_model.py +101 -0
- camel/models/anthropic_model.py +6 -19
- camel/models/aws_bedrock_model.py +2 -33
- camel/models/azure_openai_model.py +114 -89
- camel/models/base_audio_model.py +3 -1
- camel/models/base_model.py +32 -14
- camel/models/cohere_model.py +1 -16
- camel/models/cometapi_model.py +83 -0
- camel/models/crynux_model.py +1 -16
- camel/models/deepseek_model.py +1 -16
- camel/models/fish_audio_model.py +6 -0
- camel/models/gemini_model.py +36 -18
- camel/models/groq_model.py +1 -17
- camel/models/internlm_model.py +1 -16
- camel/models/litellm_model.py +1 -16
- camel/models/lmstudio_model.py +1 -17
- camel/models/minimax_model.py +83 -0
- camel/models/mistral_model.py +1 -16
- camel/models/model_factory.py +27 -1
- camel/models/modelscope_model.py +1 -16
- camel/models/moonshot_model.py +105 -24
- camel/models/nebius_model.py +83 -0
- camel/models/nemotron_model.py +0 -5
- camel/models/netmind_model.py +1 -16
- camel/models/novita_model.py +1 -16
- camel/models/nvidia_model.py +1 -16
- camel/models/ollama_model.py +4 -19
- camel/models/openai_compatible_model.py +62 -41
- camel/models/openai_model.py +62 -57
- camel/models/openrouter_model.py +1 -17
- camel/models/ppio_model.py +1 -16
- camel/models/qianfan_model.py +1 -16
- camel/models/qwen_model.py +1 -16
- camel/models/reka_model.py +1 -16
- camel/models/samba_model.py +34 -47
- camel/models/sglang_model.py +64 -31
- camel/models/siliconflow_model.py +1 -16
- camel/models/stub_model.py +0 -4
- camel/models/togetherai_model.py +1 -16
- camel/models/vllm_model.py +1 -16
- camel/models/volcano_model.py +0 -17
- camel/models/watsonx_model.py +1 -16
- camel/models/yi_model.py +1 -16
- camel/models/zhipuai_model.py +60 -16
- camel/parsers/__init__.py +18 -0
- camel/parsers/mcp_tool_call_parser.py +176 -0
- camel/retrievers/auto_retriever.py +1 -0
- camel/runtimes/daytona_runtime.py +11 -12
- camel/societies/__init__.py +2 -0
- camel/societies/workforce/__init__.py +2 -0
- camel/societies/workforce/events.py +122 -0
- camel/societies/workforce/prompts.py +146 -66
- camel/societies/workforce/role_playing_worker.py +15 -11
- camel/societies/workforce/single_agent_worker.py +302 -65
- camel/societies/workforce/structured_output_handler.py +30 -18
- camel/societies/workforce/task_channel.py +163 -27
- camel/societies/workforce/utils.py +107 -13
- camel/societies/workforce/workflow_memory_manager.py +772 -0
- camel/societies/workforce/workforce.py +1949 -579
- camel/societies/workforce/workforce_callback.py +74 -0
- camel/societies/workforce/workforce_logger.py +168 -145
- camel/societies/workforce/workforce_metrics.py +33 -0
- camel/storages/key_value_storages/json.py +15 -2
- camel/storages/key_value_storages/mem0_cloud.py +48 -47
- camel/storages/object_storages/google_cloud.py +1 -1
- camel/storages/vectordb_storages/oceanbase.py +13 -13
- camel/storages/vectordb_storages/qdrant.py +3 -3
- camel/storages/vectordb_storages/tidb.py +8 -6
- camel/tasks/task.py +4 -3
- camel/toolkits/__init__.py +20 -7
- camel/toolkits/aci_toolkit.py +45 -0
- camel/toolkits/base.py +6 -4
- camel/toolkits/code_execution.py +28 -1
- camel/toolkits/context_summarizer_toolkit.py +684 -0
- camel/toolkits/dappier_toolkit.py +5 -1
- camel/toolkits/dingtalk.py +1135 -0
- camel/toolkits/edgeone_pages_mcp_toolkit.py +11 -31
- camel/toolkits/excel_toolkit.py +1 -1
- camel/toolkits/{file_write_toolkit.py → file_toolkit.py} +430 -36
- camel/toolkits/function_tool.py +13 -3
- camel/toolkits/github_toolkit.py +104 -17
- camel/toolkits/gmail_toolkit.py +1839 -0
- camel/toolkits/google_calendar_toolkit.py +38 -4
- camel/toolkits/google_drive_mcp_toolkit.py +12 -31
- camel/toolkits/hybrid_browser_toolkit/config_loader.py +15 -0
- camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit.py +77 -8
- camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit_ts.py +884 -88
- camel/toolkits/hybrid_browser_toolkit/installer.py +203 -0
- camel/toolkits/hybrid_browser_toolkit/ts/package-lock.json +5 -612
- camel/toolkits/hybrid_browser_toolkit/ts/package.json +0 -1
- camel/toolkits/hybrid_browser_toolkit/ts/src/browser-session.ts +959 -89
- camel/toolkits/hybrid_browser_toolkit/ts/src/config-loader.ts +9 -2
- camel/toolkits/hybrid_browser_toolkit/ts/src/hybrid-browser-toolkit.ts +281 -213
- camel/toolkits/hybrid_browser_toolkit/ts/src/parent-child-filter.ts +226 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/snapshot-parser.ts +219 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/som-screenshot-injected.ts +543 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/types.ts +23 -3
- camel/toolkits/hybrid_browser_toolkit/ts/websocket-server.js +72 -7
- camel/toolkits/hybrid_browser_toolkit/ws_wrapper.py +582 -132
- camel/toolkits/hybrid_browser_toolkit_py/actions.py +158 -0
- camel/toolkits/hybrid_browser_toolkit_py/browser_session.py +55 -8
- camel/toolkits/hybrid_browser_toolkit_py/config_loader.py +43 -0
- camel/toolkits/hybrid_browser_toolkit_py/hybrid_browser_toolkit.py +321 -8
- camel/toolkits/hybrid_browser_toolkit_py/snapshot.py +10 -4
- camel/toolkits/hybrid_browser_toolkit_py/unified_analyzer.js +45 -4
- camel/toolkits/{openai_image_toolkit.py → image_generation_toolkit.py} +151 -53
- camel/toolkits/klavis_toolkit.py +5 -1
- camel/toolkits/markitdown_toolkit.py +27 -1
- camel/toolkits/math_toolkit.py +64 -10
- camel/toolkits/mcp_toolkit.py +366 -71
- camel/toolkits/memory_toolkit.py +5 -1
- camel/toolkits/message_integration.py +18 -13
- camel/toolkits/minimax_mcp_toolkit.py +195 -0
- camel/toolkits/note_taking_toolkit.py +19 -10
- camel/toolkits/notion_mcp_toolkit.py +16 -26
- camel/toolkits/openbb_toolkit.py +5 -1
- camel/toolkits/origene_mcp_toolkit.py +8 -49
- camel/toolkits/playwright_mcp_toolkit.py +12 -31
- camel/toolkits/resend_toolkit.py +168 -0
- camel/toolkits/search_toolkit.py +264 -91
- camel/toolkits/slack_toolkit.py +64 -10
- camel/toolkits/terminal_toolkit/__init__.py +18 -0
- camel/toolkits/terminal_toolkit/terminal_toolkit.py +957 -0
- camel/toolkits/terminal_toolkit/utils.py +532 -0
- camel/toolkits/vertex_ai_veo_toolkit.py +590 -0
- camel/toolkits/video_analysis_toolkit.py +17 -11
- camel/toolkits/wechat_official_toolkit.py +483 -0
- camel/toolkits/zapier_toolkit.py +5 -1
- camel/types/__init__.py +2 -2
- camel/types/enums.py +274 -7
- camel/types/openai_types.py +2 -2
- camel/types/unified_model_type.py +15 -0
- camel/utils/commons.py +36 -5
- camel/utils/constants.py +3 -0
- camel/utils/context_utils.py +1003 -0
- camel/utils/mcp.py +138 -4
- camel/utils/token_counting.py +43 -20
- {camel_ai-0.2.73a4.dist-info → camel_ai-0.2.80a2.dist-info}/METADATA +223 -83
- {camel_ai-0.2.73a4.dist-info → camel_ai-0.2.80a2.dist-info}/RECORD +170 -141
- camel/loaders/pandas_reader.py +0 -368
- camel/toolkits/openai_agent_toolkit.py +0 -135
- camel/toolkits/terminal_toolkit.py +0 -1550
- {camel_ai-0.2.73a4.dist-info → camel_ai-0.2.80a2.dist-info}/WHEEL +0 -0
- {camel_ai-0.2.73a4.dist-info → camel_ai-0.2.80a2.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
|
|
2
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
# you may not use this file except in compliance with the License.
|
|
4
|
+
# You may obtain a copy of the License at
|
|
5
|
+
#
|
|
6
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
#
|
|
8
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
# See the License for the specific language governing permissions and
|
|
12
|
+
# limitations under the License.
|
|
13
|
+
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
|
|
14
|
+
"""Utility functions for parsing MCP tool calls from model output."""
|
|
15
|
+
|
|
16
|
+
import ast
|
|
17
|
+
import json
|
|
18
|
+
import logging
|
|
19
|
+
import re
|
|
20
|
+
from typing import Any, Dict, List, Optional
|
|
21
|
+
|
|
22
|
+
try: # pragma: no cover - optional dependency
|
|
23
|
+
import yaml
|
|
24
|
+
except ImportError: # pragma: no cover
|
|
25
|
+
yaml = None # type: ignore[assignment]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
CODE_BLOCK_PATTERN = re.compile(
|
|
29
|
+
r"```(?:[a-z0-9_-]+)?\s*([\s\S]+?)\s*```",
|
|
30
|
+
re.IGNORECASE,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
JSON_START_PATTERN = re.compile(r"[{\[]")
|
|
34
|
+
JSON_TOKEN_PATTERN = re.compile(
|
|
35
|
+
r"""
|
|
36
|
+
(?P<double>"(?:\\.|[^"\\])*")
|
|
37
|
+
|
|
|
38
|
+
(?P<single>'(?:\\.|[^'\\])*')
|
|
39
|
+
|
|
|
40
|
+
(?P<brace>[{}\[\]])
|
|
41
|
+
""",
|
|
42
|
+
re.VERBOSE,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
logger = logging.getLogger(__name__)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def extract_tool_calls_from_text(content: str) -> List[Dict[str, Any]]:
|
|
49
|
+
"""Extract tool call dictionaries from raw text output."""
|
|
50
|
+
|
|
51
|
+
if not content:
|
|
52
|
+
return []
|
|
53
|
+
|
|
54
|
+
tool_calls: List[Dict[str, Any]] = []
|
|
55
|
+
seen_ranges: List[tuple[int, int]] = []
|
|
56
|
+
|
|
57
|
+
for match in CODE_BLOCK_PATTERN.finditer(content):
|
|
58
|
+
snippet = match.group(1).strip()
|
|
59
|
+
if not snippet:
|
|
60
|
+
continue
|
|
61
|
+
|
|
62
|
+
parsed = _try_parse_json_like(snippet)
|
|
63
|
+
if parsed is None:
|
|
64
|
+
logger.warning(
|
|
65
|
+
"Failed to parse JSON payload from fenced block: %s",
|
|
66
|
+
snippet,
|
|
67
|
+
)
|
|
68
|
+
continue
|
|
69
|
+
|
|
70
|
+
_collect_tool_calls(parsed, tool_calls)
|
|
71
|
+
seen_ranges.append((match.start(1), match.end(1)))
|
|
72
|
+
|
|
73
|
+
for start_match in JSON_START_PATTERN.finditer(content):
|
|
74
|
+
start_idx = start_match.start()
|
|
75
|
+
|
|
76
|
+
if any(start <= start_idx < stop for start, stop in seen_ranges):
|
|
77
|
+
continue
|
|
78
|
+
|
|
79
|
+
segment = _find_json_candidate(content, start_idx)
|
|
80
|
+
if segment is None:
|
|
81
|
+
continue
|
|
82
|
+
|
|
83
|
+
end_idx = start_idx + len(segment)
|
|
84
|
+
if any(start <= start_idx < stop for start, stop in seen_ranges):
|
|
85
|
+
continue
|
|
86
|
+
|
|
87
|
+
parsed = _try_parse_json_like(segment)
|
|
88
|
+
if parsed is None:
|
|
89
|
+
logger.debug(
|
|
90
|
+
"Unable to parse JSON-like candidate: %s",
|
|
91
|
+
_truncate_snippet(segment),
|
|
92
|
+
)
|
|
93
|
+
continue
|
|
94
|
+
|
|
95
|
+
_collect_tool_calls(parsed, tool_calls)
|
|
96
|
+
seen_ranges.append((start_idx, end_idx))
|
|
97
|
+
|
|
98
|
+
return tool_calls
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _collect_tool_calls(
|
|
102
|
+
payload: Any, accumulator: List[Dict[str, Any]]
|
|
103
|
+
) -> None:
|
|
104
|
+
"""Collect valid tool call dictionaries from parsed payloads."""
|
|
105
|
+
|
|
106
|
+
if isinstance(payload, dict):
|
|
107
|
+
if payload.get("tool_name") is None:
|
|
108
|
+
return
|
|
109
|
+
accumulator.append(payload)
|
|
110
|
+
elif isinstance(payload, list):
|
|
111
|
+
for item in payload:
|
|
112
|
+
_collect_tool_calls(item, accumulator)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _try_parse_json_like(snippet: str) -> Optional[Any]:
|
|
116
|
+
"""Parse a JSON or JSON-like snippet into Python data."""
|
|
117
|
+
|
|
118
|
+
try:
|
|
119
|
+
return json.loads(snippet)
|
|
120
|
+
except json.JSONDecodeError as exc:
|
|
121
|
+
logger.debug(
|
|
122
|
+
"json.loads failed: %s | snippet=%s",
|
|
123
|
+
exc,
|
|
124
|
+
_truncate_snippet(snippet),
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
if yaml is not None:
|
|
128
|
+
try:
|
|
129
|
+
return yaml.safe_load(snippet)
|
|
130
|
+
except yaml.YAMLError:
|
|
131
|
+
pass
|
|
132
|
+
|
|
133
|
+
try:
|
|
134
|
+
return ast.literal_eval(snippet)
|
|
135
|
+
except (ValueError, SyntaxError):
|
|
136
|
+
return None
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _find_json_candidate(content: str, start_idx: int) -> Optional[str]:
|
|
140
|
+
"""Locate a balanced JSON-like segment starting at ``start_idx``."""
|
|
141
|
+
|
|
142
|
+
opening = content[start_idx]
|
|
143
|
+
if opening not in "{[":
|
|
144
|
+
return None
|
|
145
|
+
|
|
146
|
+
stack = ["}" if opening == "{" else "]"]
|
|
147
|
+
|
|
148
|
+
for token in JSON_TOKEN_PATTERN.finditer(content, start_idx + 1):
|
|
149
|
+
if token.lastgroup in {"double", "single"}:
|
|
150
|
+
continue
|
|
151
|
+
|
|
152
|
+
brace = token.group("brace")
|
|
153
|
+
if brace in "{[":
|
|
154
|
+
stack.append("}" if brace == "{" else "]")
|
|
155
|
+
continue
|
|
156
|
+
|
|
157
|
+
if not stack:
|
|
158
|
+
return None
|
|
159
|
+
|
|
160
|
+
expected = stack.pop()
|
|
161
|
+
if brace != expected:
|
|
162
|
+
return None
|
|
163
|
+
|
|
164
|
+
if not stack:
|
|
165
|
+
return content[start_idx : token.end()]
|
|
166
|
+
|
|
167
|
+
return None
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def _truncate_snippet(snippet: str, limit: int = 120) -> str:
|
|
171
|
+
"""Return a truncated representation suitable for logging."""
|
|
172
|
+
|
|
173
|
+
compact = " ".join(snippet.strip().split())
|
|
174
|
+
if len(compact) <= limit:
|
|
175
|
+
return compact
|
|
176
|
+
return f"{compact[: limit - 3]}..."
|
|
@@ -97,6 +97,7 @@ class AutoRetriever:
|
|
|
97
97
|
"URL (database url) and API key required for TiDB storage "
|
|
98
98
|
"are not provided. Format: "
|
|
99
99
|
"mysql+pymysql://<username>:<password>@<host>:4000/test"
|
|
100
|
+
"You can get the database url from https://tidbcloud.com/console/clusters"
|
|
100
101
|
)
|
|
101
102
|
return TiDBStorage(
|
|
102
103
|
vector_dim=self.embedding_model.get_output_dim(),
|
|
@@ -16,7 +16,7 @@ import inspect
|
|
|
16
16
|
import json
|
|
17
17
|
import os
|
|
18
18
|
from functools import wraps
|
|
19
|
-
from typing import Any, Dict, List, Optional, Union
|
|
19
|
+
from typing import Any, Callable, Dict, List, Optional, Union
|
|
20
20
|
|
|
21
21
|
from pydantic import BaseModel
|
|
22
22
|
|
|
@@ -49,7 +49,7 @@ class DaytonaRuntime(BaseRuntime):
|
|
|
49
49
|
api_url: Optional[str] = None,
|
|
50
50
|
language: Optional[str] = "python",
|
|
51
51
|
):
|
|
52
|
-
from daytona_sdk import Daytona, DaytonaConfig
|
|
52
|
+
from daytona_sdk import Daytona, DaytonaConfig, Sandbox
|
|
53
53
|
|
|
54
54
|
super().__init__()
|
|
55
55
|
self.api_key = api_key or os.environ.get('DAYTONA_API_KEY')
|
|
@@ -57,7 +57,7 @@ class DaytonaRuntime(BaseRuntime):
|
|
|
57
57
|
self.language = language
|
|
58
58
|
self.config = DaytonaConfig(api_key=self.api_key, api_url=self.api_url)
|
|
59
59
|
self.daytona = Daytona(self.config)
|
|
60
|
-
self.sandbox = None
|
|
60
|
+
self.sandbox: Optional[Sandbox] = None
|
|
61
61
|
self.entrypoint: Dict[str, str] = dict()
|
|
62
62
|
|
|
63
63
|
def build(self) -> "DaytonaRuntime":
|
|
@@ -66,10 +66,10 @@ class DaytonaRuntime(BaseRuntime):
|
|
|
66
66
|
Returns:
|
|
67
67
|
DaytonaRuntime: The current runtime.
|
|
68
68
|
"""
|
|
69
|
-
from daytona_sdk import
|
|
69
|
+
from daytona_sdk import CreateSandboxBaseParams
|
|
70
70
|
|
|
71
71
|
try:
|
|
72
|
-
params =
|
|
72
|
+
params = CreateSandboxBaseParams(language=self.language)
|
|
73
73
|
self.sandbox = self.daytona.create(params)
|
|
74
74
|
if self.sandbox is None:
|
|
75
75
|
raise RuntimeError("Failed to create sandbox.")
|
|
@@ -83,7 +83,7 @@ class DaytonaRuntime(BaseRuntime):
|
|
|
83
83
|
r"""Clean up the sandbox when exiting."""
|
|
84
84
|
if self.sandbox:
|
|
85
85
|
try:
|
|
86
|
-
self.daytona.
|
|
86
|
+
self.daytona.delete(self.sandbox)
|
|
87
87
|
logger.info(f"Sandbox {self.sandbox.id} removed")
|
|
88
88
|
self.sandbox = None
|
|
89
89
|
except Exception as e:
|
|
@@ -112,7 +112,7 @@ class DaytonaRuntime(BaseRuntime):
|
|
|
112
112
|
if arguments is not None:
|
|
113
113
|
entrypoint += json.dumps(arguments, ensure_ascii=False)
|
|
114
114
|
|
|
115
|
-
def make_wrapper(inner_func, func_name, func_code):
|
|
115
|
+
def make_wrapper(inner_func: Callable, func_name: str, func_code: str):
|
|
116
116
|
r"""Creates a wrapper for a function to execute it in the
|
|
117
117
|
Daytona sandbox.
|
|
118
118
|
|
|
@@ -208,12 +208,11 @@ class DaytonaRuntime(BaseRuntime):
|
|
|
208
208
|
RuntimeError: If the sandbox is not initialized.
|
|
209
209
|
"""
|
|
210
210
|
if self.sandbox is None:
|
|
211
|
-
raise RuntimeError("
|
|
212
|
-
info = self.sandbox.info()
|
|
211
|
+
raise RuntimeError("Sandbox not initialized.")
|
|
213
212
|
return (
|
|
214
|
-
f"Sandbox {
|
|
215
|
-
f"State: {
|
|
216
|
-
f"Resources: {
|
|
213
|
+
f"Sandbox {self.sandbox.id}:\n"
|
|
214
|
+
f"State: {self.sandbox.state}\n"
|
|
215
|
+
f"Resources: {self.sandbox.cpu} CPU, {self.sandbox.memory} RAM"
|
|
217
216
|
)
|
|
218
217
|
|
|
219
218
|
def __del__(self):
|
camel/societies/__init__.py
CHANGED
|
@@ -13,8 +13,10 @@
|
|
|
13
13
|
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
|
|
14
14
|
from .babyagi_playing import BabyAGI
|
|
15
15
|
from .role_playing import RolePlaying
|
|
16
|
+
from .workforce import Workforce
|
|
16
17
|
|
|
17
18
|
__all__ = [
|
|
18
19
|
'RolePlaying',
|
|
19
20
|
'BabyAGI',
|
|
21
|
+
'Workforce',
|
|
20
22
|
]
|
|
@@ -14,10 +14,12 @@
|
|
|
14
14
|
|
|
15
15
|
from .role_playing_worker import RolePlayingWorker
|
|
16
16
|
from .single_agent_worker import SingleAgentWorker
|
|
17
|
+
from .workflow_memory_manager import WorkflowSelectionMethod
|
|
17
18
|
from .workforce import Workforce
|
|
18
19
|
|
|
19
20
|
__all__ = [
|
|
20
21
|
"Workforce",
|
|
21
22
|
"SingleAgentWorker",
|
|
22
23
|
"RolePlayingWorker",
|
|
24
|
+
"WorkflowSelectionMethod",
|
|
23
25
|
]
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
|
|
2
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
# you may not use this file except in compliance with the License.
|
|
4
|
+
# You may obtain a copy of the License at
|
|
5
|
+
#
|
|
6
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
#
|
|
8
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
# See the License for the specific language governing permissions and
|
|
12
|
+
# limitations under the License.
|
|
13
|
+
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from datetime import datetime, timezone
|
|
17
|
+
from typing import Any, Dict, List, Literal, Optional, Union
|
|
18
|
+
|
|
19
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class WorkforceEventBase(BaseModel):
|
|
23
|
+
model_config = ConfigDict(frozen=True, extra='forbid')
|
|
24
|
+
event_type: Literal[
|
|
25
|
+
"task_decomposed",
|
|
26
|
+
"task_created",
|
|
27
|
+
"task_assigned",
|
|
28
|
+
"task_started",
|
|
29
|
+
"task_completed",
|
|
30
|
+
"task_failed",
|
|
31
|
+
"worker_created",
|
|
32
|
+
"worker_deleted",
|
|
33
|
+
"queue_status",
|
|
34
|
+
"all_tasks_completed",
|
|
35
|
+
]
|
|
36
|
+
metadata: Optional[Dict[str, Any]] = None
|
|
37
|
+
timestamp: datetime = Field(
|
|
38
|
+
default_factory=lambda: datetime.now(timezone.utc)
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class WorkerCreatedEvent(WorkforceEventBase):
|
|
43
|
+
event_type: Literal["worker_created"] = "worker_created"
|
|
44
|
+
worker_id: str
|
|
45
|
+
worker_type: str
|
|
46
|
+
role: str
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class WorkerDeletedEvent(WorkforceEventBase):
|
|
50
|
+
event_type: Literal["worker_deleted"] = "worker_deleted"
|
|
51
|
+
worker_id: str
|
|
52
|
+
reason: Optional[str] = None
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class TaskDecomposedEvent(WorkforceEventBase):
|
|
56
|
+
event_type: Literal["task_decomposed"] = "task_decomposed"
|
|
57
|
+
parent_task_id: str
|
|
58
|
+
subtask_ids: List[str]
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class TaskCreatedEvent(WorkforceEventBase):
|
|
62
|
+
event_type: Literal["task_created"] = "task_created"
|
|
63
|
+
task_id: str
|
|
64
|
+
description: str
|
|
65
|
+
parent_task_id: Optional[str] = None
|
|
66
|
+
task_type: Optional[str] = None
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class TaskAssignedEvent(WorkforceEventBase):
|
|
70
|
+
event_type: Literal["task_assigned"] = "task_assigned"
|
|
71
|
+
task_id: str
|
|
72
|
+
worker_id: str
|
|
73
|
+
queue_time_seconds: Optional[float] = None
|
|
74
|
+
dependencies: Optional[List[str]] = None
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class TaskStartedEvent(WorkforceEventBase):
|
|
78
|
+
event_type: Literal["task_started"] = "task_started"
|
|
79
|
+
task_id: str
|
|
80
|
+
worker_id: str
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class TaskCompletedEvent(WorkforceEventBase):
|
|
84
|
+
event_type: Literal["task_completed"] = "task_completed"
|
|
85
|
+
task_id: str
|
|
86
|
+
worker_id: str
|
|
87
|
+
result_summary: Optional[str] = None
|
|
88
|
+
processing_time_seconds: Optional[float] = None
|
|
89
|
+
token_usage: Optional[Dict[str, int]] = None
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class TaskFailedEvent(WorkforceEventBase):
|
|
93
|
+
event_type: Literal["task_failed"] = "task_failed"
|
|
94
|
+
task_id: str
|
|
95
|
+
error_message: str
|
|
96
|
+
worker_id: Optional[str] = None
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class AllTasksCompletedEvent(WorkforceEventBase):
|
|
100
|
+
event_type: Literal["all_tasks_completed"] = "all_tasks_completed"
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class QueueStatusEvent(WorkforceEventBase):
|
|
104
|
+
event_type: Literal["queue_status"] = "queue_status"
|
|
105
|
+
queue_name: str
|
|
106
|
+
length: int
|
|
107
|
+
pending_task_ids: Optional[List[str]] = None
|
|
108
|
+
metadata: Optional[Dict[str, Any]] = None
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
WorkforceEvent = Union[
|
|
112
|
+
TaskDecomposedEvent,
|
|
113
|
+
TaskCreatedEvent,
|
|
114
|
+
TaskAssignedEvent,
|
|
115
|
+
TaskStartedEvent,
|
|
116
|
+
TaskCompletedEvent,
|
|
117
|
+
TaskFailedEvent,
|
|
118
|
+
WorkerCreatedEvent,
|
|
119
|
+
WorkerDeletedEvent,
|
|
120
|
+
AllTasksCompletedEvent,
|
|
121
|
+
QueueStatusEvent,
|
|
122
|
+
]
|
|
@@ -61,14 +61,7 @@ Each assignment dictionary should have:
|
|
|
61
61
|
- "dependencies": list of task IDs that this task depends on (empty list if no dependencies)
|
|
62
62
|
|
|
63
63
|
Example valid response:
|
|
64
|
-
{{
|
|
65
|
-
"assignments": [
|
|
66
|
-
{{"task_id": "task_1", "assignee_id": "node_12345", "dependencies": []}},
|
|
67
|
-
{{"task_id": "task_2", "assignee_id": "node_67890", "dependencies": ["task_1"]}},
|
|
68
|
-
{{"task_id": "task_3", "assignee_id": "node_12345", "dependencies": []}},
|
|
69
|
-
{{"task_id": "task_4", "assignee_id": "node_67890", "dependencies": ["task_1", "task_2"]}}
|
|
70
|
-
]
|
|
71
|
-
}}
|
|
64
|
+
{{"assignments": [{{"task_id": "task_1", "assignee_id": "node_12345", "dependencies": []}}, {{"task_id": "task_2", "assignee_id": "node_67890", "dependencies": ["task_1"]}}, {{"task_id": "task_3", "assignee_id": "node_12345", "dependencies": []}}, {{"task_id": "task_4", "assignee_id": "node_67890", "dependencies": ["task_1", "task_2"]}}]}}
|
|
72
65
|
|
|
73
66
|
***CRITICAL: DEPENDENCY MANAGEMENT IS YOUR IMPORTANT RESPONSIBILITY.***
|
|
74
67
|
Carefully analyze the sequence of tasks. A task's dependencies MUST include the IDs of all prior tasks whose outputs are necessary for its execution. For example, a task to 'Summarize Paper X' MUST depend on the task that 'Finds/Retrieves Paper X'. Similarly, a task that 'Compiles a report from summaries' MUST depend on all 'Summarize Paper X' tasks. **Incorrect or missing dependencies will lead to critical operational failures and an inability to complete the overall objective.** Be meticulous in defining these relationships.
|
|
@@ -196,27 +189,31 @@ Now you should summarize the scenario and return the result of the task.
|
|
|
196
189
|
"""
|
|
197
190
|
)
|
|
198
191
|
|
|
199
|
-
TASK_DECOMPOSE_PROMPT = r"""You need to decompose
|
|
192
|
+
TASK_DECOMPOSE_PROMPT = r"""You need to either decompose a complex task or enhance a simple one, following these important principles to maximize efficiency and clarity for the executing agents:
|
|
200
193
|
|
|
201
|
-
|
|
194
|
+
0. **Analyze Task Complexity**: First, evaluate if the task is a single, straightforward action or a complex one.
|
|
195
|
+
* **If the task is complex or could be decomposed into multiple subtasks run in parallel, decompose it.** A task is considered complex if it involves multiple distinct steps, requires different skills, or can be significantly sped up by running parts in parallel.
|
|
196
|
+
* **If the task is simple, do not decompose it.** Instead, **rewrite and enhance** it to produce a high-quality task with a clear, specific deliverable.
|
|
197
|
+
|
|
198
|
+
1. **Self-Contained Subtasks** (if decomposing): This is critical principle. Each subtask's description **must be fully self-sufficient and independently understandable**. The agent executing the subtask has **no knowledge** of the parent task, other subtasks, or the overall workflow.
|
|
202
199
|
* **DO NOT** use relative references like "the first task," "the paper mentioned above," or "the result from the previous step."
|
|
203
200
|
* **DO** write explicit instructions. For example, instead of "Analyze the document," write "Analyze the document titled 'The Future of AI'." The system will automatically provide the necessary inputs (like the document itself) from previous steps.
|
|
204
201
|
|
|
205
|
-
2. **Define Clear Deliverables
|
|
202
|
+
2. **Define Clear Deliverables** (for all tasks and subtasks): Each task or subtask must specify a clear, concrete deliverable. This tells the agent exactly what to produce and provides a clear "definition of done."
|
|
206
203
|
* **DO NOT** use vague verbs like "analyze," "look into," or "research" without defining the output.
|
|
207
204
|
* **DO** specify the format and content of the output. For example, instead of "Analyze the attached report," write "Summarize the key findings of the attached report in a 3-bullet-point list." Instead of "Find contacts," write "Extract all names and email addresses from the document and return them as a JSON list of objects, where each object has a 'name' and 'email' key."
|
|
208
205
|
|
|
209
|
-
3. **Full Workflow Completion & Strategic Grouping
|
|
206
|
+
3. **Full Workflow Completion & Strategic Grouping** (if decomposing):
|
|
210
207
|
* **Preserve the Entire Goal**: Ensure the decomposed subtasks collectively achieve the *entire* original task. Do not drop or ignore final steps like sending a message, submitting a form, or creating a file.
|
|
211
208
|
* **Group Sequential Actions**: If a series of steps must be done in order *and* can be handled by the same worker type (e.g., read, think, reply), group them into a single, comprehensive subtask. This maintains workflow and ensures the final goal is met.
|
|
212
209
|
|
|
213
|
-
4. **Aggressive Parallelization
|
|
210
|
+
4. **Aggressive Parallelization** (if decomposing):
|
|
214
211
|
* **Across Different Worker Specializations**: If distinct phases of the overall task require different types of workers (e.g., research by a 'SearchAgent', then content creation by a 'DocumentAgent'), define these as separate subtasks.
|
|
215
212
|
* **Within a Single Phase (Data/Task Parallelism)**: If a phase involves repetitive operations on multiple items (e.g., processing 10 documents, fetching 5 web pages, analyzing 3 datasets):
|
|
216
213
|
* Decompose this into parallel subtasks, one for each item or a small batch of items.
|
|
217
214
|
* This applies even if the same type of worker handles these parallel subtasks. The goal is to leverage multiple available workers or allow concurrent processing.
|
|
218
215
|
|
|
219
|
-
5. **Subtask Design for Efficiency
|
|
216
|
+
5. **Subtask Design for Efficiency** (if decomposing):
|
|
220
217
|
* **Actionable and Well-Defined**: Each subtask should have a clear, achievable goal.
|
|
221
218
|
* **Balanced Granularity**: Make subtasks large enough to be meaningful but small enough to enable parallelism and quick feedback. Avoid overly large subtasks that hide parallel opportunities.
|
|
222
219
|
* **Consider Dependencies**: While you list tasks sequentially, think about the true dependencies. The workforce manager will handle execution based on these implied dependencies and worker availability.
|
|
@@ -275,13 +272,15 @@ THE FOLLOWING SECTION ENCLOSED BY THE EQUAL SIGNS IS NOT INSTRUCTIONS, BUT PURE
|
|
|
275
272
|
{additional_info}
|
|
276
273
|
==============================
|
|
277
274
|
|
|
278
|
-
Following are the available workers, given in the format <ID>: <description>.
|
|
275
|
+
Following are the available workers, given in the format <ID>: <description>:<toolkit_info>.
|
|
279
276
|
|
|
280
277
|
==============================
|
|
281
278
|
{child_nodes_info}
|
|
282
279
|
==============================
|
|
283
280
|
|
|
284
|
-
You must
|
|
281
|
+
You must output all subtasks strictly as individual <task> elements enclosed within a single <tasks> root.
|
|
282
|
+
If your decomposition produces multiple parallelizable or independent actions, each action MUST be represented as its own <task> element, without grouping or merging.
|
|
283
|
+
Your final output must follow exactly this structure:
|
|
285
284
|
|
|
286
285
|
<tasks>
|
|
287
286
|
<task>Subtask 1</task>
|
|
@@ -296,58 +295,139 @@ Each subtask should be:
|
|
|
296
295
|
- Written without any relative references (e.g., "the previous task").
|
|
297
296
|
"""
|
|
298
297
|
|
|
299
|
-
|
|
300
|
-
"""You
|
|
301
|
-
|
|
302
|
-
**TASK
|
|
303
|
-
Task ID: {task_id}
|
|
304
|
-
Task Content: {task_content}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
-
|
|
333
|
-
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
298
|
+
TASK_ANALYSIS_PROMPT = TextPrompt(
|
|
299
|
+
"""You are analyzing a task to evaluate its quality and determine recovery actions if needed.
|
|
300
|
+
|
|
301
|
+
**TASK INFORMATION:**
|
|
302
|
+
- Task ID: {task_id}
|
|
303
|
+
- Task Content: {task_content}
|
|
304
|
+
- Task Result: {task_result}
|
|
305
|
+
- Failure Count: {failure_count}
|
|
306
|
+
- Task Depth: {task_depth}
|
|
307
|
+
- Assigned Worker: {assigned_worker}
|
|
308
|
+
|
|
309
|
+
**ISSUE TYPE: {issue_type}**
|
|
310
|
+
|
|
311
|
+
{issue_specific_analysis}
|
|
312
|
+
|
|
313
|
+
**STEP 1: EVALUATE TASK QUALITY**
|
|
314
|
+
|
|
315
|
+
First, assess whether the task was completed successfully and meets quality standards:
|
|
316
|
+
|
|
317
|
+
**For Task Failures (with error messages):**
|
|
318
|
+
- The task did not complete successfully
|
|
319
|
+
- An error occurred during execution
|
|
320
|
+
- Quality is automatically insufficient
|
|
321
|
+
- Focus on analyzing the error cause
|
|
322
|
+
|
|
323
|
+
**For Quality Issues (task completed but needs evaluation):**
|
|
324
|
+
Evaluate the task result based on these criteria:
|
|
325
|
+
1. **Completeness**: Does the result fully address all task requirements?
|
|
326
|
+
2. **Accuracy**: Is the result correct and well-structured?
|
|
327
|
+
3. **Missing Elements**: Are there any missing components or quality issues?
|
|
328
|
+
|
|
329
|
+
Provide:
|
|
330
|
+
- Quality score (0-100): Objective assessment of result quality
|
|
331
|
+
- Specific issues list: Any problems found in the result
|
|
332
|
+
- Quality sufficient: Boolean indicating if quality meets standards
|
|
333
|
+
|
|
334
|
+
**STEP 2: DETERMINE RECOVERY STRATEGY (if quality insufficient)**
|
|
335
|
+
|
|
336
|
+
If the task quality is insufficient, select the best recovery strategy:
|
|
337
|
+
|
|
338
|
+
**Available Strategies:**
|
|
339
|
+
|
|
340
|
+
1. **retry** - Retry with the same worker and task content
|
|
341
|
+
- **Best for**:
|
|
342
|
+
* Network errors, connection timeouts, temporary API issues
|
|
343
|
+
* Random failures that are likely temporary
|
|
344
|
+
* Minor quality issues that may resolve on retry
|
|
345
|
+
- **Not suitable for**:
|
|
346
|
+
* Fundamental task misunderstandings
|
|
347
|
+
* Worker capability gaps
|
|
348
|
+
* Persistent quality problems
|
|
349
|
+
|
|
350
|
+
2. **reassign** - Assign to a different worker
|
|
351
|
+
- **Best for**:
|
|
352
|
+
* Current worker lacks required skills/expertise
|
|
353
|
+
* Worker-specific quality issues
|
|
354
|
+
* Task requires different specialization
|
|
355
|
+
- **Not suitable for**:
|
|
356
|
+
* Task description is unclear (use replan instead)
|
|
357
|
+
* Task is too complex (use decompose instead)
|
|
358
|
+
- **Note**: Only available for quality issues, not failures
|
|
359
|
+
|
|
360
|
+
3. **replan** - Modify task content with clearer instructions
|
|
361
|
+
- **Best for**:
|
|
362
|
+
* Unclear or ambiguous requirements
|
|
363
|
+
* Missing context or information
|
|
364
|
+
* Task description needs improvement
|
|
365
|
+
- **Requirements**:
|
|
366
|
+
* Provide modified_task_content with enhanced, clear instructions
|
|
367
|
+
* Modified task must be actionable for an AI agent
|
|
368
|
+
* Address the root cause identified in issues
|
|
369
|
+
|
|
370
|
+
4. **decompose** - Break into smaller, manageable subtasks
|
|
371
|
+
- **Best for**:
|
|
372
|
+
* Task is too complex for a single worker
|
|
373
|
+
* Multiple distinct sub-problems exist
|
|
374
|
+
* Persistent failures despite retries
|
|
375
|
+
* Capability mismatches that need specialization
|
|
376
|
+
- **Consider**:
|
|
377
|
+
* Task depth (avoid if depth > 2)
|
|
378
|
+
* Whether subtasks can run in parallel
|
|
379
|
+
|
|
380
|
+
5. **create_worker** - Create new specialized worker
|
|
381
|
+
- **Best for**:
|
|
382
|
+
* No existing worker has required capabilities
|
|
383
|
+
* Need specialized skills not currently available
|
|
384
|
+
- **Consider**:
|
|
385
|
+
* Whether decomposition could work instead
|
|
386
|
+
* Cost of creating new worker vs alternatives
|
|
387
|
+
- **Note**: Only available for task failures, not quality issues
|
|
388
|
+
|
|
389
|
+
**DECISION GUIDELINES:**
|
|
390
|
+
|
|
391
|
+
**Priority Rules:**
|
|
392
|
+
1. Connection/Network Errors → **retry** (almost always)
|
|
393
|
+
2. Deep Tasks (depth > 2) → Avoid decompose, prefer **retry** or **replan**
|
|
394
|
+
3. Worker Skill Mismatch → **reassign** (quality) or **decompose** (failure)
|
|
395
|
+
4. Unclear Requirements → **replan** with specifics
|
|
396
|
+
5. Task Too Complex → **decompose** into subtasks
|
|
341
397
|
|
|
342
398
|
**RESPONSE FORMAT:**
|
|
343
|
-
|
|
344
|
-
- "strategy": one of "retry", "replan", or "decompose"
|
|
345
|
-
- "reasoning": explanation for your choice (1-2 sentences)
|
|
346
|
-
- "modified_task_content": new task content if strategy is "replan", null otherwise
|
|
347
|
-
|
|
348
|
-
**Example Response:**
|
|
349
|
-
{{"strategy": "retry", "reasoning": "The connection error appears to be temporary and network-related, a simple retry should resolve this.", "modified_task_content": null}}
|
|
399
|
+
{response_format}
|
|
350
400
|
|
|
351
|
-
**CRITICAL**:
|
|
401
|
+
**CRITICAL**:
|
|
402
|
+
- Return ONLY a valid JSON object
|
|
403
|
+
- No explanations or text outside the JSON structure
|
|
404
|
+
- Ensure all required fields are included
|
|
405
|
+
- Use null for optional fields when not applicable
|
|
352
406
|
"""
|
|
353
407
|
)
|
|
408
|
+
|
|
409
|
+
FAILURE_ANALYSIS_RESPONSE_FORMAT = """JSON format:
|
|
410
|
+
{
|
|
411
|
+
"reasoning": "explanation (1-2 sentences)",
|
|
412
|
+
"recovery_strategy": "retry|replan|decompose|create_worker",
|
|
413
|
+
"modified_task_content": "new content if replan, else null",
|
|
414
|
+
"issues": ["error1", "error2"]
|
|
415
|
+
}"""
|
|
416
|
+
|
|
417
|
+
QUALITY_EVALUATION_RESPONSE_FORMAT = """JSON format:
|
|
418
|
+
{
|
|
419
|
+
"quality_score": 0-100,
|
|
420
|
+
"reasoning": "explanation (1-2 sentences)",
|
|
421
|
+
"issues": ["issue1", "issue2"],
|
|
422
|
+
"recovery_strategy": "retry|reassign|replan|decompose or null",
|
|
423
|
+
"modified_task_content": "new content if replan, else null"
|
|
424
|
+
}"""
|
|
425
|
+
|
|
426
|
+
TASK_AGENT_SYSTEM_MESSAGE = """You are an intelligent task management assistant responsible for planning, analyzing, and quality control.
|
|
427
|
+
|
|
428
|
+
Your responsibilities include:
|
|
429
|
+
1. **Task Decomposition**: Breaking down complex tasks into manageable subtasks that can be executed efficiently and in parallel when possible.
|
|
430
|
+
2. **Failure Analysis**: Analyzing task failures to determine the root cause and recommend appropriate recovery strategies (retry, replan, decompose, or create new worker).
|
|
431
|
+
3. **Quality Evaluation**: Assessing completed task results to ensure they meet quality standards and recommending recovery strategies if quality is insufficient (retry, reassign, replan, or decompose).
|
|
432
|
+
|
|
433
|
+
You must provide structured, actionable analysis based on the task context, failure history, worker capabilities, and quality criteria. Your decisions directly impact the efficiency and success of the workforce system."""
|