camel-ai 0.2.75a5__py3-none-any.whl → 0.2.76__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.

Potentially problematic release.


This version of camel-ai might be problematic. Click here for more details.

Files changed (103) hide show
  1. camel/__init__.py +1 -1
  2. camel/agents/chat_agent.py +1148 -298
  3. camel/agents/mcp_agent.py +30 -27
  4. camel/configs/__init__.py +9 -0
  5. camel/configs/amd_config.py +70 -0
  6. camel/configs/cometapi_config.py +104 -0
  7. camel/configs/nebius_config.py +103 -0
  8. camel/data_collectors/alpaca_collector.py +15 -6
  9. camel/environments/tic_tac_toe.py +1 -1
  10. camel/interpreters/__init__.py +2 -0
  11. camel/interpreters/docker/Dockerfile +3 -12
  12. camel/interpreters/microsandbox_interpreter.py +395 -0
  13. camel/loaders/__init__.py +11 -2
  14. camel/loaders/chunkr_reader.py +9 -0
  15. camel/memories/__init__.py +2 -1
  16. camel/memories/agent_memories.py +3 -1
  17. camel/memories/blocks/chat_history_block.py +21 -3
  18. camel/memories/records.py +88 -8
  19. camel/messages/base.py +127 -34
  20. camel/models/__init__.py +6 -0
  21. camel/models/amd_model.py +101 -0
  22. camel/models/azure_openai_model.py +0 -6
  23. camel/models/base_model.py +30 -0
  24. camel/models/cometapi_model.py +83 -0
  25. camel/models/model_factory.py +6 -0
  26. camel/models/nebius_model.py +83 -0
  27. camel/models/ollama_model.py +3 -3
  28. camel/models/openai_compatible_model.py +0 -6
  29. camel/models/openai_model.py +0 -6
  30. camel/models/zhipuai_model.py +61 -2
  31. camel/parsers/__init__.py +18 -0
  32. camel/parsers/mcp_tool_call_parser.py +176 -0
  33. camel/retrievers/auto_retriever.py +1 -0
  34. camel/runtimes/daytona_runtime.py +11 -12
  35. camel/societies/workforce/prompts.py +131 -50
  36. camel/societies/workforce/single_agent_worker.py +434 -49
  37. camel/societies/workforce/structured_output_handler.py +30 -18
  38. camel/societies/workforce/task_channel.py +163 -27
  39. camel/societies/workforce/utils.py +105 -12
  40. camel/societies/workforce/workforce.py +1357 -314
  41. camel/societies/workforce/workforce_logger.py +24 -5
  42. camel/storages/key_value_storages/json.py +15 -2
  43. camel/storages/object_storages/google_cloud.py +1 -1
  44. camel/storages/vectordb_storages/oceanbase.py +10 -11
  45. camel/storages/vectordb_storages/tidb.py +8 -6
  46. camel/tasks/task.py +4 -3
  47. camel/toolkits/__init__.py +18 -5
  48. camel/toolkits/aci_toolkit.py +45 -0
  49. camel/toolkits/code_execution.py +28 -1
  50. camel/toolkits/context_summarizer_toolkit.py +684 -0
  51. camel/toolkits/dingtalk.py +1135 -0
  52. camel/toolkits/edgeone_pages_mcp_toolkit.py +11 -31
  53. camel/toolkits/{file_write_toolkit.py → file_toolkit.py} +194 -34
  54. camel/toolkits/function_tool.py +6 -1
  55. camel/toolkits/github_toolkit.py +104 -17
  56. camel/toolkits/google_drive_mcp_toolkit.py +12 -31
  57. camel/toolkits/hybrid_browser_toolkit/config_loader.py +12 -0
  58. camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit.py +79 -2
  59. camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit_ts.py +95 -59
  60. camel/toolkits/hybrid_browser_toolkit/installer.py +203 -0
  61. camel/toolkits/hybrid_browser_toolkit/ts/package-lock.json +5 -612
  62. camel/toolkits/hybrid_browser_toolkit/ts/package.json +0 -1
  63. camel/toolkits/hybrid_browser_toolkit/ts/src/browser-session.ts +619 -95
  64. camel/toolkits/hybrid_browser_toolkit/ts/src/config-loader.ts +7 -2
  65. camel/toolkits/hybrid_browser_toolkit/ts/src/hybrid-browser-toolkit.ts +115 -219
  66. camel/toolkits/hybrid_browser_toolkit/ts/src/parent-child-filter.ts +226 -0
  67. camel/toolkits/hybrid_browser_toolkit/ts/src/snapshot-parser.ts +219 -0
  68. camel/toolkits/hybrid_browser_toolkit/ts/src/som-screenshot-injected.ts +543 -0
  69. camel/toolkits/hybrid_browser_toolkit/ts/src/types.ts +1 -0
  70. camel/toolkits/hybrid_browser_toolkit/ts/websocket-server.js +39 -6
  71. camel/toolkits/hybrid_browser_toolkit/ws_wrapper.py +412 -133
  72. camel/toolkits/hybrid_browser_toolkit_py/hybrid_browser_toolkit.py +9 -5
  73. camel/toolkits/{openai_image_toolkit.py → image_generation_toolkit.py} +98 -31
  74. camel/toolkits/markitdown_toolkit.py +27 -1
  75. camel/toolkits/math_toolkit.py +64 -10
  76. camel/toolkits/mcp_toolkit.py +348 -348
  77. camel/toolkits/message_integration.py +3 -0
  78. camel/toolkits/minimax_mcp_toolkit.py +195 -0
  79. camel/toolkits/note_taking_toolkit.py +18 -8
  80. camel/toolkits/notion_mcp_toolkit.py +16 -26
  81. camel/toolkits/origene_mcp_toolkit.py +8 -49
  82. camel/toolkits/playwright_mcp_toolkit.py +12 -31
  83. camel/toolkits/resend_toolkit.py +168 -0
  84. camel/toolkits/search_toolkit.py +13 -2
  85. camel/toolkits/slack_toolkit.py +50 -1
  86. camel/toolkits/terminal_toolkit/__init__.py +18 -0
  87. camel/toolkits/terminal_toolkit/terminal_toolkit.py +924 -0
  88. camel/toolkits/terminal_toolkit/utils.py +532 -0
  89. camel/toolkits/vertex_ai_veo_toolkit.py +590 -0
  90. camel/toolkits/video_analysis_toolkit.py +17 -11
  91. camel/toolkits/wechat_official_toolkit.py +483 -0
  92. camel/types/enums.py +155 -1
  93. camel/types/unified_model_type.py +10 -0
  94. camel/utils/commons.py +17 -0
  95. camel/utils/context_utils.py +804 -0
  96. camel/utils/mcp.py +136 -2
  97. camel/utils/token_counting.py +25 -17
  98. {camel_ai-0.2.75a5.dist-info → camel_ai-0.2.76.dist-info}/METADATA +158 -67
  99. {camel_ai-0.2.75a5.dist-info → camel_ai-0.2.76.dist-info}/RECORD +101 -80
  100. camel/loaders/pandas_reader.py +0 -368
  101. camel/toolkits/terminal_toolkit.py +0 -1788
  102. {camel_ai-0.2.75a5.dist-info → camel_ai-0.2.76.dist-info}/WHEEL +0 -0
  103. {camel_ai-0.2.75a5.dist-info → camel_ai-0.2.76.dist-info}/licenses/LICENSE +0 -0
@@ -35,8 +35,8 @@ class OllamaModel(OpenAICompatibleModel):
35
35
  If:obj:`None`, :obj:`OllamaConfig().as_dict()` will be used.
36
36
  (default: :obj:`None`)
37
37
  api_key (Optional[str], optional): The API key for authenticating with
38
- the model service. Ollama doesn't need API key, it would be
39
- ignored if set. (default: :obj:`None`)
38
+ the model service. Required for Ollama cloud services. If not
39
+ provided, defaults to "Not_Provided". (default: :obj:`None`)
40
40
  url (Optional[str], optional): The url to the model service.
41
41
  (default: :obj:`None`)
42
42
  token_counter (Optional[BaseTokenCounter], optional): Token counter to
@@ -79,7 +79,7 @@ class OllamaModel(OpenAICompatibleModel):
79
79
  super().__init__(
80
80
  model_type=self._model_type,
81
81
  model_config_dict=model_config_dict,
82
- api_key="Not_Used",
82
+ api_key=api_key or "Not_Provided",
83
83
  url=self._url,
84
84
  token_counter=token_counter,
85
85
  timeout=timeout,
@@ -190,9 +190,6 @@ class OpenAICompatibleModel(BaseModelBackend):
190
190
  is_streaming = self.model_config_dict.get("stream", False)
191
191
 
192
192
  if response_format:
193
- result: Union[ChatCompletion, Stream[ChatCompletionChunk]] = (
194
- self._request_parse(messages, response_format, tools)
195
- )
196
193
  if is_streaming:
197
194
  # Use streaming parse for structured output
198
195
  return self._request_stream_parse(
@@ -256,9 +253,6 @@ class OpenAICompatibleModel(BaseModelBackend):
256
253
  is_streaming = self.model_config_dict.get("stream", False)
257
254
 
258
255
  if response_format:
259
- result: Union[
260
- ChatCompletion, AsyncStream[ChatCompletionChunk]
261
- ] = await self._arequest_parse(messages, response_format, tools)
262
256
  if is_streaming:
263
257
  # Use streaming parse for structured output
264
258
  return await self._arequest_stream_parse(
@@ -303,9 +303,6 @@ class OpenAIModel(BaseModelBackend):
303
303
  is_streaming = self.model_config_dict.get("stream", False)
304
304
 
305
305
  if response_format:
306
- result: Union[ChatCompletion, Stream[ChatCompletionChunk]] = (
307
- self._request_parse(messages, response_format, tools)
308
- )
309
306
  if is_streaming:
310
307
  # Use streaming parse for structured output
311
308
  return self._request_stream_parse(
@@ -377,9 +374,6 @@ class OpenAIModel(BaseModelBackend):
377
374
  is_streaming = self.model_config_dict.get("stream", False)
378
375
 
379
376
  if response_format:
380
- result: Union[
381
- ChatCompletion, AsyncStream[ChatCompletionChunk]
382
- ] = await self._arequest_parse(messages, response_format, tools)
383
377
  if is_streaming:
384
378
  # Use streaming parse for structured output
385
379
  return await self._arequest_stream_parse(
@@ -13,16 +13,26 @@
13
13
  # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
14
14
 
15
15
  import os
16
- from typing import Any, Dict, Optional, Union
16
+ from typing import Any, Dict, List, Optional, Type, Union
17
+
18
+ from pydantic import BaseModel
17
19
 
18
20
  from camel.configs import ZhipuAIConfig
21
+ from camel.logger import get_logger
22
+ from camel.messages import OpenAIMessage
23
+ from camel.models._utils import try_modify_message_with_format
19
24
  from camel.models.openai_compatible_model import OpenAICompatibleModel
20
- from camel.types import ModelType
25
+ from camel.types import (
26
+ ChatCompletion,
27
+ ModelType,
28
+ )
21
29
  from camel.utils import (
22
30
  BaseTokenCounter,
23
31
  api_keys_required,
24
32
  )
25
33
 
34
+ logger = get_logger(__name__)
35
+
26
36
 
27
37
  class ZhipuAIModel(OpenAICompatibleModel):
28
38
  r"""ZhipuAI API in a unified OpenAICompatibleModel interface.
@@ -85,3 +95,52 @@ class ZhipuAIModel(OpenAICompatibleModel):
85
95
  max_retries=max_retries,
86
96
  **kwargs,
87
97
  )
98
+
99
+ def _request_parse(
100
+ self,
101
+ messages: List[OpenAIMessage],
102
+ response_format: Type[BaseModel],
103
+ tools: Optional[List[Dict[str, Any]]] = None,
104
+ ) -> ChatCompletion:
105
+ import copy
106
+
107
+ request_config = copy.deepcopy(self.model_config_dict)
108
+ request_config.pop("stream", None)
109
+ if tools is not None:
110
+ request_config["tools"] = tools
111
+
112
+ try_modify_message_with_format(messages[-1], response_format)
113
+ request_config["response_format"] = {"type": "json_object"}
114
+ try:
115
+ return self._client.beta.chat.completions.parse(
116
+ messages=messages,
117
+ model=self.model_type,
118
+ **request_config,
119
+ )
120
+ except Exception as e:
121
+ logger.error(f"Fallback attempt also failed: {e}")
122
+ raise
123
+
124
+ async def _arequest_parse(
125
+ self,
126
+ messages: List[OpenAIMessage],
127
+ response_format: Type[BaseModel],
128
+ tools: Optional[List[Dict[str, Any]]] = None,
129
+ ) -> ChatCompletion:
130
+ import copy
131
+
132
+ request_config = copy.deepcopy(self.model_config_dict)
133
+ request_config.pop("stream", None)
134
+ if tools is not None:
135
+ request_config["tools"] = tools
136
+ try_modify_message_with_format(messages[-1], response_format)
137
+ request_config["response_format"] = {"type": "json_object"}
138
+ try:
139
+ return await self._async_client.beta.chat.completions.parse(
140
+ messages=messages,
141
+ model=self.model_type,
142
+ **request_config,
143
+ )
144
+ except Exception as e:
145
+ logger.error(f"Fallback attempt also failed: {e}")
146
+ raise
@@ -0,0 +1,18 @@
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
+ """Helper parsers used across the CAMEL project."""
15
+
16
+ from .mcp_tool_call_parser import extract_tool_calls_from_text
17
+
18
+ __all__ = ["extract_tool_calls_from_text"]
@@ -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 CreateSandboxParams
69
+ from daytona_sdk import CreateSandboxBaseParams
70
70
 
71
71
  try:
72
- params = CreateSandboxParams(language=self.language)
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.remove(self.sandbox)
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("Failed to create sandbox.")
212
- info = self.sandbox.info()
211
+ raise RuntimeError("Sandbox not initialized.")
213
212
  return (
214
- f"Sandbox {info.name}:\n"
215
- f"State: {info.state}\n"
216
- f"Resources: {info.resources.cpu} CPU, {info.resources.memory} RAM"
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):
@@ -293,58 +293,139 @@ Each subtask should be:
293
293
  - Written without any relative references (e.g., "the previous task").
294
294
  """
295
295
 
296
- FAILURE_ANALYSIS_PROMPT = TextPrompt(
297
- """You need to analyze a task failure and decide on the best recovery strategy.
298
-
299
- **TASK FAILURE DETAILS:**
300
- Task ID: {task_id}
301
- Task Content: {task_content}
302
- Failure Count: {failure_count}/3
303
- Error Message: {error_message}
304
- Worker ID: {worker_id}
305
- Task Depth: {task_depth}
306
- Additional Info: {additional_info}
307
-
308
- **AVAILABLE RECOVERY STRATEGIES:**
309
-
310
- 1. **RETRY**: Attempt the same task again without changes
311
- - Use for: Network errors, temporary API issues, random failures
312
- - Avoid for: Fundamental task misunderstanding, capability gaps
313
-
314
- 2. **REPLAN**: Modify the task content to address the underlying issue
315
- - Use for: Unclear requirements, insufficient context, correctable errors
316
- - Provide: Modified task content that addresses the failure cause
317
- - **CRITICAL**: The replanned task MUST be a clear, actionable
318
- instruction for an AI agent, not a question or request for a human.
319
-
320
- 3. **DECOMPOSE**: Break the task into smaller, more manageable subtasks
321
- - Use for: Complex tasks, capability mismatches, persistent failures
322
- - Consider: Whether the task is too complex for a single worker
323
-
324
- 4. **CREATE_WORKER**: Create a new worker node to handle the task
325
- - Use for: Fundamental task misunderstanding, capability gaps
326
-
327
- **ANALYSIS GUIDELINES:**
328
-
329
- - **Connection/Network Errors**: Almost always choose RETRY
330
- - **Model Processing Errors**: Consider REPLAN if the task can be clarified, otherwise DECOMPOSE
331
- - **Capability Gaps**: Choose DECOMPOSE to break into simpler parts. If a
332
- replan can work, ensure the new task is a command for an agent, not a
333
- request to a user.
334
- - **Ambiguous Requirements**: Choose REPLAN with clearer instructions
335
- - **High Failure Count**: Lean towards DECOMPOSE rather than repeated retries
336
- - **Deep Tasks (depth > 2)**: Prefer RETRY or REPLAN over further
337
- decomposition
296
+ TASK_ANALYSIS_PROMPT = TextPrompt(
297
+ """You are analyzing a task to evaluate its quality and determine recovery actions if needed.
298
+
299
+ **TASK INFORMATION:**
300
+ - Task ID: {task_id}
301
+ - Task Content: {task_content}
302
+ - Task Result: {task_result}
303
+ - Failure Count: {failure_count}
304
+ - Task Depth: {task_depth}
305
+ - Assigned Worker: {assigned_worker}
306
+
307
+ **ISSUE TYPE: {issue_type}**
308
+
309
+ {issue_specific_analysis}
310
+
311
+ **STEP 1: EVALUATE TASK QUALITY**
312
+
313
+ First, assess whether the task was completed successfully and meets quality standards:
314
+
315
+ **For Task Failures (with error messages):**
316
+ - The task did not complete successfully
317
+ - An error occurred during execution
318
+ - Quality is automatically insufficient
319
+ - Focus on analyzing the error cause
320
+
321
+ **For Quality Issues (task completed but needs evaluation):**
322
+ Evaluate the task result based on these criteria:
323
+ 1. **Completeness**: Does the result fully address all task requirements?
324
+ 2. **Accuracy**: Is the result correct and well-structured?
325
+ 3. **Missing Elements**: Are there any missing components or quality issues?
326
+
327
+ Provide:
328
+ - Quality score (0-100): Objective assessment of result quality
329
+ - Specific issues list: Any problems found in the result
330
+ - Quality sufficient: Boolean indicating if quality meets standards
331
+
332
+ **STEP 2: DETERMINE RECOVERY STRATEGY (if quality insufficient)**
333
+
334
+ If the task quality is insufficient, select the best recovery strategy:
335
+
336
+ **Available Strategies:**
337
+
338
+ 1. **retry** - Retry with the same worker and task content
339
+ - **Best for**:
340
+ * Network errors, connection timeouts, temporary API issues
341
+ * Random failures that are likely temporary
342
+ * Minor quality issues that may resolve on retry
343
+ - **Not suitable for**:
344
+ * Fundamental task misunderstandings
345
+ * Worker capability gaps
346
+ * Persistent quality problems
347
+
348
+ 2. **reassign** - Assign to a different worker
349
+ - **Best for**:
350
+ * Current worker lacks required skills/expertise
351
+ * Worker-specific quality issues
352
+ * Task requires different specialization
353
+ - **Not suitable for**:
354
+ * Task description is unclear (use replan instead)
355
+ * Task is too complex (use decompose instead)
356
+ - **Note**: Only available for quality issues, not failures
357
+
358
+ 3. **replan** - Modify task content with clearer instructions
359
+ - **Best for**:
360
+ * Unclear or ambiguous requirements
361
+ * Missing context or information
362
+ * Task description needs improvement
363
+ - **Requirements**:
364
+ * Provide modified_task_content with enhanced, clear instructions
365
+ * Modified task must be actionable for an AI agent
366
+ * Address the root cause identified in issues
367
+
368
+ 4. **decompose** - Break into smaller, manageable subtasks
369
+ - **Best for**:
370
+ * Task is too complex for a single worker
371
+ * Multiple distinct sub-problems exist
372
+ * Persistent failures despite retries
373
+ * Capability mismatches that need specialization
374
+ - **Consider**:
375
+ * Task depth (avoid if depth > 2)
376
+ * Whether subtasks can run in parallel
377
+
378
+ 5. **create_worker** - Create new specialized worker
379
+ - **Best for**:
380
+ * No existing worker has required capabilities
381
+ * Need specialized skills not currently available
382
+ - **Consider**:
383
+ * Whether decomposition could work instead
384
+ * Cost of creating new worker vs alternatives
385
+ - **Note**: Only available for task failures, not quality issues
386
+
387
+ **DECISION GUIDELINES:**
388
+
389
+ **Priority Rules:**
390
+ 1. Connection/Network Errors → **retry** (almost always)
391
+ 2. Deep Tasks (depth > 2) → Avoid decompose, prefer **retry** or **replan**
392
+ 3. Worker Skill Mismatch → **reassign** (quality) or **decompose** (failure)
393
+ 4. Unclear Requirements → **replan** with specifics
394
+ 5. Task Too Complex → **decompose** into subtasks
338
395
 
339
396
  **RESPONSE FORMAT:**
340
- You must return a valid JSON object with these fields:
341
- - "strategy": one of "retry", "replan", or "decompose"
342
- - "reasoning": explanation for your choice (1-2 sentences)
343
- - "modified_task_content": new task content if strategy is "replan", null otherwise
397
+ {response_format}
344
398
 
345
- **Example Response:**
346
- {{"strategy": "retry", "reasoning": "The connection error appears to be temporary and network-related, a simple retry should resolve this.", "modified_task_content": null}}
347
-
348
- **CRITICAL**: Return ONLY the JSON object. No explanations or text outside the JSON structure.
399
+ **CRITICAL**:
400
+ - Return ONLY a valid JSON object
401
+ - No explanations or text outside the JSON structure
402
+ - Ensure all required fields are included
403
+ - Use null for optional fields when not applicable
349
404
  """
350
405
  )
406
+
407
+ FAILURE_ANALYSIS_RESPONSE_FORMAT = """JSON format:
408
+ {
409
+ "reasoning": "explanation (1-2 sentences)",
410
+ "recovery_strategy": "retry|replan|decompose|create_worker",
411
+ "modified_task_content": "new content if replan, else null",
412
+ "issues": ["error1", "error2"]
413
+ }"""
414
+
415
+ QUALITY_EVALUATION_RESPONSE_FORMAT = """JSON format:
416
+ {
417
+ "quality_score": 0-100,
418
+ "reasoning": "explanation (1-2 sentences)",
419
+ "issues": ["issue1", "issue2"],
420
+ "recovery_strategy": "retry|reassign|replan|decompose or null",
421
+ "modified_task_content": "new content if replan, else null"
422
+ }"""
423
+
424
+ TASK_AGENT_SYSTEM_MESSAGE = """You are an intelligent task management assistant responsible for planning, analyzing, and quality control.
425
+
426
+ Your responsibilities include:
427
+ 1. **Task Decomposition**: Breaking down complex tasks into manageable subtasks that can be executed efficiently and in parallel when possible.
428
+ 2. **Failure Analysis**: Analyzing task failures to determine the root cause and recommend appropriate recovery strategies (retry, replan, decompose, or create new worker).
429
+ 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).
430
+
431
+ 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."""