camel-ai 0.2.16__py3-none-any.whl → 0.2.18__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 (51) hide show
  1. camel/__init__.py +1 -1
  2. camel/agents/chat_agent.py +30 -6
  3. camel/agents/multi_hop_generator_agent.py +85 -0
  4. camel/agents/programmed_agent_instruction.py +148 -0
  5. camel/benchmarks/__init__.py +2 -0
  6. camel/benchmarks/apibank.py +5 -0
  7. camel/benchmarks/apibench.py +8 -4
  8. camel/benchmarks/gaia.py +2 -2
  9. camel/benchmarks/ragbench.py +333 -0
  10. camel/bots/__init__.py +1 -1
  11. camel/bots/discord/__init__.py +26 -0
  12. camel/bots/discord/discord_app.py +384 -0
  13. camel/bots/discord/discord_installation.py +64 -0
  14. camel/bots/discord/discord_store.py +160 -0
  15. camel/configs/__init__.py +3 -0
  16. camel/configs/anthropic_config.py +17 -15
  17. camel/configs/deepseek_config.py +2 -2
  18. camel/configs/internlm_config.py +60 -0
  19. camel/data_collector/base.py +5 -5
  20. camel/data_collector/sharegpt_collector.py +2 -2
  21. camel/datagen/self_instruct/self_instruct.py +4 -1
  22. camel/datagen/self_instruct/templates.py +12 -14
  23. camel/interpreters/internal_python_interpreter.py +24 -7
  24. camel/loaders/__init__.py +2 -0
  25. camel/loaders/panda_reader.py +337 -0
  26. camel/messages/__init__.py +10 -4
  27. camel/messages/func_message.py +30 -22
  28. camel/models/__init__.py +2 -0
  29. camel/models/anthropic_model.py +1 -22
  30. camel/models/cohere_model.py +8 -0
  31. camel/models/deepseek_model.py +67 -0
  32. camel/models/gemini_model.py +10 -1
  33. camel/models/internlm_model.py +143 -0
  34. camel/models/mistral_model.py +14 -7
  35. camel/models/model_factory.py +3 -0
  36. camel/models/reward/__init__.py +2 -0
  37. camel/models/reward/skywork_model.py +88 -0
  38. camel/synthetic_datagen/source2synth/data_processor.py +373 -0
  39. camel/synthetic_datagen/source2synth/models.py +68 -0
  40. camel/synthetic_datagen/source2synth/user_data_processor_config.py +73 -0
  41. camel/toolkits/google_scholar_toolkit.py +9 -0
  42. camel/types/__init__.py +4 -2
  43. camel/types/enums.py +81 -1
  44. camel/types/openai_types.py +6 -4
  45. camel/types/unified_model_type.py +5 -0
  46. camel/utils/token_counting.py +3 -3
  47. {camel_ai-0.2.16.dist-info → camel_ai-0.2.18.dist-info}/METADATA +158 -187
  48. {camel_ai-0.2.16.dist-info → camel_ai-0.2.18.dist-info}/RECORD +50 -37
  49. {camel_ai-0.2.16.dist-info → camel_ai-0.2.18.dist-info}/WHEEL +1 -1
  50. camel/bots/discord_app.py +0 -138
  51. {camel_ai-0.2.16.dist-info → camel_ai-0.2.18.dist-info}/LICENSE +0 -0
camel/__init__.py CHANGED
@@ -14,7 +14,7 @@
14
14
 
15
15
  from camel.logger import disable_logging, enable_logging, set_log_level
16
16
 
17
- __version__ = '0.2.16'
17
+ __version__ = '0.2.18'
18
18
 
19
19
  __all__ = [
20
20
  '__version__',
@@ -94,11 +94,13 @@ class FunctionCallingRecord(BaseModel):
94
94
  args (Dict[str, Any]): The dictionary of arguments passed to
95
95
  the function.
96
96
  result (Any): The execution result of calling this function.
97
+ tool_call_id (str): The ID of the tool call, if available.
97
98
  """
98
99
 
99
100
  func_name: str
100
101
  args: Dict[str, Any]
101
102
  result: Any
103
+ tool_call_id: str
102
104
 
103
105
  def __str__(self) -> str:
104
106
  r"""Overridden version of the string function.
@@ -109,7 +111,7 @@ class FunctionCallingRecord(BaseModel):
109
111
  return (
110
112
  f"Function Execution: {self.func_name}\n"
111
113
  f"\tArgs: {self.args}\n"
112
- f"\tResult: {self.result}"
114
+ f"\tResult: {self.result}\n"
113
115
  )
114
116
 
115
117
  def as_dict(self) -> dict[str, Any]:
@@ -577,13 +579,18 @@ class ChatAgent(BaseAgent):
577
579
  )
578
580
 
579
581
  self.original_model_dict = self.model_backend.model_config_dict
580
- if response_format and self.model_type in {"gpt-4o", "gpt-4o-mini"}:
582
+ model_response_format_modified = False
583
+ if (
584
+ response_format
585
+ and self.model_type.support_native_structured_output
586
+ ):
581
587
  self.model_backend.model_config_dict = (
582
588
  self.original_model_dict.copy()
583
589
  )
584
590
  self.model_backend.model_config_dict["response_format"] = (
585
591
  response_format
586
592
  )
593
+ model_response_format_modified = True
587
594
 
588
595
  # Convert input message to BaseMessage if necessary
589
596
  if isinstance(input_message, str):
@@ -602,7 +609,12 @@ class ChatAgent(BaseAgent):
602
609
  # Add user input to memory
603
610
  self.update_memory(input_message, OpenAIBackendRole.USER)
604
611
 
605
- return self._handle_step(response_format, self.single_iteration)
612
+ try:
613
+ return self._handle_step(response_format, self.single_iteration)
614
+ finally:
615
+ if model_response_format_modified:
616
+ # Reset model config back to original state
617
+ self.model_backend.model_config_dict = self.original_model_dict
606
618
 
607
619
  def _inject_tool_prompt(self) -> None:
608
620
  r"""Generate and add the tool prompt to memory."""
@@ -824,7 +836,7 @@ class ChatAgent(BaseAgent):
824
836
  return True
825
837
 
826
838
  if self.model_type.support_native_tool_calling:
827
- return response.choices[0].message.tool_calls is None
839
+ return not response.choices[0].message.tool_calls
828
840
 
829
841
  return "</function>" not in str(
830
842
  response.choices[0].message.content or ""
@@ -1384,6 +1396,7 @@ class ChatAgent(BaseAgent):
1384
1396
 
1385
1397
  tool = self.tool_dict[func_name]
1386
1398
  result = tool(**args)
1399
+ tool_call_id = choice.message.tool_calls[0].id
1387
1400
 
1388
1401
  assist_msg = FunctionCallingMessage(
1389
1402
  role_name=self.role_name,
@@ -1392,6 +1405,7 @@ class ChatAgent(BaseAgent):
1392
1405
  content="",
1393
1406
  func_name=func_name,
1394
1407
  args=args,
1408
+ tool_call_id=tool_call_id,
1395
1409
  )
1396
1410
  func_msg = FunctionCallingMessage(
1397
1411
  role_name=self.role_name,
@@ -1400,11 +1414,15 @@ class ChatAgent(BaseAgent):
1400
1414
  content="",
1401
1415
  func_name=func_name,
1402
1416
  result=result,
1417
+ tool_call_id=tool_call_id,
1403
1418
  )
1404
1419
 
1405
1420
  # Record information about this function call
1406
1421
  func_record = FunctionCallingRecord(
1407
- func_name=func_name, args=args, result=result
1422
+ func_name=func_name,
1423
+ args=args,
1424
+ result=result,
1425
+ tool_call_id=tool_call_id,
1408
1426
  )
1409
1427
  return assist_msg, func_msg, func_record
1410
1428
 
@@ -1448,6 +1466,7 @@ class ChatAgent(BaseAgent):
1448
1466
  args = json.loads(choice.message.tool_calls[0].function.arguments)
1449
1467
  tool = self.tool_dict[func_name]
1450
1468
  result = await tool(**args)
1469
+ tool_call_id = choice.message.tool_calls[0].id
1451
1470
 
1452
1471
  assist_msg = FunctionCallingMessage(
1453
1472
  role_name=self.role_name,
@@ -1456,6 +1475,7 @@ class ChatAgent(BaseAgent):
1456
1475
  content="",
1457
1476
  func_name=func_name,
1458
1477
  args=args,
1478
+ tool_call_id=tool_call_id,
1459
1479
  )
1460
1480
  func_msg = FunctionCallingMessage(
1461
1481
  role_name=self.role_name,
@@ -1464,11 +1484,15 @@ class ChatAgent(BaseAgent):
1464
1484
  content="",
1465
1485
  func_name=func_name,
1466
1486
  result=result,
1487
+ tool_call_id=tool_call_id,
1467
1488
  )
1468
1489
 
1469
1490
  # Record information about this function call
1470
1491
  func_record = FunctionCallingRecord(
1471
- func_name=func_name, args=args, result=result
1492
+ func_name=func_name,
1493
+ args=args,
1494
+ result=result,
1495
+ tool_call_id=tool_call_id,
1472
1496
  )
1473
1497
  return assist_msg, func_msg, func_record
1474
1498
 
@@ -0,0 +1,85 @@
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
+
15
+ import textwrap
16
+ from typing import Any
17
+
18
+ from pydantic import ConfigDict
19
+
20
+ from camel.agents.programmed_agent_instruction import (
21
+ ProgrammableChatAgent,
22
+ ProgrammedAgentInstructionResult,
23
+ programmable_capability,
24
+ )
25
+ from camel.messages import BaseMessage
26
+ from camel.synthetic_datagen.source2synth.models import (
27
+ ContextPrompt,
28
+ MultiHopQA,
29
+ )
30
+
31
+
32
+ class MultiHopGeneratorAgent(ProgrammableChatAgent):
33
+ model_config = ConfigDict(arbitrary_types_allowed=True)
34
+
35
+ def __init__(self, **kwargs: Any):
36
+ super().__init__(**kwargs)
37
+
38
+ system_text: str = textwrap.dedent(
39
+ """\
40
+ You are an expert at generating
41
+ multi-hop question-answer pairs.
42
+ For each context, you should:
43
+ 1. Identify multiple related facts or pieces of information
44
+ 2. Create questions that require reasoning across these multiple pieces
45
+ 3. Ensure the reasoning chain is clear and logical
46
+ 4. Generate questions that require at least 2-3 steps of reasoning
47
+ 5. Include the reasoning steps in the answer
48
+
49
+ Give your response with this information:
50
+ Question: [Complex question requiring multiple reasoning steps]
51
+ Reasoning Steps:
52
+ 1. [First reasoning step]
53
+ 2. [Second reasoning step]
54
+ 3. [Final reasoning step]
55
+ Answer: [Final answer]
56
+ Supporting Facts: [List of relevant text segments used]
57
+ """ # noqa: E501
58
+ )
59
+ self.system_message = BaseMessage.make_assistant_message(
60
+ role_name='Assistant', content=system_text
61
+ )
62
+
63
+ @programmable_capability
64
+ def generate_multi_hop_qa(
65
+ self, context: str
66
+ ) -> ProgrammedAgentInstructionResult[MultiHopQA]:
67
+ context_prompt = ContextPrompt(
68
+ main_context=context, related_contexts=None
69
+ )
70
+
71
+ user_message = BaseMessage.make_user_message(
72
+ content=context_prompt.model_dump_json(), role_name="User"
73
+ )
74
+ response = self.step(
75
+ input_message=user_message, response_format=MultiHopQA
76
+ )
77
+ value = MultiHopQA.model_validate_json(response.msgs[0].content)
78
+
79
+ if response.msgs:
80
+ return ProgrammedAgentInstructionResult(
81
+ user_message=user_message,
82
+ agent_message=response.msgs[0],
83
+ value=value,
84
+ )
85
+ raise RuntimeError("No response from agent")
@@ -0,0 +1,148 @@
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
+ import abc
15
+ import threading
16
+ from enum import Enum
17
+ from functools import wraps
18
+ from typing import Any, Callable, Generic, Optional, TypeVar
19
+
20
+ from pydantic import BaseModel, ConfigDict
21
+
22
+ from camel.agents import ChatAgent
23
+ from camel.messages import BaseMessage
24
+
25
+ T = TypeVar('T')
26
+
27
+
28
+ class ProgrammableAgentRequirement(Enum):
29
+ LAST_MESSAGE_NOT_USER = "LAST_MESSAGE_NOT_USER"
30
+
31
+
32
+ class ProgrammedAgentInstructionResult(BaseModel, Generic[T]):
33
+ r"""Result of a programmable agent instruction execution.
34
+
35
+ Contains the messages exchanged during execution and the computed value.
36
+ The value type is specified by the generic type parameter T.
37
+ """
38
+
39
+ user_message: BaseMessage
40
+ agent_message: BaseMessage
41
+ value: T
42
+
43
+ model_config = ConfigDict(arbitrary_types_allowed=True)
44
+
45
+
46
+ class AbstractProgrammableAgent(abc.ABC):
47
+ r"""Abstract class for a programmable agent.
48
+
49
+ A programmable agent is an agent that can be programmed to perform a
50
+ specific function or task. This class defines the interface for a
51
+ programmable
52
+ agent.
53
+
54
+ These methods should be implemented in order to ensure the agent supports
55
+ the necessary guarantees to enable a programming interface while
56
+ maintaining compatibility in a multi-agent system.
57
+
58
+ A programmable agent is responsible for providing and maintaining a
59
+ programming interface for its functionality.
60
+ """
61
+
62
+ @abc.abstractmethod
63
+ def run_atomic(
64
+ self, callback: Callable[[], ProgrammedAgentInstructionResult[T]]
65
+ ) -> ProgrammedAgentInstructionResult[T]:
66
+ r"""Run an atomic operation on the agent.
67
+
68
+ An atomic operation is an operation that is guaranteed to
69
+ be executed without interruption by any other operation.
70
+
71
+ If the operation fails or times out the agents state should be
72
+ unchanged.
73
+
74
+ If an operation is already in progress, this method should throw an
75
+ exception. (It is up to the caller to do any queuing)
76
+
77
+ If the agent is in a state where it can perform the operation,
78
+ it must leave the agent in a state where it can perform the
79
+ operation again. Though if state changes in successful operation
80
+ improve its ability to perform the operation, it should keep them.
81
+ """
82
+ raise NotImplementedError
83
+
84
+ @abc.abstractmethod
85
+ def repair_state(self, requirement: ProgrammableAgentRequirement) -> None:
86
+ r"""Repair the state of the agent.
87
+
88
+ Agents may have other non-atomic interfaces, such as a user interface,
89
+ or chat between other agents.
90
+
91
+ This method should restore the agent to a state where it can perform
92
+ operations according to the specified requirement.
93
+ """
94
+ raise NotImplementedError
95
+
96
+
97
+ def programmable_capability(
98
+ func: Callable[..., ProgrammedAgentInstructionResult[T]],
99
+ ) -> Callable[..., ProgrammedAgentInstructionResult[T]]:
100
+ r"""Decorator for programmable agent capabilities.
101
+
102
+ Wraps a method to ensure it is executed atomically via the agent's
103
+ run_atomic interface.
104
+ The decorated method must return a ProgrammedAgentInstructionResult with
105
+ appropriate type parameter.
106
+ """
107
+
108
+ @wraps(func)
109
+ def wrapper(
110
+ self, *args: Any, **kwargs: Any
111
+ ) -> ProgrammedAgentInstructionResult[T]:
112
+ return self.run_atomic(lambda: func(self, *args, **kwargs))
113
+
114
+ return wrapper
115
+
116
+
117
+ class ProgrammableChatAgent(ChatAgent, AbstractProgrammableAgent):
118
+ r"""A chat agent that can be programmed to perform specific tasks.
119
+
120
+ Provides a default implementation of atomic execution using threading locks
121
+ and basic state tracking for message roles. Implementing classes need to
122
+ provide specific repair logic for their use cases.
123
+ """
124
+
125
+ def __init__(self, **kwargs: Any):
126
+ super().__init__(**kwargs)
127
+ self._operation_lock = threading.Lock()
128
+ self._last_message_role: Optional[str] = None
129
+
130
+ def run_atomic(
131
+ self, callback: Callable[[], ProgrammedAgentInstructionResult[T]]
132
+ ) -> ProgrammedAgentInstructionResult[T]:
133
+ if not self._operation_lock.acquire(blocking=False):
134
+ raise RuntimeError("Operation already in progress")
135
+
136
+ try:
137
+ result = callback()
138
+ self._last_message_role = result.agent_message.role_name
139
+ return result
140
+ finally:
141
+ self._operation_lock.release()
142
+
143
+ def repair_state(self, requirement: ProgrammableAgentRequirement) -> None:
144
+ if requirement == ProgrammableAgentRequirement.LAST_MESSAGE_NOT_USER:
145
+ if self._last_message_role == "user":
146
+ raise NotImplementedError(
147
+ "Must implement repair for LAST_MESSAGE_NOT_USER"
148
+ )
@@ -17,6 +17,7 @@ from .apibench import APIBenchBenchmark
17
17
  from .base import BaseBenchmark
18
18
  from .gaia import DefaultGAIARetriever, GAIABenchmark
19
19
  from .nexus import NexusBenchmark
20
+ from .ragbench import RAGBenchBenchmark
20
21
 
21
22
  __all__ = [
22
23
  "BaseBenchmark",
@@ -25,4 +26,5 @@ __all__ = [
25
26
  "NexusBenchmark",
26
27
  "APIBenchBenchmark",
27
28
  "APIBankBenchmark",
29
+ "RAGBenchBenchmark",
28
30
  ]
@@ -32,6 +32,11 @@ from camel.utils import download_github_subdirectory
32
32
 
33
33
  logger = logging.getLogger(__name__)
34
34
 
35
+ # Add current folder to sys.path to enable relative import
36
+ current_folder = os.getcwd()
37
+ if current_folder not in sys.path:
38
+ sys.path.append(current_folder)
39
+
35
40
 
36
41
  def process_messages(
37
42
  chat_history: List[Dict[str, Any]],
@@ -29,24 +29,28 @@ from camel.utils import download_github_subdirectory
29
29
 
30
30
  logger = logging.getLogger(__name__)
31
31
 
32
+
33
+ # Mapping of dataset names to file names
34
+ # 'Oracle' retriver used here which means all the full
35
+ # API documentation will be included in the prompt
32
36
  dataset_mapping = {
33
37
  "huggingface": {
34
38
  "api": "huggingface_api.jsonl",
35
39
  "eval": "huggingface_eval.json",
36
40
  "train": "huggingface_train.json",
37
- "questions": "questions_huggingface_0_shot.jsonl",
41
+ "questions": "questions_huggingface_oracle.jsonl",
38
42
  },
39
43
  "tensorflowhub": {
40
44
  "api": "tensorflowhub_api.jsonl",
41
45
  "eval": "tensorflow_eval.json",
42
46
  "train": "tensorflow_train.json",
43
- "questions": "questions_tensorflowhub_0_shot.jsonl",
47
+ "questions": "questions_tensorflowhub_oracle.jsonl",
44
48
  },
45
49
  "torchhub": {
46
50
  "api": "torchhub_api.jsonl",
47
51
  "eval": "torchhub_eval.json",
48
52
  "train": "torchhub_train.json",
49
- "questions": "questions_torchhub_0_shot.jsonl",
53
+ "questions": "questions_torchhub_oracle.jsonl",
50
54
  },
51
55
  }
52
56
 
@@ -173,7 +177,7 @@ class APIBenchBenchmark(BaseBenchmark):
173
177
  )
174
178
 
175
179
  repo = "ShishirPatil/gorilla"
176
- subdir = "eval/eval-data/questions"
180
+ subdir = "/gorilla/eval/eval-data/questions"
177
181
  data_dir = self.data_dir
178
182
 
179
183
  download_github_subdirectory(repo, subdir, data_dir)
camel/benchmarks/gaia.py CHANGED
@@ -280,11 +280,11 @@ class GAIABenchmark(BaseBenchmark):
280
280
  f"Skipping task because file not found: {file_path}"
281
281
  )
282
282
  return False
283
- if file_path.suffix in ['.pdf', '.docx', '.doc', '.txt']:
283
+ if file_path.suffix in [".pdf", ".docx", ".doc", ".txt"]:
284
284
  if not self.retriever.reset(task_id=task["task_id"]):
285
285
  return False
286
286
  retrieved_info = self.retriever.retrieve(
287
- query=task["Question"], contents=[task['file_name']]
287
+ query=task["Question"], contents=[task["file_name"]]
288
288
  )
289
289
  retrieved_content = [
290
290
  item["text"]