camel-ai 0.2.62__py3-none-any.whl → 0.2.65__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 (59) hide show
  1. camel/__init__.py +1 -1
  2. camel/agents/chat_agent.py +95 -24
  3. camel/agents/mcp_agent.py +5 -1
  4. camel/benchmarks/mock_website/README.md +96 -0
  5. camel/benchmarks/mock_website/mock_web.py +299 -0
  6. camel/benchmarks/mock_website/requirements.txt +3 -0
  7. camel/benchmarks/mock_website/shopping_mall/app.py +465 -0
  8. camel/benchmarks/mock_website/task.json +104 -0
  9. camel/configs/__init__.py +3 -0
  10. camel/configs/crynux_config.py +94 -0
  11. camel/datasets/models.py +1 -1
  12. camel/datasets/static_dataset.py +6 -0
  13. camel/interpreters/base.py +14 -1
  14. camel/interpreters/docker/Dockerfile +63 -7
  15. camel/interpreters/docker_interpreter.py +65 -7
  16. camel/interpreters/e2b_interpreter.py +23 -8
  17. camel/interpreters/internal_python_interpreter.py +30 -2
  18. camel/interpreters/ipython_interpreter.py +21 -3
  19. camel/interpreters/subprocess_interpreter.py +34 -2
  20. camel/memories/records.py +5 -3
  21. camel/models/__init__.py +2 -0
  22. camel/models/azure_openai_model.py +101 -25
  23. camel/models/cohere_model.py +65 -0
  24. camel/models/crynux_model.py +94 -0
  25. camel/models/deepseek_model.py +43 -1
  26. camel/models/gemini_model.py +50 -4
  27. camel/models/litellm_model.py +38 -0
  28. camel/models/mistral_model.py +66 -0
  29. camel/models/model_factory.py +10 -1
  30. camel/models/openai_compatible_model.py +81 -17
  31. camel/models/openai_model.py +87 -16
  32. camel/models/reka_model.py +69 -0
  33. camel/models/samba_model.py +69 -2
  34. camel/models/sglang_model.py +74 -2
  35. camel/models/watsonx_model.py +62 -0
  36. camel/societies/workforce/role_playing_worker.py +11 -3
  37. camel/societies/workforce/single_agent_worker.py +31 -1
  38. camel/societies/workforce/utils.py +51 -0
  39. camel/societies/workforce/workforce.py +409 -7
  40. camel/storages/__init__.py +2 -0
  41. camel/storages/vectordb_storages/__init__.py +2 -0
  42. camel/storages/vectordb_storages/weaviate.py +714 -0
  43. camel/tasks/task.py +27 -10
  44. camel/toolkits/async_browser_toolkit.py +97 -54
  45. camel/toolkits/browser_toolkit.py +65 -18
  46. camel/toolkits/code_execution.py +37 -8
  47. camel/toolkits/function_tool.py +2 -2
  48. camel/toolkits/mcp_toolkit.py +13 -2
  49. camel/toolkits/playwright_mcp_toolkit.py +16 -3
  50. camel/toolkits/task_planning_toolkit.py +134 -0
  51. camel/types/enums.py +61 -2
  52. camel/types/unified_model_type.py +5 -0
  53. camel/utils/__init__.py +16 -0
  54. camel/utils/langfuse.py +258 -0
  55. camel/utils/mcp_client.py +84 -17
  56. {camel_ai-0.2.62.dist-info → camel_ai-0.2.65.dist-info}/METADATA +9 -12
  57. {camel_ai-0.2.62.dist-info → camel_ai-0.2.65.dist-info}/RECORD +59 -49
  58. {camel_ai-0.2.62.dist-info → camel_ai-0.2.65.dist-info}/WHEEL +0 -0
  59. {camel_ai-0.2.62.dist-info → camel_ai-0.2.65.dist-info}/licenses/LICENSE +0 -0
@@ -13,6 +13,7 @@
13
13
  # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
14
14
  import json
15
15
  import random
16
+ from collections.abc import Sequence
16
17
  from pathlib import Path
17
18
  from typing import (
18
19
  Any,
@@ -288,6 +289,11 @@ class StaticDataset(Dataset):
288
289
  )
289
290
  raw_data = []
290
291
 
292
+ if not isinstance(data, Sequence):
293
+ raise TypeError(
294
+ f"{type(data).__name__} does not support indexing."
295
+ )
296
+
291
297
  for i in range(len(data)):
292
298
  item = data[i]
293
299
  if not isinstance(item, dict):
@@ -12,7 +12,7 @@
12
12
  # limitations under the License.
13
13
  # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
14
14
  from abc import ABC, abstractmethod
15
- from typing import Any, Dict, List
15
+ from typing import Any, Dict, List, Tuple, Union
16
16
 
17
17
 
18
18
  class BaseInterpreter(ABC):
@@ -47,3 +47,16 @@ class BaseInterpreter(ABC):
47
47
  def update_action_space(self, action_space: Dict[str, Any]) -> None:
48
48
  r"""Updates action space for *python* interpreter"""
49
49
  pass
50
+
51
+ @abstractmethod
52
+ def execute_command(self, command: str) -> Union[str, Tuple[str, str]]:
53
+ r"""Executes a command in the interpreter.
54
+
55
+ Args:
56
+ command (str): The command to execute.
57
+
58
+ Returns:
59
+ Tuple[str, str]: A tuple containing the stdout and stderr of the
60
+ command execution.
61
+ """
62
+ pass
@@ -1,12 +1,68 @@
1
- FROM python:3.9-slim
1
+ # syntax=docker/dockerfile:1
2
2
 
3
- # Install R and required dependencies
3
+ FROM ubuntu:22.04
4
+
5
+ # Set environment variable to avoid interactive prompts
6
+ ENV DEBIAN_FRONTEND=noninteractive
7
+
8
+ # Update and install base utilities
4
9
  RUN apt-get update && apt-get install -y \
5
- r-base \
6
- && rm -rf /var/lib/apt/lists/*
10
+ build-essential \
11
+ software-properties-common \
12
+ curl \
13
+ wget \
14
+ git \
15
+ git-lfs \
16
+ netcat \
17
+ sudo \
18
+ tzdata \
19
+ && rm -rf /var/lib/apt/lists/* \
20
+ && apt-get clean \
21
+ && apt-get autoremove -y
22
+
23
+ # Install Python 3.10 and its dependencies
24
+ RUN add-apt-repository ppa:deadsnakes/ppa && \
25
+ apt-get update && \
26
+ apt-get install -y \
27
+ python3.10 \
28
+ python3.10-venv \
29
+ python3.10-dev \
30
+ python3.10-distutils \
31
+ python3-pip \
32
+ && ln -s /usr/bin/python3.10 /usr/bin/python \
33
+ && rm -rf /var/lib/apt/lists/* \
34
+ && apt-get clean \
35
+ && apt-get autoremove -y
36
+
37
+ # Install R
38
+ RUN apt-get update && \
39
+ apt-get install -y r-base && \
40
+ rm -rf /var/lib/apt/lists/* && \
41
+ apt-get clean && \
42
+ apt-get autoremove -y
43
+
44
+ # Install NodeJS 22.x
45
+ RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - && \
46
+ apt-get install -y nodejs && \
47
+ rm -rf /var/lib/apt/lists/* && \
48
+ apt-get clean && \
49
+ apt-get autoremove -y
50
+
51
+ # Install Poetry
52
+ RUN curl -fsSL https://install.python-poetry.org | python3.10 - && \
53
+ ln -s ~/.local/bin/poetry /usr/local/bin/poetry
54
+
55
+ # Upgrade pip and install base Python packages
56
+ RUN python3.10 -m pip install --upgrade pip setuptools wheel
57
+
58
+ # Install uv
59
+ RUN curl -LsSf https://astral.sh/uv/install.sh | sh && \
60
+ mv /root/.local/bin/uv /usr/local/bin/uv && \
61
+ mv /root/.local/bin/uvx /usr/local/bin/uvx && \
62
+ chmod +x /usr/local/bin/uv /usr/local/bin/uvx
7
63
 
8
- # Set working directory
64
+ # Setup working directory
9
65
  WORKDIR /workspace
10
66
 
11
- # Keep container running
12
- CMD ["tail", "-f", "/dev/null"]
67
+ # Set default shell
68
+ CMD ["/bin/bash"]
@@ -53,12 +53,14 @@ class DockerInterpreter(BaseInterpreter):
53
53
  "python": "python {file_name}",
54
54
  "bash": "bash {file_name}",
55
55
  "r": "Rscript {file_name}",
56
+ "node": "node {file_name}",
56
57
  }
57
58
 
58
59
  _CODE_EXTENSION_MAPPING: ClassVar[Dict[str, str]] = {
59
60
  "python": "py",
60
61
  "bash": "sh",
61
62
  "r": "R",
63
+ "node": "js",
62
64
  }
63
65
 
64
66
  _CODE_TYPE_MAPPING: ClassVar[Dict[str, str]] = {
@@ -71,6 +73,11 @@ class DockerInterpreter(BaseInterpreter):
71
73
  "sh": "bash",
72
74
  "r": "r",
73
75
  "R": "r",
76
+ "node": "node",
77
+ "js": "node",
78
+ "javascript": "node",
79
+ "typescript": "node",
80
+ "ts": "node",
74
81
  }
75
82
 
76
83
  def __init__(
@@ -92,8 +99,11 @@ class DockerInterpreter(BaseInterpreter):
92
99
  This method ensures that the Docker container is removed when the
93
100
  interpreter is deleted.
94
101
  """
95
- if self._container is not None:
96
- self._container.remove(force=True)
102
+ try:
103
+ if self._container is not None:
104
+ self.cleanup()
105
+ except ImportError as e:
106
+ logger.warning(f"Error during container cleanup: {e}")
97
107
 
98
108
  def _initialize_if_needed(self) -> None:
99
109
  if self._container is not None:
@@ -180,10 +190,24 @@ class DockerInterpreter(BaseInterpreter):
180
190
  exec_result += f"(stderr: {stderr.decode()})" if stderr else ""
181
191
  return exec_result
182
192
 
193
+ def cleanup(self) -> None:
194
+ r"""Explicitly stops and removes the Docker container.
195
+
196
+ This method should be called when you're done with the interpreter
197
+ to ensure proper cleanup of Docker resources.
198
+ """
199
+ try:
200
+ if self._container is not None:
201
+ self._container.stop()
202
+ self._container.remove(force=True)
203
+ self._container = None
204
+ except Exception as e:
205
+ logger.error(f"Error during container cleanup: {e}")
206
+
183
207
  def run(
184
208
  self,
185
209
  code: str,
186
- code_type: str,
210
+ code_type: str = "python",
187
211
  ) -> str:
188
212
  r"""Executes the given code in the container attached to the
189
213
  interpreter, and captures the stdout and stderr streams.
@@ -191,7 +215,7 @@ class DockerInterpreter(BaseInterpreter):
191
215
  Args:
192
216
  code (str): The code string to execute.
193
217
  code_type (str): The type of code to execute (e.g., 'python',
194
- 'bash').
218
+ 'bash'). (default: obj:`python`)
195
219
 
196
220
  Returns:
197
221
  str: A string containing the captured stdout and stderr of the
@@ -229,13 +253,16 @@ class DockerInterpreter(BaseInterpreter):
229
253
  try:
230
254
  temp_file_path = self._create_file_in_container(code)
231
255
  result = self._run_file_in_container(temp_file_path, code_type)
256
+ # Clean up after execution
232
257
  except docker.errors.APIError as e:
258
+ self.cleanup()
233
259
  raise InterpreterError(
234
260
  f"Execution halted due to docker API error: {e.explanation}. "
235
261
  "This choice stops the current operation and any "
236
262
  "further code execution."
237
263
  ) from e
238
264
  except docker.errors.DockerException as e:
265
+ self.cleanup() # Clean up even if there's an error
239
266
  raise InterpreterError(
240
267
  f"Execution halted due to docker exceptoin: {e}. "
241
268
  "This choice stops the current operation and any "
@@ -258,6 +285,37 @@ class DockerInterpreter(BaseInterpreter):
258
285
 
259
286
  def update_action_space(self, action_space: Dict[str, Any]) -> None:
260
287
  r"""Updates action space for *python* interpreter"""
261
- raise RuntimeError(
262
- "SubprocessInterpreter doesn't support " "`action_space`."
263
- )
288
+ raise RuntimeError("DockerInterpreter doesn't support `action_space`.")
289
+
290
+ def execute_command(self, command: str) -> str:
291
+ r"""Executes a command in the Docker container and returns its output.
292
+
293
+ Args:
294
+ command (str): The command to execute in the container.
295
+
296
+ Returns:
297
+ str: A string containing the captured stdout and stderr of the
298
+ executed command.
299
+
300
+ Raises:
301
+ InterpreterError: If the container is not initialized or there is
302
+ an error executing the command.
303
+ """
304
+ self._initialize_if_needed()
305
+
306
+ if self._container is None:
307
+ raise InterpreterError(
308
+ "Container is not initialized. Try running the command again."
309
+ )
310
+
311
+ try:
312
+ stdout, stderr = self._container.exec_run(
313
+ command,
314
+ demux=True,
315
+ ).output
316
+ exec_result = f"{stdout.decode()}" if stdout else ""
317
+ exec_result += f"(stderr: {stderr.decode()})" if stderr else ""
318
+ return exec_result
319
+
320
+ except Exception as e:
321
+ raise InterpreterError(f"Failed to execute command: {e!s}") from e
@@ -63,24 +63,27 @@ class E2BInterpreter(BaseInterpreter):
63
63
  This method ensures that the e2b sandbox is killed when the
64
64
  interpreter is deleted.
65
65
  """
66
- if (
67
- hasattr(self, '_sandbox')
68
- and self._sandbox is not None
69
- and self._sandbox.is_running()
70
- ):
71
- self._sandbox.kill()
66
+ try:
67
+ if (
68
+ hasattr(self, '_sandbox')
69
+ and self._sandbox is not None
70
+ and self._sandbox.is_running()
71
+ ):
72
+ self._sandbox.kill()
73
+ except ImportError as e:
74
+ logger.warning(f"Error during sandbox cleanup: {e}")
72
75
 
73
76
  def run(
74
77
  self,
75
78
  code: str,
76
- code_type: str,
79
+ code_type: str = "python",
77
80
  ) -> str:
78
81
  r"""Executes the given code in the e2b sandbox.
79
82
 
80
83
  Args:
81
84
  code (str): The code string to execute.
82
85
  code_type (str): The type of code to execute (e.g., 'python',
83
- 'bash').
86
+ 'bash'). (default: obj:`python`)
84
87
 
85
88
  Returns:
86
89
  str: The string representation of the output of the executed code.
@@ -138,3 +141,15 @@ class E2BInterpreter(BaseInterpreter):
138
141
  def update_action_space(self, action_space: Dict[str, Any]) -> None:
139
142
  r"""Updates action space for *python* interpreter"""
140
143
  raise RuntimeError("E2B doesn't support " "`action_space`.")
144
+
145
+ def execute_command(self, command: str) -> str:
146
+ r"""Execute a command can be used to resolve the dependency of the
147
+ code.
148
+
149
+ Args:
150
+ command (str): The command to execute.
151
+
152
+ Returns:
153
+ str: The output of the command.
154
+ """
155
+ return self._sandbox.commands.run(command)
@@ -14,6 +14,8 @@
14
14
  import ast
15
15
  import difflib
16
16
  import importlib
17
+ import os
18
+ import subprocess
17
19
  import typing
18
20
  from typing import Any, ClassVar, Dict, List, Optional
19
21
 
@@ -89,13 +91,15 @@ class InternalPythonInterpreter(BaseInterpreter):
89
91
  raise_error: bool = False,
90
92
  ) -> None:
91
93
  self.action_space = action_space or dict()
94
+ # Add print to action space
95
+ self.action_space['print'] = print
92
96
  self.state = self.action_space.copy()
93
97
  self.fuzz_state: Dict[str, Any] = dict()
94
98
  self.import_white_list = import_white_list or list()
95
99
  self.raise_error = raise_error
96
100
  self.unsafe_mode = unsafe_mode
97
101
 
98
- def run(self, code: str, code_type: str) -> str:
102
+ def run(self, code: str, code_type: str = "python") -> str:
99
103
  r"""Executes the given code with specified code type in the
100
104
  interpreter.
101
105
 
@@ -111,7 +115,7 @@ class InternalPythonInterpreter(BaseInterpreter):
111
115
  code (str): The python code to be executed.
112
116
  code_type (str): The type of the code, which should be one of the
113
117
  supported code types (`python`, `py`, `python3`, `python2`).
114
-
118
+ (default: obj:`python`)
115
119
 
116
120
  Returns:
117
121
  str: The string representation of the output of the executed code.
@@ -531,3 +535,27 @@ class InternalPythonInterpreter(BaseInterpreter):
531
535
  return self.fuzz_state[close_matches[0]]
532
536
  else:
533
537
  raise InterpreterError(f"The variable `{key}` is not defined.")
538
+
539
+ def execute_command(self, command: str) -> tuple[str, str]:
540
+ r"""Execute a command in the internal python interpreter.
541
+
542
+ Args:
543
+ command (str): The command to execute.
544
+
545
+ Returns:
546
+ tuple: A tuple containing the stdout and stderr of the command.
547
+ """
548
+ try:
549
+ proc = subprocess.Popen(
550
+ command,
551
+ stdout=subprocess.PIPE,
552
+ stderr=subprocess.PIPE,
553
+ text=True,
554
+ env=os.environ,
555
+ shell=True,
556
+ )
557
+ stdout, stderr = proc.communicate()
558
+
559
+ return stdout, stderr
560
+ except Exception as e:
561
+ raise InterpreterError(f"Error executing command: {e}")
@@ -118,13 +118,13 @@ class JupyterKernelInterpreter(BaseInterpreter):
118
118
  exec_result = "\n".join(outputs)
119
119
  return self._clean_ipython_output(exec_result)
120
120
 
121
- def run(self, code: str, code_type: str) -> str:
121
+ def run(self, code: str, code_type: str = "python") -> str:
122
122
  r"""Executes the given code in the Jupyter kernel.
123
123
 
124
124
  Args:
125
125
  code (str): The code string to execute.
126
126
  code_type (str): The type of code to execute (e.g., 'python',
127
- 'bash').
127
+ 'bash'). (default: obj:`python`)
128
128
 
129
129
  Returns:
130
130
  str: A string containing the captured result of the
@@ -144,6 +144,24 @@ class JupyterKernelInterpreter(BaseInterpreter):
144
144
 
145
145
  return result
146
146
 
147
+ def execute_command(self, command: str) -> str:
148
+ r"""Executes a shell command in the Jupyter kernel.
149
+
150
+ Args:
151
+ command (str): The shell command to execute.
152
+
153
+ Returns:
154
+ str: A string containing the captured result of the
155
+ executed command.
156
+
157
+ """
158
+ try:
159
+ self._initialize_if_needed()
160
+ system_command = f"!{command}"
161
+ return self._execute(system_command, TIMEOUT)
162
+ except Exception as e:
163
+ raise InterpreterError(f"Error executing command: {e}")
164
+
147
165
  def supported_code_types(self) -> List[str]:
148
166
  r"""Provides supported code types by the interpreter.
149
167
 
@@ -164,5 +182,5 @@ class JupyterKernelInterpreter(BaseInterpreter):
164
182
  does not support updating the action space.
165
183
  """
166
184
  raise RuntimeError(
167
- "SubprocessInterpreter doesn't support " "`action_space`."
185
+ "JupyterKernelInterpreter doesn't support " "`action_space`."
168
186
  )
@@ -87,14 +87,14 @@ class SubprocessInterpreter(BaseInterpreter):
87
87
  def run_file(
88
88
  self,
89
89
  file: Path,
90
- code_type: str,
90
+ code_type: str = "python",
91
91
  ) -> str:
92
92
  r"""Executes a code file in a subprocess and captures its output.
93
93
 
94
94
  Args:
95
95
  file (Path): The path object of the file to run.
96
96
  code_type (str): The type of code to execute (e.g., 'python',
97
- 'bash').
97
+ 'bash'). (default: obj:`python`)
98
98
 
99
99
  Returns:
100
100
  str: A string containing the captured stdout and stderr of the
@@ -425,3 +425,35 @@ class SubprocessInterpreter(BaseInterpreter):
425
425
  return True
426
426
  except subprocess.CalledProcessError:
427
427
  return False
428
+
429
+ def execute_command(self, command: str) -> tuple[str, str]:
430
+ r"""Executes a shell command in a subprocess and captures its output.
431
+
432
+ Args:
433
+ command (str): The shell command to execute.
434
+
435
+ Returns:
436
+ tuple: A tuple containing the captured stdout and stderr of the
437
+ executed command.
438
+
439
+ Raises:
440
+ InterpreterError: If the command execution fails.
441
+ """
442
+ try:
443
+ # Get current Python executable's environment
444
+ env = os.environ.copy()
445
+
446
+ proc = subprocess.Popen(
447
+ command,
448
+ stdout=subprocess.PIPE,
449
+ stderr=subprocess.PIPE,
450
+ text=True,
451
+ env=env,
452
+ shell=True, # Use shell=True for command execution
453
+ )
454
+ # Add timeout to prevent hanging processes
455
+ stdout, stderr = proc.communicate(timeout=self.execution_timeout)
456
+
457
+ return stdout, stderr
458
+ except Exception as e:
459
+ raise InterpreterError(f"Error executing command: {e}")
camel/memories/records.py CHANGED
@@ -15,8 +15,8 @@
15
15
  # Enables postponed evaluation of annotations (for string-based type hints)
16
16
  from __future__ import annotations
17
17
 
18
+ import time
18
19
  from dataclasses import asdict
19
- from datetime import datetime, timezone
20
20
  from typing import Any, ClassVar, Dict
21
21
  from uuid import UUID, uuid4
22
22
 
@@ -53,7 +53,8 @@ class MemoryRecord(BaseModel):
53
53
  uuid: UUID = Field(default_factory=uuid4)
54
54
  extra_info: Dict[str, str] = Field(default_factory=dict)
55
55
  timestamp: float = Field(
56
- default_factory=lambda: datetime.now(timezone.utc).timestamp()
56
+ default_factory=lambda: time.time_ns()
57
+ / 1_000_000_000 # Nanosecond precision
57
58
  )
58
59
  agent_id: str = Field(default="")
59
60
 
@@ -109,5 +110,6 @@ class ContextRecord(BaseModel):
109
110
  memory_record: MemoryRecord
110
111
  score: float
111
112
  timestamp: float = Field(
112
- default_factory=lambda: datetime.now(timezone.utc).timestamp()
113
+ default_factory=lambda: time.time_ns()
114
+ / 1_000_000_000 # Nanosecond precision
113
115
  )
camel/models/__init__.py CHANGED
@@ -18,6 +18,7 @@ from .azure_openai_model import AzureOpenAIModel
18
18
  from .base_audio_model import BaseAudioModel
19
19
  from .base_model import BaseModelBackend
20
20
  from .cohere_model import CohereModel
21
+ from .crynux_model import CrynuxModel
21
22
  from .deepseek_model import DeepSeekModel
22
23
  from .fish_audio_model import FishAudioModel
23
24
  from .gemini_model import GeminiModel
@@ -96,4 +97,5 @@ __all__ = [
96
97
  'VolcanoModel',
97
98
  'LMStudioModel',
98
99
  'WatsonXModel',
100
+ 'CrynuxModel',
99
101
  ]