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.
Files changed (65) hide show
  1. camel/__init__.py +1 -1
  2. camel/agents/mcp_agent.py +1 -5
  3. camel/configs/__init__.py +3 -0
  4. camel/configs/qianfan_config.py +85 -0
  5. camel/models/__init__.py +2 -0
  6. camel/models/aiml_model.py +8 -0
  7. camel/models/anthropic_model.py +8 -0
  8. camel/models/aws_bedrock_model.py +8 -0
  9. camel/models/azure_openai_model.py +14 -5
  10. camel/models/base_model.py +4 -0
  11. camel/models/cohere_model.py +9 -2
  12. camel/models/crynux_model.py +8 -0
  13. camel/models/deepseek_model.py +8 -0
  14. camel/models/gemini_model.py +8 -0
  15. camel/models/groq_model.py +8 -0
  16. camel/models/internlm_model.py +8 -0
  17. camel/models/litellm_model.py +5 -0
  18. camel/models/lmstudio_model.py +14 -1
  19. camel/models/mistral_model.py +15 -1
  20. camel/models/model_factory.py +6 -0
  21. camel/models/modelscope_model.py +8 -0
  22. camel/models/moonshot_model.py +8 -0
  23. camel/models/nemotron_model.py +17 -2
  24. camel/models/netmind_model.py +8 -0
  25. camel/models/novita_model.py +8 -0
  26. camel/models/nvidia_model.py +8 -0
  27. camel/models/ollama_model.py +8 -0
  28. camel/models/openai_compatible_model.py +23 -5
  29. camel/models/openai_model.py +21 -4
  30. camel/models/openrouter_model.py +8 -0
  31. camel/models/ppio_model.py +8 -0
  32. camel/models/qianfan_model.py +104 -0
  33. camel/models/qwen_model.py +8 -0
  34. camel/models/reka_model.py +18 -3
  35. camel/models/samba_model.py +17 -3
  36. camel/models/sglang_model.py +20 -5
  37. camel/models/siliconflow_model.py +8 -0
  38. camel/models/stub_model.py +8 -1
  39. camel/models/togetherai_model.py +8 -0
  40. camel/models/vllm_model.py +7 -0
  41. camel/models/volcano_model.py +14 -1
  42. camel/models/watsonx_model.py +4 -1
  43. camel/models/yi_model.py +8 -0
  44. camel/models/zhipuai_model.py +8 -0
  45. camel/societies/workforce/prompts.py +33 -17
  46. camel/societies/workforce/role_playing_worker.py +5 -10
  47. camel/societies/workforce/single_agent_worker.py +3 -5
  48. camel/societies/workforce/task_channel.py +16 -18
  49. camel/societies/workforce/utils.py +104 -65
  50. camel/societies/workforce/workforce.py +1263 -100
  51. camel/societies/workforce/workforce_logger.py +613 -0
  52. camel/tasks/task.py +77 -6
  53. camel/toolkits/__init__.py +2 -0
  54. camel/toolkits/code_execution.py +1 -1
  55. camel/toolkits/function_tool.py +79 -7
  56. camel/toolkits/mcp_toolkit.py +70 -19
  57. camel/toolkits/playwright_mcp_toolkit.py +2 -1
  58. camel/toolkits/pptx_toolkit.py +4 -4
  59. camel/types/enums.py +32 -0
  60. camel/types/unified_model_type.py +5 -0
  61. camel/utils/mcp_client.py +1 -35
  62. {camel_ai-0.2.65.dist-info → camel_ai-0.2.67.dist-info}/METADATA +3 -3
  63. {camel_ai-0.2.65.dist-info → camel_ai-0.2.67.dist-info}/RECORD +65 -62
  64. {camel_ai-0.2.65.dist-info → camel_ai-0.2.67.dist-info}/WHEEL +0 -0
  65. {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 TaskAssignResult(BaseModel):
49
- r"""The result of task assignment."""
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 that is assigned to the task."
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(running: bool) -> Callable:
57
- r"""Check if the workforce is (not) running, specified the boolean value.
58
- If the workforce is not in the expected status, raise an exception.
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
- if self._running != running:
68
- status = "not running" if running else "running"
69
- raise RuntimeError(
70
- f"The workforce is {status}. Cannot perform the "
71
- f"operation {func.__name__}."
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
- return func(self, *args, **kwargs)
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