camel-ai 0.2.59__py3-none-any.whl → 0.2.61__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 (55) hide show
  1. camel/__init__.py +1 -1
  2. camel/agents/chat_agent.py +158 -7
  3. camel/configs/anthropic_config.py +6 -5
  4. camel/configs/cohere_config.py +1 -1
  5. camel/configs/mistral_config.py +1 -1
  6. camel/configs/openai_config.py +3 -0
  7. camel/configs/reka_config.py +1 -1
  8. camel/configs/samba_config.py +2 -2
  9. camel/datagen/cot_datagen.py +29 -34
  10. camel/datagen/evol_instruct/scorer.py +22 -23
  11. camel/datagen/evol_instruct/templates.py +46 -46
  12. camel/datasets/static_dataset.py +144 -0
  13. camel/embeddings/jina_embedding.py +8 -1
  14. camel/embeddings/sentence_transformers_embeddings.py +2 -2
  15. camel/embeddings/vlm_embedding.py +9 -2
  16. camel/loaders/__init__.py +5 -2
  17. camel/loaders/chunkr_reader.py +117 -91
  18. camel/loaders/mistral_reader.py +148 -0
  19. camel/memories/blocks/chat_history_block.py +1 -2
  20. camel/memories/records.py +3 -0
  21. camel/messages/base.py +15 -3
  22. camel/models/azure_openai_model.py +1 -0
  23. camel/models/model_factory.py +2 -2
  24. camel/models/model_manager.py +7 -3
  25. camel/retrievers/bm25_retriever.py +1 -2
  26. camel/retrievers/hybrid_retrival.py +2 -2
  27. camel/societies/workforce/workforce.py +65 -24
  28. camel/storages/__init__.py +2 -0
  29. camel/storages/vectordb_storages/__init__.py +2 -0
  30. camel/storages/vectordb_storages/faiss.py +712 -0
  31. camel/storages/vectordb_storages/oceanbase.py +1 -2
  32. camel/toolkits/__init__.py +2 -0
  33. camel/toolkits/async_browser_toolkit.py +80 -524
  34. camel/toolkits/bohrium_toolkit.py +318 -0
  35. camel/toolkits/browser_toolkit.py +221 -541
  36. camel/toolkits/browser_toolkit_commons.py +568 -0
  37. camel/toolkits/dalle_toolkit.py +4 -0
  38. camel/toolkits/excel_toolkit.py +8 -2
  39. camel/toolkits/file_write_toolkit.py +76 -29
  40. camel/toolkits/github_toolkit.py +43 -25
  41. camel/toolkits/image_analysis_toolkit.py +3 -0
  42. camel/toolkits/jina_reranker_toolkit.py +194 -77
  43. camel/toolkits/mcp_toolkit.py +134 -16
  44. camel/toolkits/page_script.js +40 -28
  45. camel/toolkits/twitter_toolkit.py +6 -1
  46. camel/toolkits/video_analysis_toolkit.py +3 -0
  47. camel/toolkits/video_download_toolkit.py +3 -0
  48. camel/toolkits/wolfram_alpha_toolkit.py +51 -23
  49. camel/types/enums.py +27 -6
  50. camel/utils/__init__.py +2 -0
  51. camel/utils/commons.py +27 -0
  52. {camel_ai-0.2.59.dist-info → camel_ai-0.2.61.dist-info}/METADATA +17 -9
  53. {camel_ai-0.2.59.dist-info → camel_ai-0.2.61.dist-info}/RECORD +55 -51
  54. {camel_ai-0.2.59.dist-info → camel_ai-0.2.61.dist-info}/WHEEL +0 -0
  55. {camel_ai-0.2.59.dist-info → camel_ai-0.2.61.dist-info}/licenses/LICENSE +0 -0
@@ -11,8 +11,6 @@
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
-
16
14
  import re
17
15
  from datetime import datetime
18
16
  from pathlib import Path
@@ -21,7 +19,7 @@ from typing import List, Optional, Union
21
19
  from camel.logger import get_logger
22
20
  from camel.toolkits.base import BaseToolkit
23
21
  from camel.toolkits.function_tool import FunctionTool
24
- from camel.utils import MCPServer
22
+ from camel.utils import MCPServer, dependencies_required
25
23
 
26
24
  logger = get_logger(__name__)
27
25
 
@@ -148,40 +146,83 @@ class FileWriteToolkit(BaseToolkit):
148
146
  document.save(str(file_path))
149
147
  logger.debug(f"Wrote DOCX to {file_path} with default formatting")
150
148
 
151
- def _write_pdf_file(self, file_path: Path, content: str, **kwargs) -> None:
149
+ @dependencies_required('pylatex', 'fpdf')
150
+ def _write_pdf_file(
151
+ self, file_path: Path, content: str, use_latex: bool = False
152
+ ) -> None:
152
153
  r"""Write text content to a PDF file with default formatting.
153
154
 
154
155
  Args:
155
156
  file_path (Path): The target file path.
156
157
  content (str): The text content to write.
158
+ use_latex (bool): Whether to use LaTeX for rendering. (requires
159
+ LaTeX toolchain). If False, uses FPDF for simpler PDF
160
+ generation. (default: :obj: `False`)
157
161
 
158
162
  Raises:
159
- RuntimeError: If the 'fpdf' library is not installed.
163
+ RuntimeError: If the 'pylatex' or 'fpdf' library is not installed
164
+ when use_latex=True.
160
165
  """
161
- from fpdf import FPDF
162
-
163
- # Use default formatting values
164
- font_family = 'Arial'
165
- font_size = 12
166
- font_style = ''
167
- line_height = 10
168
- margin = 10
169
-
170
- pdf = FPDF()
171
- pdf.set_margins(margin, margin, margin)
172
-
173
- pdf.add_page()
174
- pdf.set_font(font_family, style=font_style, size=font_size)
175
-
176
- # Split content into paragraphs and add them
177
- for para in content.split('\n'):
178
- if para.strip(): # Skip empty paragraphs
179
- pdf.multi_cell(0, line_height, para)
180
- else:
181
- pdf.ln(line_height) # Add empty line
166
+ if use_latex:
167
+ from pylatex import (
168
+ Command,
169
+ Document,
170
+ Math,
171
+ Section,
172
+ )
173
+ from pylatex.utils import (
174
+ NoEscape,
175
+ )
182
176
 
183
- pdf.output(str(file_path))
184
- logger.debug(f"Wrote PDF to {file_path} with custom formatting")
177
+ doc = Document(documentclass="article")
178
+ doc.packages.append(Command('usepackage', 'amsmath'))
179
+
180
+ with doc.create(Section('Generated Content')):
181
+ for line in content.split('\n'):
182
+ # Remove leading whitespace
183
+ stripped_line = line.strip()
184
+ # Check if the line is intended as a standalone math
185
+ # expression
186
+ if (
187
+ stripped_line.startswith('$')
188
+ and stripped_line.endswith('$')
189
+ and len(stripped_line) > 1
190
+ ):
191
+ # Extract content between the '$' delimiters
192
+ math_data = stripped_line[1:-1]
193
+ doc.append(Math(data=math_data))
194
+ else:
195
+ doc.append(NoEscape(line))
196
+ doc.append(NoEscape(r'\par'))
197
+
198
+ doc.generate_pdf(str(file_path), clean_tex=False)
199
+
200
+ logger.info(f"Wrote PDF (with LaTeX) to {file_path}")
201
+ else:
202
+ from fpdf import FPDF
203
+
204
+ # Use default formatting values
205
+ font_family = 'Arial'
206
+ font_size = 12
207
+ font_style = ''
208
+ line_height = 10
209
+ margin = 10
210
+
211
+ pdf = FPDF()
212
+ pdf.set_margins(margin, margin, margin)
213
+
214
+ pdf.add_page()
215
+ pdf.set_font(font_family, style=font_style, size=font_size)
216
+
217
+ # Split content into paragraphs and add them
218
+ for para in content.split('\n'):
219
+ if para.strip(): # Skip empty paragraphs
220
+ pdf.multi_cell(0, line_height, para)
221
+ else:
222
+ pdf.ln(line_height) # Add empty line
223
+
224
+ pdf.output(str(file_path))
225
+ logger.debug(f"Wrote PDF to {file_path} with custom formatting")
185
226
 
186
227
  def _write_csv_file(
187
228
  self,
@@ -286,6 +327,7 @@ class FileWriteToolkit(BaseToolkit):
286
327
  content: Union[str, List[List[str]]],
287
328
  filename: str,
288
329
  encoding: Optional[str] = None,
330
+ use_latex: bool = False,
289
331
  ) -> str:
290
332
  r"""Write the given content to a file.
291
333
 
@@ -302,6 +344,9 @@ class FileWriteToolkit(BaseToolkit):
302
344
  supplied, it is resolved to self.output_dir.
303
345
  encoding (Optional[str]): The character encoding to use. (default:
304
346
  :obj: `None`)
347
+ use_latex (bool): For PDF files, whether to use LaTeX rendering
348
+ (True) or simple FPDF rendering (False). (default: :obj:
349
+ `False`)
305
350
 
306
351
  Returns:
307
352
  str: A message indicating success or error details.
@@ -326,7 +371,9 @@ class FileWriteToolkit(BaseToolkit):
326
371
  if extension in [".doc", ".docx"]:
327
372
  self._write_docx_file(file_path, str(content))
328
373
  elif extension == ".pdf":
329
- self._write_pdf_file(file_path, str(content))
374
+ self._write_pdf_file(
375
+ file_path, str(content), use_latex=use_latex
376
+ )
330
377
  elif extension == ".csv":
331
378
  self._write_csv_file(
332
379
  file_path, content, encoding=file_encoding
@@ -32,7 +32,6 @@ class GithubToolkit(BaseToolkit):
32
32
  specific issues, and creating pull requests in a GitHub repository.
33
33
 
34
34
  Args:
35
- repo_name (str): The name of the GitHub repository.
36
35
  access_token (str, optional): The access token to authenticate with
37
36
  GitHub. If not provided, it will be obtained using the
38
37
  `get_github_access_token` method.
@@ -41,7 +40,6 @@ class GithubToolkit(BaseToolkit):
41
40
  @dependencies_required('github')
42
41
  def __init__(
43
42
  self,
44
- repo_name: str,
45
43
  access_token: Optional[str] = None,
46
44
  timeout: Optional[float] = None,
47
45
  ) -> None:
@@ -61,7 +59,6 @@ class GithubToolkit(BaseToolkit):
61
59
  access_token = self.get_github_access_token()
62
60
 
63
61
  self.github = Github(auth=Token(access_token))
64
- self.repo = self.github.get_repo(repo_name)
65
62
 
66
63
  def get_github_access_token(self) -> str:
67
64
  r"""Retrieve the GitHub access token from environment variables.
@@ -85,6 +82,7 @@ class GithubToolkit(BaseToolkit):
85
82
 
86
83
  def create_pull_request(
87
84
  self,
85
+ repo_name: str,
88
86
  file_path: str,
89
87
  new_content: str,
90
88
  pr_title: str,
@@ -98,6 +96,7 @@ class GithubToolkit(BaseToolkit):
98
96
  description contains information about the issue title and number.
99
97
 
100
98
  Args:
99
+ repo_name (str): The name of the GitHub repository.
101
100
  file_path (str): The path of the file to be updated in the
102
101
  repository.
103
102
  new_content (str): The specified new content of the specified file.
@@ -111,12 +110,13 @@ class GithubToolkit(BaseToolkit):
111
110
  str: A formatted report of whether the pull request was created
112
111
  successfully or not.
113
112
  """
114
- sb = self.repo.get_branch(self.repo.default_branch)
113
+ repo = self.github.get_repo(repo_name)
114
+ default_branch = repo.get_branch(repo.default_branch)
115
115
  from github.GithubException import GithubException
116
116
 
117
117
  try:
118
- self.repo.create_git_ref(
119
- ref=f"refs/heads/{branch_name}", sha=sb.commit.sha
118
+ repo.create_git_ref(
119
+ ref=f"refs/heads/{branch_name}", sha=default_branch.commit.sha
120
120
  )
121
121
  except GithubException as e:
122
122
  if e.message == "Reference already exists":
@@ -128,19 +128,19 @@ class GithubToolkit(BaseToolkit):
128
128
  else:
129
129
  raise
130
130
 
131
- file = self.repo.get_contents(file_path)
131
+ file = repo.get_contents(file_path)
132
132
 
133
133
  from github.ContentFile import ContentFile
134
134
 
135
135
  if isinstance(file, ContentFile):
136
- self.repo.update_file(
136
+ repo.update_file(
137
137
  file.path, body, new_content, file.sha, branch=branch_name
138
138
  )
139
- pr = self.repo.create_pull(
139
+ pr = repo.create_pull(
140
140
  title=pr_title,
141
141
  body=body,
142
142
  head=branch_name,
143
- base=self.repo.default_branch,
143
+ base=repo.default_branch,
144
144
  )
145
145
 
146
146
  if pr is not None:
@@ -151,11 +151,12 @@ class GithubToolkit(BaseToolkit):
151
151
  raise ValueError("PRs with multiple files aren't supported yet.")
152
152
 
153
153
  def get_issue_list(
154
- self, state: Literal["open", "closed", "all"] = "all"
154
+ self, repo_name: str, state: Literal["open", "closed", "all"] = "all"
155
155
  ) -> List[Dict[str, object]]:
156
156
  r"""Retrieves all issues from the GitHub repository.
157
157
 
158
158
  Args:
159
+ repo_name (str): The name of the GitHub repository.
159
160
  state (Literal["open", "closed", "all"]): The state of pull
160
161
  requests to retrieve. (default: :obj: `all`)
161
162
  Options are:
@@ -167,35 +168,39 @@ class GithubToolkit(BaseToolkit):
167
168
  List[Dict[str, object]]: A list of dictionaries where each
168
169
  dictionary contains the issue number and title.
169
170
  """
171
+ repo = self.github.get_repo(repo_name)
170
172
  issues_info = []
171
- issues = self.repo.get_issues(state=state)
173
+ issues = repo.get_issues(state=state)
172
174
 
173
175
  for issue in issues:
174
176
  issues_info.append({"number": issue.number, "title": issue.title})
175
177
 
176
178
  return issues_info
177
179
 
178
- def get_issue_content(self, issue_number: int) -> str:
180
+ def get_issue_content(self, repo_name: str, issue_number: int) -> str:
179
181
  r"""Retrieves the content of a specific issue by its number.
180
182
 
181
183
  Args:
184
+ repo_name (str): The name of the GitHub repository.
182
185
  issue_number (int): The number of the issue to retrieve.
183
186
 
184
187
  Returns:
185
188
  str: issues content details.
186
189
  """
187
190
  try:
188
- issue = self.repo.get_issue(number=issue_number)
191
+ repo = self.github.get_repo(repo_name)
192
+ issue = repo.get_issue(number=issue_number)
189
193
  return issue.body
190
194
  except Exception as e:
191
195
  return f"can't get Issue number {issue_number}: {e!s}"
192
196
 
193
197
  def get_pull_request_list(
194
- self, state: Literal["open", "closed", "all"] = "all"
198
+ self, repo_name: str, state: Literal["open", "closed", "all"] = "all"
195
199
  ) -> List[Dict[str, object]]:
196
200
  r"""Retrieves all pull requests from the GitHub repository.
197
201
 
198
202
  Args:
203
+ repo_name (str): The name of the GitHub repository.
199
204
  state (Literal["open", "closed", "all"]): The state of pull
200
205
  requests to retrieve. (default: :obj: `all`)
201
206
  Options are:
@@ -207,18 +212,22 @@ class GithubToolkit(BaseToolkit):
207
212
  list: A list of dictionaries where each dictionary contains the
208
213
  pull request number and title.
209
214
  """
215
+ repo = self.github.get_repo(repo_name)
210
216
  pull_requests_info = []
211
- pull_requests = self.repo.get_pulls(state=state)
217
+ pull_requests = repo.get_pulls(state=state)
212
218
 
213
219
  for pr in pull_requests:
214
220
  pull_requests_info.append({"number": pr.number, "title": pr.title})
215
221
 
216
222
  return pull_requests_info
217
223
 
218
- def get_pull_request_code(self, pr_number: int) -> List[Dict[str, str]]:
224
+ def get_pull_request_code(
225
+ self, repo_name: str, pr_number: int
226
+ ) -> List[Dict[str, str]]:
219
227
  r"""Retrieves the code changes of a specific pull request.
220
228
 
221
229
  Args:
230
+ repo_name (str): The name of the GitHub repository.
222
231
  pr_number (int): The number of the pull request to retrieve.
223
232
 
224
233
  Returns:
@@ -226,8 +235,9 @@ class GithubToolkit(BaseToolkit):
226
235
  contains the file name and the corresponding code changes
227
236
  (patch).
228
237
  """
238
+ repo = self.github.get_repo(repo_name)
229
239
  # Retrieve the specific pull request
230
- pr = self.repo.get_pull(number=pr_number)
240
+ pr = repo.get_pull(number=pr_number)
231
241
 
232
242
  # Collect the file changes from the pull request
233
243
  files_changed = []
@@ -244,19 +254,21 @@ class GithubToolkit(BaseToolkit):
244
254
  return files_changed
245
255
 
246
256
  def get_pull_request_comments(
247
- self, pr_number: int
257
+ self, repo_name: str, pr_number: int
248
258
  ) -> List[Dict[str, str]]:
249
259
  r"""Retrieves the comments from a specific pull request.
250
260
 
251
261
  Args:
262
+ repo_name (str): The name of the GitHub repository.
252
263
  pr_number (int): The number of the pull request to retrieve.
253
264
 
254
265
  Returns:
255
266
  List[Dict[str, str]]: A list of dictionaries where each dictionary
256
267
  contains the user ID and the comment body.
257
268
  """
269
+ repo = self.github.get_repo(repo_name)
258
270
  # Retrieve the specific pull request
259
- pr = self.repo.get_pull(number=pr_number)
271
+ pr = repo.get_pull(number=pr_number)
260
272
 
261
273
  # Collect the comments from the pull request
262
274
  comments = []
@@ -266,10 +278,11 @@ class GithubToolkit(BaseToolkit):
266
278
 
267
279
  return comments
268
280
 
269
- def get_all_file_paths(self, path: str = "") -> List[str]:
281
+ def get_all_file_paths(self, repo_name: str, path: str = "") -> List[str]:
270
282
  r"""Recursively retrieves all file paths in the GitHub repository.
271
283
 
272
284
  Args:
285
+ repo_name (str): The name of the GitHub repository.
273
286
  path (str): The repository path to start the traversal from.
274
287
  empty string means starts from the root directory.
275
288
  (default: :obj: `""`)
@@ -280,11 +293,13 @@ class GithubToolkit(BaseToolkit):
280
293
  """
281
294
  from github.ContentFile import ContentFile
282
295
 
296
+ repo = self.github.get_repo(repo_name)
297
+
283
298
  files: List[str] = []
284
299
 
285
300
  # Retrieves all contents of the current directory
286
- contents: Union[List[ContentFile], ContentFile] = (
287
- self.repo.get_contents(path)
301
+ contents: Union[List[ContentFile], ContentFile] = repo.get_contents(
302
+ path
288
303
  )
289
304
 
290
305
  if isinstance(contents, ContentFile):
@@ -299,10 +314,11 @@ class GithubToolkit(BaseToolkit):
299
314
  files.append(content.path)
300
315
  return files
301
316
 
302
- def retrieve_file_content(self, file_path: str) -> str:
317
+ def retrieve_file_content(self, repo_name: str, file_path: str) -> str:
303
318
  r"""Retrieves the content of a file from the GitHub repository.
304
319
 
305
320
  Args:
321
+ repo_name (str): The name of the GitHub repository.
306
322
  file_path (str): The path of the file to retrieve.
307
323
 
308
324
  Returns:
@@ -310,7 +326,9 @@ class GithubToolkit(BaseToolkit):
310
326
  """
311
327
  from github.ContentFile import ContentFile
312
328
 
313
- file_content = self.repo.get_contents(file_path)
329
+ repo = self.github.get_repo(repo_name)
330
+
331
+ file_content = repo.get_contents(file_path)
314
332
  if isinstance(file_content, ContentFile):
315
333
  return file_content.decoded_content.decode()
316
334
  else:
@@ -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 io import BytesIO
16
19
  from typing import List, Optional
17
20
  from urllib.parse import urlparse