camel-ai 0.2.58__py3-none-any.whl → 0.2.60__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 (44) hide show
  1. camel/__init__.py +1 -1
  2. camel/agents/chat_agent.py +126 -9
  3. camel/agents/critic_agent.py +73 -8
  4. camel/benchmarks/__init__.py +2 -0
  5. camel/benchmarks/browsecomp.py +854 -0
  6. camel/configs/cohere_config.py +1 -1
  7. camel/configs/mistral_config.py +1 -1
  8. camel/configs/openai_config.py +3 -0
  9. camel/configs/reka_config.py +1 -1
  10. camel/configs/samba_config.py +2 -2
  11. camel/datagen/cot_datagen.py +29 -34
  12. camel/embeddings/jina_embedding.py +8 -1
  13. camel/embeddings/sentence_transformers_embeddings.py +2 -2
  14. camel/embeddings/vlm_embedding.py +9 -2
  15. camel/human.py +14 -0
  16. camel/memories/records.py +3 -0
  17. camel/messages/base.py +15 -3
  18. camel/models/azure_openai_model.py +1 -0
  19. camel/models/model_factory.py +2 -2
  20. camel/retrievers/bm25_retriever.py +1 -2
  21. camel/retrievers/hybrid_retrival.py +2 -2
  22. camel/societies/role_playing.py +50 -0
  23. camel/societies/workforce/role_playing_worker.py +17 -8
  24. camel/societies/workforce/workforce.py +70 -14
  25. camel/storages/vectordb_storages/oceanbase.py +1 -2
  26. camel/toolkits/async_browser_toolkit.py +5 -1
  27. camel/toolkits/base.py +4 -2
  28. camel/toolkits/browser_toolkit.py +6 -3
  29. camel/toolkits/dalle_toolkit.py +4 -0
  30. camel/toolkits/excel_toolkit.py +11 -3
  31. camel/toolkits/github_toolkit.py +43 -25
  32. camel/toolkits/image_analysis_toolkit.py +3 -0
  33. camel/toolkits/jina_reranker_toolkit.py +194 -77
  34. camel/toolkits/mcp_toolkit.py +60 -16
  35. camel/toolkits/page_script.js +40 -28
  36. camel/toolkits/twitter_toolkit.py +6 -1
  37. camel/toolkits/video_analysis_toolkit.py +3 -0
  38. camel/toolkits/video_download_toolkit.py +3 -0
  39. camel/toolkits/wolfram_alpha_toolkit.py +46 -22
  40. camel/types/enums.py +14 -5
  41. {camel_ai-0.2.58.dist-info → camel_ai-0.2.60.dist-info}/METADATA +7 -9
  42. {camel_ai-0.2.58.dist-info → camel_ai-0.2.60.dist-info}/RECORD +44 -43
  43. {camel_ai-0.2.58.dist-info → camel_ai-0.2.60.dist-info}/WHEEL +0 -0
  44. {camel_ai-0.2.58.dist-info → camel_ai-0.2.60.dist-info}/licenses/LICENSE +0 -0
@@ -74,4 +74,4 @@ class CohereConfig(BaseConfig):
74
74
  p: Optional[float] = None
75
75
 
76
76
 
77
- COHERE_API_PARAMS = {param for param in CohereConfig().model_fields.keys()}
77
+ COHERE_API_PARAMS = {param for param in CohereConfig.model_fields.keys()}
@@ -76,4 +76,4 @@ class MistralConfig(BaseConfig):
76
76
  return response_format
77
77
 
78
78
 
79
- MISTRAL_API_PARAMS = {param for param in MistralConfig().model_fields.keys()}
79
+ MISTRAL_API_PARAMS = {param for param in MistralConfig.model_fields.keys()}
@@ -104,6 +104,8 @@ class ChatGPTConfig(BaseConfig):
104
104
  parallel_tool_calls (bool, optional): A parameter specifying whether
105
105
  the model should call tools in parallel or not.
106
106
  (default: :obj:`None`)
107
+ extra_headers: Optional[Dict[str, str]]: Extra headers to use for the
108
+ model. (default: :obj:`None`)
107
109
  """
108
110
 
109
111
  temperature: Optional[float] = None
@@ -120,6 +122,7 @@ class ChatGPTConfig(BaseConfig):
120
122
  tool_choice: Optional[Union[Dict[str, str], str]] = None
121
123
  reasoning_effort: Optional[str] = None
122
124
  parallel_tool_calls: Optional[bool] = None
125
+ extra_headers: Optional[Dict[str, str]] = None
123
126
 
124
127
 
125
128
  OPENAI_API_PARAMS = {param for param in ChatGPTConfig.model_fields.keys()}
@@ -66,4 +66,4 @@ class RekaConfig(BaseConfig):
66
66
  use_search_engine: Optional[bool] = None
67
67
 
68
68
 
69
- REKA_API_PARAMS = {param for param in RekaConfig().model_fields.keys()}
69
+ REKA_API_PARAMS = {param for param in RekaConfig.model_fields.keys()}
@@ -65,7 +65,7 @@ class SambaVerseAPIConfig(BaseConfig):
65
65
 
66
66
 
67
67
  SAMBA_VERSE_API_PARAMS = {
68
- param for param in SambaVerseAPIConfig().model_fields.keys()
68
+ param for param in SambaVerseAPIConfig.model_fields.keys()
69
69
  }
70
70
 
71
71
 
@@ -160,5 +160,5 @@ class SambaCloudAPIConfig(BaseConfig):
160
160
 
161
161
 
162
162
  SAMBA_CLOUD_API_PARAMS = {
163
- param for param in SambaCloudAPIConfig().model_fields.keys()
163
+ param for param in SambaCloudAPIConfig.model_fields.keys()
164
164
  }
@@ -204,26 +204,23 @@ class CoTDataGenerator:
204
204
  logger.info("Answer verification result: %s", is_correct)
205
205
  return is_correct
206
206
 
207
- def monte_carlo_tree_search(
207
+ def evaluate_partial_solution(
208
208
  self, question: str, partial_solution: str = ""
209
209
  ) -> float:
210
- r"""Perform Monte Carlo Tree Search to find the best solution.
210
+ r"""Evaluate the quality of a partial solution against the
211
+ golden answer.
211
212
 
212
- Process:
213
- a. Selection: Choose promising partial solutions based on previous
214
- scores
215
- b. Expansion: Generate new solution steps using the generator agent
216
- c. Simulation: Evaluate solution quality using similarity scores
217
- d. Backpropagation: Update solution tree with new findings
213
+ This function generates a similarity score between the given partial
214
+ solution and the correct answer (golden answer).
218
215
 
219
216
  Args:
220
- question (str): The question to solve.
221
- partial_solution (str): The current partial solution.
217
+ question (str): The question being solved.
218
+ partial_solution (str): The partial solution generated so far.
222
219
  (default::obj:`""`)
223
220
 
224
221
  Returns:
225
- float: The similarity score between the current
226
- solution and golden answer.
222
+ float: A similarity score between 0 and 1, indicating how close the
223
+ partial solution is to the golden answer.
227
224
  """
228
225
  if question not in self.golden_answers:
229
226
  raise ValueError(
@@ -293,10 +290,21 @@ class CoTDataGenerator:
293
290
  r"""Solve a question using a multi-step approach.
294
291
 
295
292
  The solution process follows these steps:
296
- 1. Try to solve directly - if correct, return the solution
297
- 2. If not correct, use Monte Carlo Tree Search to find a good solution
298
- 3. If the solution isn't perfect, use binary search to locate errors
299
- 4. Generate a new solution based on the correct part
293
+ 1. Try to solve directly - if correct, return the solution.
294
+ 2. If not correct, perform a search by iteratively generating
295
+ new solutions and evaluating their similarity scores to
296
+ find a good solution. The search process involves:
297
+ a. Generation: Generate new solution candidates using
298
+ the generator agent.
299
+ b. Evaluation: Score each solution candidate for similarity
300
+ to the golden answer.
301
+ c. Selection: Keep the best-scoring candidate found so far.
302
+ d. Early stopping: If a sufficiently high-scoring solution
303
+ is found (score > 0.9), stop early.
304
+ 3. If the solution isn't perfect, use binary search to locate
305
+ errors.
306
+ 4. Generate a new solution based on the correct part of the
307
+ initial solution.
300
308
 
301
309
  Args:
302
310
  question (str): The question to solve.
@@ -304,14 +312,14 @@ class CoTDataGenerator:
304
312
  Returns:
305
313
  str: The best solution found.
306
314
  """
315
+
307
316
  # 1. Try direct solution first
308
317
  solution = self.get_answer(question)
309
318
  if self.verify_answer(question, solution):
310
319
  logger.info("Initial solution is correct")
311
320
  return solution
312
321
 
313
- # 2. If direct solution fails, try Monte Carlo Tree Search
314
- # to find a solution with high similarity score
322
+ # 2. If direct solution fails, iteratively search for a better solution
315
323
  best_solution = ""
316
324
  best_score: float = 0.0
317
325
  for i in range(self.search_limit):
@@ -319,23 +327,10 @@ class CoTDataGenerator:
319
327
  current_solution = self.get_answer(question, best_solution)
320
328
 
321
329
  # Evaluate solution similarity score
322
- prompt = (
323
- f"Please evaluate this solution and "
324
- f"give a score between 0-1:\n"
325
- f"Question: {question}\n"
326
- f"Solution: {current_solution}\n"
327
- f"Correct answer: {self.golden_answers.get(question, '')}\n"
328
- f"Return a JSON object with a single field 'score' containing "
329
- f"a float between 0 and 1, like this: {{'score': 0.85}}\n"
330
- )
331
- self.generator_agent.reset()
332
- response = self.generator_agent.step(prompt)
333
330
  try:
334
- response = self.generator_agent.step(
335
- prompt, response_format=AgentResponse
331
+ score = self.evaluate_partial_solution(
332
+ question, current_solution
336
333
  )
337
- agent_response = response.msgs[0].parsed.score # type: ignore [union-attr]
338
- score = agent_response
339
334
 
340
335
  # Exit early if we find a very good solution (score > 0.9)
341
336
  if score > 0.9:
@@ -357,7 +352,7 @@ class CoTDataGenerator:
357
352
  best_score,
358
353
  )
359
354
  except Exception as e:
360
- logger.error("Error parsing agent response: %s", str(e))
355
+ logger.error("Error evaluating partial solution: %s", str(e))
361
356
  continue
362
357
 
363
358
  # 3. If the answer is not completely correct,
@@ -12,6 +12,9 @@
12
12
  # limitations under the License.
13
13
  # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
14
14
 
15
+ # Enables postponed evaluation of annotations (for string-based type hints)
16
+ from __future__ import annotations
17
+
15
18
  import base64
16
19
  import io
17
20
  import os
@@ -104,6 +107,7 @@ class JinaEmbedding(BaseEmbedding[Union[str, Image.Image]]):
104
107
  ValueError: If the input type is not supported.
105
108
  RuntimeError: If the API request fails.
106
109
  """
110
+
107
111
  input_data = []
108
112
  for obj in objs:
109
113
  if isinstance(obj, str):
@@ -111,7 +115,10 @@ class JinaEmbedding(BaseEmbedding[Union[str, Image.Image]]):
111
115
  input_data.append({"text": obj})
112
116
  else:
113
117
  input_data.append(obj) # type: ignore[arg-type]
114
- elif isinstance(obj, Image.Image):
118
+ elif (
119
+ obj.__class__.__module__ == "PIL.Image"
120
+ and obj.__class__.__name__ == "Image"
121
+ ):
115
122
  if self.model_type != EmbeddingModelType.JINA_CLIP_V2:
116
123
  raise ValueError(
117
124
  f"Model {self.model_type} does not support "
@@ -15,8 +15,6 @@ from __future__ import annotations
15
15
 
16
16
  from typing import Any
17
17
 
18
- from numpy import ndarray
19
-
20
18
  from camel.embeddings.base import BaseEmbedding
21
19
 
22
20
 
@@ -61,6 +59,8 @@ class SentenceTransformerEncoder(BaseEmbedding[str]):
61
59
  list[list[float]]: A list that represents the generated embedding
62
60
  as a list of floating-point numbers.
63
61
  """
62
+ from numpy import ndarray
63
+
64
64
  if not objs:
65
65
  raise ValueError("Input text list is empty")
66
66
  embeddings = self.model.encode(
@@ -11,6 +11,10 @@
11
11
  # See the License for the specific language governing permissions and
12
12
  # limitations under the License.
13
13
  # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
14
+
15
+ # Enables postponed evaluation of annotations (for string-based type hints)
16
+ from __future__ import annotations
17
+
14
18
  from typing import Any, List, Optional, Union
15
19
 
16
20
  from PIL import Image
@@ -70,7 +74,7 @@ class VisionLanguageEmbedding(BaseEmbedding[Union[str, Image.Image]]):
70
74
  def embed_list(
71
75
  self, objs: List[Union[Image.Image, str]], **kwargs: Any
72
76
  ) -> List[List[float]]:
73
- """Generates embeddings for the given images or texts.
77
+ r"""Generates embeddings for the given images or texts.
74
78
 
75
79
  Args:
76
80
  objs (List[Image.Image|str]): The list of images or texts for
@@ -98,7 +102,10 @@ class VisionLanguageEmbedding(BaseEmbedding[Union[str, Image.Image]]):
98
102
 
99
103
  result_list = []
100
104
  for obj in objs:
101
- if isinstance(obj, Image.Image):
105
+ if (
106
+ obj.__class__.__module__ == "PIL.Image"
107
+ and obj.__class__.__name__ == "Image"
108
+ ):
102
109
  image_input = self.processor(
103
110
  images=obj,
104
111
  return_tensors="pt",
camel/human.py CHANGED
@@ -136,3 +136,17 @@ class Human:
136
136
  content = self.parse_input(human_input)
137
137
  message = meta_chat_message.create_new_instance(content)
138
138
  return ChatAgentResponse(msgs=[message], terminated=False, info={})
139
+
140
+ def clone(self, with_memory: bool = False) -> 'Human':
141
+ r"""Creates a new instance of the Human class with the same
142
+ attributes.
143
+
144
+ Args:
145
+ with_memory (bool): Flag indicating whether to include memory in
146
+ the cloned instance. Currently not used.
147
+ (default: :obj:`False`)
148
+
149
+ Returns:
150
+ Human: A new Human instance with the same name and logger_color.
151
+ """
152
+ return Human(name=self.name, logger_color=self.logger_color)
camel/memories/records.py CHANGED
@@ -12,6 +12,9 @@
12
12
  # limitations under the License.
13
13
  # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
14
14
 
15
+ # Enables postponed evaluation of annotations (for string-based type hints)
16
+ from __future__ import annotations
17
+
15
18
  from dataclasses import asdict
16
19
  from datetime import datetime, timezone
17
20
  from typing import Any, ClassVar, Dict
camel/messages/base.py CHANGED
@@ -11,13 +11,24 @@
11
11
  # See the License for the specific language governing permissions and
12
12
  # limitations under the License.
13
13
  # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
14
+
15
+ # Enables postponed evaluation of annotations (for string-based type hints)
16
+ from __future__ import annotations
17
+
14
18
  import base64
15
19
  import io
16
20
  import re
17
21
  from dataclasses import dataclass
18
- from typing import Any, Dict, List, Literal, Optional, Tuple, Union
22
+ from typing import (
23
+ Any,
24
+ Dict,
25
+ List,
26
+ Literal,
27
+ Optional,
28
+ Tuple,
29
+ Union,
30
+ )
19
31
 
20
- import numpy as np
21
32
  from PIL import Image
22
33
  from pydantic import BaseModel
23
34
 
@@ -48,7 +59,7 @@ class BaseMessage:
48
59
  role_name (str): The name of the user or assistant role.
49
60
  role_type (RoleType): The type of role, either :obj:`RoleType.
50
61
  ASSISTANT` or :obj:`RoleType.USER`.
51
- meta_dict (Optional[Dict[str, str]]): Additional metadata dictionary
62
+ meta_dict (Optional[Dict[str, Any]]): Additional metadata dictionary
52
63
  for the message.
53
64
  content (str): The content of the message.
54
65
  video_bytes (Optional[bytes]): Optional bytes of a video associated
@@ -457,6 +468,7 @@ class BaseMessage:
457
468
 
458
469
  if self.video_bytes:
459
470
  import imageio.v3 as iio
471
+ import numpy as np
460
472
 
461
473
  base64Frames: List[str] = []
462
474
  frame_count = 0
@@ -62,6 +62,7 @@ class AzureOpenAIModel(BaseModelBackend):
62
62
  environment variable or default to 180 seconds.
63
63
  (default: :obj:`None`)
64
64
 
65
+
65
66
  References:
66
67
  https://learn.microsoft.com/en-us/azure/ai-services/openai/
67
68
  """
@@ -14,8 +14,6 @@
14
14
  import json
15
15
  from typing import ClassVar, Dict, Optional, Type, Union
16
16
 
17
- import yaml
18
-
19
17
  from camel.models.aiml_model import AIMLModel
20
18
  from camel.models.anthropic_model import AnthropicModel
21
19
  from camel.models.aws_bedrock_model import AWSBedrockModel
@@ -221,6 +219,8 @@ class ModelFactory:
221
219
 
222
220
  @classmethod
223
221
  def __load_yaml(cls, filepath: str) -> Dict:
222
+ import yaml
223
+
224
224
  r"""Loads and parses a YAML file into a dictionary.
225
225
 
226
226
  Args:
@@ -13,8 +13,6 @@
13
13
  # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
14
14
  from typing import Any, Dict, List
15
15
 
16
- import numpy as np
17
-
18
16
  from camel.loaders import UnstructuredIO
19
17
  from camel.retrievers import BaseRetriever
20
18
  from camel.utils import dependencies_required
@@ -106,6 +104,7 @@ class BM25Retriever(BaseRetriever):
106
104
  model has not been initialized by calling `process`
107
105
  first.
108
106
  """
107
+ import numpy as np
109
108
 
110
109
  if top_k <= 0:
111
110
  raise ValueError("top_k must be a positive integer.")
@@ -13,8 +13,6 @@
13
13
  # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
14
14
  from typing import Any, Collection, Dict, List, Optional, Sequence, Union
15
15
 
16
- import numpy as np
17
-
18
16
  from camel.embeddings import BaseEmbedding
19
17
  from camel.retrievers import BaseRetriever, BM25Retriever, VectorRetriever
20
18
  from camel.storages import BaseVectorStorage
@@ -96,6 +94,8 @@ class HybridRetriever(BaseRetriever):
96
94
  https://medium.com/@devalshah1619/mathematical-intuition-behind-reciprocal-rank-fusion-rrf-explained-in-2-mins-002df0cc5e2a
97
95
  https://colab.research.google.com/drive/1iwVJrN96fiyycxN1pBqWlEr_4EPiGdGy#scrollTo=0qh83qGV2dY8
98
96
  """
97
+ import numpy as np
98
+
99
99
  text_to_id = {}
100
100
  id_to_info = {}
101
101
  current_id = 1
@@ -120,6 +120,9 @@ class RolePlaying:
120
120
  self.task_type = task_type
121
121
  self.task_prompt = task_prompt
122
122
 
123
+ self.task_specify_agent_kwargs = task_specify_agent_kwargs
124
+ self.task_planner_agent_kwargs = task_planner_agent_kwargs
125
+
123
126
  self.specified_task_prompt: Optional[TextPrompt] = None
124
127
  self._init_specified_task_prompt(
125
128
  assistant_role_name,
@@ -680,3 +683,50 @@ class RolePlaying:
680
683
  info=user_response.info,
681
684
  ),
682
685
  )
686
+
687
+ def clone(
688
+ self, task_prompt: str, with_memory: bool = False
689
+ ) -> 'RolePlaying':
690
+ r"""Creates a new instance of RolePlaying with the same configuration.
691
+
692
+ Args:
693
+ task_prompt (str): The task prompt to be used by the new instance.
694
+ with_memory (bool, optional): Whether to copy the memory
695
+ (conversation history) to the new instance. If True, the new
696
+ instance will have the same conversation history. If False,
697
+ the new instance will have a fresh memory.
698
+ (default: :obj:`False`)
699
+
700
+ Returns:
701
+ RolePlaying: A new instance of RolePlaying with the same
702
+ configuration.
703
+ """
704
+
705
+ new_instance = RolePlaying(
706
+ assistant_role_name=self.assistant_agent.role_name,
707
+ user_role_name=self.user_agent.role_name,
708
+ task_prompt=task_prompt,
709
+ with_task_specify=self.with_task_specify,
710
+ task_specify_agent_kwargs=self.task_specify_agent_kwargs,
711
+ with_task_planner=self.with_task_planner,
712
+ task_planner_agent_kwargs=self.task_planner_agent_kwargs,
713
+ with_critic_in_the_loop=False,
714
+ model=self.model,
715
+ task_type=self.task_type,
716
+ )
717
+ tmp_assistant_sys_msg = new_instance.assistant_sys_msg
718
+ new_instance.assistant_agent = self.assistant_agent.clone(with_memory)
719
+ new_instance.assistant_sys_msg = tmp_assistant_sys_msg
720
+ new_instance.assistant_agent._system_message = tmp_assistant_sys_msg
721
+
722
+ tmp_user_sys_msg = new_instance.user_sys_msg
723
+ new_instance.user_agent = self.user_agent.clone(with_memory)
724
+ new_instance.user_sys_msg = tmp_user_sys_msg
725
+ new_instance.user_agent._system_message = tmp_user_sys_msg
726
+
727
+ new_instance.with_critic_in_the_loop = self.with_critic_in_the_loop
728
+ new_instance.critic_sys_msg = self.critic_sys_msg
729
+ if self.critic:
730
+ new_instance.critic = self.critic.clone(with_memory)
731
+
732
+ return new_instance
@@ -38,14 +38,17 @@ class RolePlayingWorker(Worker):
38
38
  description (str): Description of the node.
39
39
  assistant_role_name (str): The role name of the assistant agent.
40
40
  user_role_name (str): The role name of the user agent.
41
- assistant_agent_kwargs (Optional[Dict], optional): The keyword
42
- arguments to initialize the assistant agent in the role playing,
43
- like the model name, etc. Defaults to None.
44
- user_agent_kwargs (Optional[Dict], optional): The keyword arguments to
41
+ assistant_agent_kwargs (Optional[Dict]): The keyword arguments to
42
+ initialize the assistant agent in the role playing, like the model
43
+ name, etc. (default: :obj:`None`)
44
+ user_agent_kwargs (Optional[Dict]): The keyword arguments to
45
45
  initialize the user agent in the role playing, like the model name,
46
- etc. Defaults to None.
47
- chat_turn_limit (int, optional): The maximum number of chat turns in
48
- the role playing. Defaults to 3.
46
+ etc. (default: :obj:`None`)
47
+ summarize_agent_kwargs (Optional[Dict]): The keyword arguments to
48
+ initialize the summarize agent, like the model name, etc.
49
+ (default: :obj:`None`)
50
+ chat_turn_limit (int): The maximum number of chat turns in the role
51
+ playing. (default: :obj:`3`)
49
52
  """
50
53
 
51
54
  def __init__(
@@ -55,9 +58,11 @@ class RolePlayingWorker(Worker):
55
58
  user_role_name: str,
56
59
  assistant_agent_kwargs: Optional[Dict] = None,
57
60
  user_agent_kwargs: Optional[Dict] = None,
61
+ summarize_agent_kwargs: Optional[Dict] = None,
58
62
  chat_turn_limit: int = 3,
59
63
  ) -> None:
60
64
  super().__init__(description)
65
+ self.summarize_agent_kwargs = summarize_agent_kwargs
61
66
  summ_sys_msg = BaseMessage.make_assistant_message(
62
67
  role_name="Summarizer",
63
68
  content="You are a good summarizer. You will be presented with "
@@ -65,7 +70,11 @@ class RolePlayingWorker(Worker):
65
70
  "are trying to solve a task. Your job is summarizing the result "
66
71
  "of the task based on the chat history.",
67
72
  )
68
- self.summarize_agent = ChatAgent(summ_sys_msg)
73
+ summarize_agent_dict = (
74
+ summarize_agent_kwargs if summarize_agent_kwargs else {}
75
+ )
76
+ summarize_agent_dict['system_message'] = summ_sys_msg
77
+ self.summarize_agent = ChatAgent(**summarize_agent_dict)
69
78
  self.chat_turn_limit = chat_turn_limit
70
79
  self.assistant_role_name = assistant_role_name
71
80
  self.user_role_name = user_role_name
@@ -202,6 +202,7 @@ class Workforce(BaseNode):
202
202
  user_role_name: str,
203
203
  assistant_agent_kwargs: Optional[Dict] = None,
204
204
  user_agent_kwargs: Optional[Dict] = None,
205
+ summarize_agent_kwargs: Optional[Dict] = None,
205
206
  chat_turn_limit: int = 3,
206
207
  ) -> Workforce:
207
208
  r"""Add a worker node to the workforce that uses `RolePlaying` system.
@@ -210,25 +211,29 @@ class Workforce(BaseNode):
210
211
  description (str): Description of the node.
211
212
  assistant_role_name (str): The role name of the assistant agent.
212
213
  user_role_name (str): The role name of the user agent.
213
- assistant_agent_kwargs (Optional[Dict], optional): The keyword
214
- arguments to initialize the assistant agent in the role
215
- playing, like the model name, etc. Defaults to `None`.
216
- user_agent_kwargs (Optional[Dict], optional): The keyword arguments
217
- to initialize the user agent in the role playing, like the
218
- model name, etc. Defaults to `None`.
219
- chat_turn_limit (int, optional): The maximum number of chat turns
220
- in the role playing. Defaults to 3.
214
+ assistant_agent_kwargs (Optional[Dict]): The keyword arguments to
215
+ initialize the assistant agent in the role playing, like the
216
+ model name, etc. (default: :obj:`None`)
217
+ user_agent_kwargs (Optional[Dict]): The keyword arguments to
218
+ initialize the user agent in the role playing, like the
219
+ model name, etc. (default: :obj:`None`)
220
+ summarize_agent_kwargs (Optional[Dict]): The keyword arguments to
221
+ initialize the summarize agent, like the model name, etc.
222
+ (default: :obj:`None`)
223
+ chat_turn_limit (int): The maximum number of chat turns in the
224
+ role playing. (default: :obj:`3`)
221
225
 
222
226
  Returns:
223
227
  Workforce: The workforce node itself.
224
228
  """
225
229
  worker_node = RolePlayingWorker(
226
- description,
227
- assistant_role_name,
228
- user_role_name,
229
- assistant_agent_kwargs,
230
- user_agent_kwargs,
231
- chat_turn_limit,
230
+ description=description,
231
+ assistant_role_name=assistant_role_name,
232
+ user_role_name=user_role_name,
233
+ assistant_agent_kwargs=assistant_agent_kwargs,
234
+ user_agent_kwargs=user_agent_kwargs,
235
+ summarize_agent_kwargs=summarize_agent_kwargs,
236
+ chat_turn_limit=chat_turn_limit,
232
237
  )
233
238
  self._children.append(worker_node)
234
239
  return self
@@ -501,3 +506,54 @@ class Workforce(BaseNode):
501
506
  for child_task in self._child_listening_tasks:
502
507
  child_task.cancel()
503
508
  self._running = False
509
+
510
+ def clone(self, with_memory: bool = False) -> 'Workforce':
511
+ r"""Creates a new instance of Workforce with the same configuration.
512
+
513
+ Args:
514
+ with_memory (bool, optional): Whether to copy the memory
515
+ (conversation history) to the new instance. If True, the new
516
+ instance will have the same conversation history. If False,
517
+ the new instance will have a fresh memory.
518
+ (default: :obj:`False`)
519
+
520
+ Returns:
521
+ Workforce: A new instance of Workforce with the same configuration.
522
+ """
523
+
524
+ # Create a new instance with the same configuration
525
+ new_instance = Workforce(
526
+ description=self.description,
527
+ coordinator_agent_kwargs={},
528
+ task_agent_kwargs={},
529
+ new_worker_agent_kwargs=self.new_worker_agent_kwargs,
530
+ )
531
+
532
+ new_instance.task_agent = self.task_agent.clone(with_memory)
533
+ new_instance.coordinator_agent = self.coordinator_agent.clone(
534
+ with_memory
535
+ )
536
+
537
+ for child in self._children:
538
+ if isinstance(child, SingleAgentWorker):
539
+ cloned_worker = child.worker.clone(with_memory)
540
+ new_instance.add_single_agent_worker(
541
+ child.description, cloned_worker
542
+ )
543
+ elif isinstance(child, RolePlayingWorker):
544
+ new_instance.add_role_playing_worker(
545
+ child.description,
546
+ child.assistant_role_name,
547
+ child.user_role_name,
548
+ child.assistant_agent_kwargs,
549
+ child.user_agent_kwargs,
550
+ child.summarize_agent_kwargs,
551
+ child.chat_turn_limit,
552
+ )
553
+ elif isinstance(child, Workforce):
554
+ new_instance.add_workforce(child.clone(with_memory))
555
+ else:
556
+ logger.warning(f"{type(child)} is not being cloned.")
557
+ continue
558
+
559
+ return new_instance
@@ -16,8 +16,6 @@ import json
16
16
  import logging
17
17
  from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional
18
18
 
19
- from sqlalchemy import JSON, Column, Integer
20
-
21
19
  if TYPE_CHECKING:
22
20
  from pyobvector.client import ObVecClient
23
21
 
@@ -80,6 +78,7 @@ class OceanBaseStorage(BaseVectorStorage):
80
78
  IndexParams,
81
79
  )
82
80
  from pyobvector.schema import VECTOR
81
+ from sqlalchemy import JSON, Column, Integer
83
82
 
84
83
  self.vector_dim: int = vector_dim
85
84
  self.table_name: str = table_name
@@ -11,6 +11,10 @@
11
11
  # See the License for the specific language governing permissions and
12
12
  # limitations under the License.
13
13
  # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
14
+
15
+ # Enables postponed evaluation of annotations (for string-based type hints)
16
+ from __future__ import annotations
17
+
14
18
  import asyncio
15
19
  import datetime
16
20
  import io
@@ -1336,7 +1340,7 @@ class AsyncBrowserToolkit(BaseToolkit):
1336
1340
 
1337
1341
  def _initialize_agent(self) -> Tuple["ChatAgent", "ChatAgent"]:
1338
1342
  r"""Initialize the agent."""
1339
- from camel.agents import ChatAgent
1343
+ from camel.agents.chat_agent import ChatAgent
1340
1344
 
1341
1345
  if self.web_agent_model is None:
1342
1346
  web_agent_model = ModelFactory.create(
camel/toolkits/base.py CHANGED
@@ -53,11 +53,13 @@ class BaseToolkit(metaclass=AgentOpsMeta):
53
53
  """
54
54
  raise NotImplementedError("Subclasses must implement this method.")
55
55
 
56
- def run_mcp_server(self, mode: Literal["stdio", "sse"]) -> None:
56
+ def run_mcp_server(
57
+ self, mode: Literal["stdio", "sse", "streamable-http"]
58
+ ) -> None:
57
59
  r"""Run the MCP server in the specified mode.
58
60
 
59
61
  Args:
60
- mode (Literal["stdio", "sse"]): The mode to run
62
+ mode (Literal["stdio", "sse", "streamable-http"]): The mode to run
61
63
  the MCP server in.
62
64
  """
63
65
  self.mcp.run(mode)