camel-ai 0.2.65__py3-none-any.whl → 0.2.67__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/mcp_agent.py +1 -5
- camel/configs/__init__.py +3 -0
- camel/configs/qianfan_config.py +85 -0
- camel/models/__init__.py +2 -0
- camel/models/aiml_model.py +8 -0
- camel/models/anthropic_model.py +8 -0
- camel/models/aws_bedrock_model.py +8 -0
- camel/models/azure_openai_model.py +14 -5
- camel/models/base_model.py +4 -0
- camel/models/cohere_model.py +9 -2
- camel/models/crynux_model.py +8 -0
- camel/models/deepseek_model.py +8 -0
- camel/models/gemini_model.py +8 -0
- camel/models/groq_model.py +8 -0
- camel/models/internlm_model.py +8 -0
- camel/models/litellm_model.py +5 -0
- camel/models/lmstudio_model.py +14 -1
- camel/models/mistral_model.py +15 -1
- camel/models/model_factory.py +6 -0
- camel/models/modelscope_model.py +8 -0
- camel/models/moonshot_model.py +8 -0
- camel/models/nemotron_model.py +17 -2
- camel/models/netmind_model.py +8 -0
- camel/models/novita_model.py +8 -0
- camel/models/nvidia_model.py +8 -0
- camel/models/ollama_model.py +8 -0
- camel/models/openai_compatible_model.py +23 -5
- camel/models/openai_model.py +21 -4
- camel/models/openrouter_model.py +8 -0
- camel/models/ppio_model.py +8 -0
- camel/models/qianfan_model.py +104 -0
- camel/models/qwen_model.py +8 -0
- camel/models/reka_model.py +18 -3
- camel/models/samba_model.py +17 -3
- camel/models/sglang_model.py +20 -5
- camel/models/siliconflow_model.py +8 -0
- camel/models/stub_model.py +8 -1
- camel/models/togetherai_model.py +8 -0
- camel/models/vllm_model.py +7 -0
- camel/models/volcano_model.py +14 -1
- camel/models/watsonx_model.py +4 -1
- camel/models/yi_model.py +8 -0
- camel/models/zhipuai_model.py +8 -0
- camel/societies/workforce/prompts.py +33 -17
- camel/societies/workforce/role_playing_worker.py +5 -10
- camel/societies/workforce/single_agent_worker.py +3 -5
- camel/societies/workforce/task_channel.py +16 -18
- camel/societies/workforce/utils.py +104 -65
- camel/societies/workforce/workforce.py +1263 -100
- camel/societies/workforce/workforce_logger.py +613 -0
- camel/tasks/task.py +77 -6
- camel/toolkits/__init__.py +2 -0
- camel/toolkits/code_execution.py +1 -1
- camel/toolkits/function_tool.py +79 -7
- camel/toolkits/mcp_toolkit.py +70 -19
- camel/toolkits/playwright_mcp_toolkit.py +2 -1
- camel/toolkits/pptx_toolkit.py +4 -4
- camel/types/enums.py +32 -0
- camel/types/unified_model_type.py +5 -0
- camel/utils/mcp_client.py +1 -35
- {camel_ai-0.2.65.dist-info → camel_ai-0.2.67.dist-info}/METADATA +3 -3
- {camel_ai-0.2.65.dist-info → camel_ai-0.2.67.dist-info}/RECORD +65 -62
- {camel_ai-0.2.65.dist-info → camel_ai-0.2.67.dist-info}/WHEEL +0 -0
- {camel_ai-0.2.65.dist-info → camel_ai-0.2.67.dist-info}/licenses/LICENSE +0 -0
|
@@ -12,14 +12,10 @@
|
|
|
12
12
|
# limitations under the License.
|
|
13
13
|
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
|
|
14
14
|
from functools import wraps
|
|
15
|
-
from typing import Callable
|
|
15
|
+
from typing import Callable, List
|
|
16
16
|
|
|
17
17
|
from pydantic import BaseModel, Field
|
|
18
18
|
|
|
19
|
-
from camel.logger import get_logger
|
|
20
|
-
|
|
21
|
-
logger = get_logger(__name__)
|
|
22
|
-
|
|
23
19
|
|
|
24
20
|
class WorkerConf(BaseModel):
|
|
25
21
|
r"""The configuration of a worker."""
|
|
@@ -45,80 +41,123 @@ class TaskResult(BaseModel):
|
|
|
45
41
|
)
|
|
46
42
|
|
|
47
43
|
|
|
48
|
-
class
|
|
49
|
-
r"""
|
|
44
|
+
class TaskAssignment(BaseModel):
|
|
45
|
+
r"""An individual task assignment within a batch."""
|
|
50
46
|
|
|
47
|
+
task_id: str = Field(description="The ID of the task to be assigned.")
|
|
51
48
|
assignee_id: str = Field(
|
|
52
|
-
description="The ID of the workforce
|
|
49
|
+
description="The ID of the worker/workforce to assign the task to."
|
|
50
|
+
)
|
|
51
|
+
dependencies: List[str] = Field(
|
|
52
|
+
default_factory=list,
|
|
53
|
+
description="List of task IDs that must complete before this task.",
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class TaskAssignResult(BaseModel):
|
|
58
|
+
r"""The result of task assignment for both single and batch assignments."""
|
|
59
|
+
|
|
60
|
+
assignments: List[TaskAssignment] = Field(
|
|
61
|
+
description="List of task assignments."
|
|
53
62
|
)
|
|
54
63
|
|
|
55
64
|
|
|
56
|
-
def check_if_running(
|
|
57
|
-
|
|
58
|
-
|
|
65
|
+
def check_if_running(
|
|
66
|
+
running: bool,
|
|
67
|
+
max_retries: int = 3,
|
|
68
|
+
retry_delay: float = 1.0,
|
|
69
|
+
handle_exceptions: bool = False,
|
|
70
|
+
) -> Callable:
|
|
71
|
+
r"""Check if the workforce is (not) running, specified by the boolean
|
|
72
|
+
value. Provides fault tolerance through automatic retries and exception
|
|
73
|
+
handling.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
running (bool): Expected running state (True or False).
|
|
77
|
+
max_retries (int, optional): Maximum number of retry attempts if the
|
|
78
|
+
operation fails. Set to 0 to disable retries. (default: :obj:`3`)
|
|
79
|
+
retry_delay (float, optional): Delay in seconds between retry attempts.
|
|
80
|
+
(default: :obj:`1.0`)
|
|
81
|
+
handle_exceptions (bool, optional): If True, catch and log exceptions
|
|
82
|
+
instead of propagating them. (default: :obj:`False`)
|
|
59
83
|
|
|
60
84
|
Raises:
|
|
61
|
-
RuntimeError: If the workforce is not in the expected status
|
|
85
|
+
RuntimeError: If the workforce is not in the expected status and
|
|
86
|
+
retries are exhausted or disabled.
|
|
87
|
+
Exception: Any exception raised by the decorated function if
|
|
88
|
+
handle_exceptions is False and retries are exhausted.
|
|
62
89
|
"""
|
|
90
|
+
import logging
|
|
91
|
+
import time
|
|
92
|
+
|
|
93
|
+
logger = logging.getLogger(__name__)
|
|
63
94
|
|
|
64
95
|
def decorator(func):
|
|
65
96
|
@wraps(func)
|
|
66
97
|
def wrapper(self, *args, **kwargs):
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
98
|
+
retries = 0
|
|
99
|
+
last_exception = None
|
|
100
|
+
|
|
101
|
+
while retries <= max_retries:
|
|
102
|
+
try:
|
|
103
|
+
# Check running state
|
|
104
|
+
if self._running != running:
|
|
105
|
+
status = "not running" if running else "running"
|
|
106
|
+
error_msg = (
|
|
107
|
+
f"The workforce is {status}. Cannot perform the "
|
|
108
|
+
f"operation {func.__name__}."
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
# If we have retries left, wait and try again
|
|
112
|
+
if retries < max_retries:
|
|
113
|
+
logger.warning(
|
|
114
|
+
f"{error_msg} Retrying in {retry_delay}s... "
|
|
115
|
+
f"(Attempt {retries+1}/{max_retries})"
|
|
116
|
+
)
|
|
117
|
+
time.sleep(retry_delay)
|
|
118
|
+
retries += 1
|
|
119
|
+
continue
|
|
120
|
+
else:
|
|
121
|
+
raise RuntimeError(error_msg)
|
|
122
|
+
|
|
123
|
+
return func(self, *args, **kwargs)
|
|
124
|
+
|
|
125
|
+
except Exception as e:
|
|
126
|
+
last_exception = e
|
|
127
|
+
|
|
128
|
+
if isinstance(e, RuntimeError) and "workforce is" in str(
|
|
129
|
+
e
|
|
130
|
+
):
|
|
131
|
+
raise
|
|
132
|
+
|
|
133
|
+
if retries < max_retries:
|
|
134
|
+
logger.warning(
|
|
135
|
+
f"Exception in {func.__name__}: {e}. "
|
|
136
|
+
f"Retrying in {retry_delay}s... "
|
|
137
|
+
f"(Attempt {retries+1}/{max_retries})"
|
|
138
|
+
)
|
|
139
|
+
time.sleep(retry_delay)
|
|
140
|
+
retries += 1
|
|
141
|
+
else:
|
|
142
|
+
if handle_exceptions:
|
|
143
|
+
logger.error(
|
|
144
|
+
f"Failed to execute {func.__name__} after "
|
|
145
|
+
f"{max_retries} retries: {e}"
|
|
146
|
+
)
|
|
147
|
+
return None
|
|
148
|
+
else:
|
|
149
|
+
# Re-raise the exception
|
|
150
|
+
raise
|
|
151
|
+
|
|
152
|
+
# This should not be reached, but just in case
|
|
153
|
+
if handle_exceptions:
|
|
154
|
+
logger.error(
|
|
155
|
+
f"Unexpected failure in {func.__name__}: {last_exception}"
|
|
72
156
|
)
|
|
73
|
-
|
|
157
|
+
return None
|
|
158
|
+
else:
|
|
159
|
+
raise last_exception
|
|
74
160
|
|
|
75
161
|
return wrapper
|
|
76
162
|
|
|
77
163
|
return decorator
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
def validate_task_content(
|
|
81
|
-
content: str, task_id: str = "unknown", min_length: int = 10
|
|
82
|
-
) -> bool:
|
|
83
|
-
r"""Validates task result content to avoid silent failures.
|
|
84
|
-
It performs basic checks to ensure the content meets minimum
|
|
85
|
-
quality standards.
|
|
86
|
-
|
|
87
|
-
Args:
|
|
88
|
-
content (str): The task result content to validate.
|
|
89
|
-
task_id (str): Task ID for logging purposes.
|
|
90
|
-
(default: :obj:`"unknown"`)
|
|
91
|
-
min_length (int): Minimum content length after stripping whitespace.
|
|
92
|
-
(default: :obj:`10`)
|
|
93
|
-
|
|
94
|
-
Returns:
|
|
95
|
-
bool: True if content passes validation, False otherwise.
|
|
96
|
-
"""
|
|
97
|
-
# 1: Content must not be None
|
|
98
|
-
if content is None:
|
|
99
|
-
logger.warning(f"Task {task_id}: None content rejected")
|
|
100
|
-
return False
|
|
101
|
-
|
|
102
|
-
# 2: Content must not be empty after stripping whitespace
|
|
103
|
-
stripped_content = content.strip()
|
|
104
|
-
if not stripped_content:
|
|
105
|
-
logger.warning(
|
|
106
|
-
f"Task {task_id}: Empty or whitespace-only content rejected."
|
|
107
|
-
)
|
|
108
|
-
return False
|
|
109
|
-
|
|
110
|
-
# 3: Content must meet minimum meaningful length
|
|
111
|
-
if len(stripped_content) < min_length:
|
|
112
|
-
logger.warning(
|
|
113
|
-
f"Task {task_id}: Content too short ({len(stripped_content)} "
|
|
114
|
-
f"chars < {min_length} minimum). Content preview: "
|
|
115
|
-
f"'{stripped_content[:50]}...'"
|
|
116
|
-
)
|
|
117
|
-
return False
|
|
118
|
-
|
|
119
|
-
# All validation checks passed
|
|
120
|
-
logger.debug(
|
|
121
|
-
f"Task {task_id}: Content validation passed "
|
|
122
|
-
f"({len(stripped_content)} chars)"
|
|
123
|
-
)
|
|
124
|
-
return True
|