camel-ai 0.2.67__py3-none-any.whl → 0.2.80a2__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.
- camel/__init__.py +1 -1
- camel/agents/_types.py +6 -2
- camel/agents/_utils.py +38 -0
- camel/agents/chat_agent.py +4014 -410
- camel/agents/mcp_agent.py +30 -27
- camel/agents/repo_agent.py +2 -1
- camel/benchmarks/browsecomp.py +6 -6
- camel/configs/__init__.py +15 -0
- camel/configs/aihubmix_config.py +88 -0
- camel/configs/amd_config.py +70 -0
- camel/configs/cometapi_config.py +104 -0
- camel/configs/minimax_config.py +93 -0
- camel/configs/nebius_config.py +103 -0
- camel/configs/vllm_config.py +2 -0
- camel/data_collectors/alpaca_collector.py +15 -6
- camel/datagen/self_improving_cot.py +1 -1
- camel/datasets/base_generator.py +39 -10
- camel/environments/__init__.py +12 -0
- camel/environments/rlcards_env.py +860 -0
- camel/environments/single_step.py +28 -3
- camel/environments/tic_tac_toe.py +1 -1
- camel/interpreters/__init__.py +2 -0
- camel/interpreters/docker/Dockerfile +4 -16
- camel/interpreters/docker_interpreter.py +3 -2
- camel/interpreters/e2b_interpreter.py +34 -1
- camel/interpreters/internal_python_interpreter.py +51 -2
- camel/interpreters/microsandbox_interpreter.py +395 -0
- camel/loaders/__init__.py +11 -2
- camel/loaders/base_loader.py +85 -0
- camel/loaders/chunkr_reader.py +9 -0
- camel/loaders/firecrawl_reader.py +4 -4
- camel/logger.py +1 -1
- camel/memories/agent_memories.py +84 -1
- camel/memories/base.py +34 -0
- camel/memories/blocks/chat_history_block.py +122 -4
- camel/memories/blocks/vectordb_block.py +8 -1
- camel/memories/context_creators/score_based.py +29 -237
- camel/memories/records.py +88 -8
- camel/messages/base.py +166 -40
- camel/messages/func_message.py +32 -5
- camel/models/__init__.py +10 -0
- camel/models/aihubmix_model.py +83 -0
- camel/models/aiml_model.py +1 -16
- camel/models/amd_model.py +101 -0
- camel/models/anthropic_model.py +117 -18
- camel/models/aws_bedrock_model.py +2 -33
- camel/models/azure_openai_model.py +205 -91
- camel/models/base_audio_model.py +3 -1
- camel/models/base_model.py +189 -24
- camel/models/cohere_model.py +5 -17
- camel/models/cometapi_model.py +83 -0
- camel/models/crynux_model.py +1 -16
- camel/models/deepseek_model.py +6 -16
- camel/models/fish_audio_model.py +6 -0
- camel/models/gemini_model.py +71 -20
- camel/models/groq_model.py +1 -17
- camel/models/internlm_model.py +1 -16
- camel/models/litellm_model.py +49 -32
- camel/models/lmstudio_model.py +1 -17
- camel/models/minimax_model.py +83 -0
- camel/models/mistral_model.py +1 -16
- camel/models/model_factory.py +27 -1
- camel/models/model_manager.py +24 -6
- camel/models/modelscope_model.py +1 -16
- camel/models/moonshot_model.py +185 -19
- camel/models/nebius_model.py +83 -0
- camel/models/nemotron_model.py +0 -5
- camel/models/netmind_model.py +1 -16
- camel/models/novita_model.py +1 -16
- camel/models/nvidia_model.py +1 -16
- camel/models/ollama_model.py +4 -19
- camel/models/openai_compatible_model.py +171 -46
- camel/models/openai_model.py +205 -77
- camel/models/openrouter_model.py +1 -17
- camel/models/ppio_model.py +1 -16
- camel/models/qianfan_model.py +1 -16
- camel/models/qwen_model.py +1 -16
- camel/models/reka_model.py +1 -16
- camel/models/samba_model.py +34 -47
- camel/models/sglang_model.py +64 -31
- camel/models/siliconflow_model.py +1 -16
- camel/models/stub_model.py +0 -4
- camel/models/togetherai_model.py +1 -16
- camel/models/vllm_model.py +1 -16
- camel/models/volcano_model.py +0 -17
- camel/models/watsonx_model.py +1 -16
- camel/models/yi_model.py +1 -16
- camel/models/zhipuai_model.py +60 -16
- camel/parsers/__init__.py +18 -0
- camel/parsers/mcp_tool_call_parser.py +176 -0
- camel/retrievers/auto_retriever.py +1 -0
- camel/runtimes/configs.py +11 -11
- camel/runtimes/daytona_runtime.py +15 -16
- camel/runtimes/docker_runtime.py +6 -6
- camel/runtimes/remote_http_runtime.py +5 -5
- camel/services/agent_openapi_server.py +380 -0
- camel/societies/__init__.py +2 -0
- camel/societies/role_playing.py +26 -28
- camel/societies/workforce/__init__.py +2 -0
- camel/societies/workforce/events.py +122 -0
- camel/societies/workforce/prompts.py +249 -38
- camel/societies/workforce/role_playing_worker.py +82 -20
- camel/societies/workforce/single_agent_worker.py +634 -34
- camel/societies/workforce/structured_output_handler.py +512 -0
- camel/societies/workforce/task_channel.py +169 -23
- camel/societies/workforce/utils.py +176 -9
- camel/societies/workforce/worker.py +77 -23
- camel/societies/workforce/workflow_memory_manager.py +772 -0
- camel/societies/workforce/workforce.py +3168 -478
- camel/societies/workforce/workforce_callback.py +74 -0
- camel/societies/workforce/workforce_logger.py +203 -175
- camel/societies/workforce/workforce_metrics.py +33 -0
- camel/storages/__init__.py +4 -0
- camel/storages/key_value_storages/json.py +15 -2
- camel/storages/key_value_storages/mem0_cloud.py +48 -47
- camel/storages/object_storages/google_cloud.py +1 -1
- camel/storages/vectordb_storages/__init__.py +6 -0
- camel/storages/vectordb_storages/chroma.py +731 -0
- camel/storages/vectordb_storages/oceanbase.py +13 -13
- camel/storages/vectordb_storages/pgvector.py +349 -0
- camel/storages/vectordb_storages/qdrant.py +3 -3
- camel/storages/vectordb_storages/surreal.py +365 -0
- camel/storages/vectordb_storages/tidb.py +8 -6
- camel/tasks/task.py +244 -27
- camel/toolkits/__init__.py +46 -8
- camel/toolkits/aci_toolkit.py +64 -19
- camel/toolkits/arxiv_toolkit.py +6 -6
- camel/toolkits/base.py +63 -5
- camel/toolkits/code_execution.py +28 -1
- camel/toolkits/context_summarizer_toolkit.py +684 -0
- camel/toolkits/craw4ai_toolkit.py +93 -0
- camel/toolkits/dappier_toolkit.py +10 -6
- camel/toolkits/dingtalk.py +1135 -0
- camel/toolkits/edgeone_pages_mcp_toolkit.py +49 -0
- camel/toolkits/excel_toolkit.py +901 -67
- camel/toolkits/file_toolkit.py +1402 -0
- camel/toolkits/function_tool.py +30 -6
- camel/toolkits/github_toolkit.py +107 -20
- camel/toolkits/gmail_toolkit.py +1839 -0
- camel/toolkits/google_calendar_toolkit.py +38 -4
- camel/toolkits/google_drive_mcp_toolkit.py +54 -0
- camel/toolkits/human_toolkit.py +34 -10
- camel/toolkits/hybrid_browser_toolkit/__init__.py +18 -0
- camel/toolkits/hybrid_browser_toolkit/config_loader.py +185 -0
- camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit.py +246 -0
- camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit_ts.py +1973 -0
- camel/toolkits/hybrid_browser_toolkit/installer.py +203 -0
- camel/toolkits/hybrid_browser_toolkit/ts/package-lock.json +3749 -0
- camel/toolkits/hybrid_browser_toolkit/ts/package.json +32 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/browser-scripts.js +125 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/browser-session.ts +1815 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/config-loader.ts +233 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/hybrid-browser-toolkit.ts +590 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/index.ts +7 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/parent-child-filter.ts +226 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/snapshot-parser.ts +219 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/som-screenshot-injected.ts +543 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/types.ts +130 -0
- camel/toolkits/hybrid_browser_toolkit/ts/tsconfig.json +26 -0
- camel/toolkits/hybrid_browser_toolkit/ts/websocket-server.js +319 -0
- camel/toolkits/hybrid_browser_toolkit/ws_wrapper.py +1032 -0
- camel/toolkits/hybrid_browser_toolkit_py/__init__.py +17 -0
- camel/toolkits/hybrid_browser_toolkit_py/actions.py +575 -0
- camel/toolkits/hybrid_browser_toolkit_py/agent.py +311 -0
- camel/toolkits/hybrid_browser_toolkit_py/browser_session.py +787 -0
- camel/toolkits/hybrid_browser_toolkit_py/config_loader.py +490 -0
- camel/toolkits/hybrid_browser_toolkit_py/hybrid_browser_toolkit.py +2390 -0
- camel/toolkits/hybrid_browser_toolkit_py/snapshot.py +233 -0
- camel/toolkits/hybrid_browser_toolkit_py/stealth_script.js +0 -0
- camel/toolkits/hybrid_browser_toolkit_py/unified_analyzer.js +1043 -0
- camel/toolkits/image_generation_toolkit.py +390 -0
- camel/toolkits/jina_reranker_toolkit.py +3 -4
- camel/toolkits/klavis_toolkit.py +5 -1
- camel/toolkits/markitdown_toolkit.py +104 -0
- camel/toolkits/math_toolkit.py +64 -10
- camel/toolkits/mcp_toolkit.py +370 -45
- camel/toolkits/memory_toolkit.py +5 -1
- camel/toolkits/message_agent_toolkit.py +608 -0
- camel/toolkits/message_integration.py +724 -0
- camel/toolkits/minimax_mcp_toolkit.py +195 -0
- camel/toolkits/note_taking_toolkit.py +277 -0
- camel/toolkits/notion_mcp_toolkit.py +224 -0
- camel/toolkits/openbb_toolkit.py +5 -1
- camel/toolkits/origene_mcp_toolkit.py +56 -0
- camel/toolkits/playwright_mcp_toolkit.py +12 -31
- camel/toolkits/pptx_toolkit.py +25 -12
- camel/toolkits/resend_toolkit.py +168 -0
- camel/toolkits/screenshot_toolkit.py +213 -0
- camel/toolkits/search_toolkit.py +437 -142
- camel/toolkits/slack_toolkit.py +104 -50
- camel/toolkits/sympy_toolkit.py +1 -1
- camel/toolkits/task_planning_toolkit.py +3 -3
- camel/toolkits/terminal_toolkit/__init__.py +18 -0
- camel/toolkits/terminal_toolkit/terminal_toolkit.py +957 -0
- camel/toolkits/terminal_toolkit/utils.py +532 -0
- camel/toolkits/thinking_toolkit.py +1 -1
- camel/toolkits/vertex_ai_veo_toolkit.py +590 -0
- camel/toolkits/video_analysis_toolkit.py +106 -26
- camel/toolkits/video_download_toolkit.py +17 -14
- camel/toolkits/web_deploy_toolkit.py +1219 -0
- camel/toolkits/wechat_official_toolkit.py +483 -0
- camel/toolkits/zapier_toolkit.py +5 -1
- camel/types/__init__.py +2 -2
- camel/types/agents/tool_calling_record.py +4 -1
- camel/types/enums.py +316 -40
- camel/types/openai_types.py +2 -2
- camel/types/unified_model_type.py +31 -4
- camel/utils/commons.py +36 -5
- camel/utils/constants.py +3 -0
- camel/utils/context_utils.py +1003 -0
- camel/utils/mcp.py +138 -4
- camel/utils/mcp_client.py +45 -1
- camel/utils/message_summarizer.py +148 -0
- camel/utils/token_counting.py +43 -20
- camel/utils/tool_result.py +44 -0
- {camel_ai-0.2.67.dist-info → camel_ai-0.2.80a2.dist-info}/METADATA +296 -85
- {camel_ai-0.2.67.dist-info → camel_ai-0.2.80a2.dist-info}/RECORD +219 -146
- camel/loaders/pandas_reader.py +0 -368
- camel/toolkits/dalle_toolkit.py +0 -175
- camel/toolkits/file_write_toolkit.py +0 -444
- camel/toolkits/openai_agent_toolkit.py +0 -135
- camel/toolkits/terminal_toolkit.py +0 -1037
- {camel_ai-0.2.67.dist-info → camel_ai-0.2.80a2.dist-info}/WHEEL +0 -0
- {camel_ai-0.2.67.dist-info → camel_ai-0.2.80a2.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,444 +0,0 @@
|
|
|
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 re
|
|
15
|
-
from datetime import datetime
|
|
16
|
-
from pathlib import Path
|
|
17
|
-
from typing import List, Optional, Union
|
|
18
|
-
|
|
19
|
-
from camel.logger import get_logger
|
|
20
|
-
from camel.toolkits.base import BaseToolkit
|
|
21
|
-
from camel.toolkits.function_tool import FunctionTool
|
|
22
|
-
from camel.utils import MCPServer, dependencies_required
|
|
23
|
-
|
|
24
|
-
logger = get_logger(__name__)
|
|
25
|
-
|
|
26
|
-
# Default format when no extension is provided
|
|
27
|
-
DEFAULT_FORMAT = '.md'
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
@MCPServer()
|
|
31
|
-
class FileWriteToolkit(BaseToolkit):
|
|
32
|
-
r"""A toolkit for creating, writing, and modifying text in files.
|
|
33
|
-
|
|
34
|
-
This class provides cross-platform (macOS, Linux, Windows) support for
|
|
35
|
-
writing to various file formats (Markdown, DOCX, PDF, and plaintext),
|
|
36
|
-
replacing text in existing files, automatic backups, custom encoding,
|
|
37
|
-
and enhanced formatting options for specialized formats.
|
|
38
|
-
"""
|
|
39
|
-
|
|
40
|
-
def __init__(
|
|
41
|
-
self,
|
|
42
|
-
output_dir: str = "./",
|
|
43
|
-
timeout: Optional[float] = None,
|
|
44
|
-
default_encoding: str = "utf-8",
|
|
45
|
-
backup_enabled: bool = True,
|
|
46
|
-
) -> None:
|
|
47
|
-
r"""Initialize the FileWriteToolkit.
|
|
48
|
-
|
|
49
|
-
Args:
|
|
50
|
-
output_dir (str): The default directory for output files.
|
|
51
|
-
Defaults to the current working directory.
|
|
52
|
-
timeout (Optional[float]): The timeout for the toolkit.
|
|
53
|
-
(default: :obj: `None`)
|
|
54
|
-
default_encoding (str): Default character encoding for text
|
|
55
|
-
operations. (default: :obj: `utf-8`)
|
|
56
|
-
backup_enabled (bool): Whether to create backups of existing files
|
|
57
|
-
before overwriting. (default: :obj: `True`)
|
|
58
|
-
"""
|
|
59
|
-
super().__init__(timeout=timeout)
|
|
60
|
-
self.output_dir = Path(output_dir).resolve()
|
|
61
|
-
self.output_dir.mkdir(parents=True, exist_ok=True)
|
|
62
|
-
self.default_encoding = default_encoding
|
|
63
|
-
self.backup_enabled = backup_enabled
|
|
64
|
-
logger.info(
|
|
65
|
-
f"FileWriteToolkit initialized with output directory"
|
|
66
|
-
f": {self.output_dir}, encoding: {default_encoding}"
|
|
67
|
-
)
|
|
68
|
-
|
|
69
|
-
def _resolve_filepath(self, file_path: str) -> Path:
|
|
70
|
-
r"""Convert the given string path to a Path object.
|
|
71
|
-
|
|
72
|
-
If the provided path is not absolute, it is made relative to the
|
|
73
|
-
default output directory. The filename part is sanitized to replace
|
|
74
|
-
spaces and special characters with underscores, ensuring safe usage
|
|
75
|
-
in downstream processing.
|
|
76
|
-
|
|
77
|
-
Args:
|
|
78
|
-
file_path (str): The file path to resolve.
|
|
79
|
-
|
|
80
|
-
Returns:
|
|
81
|
-
Path: A fully resolved (absolute) and sanitized Path object.
|
|
82
|
-
"""
|
|
83
|
-
path_obj = Path(file_path)
|
|
84
|
-
if not path_obj.is_absolute():
|
|
85
|
-
path_obj = self.output_dir / path_obj
|
|
86
|
-
|
|
87
|
-
sanitized_filename = self._sanitize_filename(path_obj.name)
|
|
88
|
-
path_obj = path_obj.parent / sanitized_filename
|
|
89
|
-
return path_obj.resolve()
|
|
90
|
-
|
|
91
|
-
def _write_text_file(
|
|
92
|
-
self, file_path: Path, content: str, encoding: str = "utf-8"
|
|
93
|
-
) -> None:
|
|
94
|
-
r"""Write text content to a plaintext file.
|
|
95
|
-
|
|
96
|
-
Args:
|
|
97
|
-
file_path (Path): The target file path.
|
|
98
|
-
content (str): The text content to write.
|
|
99
|
-
encoding (str): Character encoding to use. (default: :obj: `utf-8`)
|
|
100
|
-
"""
|
|
101
|
-
with file_path.open("w", encoding=encoding) as f:
|
|
102
|
-
f.write(content)
|
|
103
|
-
logger.debug(f"Wrote text to {file_path} with {encoding} encoding")
|
|
104
|
-
|
|
105
|
-
def _create_backup(self, file_path: Path) -> None:
|
|
106
|
-
r"""Create a backup of the file if it exists and backup is enabled.
|
|
107
|
-
|
|
108
|
-
Args:
|
|
109
|
-
file_path (Path): Path to the file to backup.
|
|
110
|
-
"""
|
|
111
|
-
import shutil
|
|
112
|
-
|
|
113
|
-
if not self.backup_enabled or not file_path.exists():
|
|
114
|
-
return
|
|
115
|
-
|
|
116
|
-
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
117
|
-
backup_path = file_path.parent / f"{file_path.name}.{timestamp}.bak"
|
|
118
|
-
shutil.copy2(file_path, backup_path)
|
|
119
|
-
logger.info(f"Created backup at {backup_path}")
|
|
120
|
-
|
|
121
|
-
def _write_docx_file(self, file_path: Path, content: str) -> None:
|
|
122
|
-
r"""Write text content to a DOCX file with default formatting.
|
|
123
|
-
|
|
124
|
-
Args:
|
|
125
|
-
file_path (Path): The target file path.
|
|
126
|
-
content (str): The text content to write.
|
|
127
|
-
"""
|
|
128
|
-
import docx
|
|
129
|
-
|
|
130
|
-
# Use default formatting values
|
|
131
|
-
font_name = 'Calibri'
|
|
132
|
-
font_size = 11
|
|
133
|
-
line_spacing = 1.0
|
|
134
|
-
|
|
135
|
-
document = docx.Document()
|
|
136
|
-
style = document.styles['Normal']
|
|
137
|
-
style.font.name = font_name
|
|
138
|
-
style.font.size = docx.shared.Pt(font_size)
|
|
139
|
-
style.paragraph_format.line_spacing = line_spacing
|
|
140
|
-
|
|
141
|
-
# Split content into paragraphs and add them
|
|
142
|
-
for para_text in content.split('\n'):
|
|
143
|
-
para = document.add_paragraph(para_text)
|
|
144
|
-
para.style = style
|
|
145
|
-
|
|
146
|
-
document.save(str(file_path))
|
|
147
|
-
logger.debug(f"Wrote DOCX to {file_path} with default formatting")
|
|
148
|
-
|
|
149
|
-
@dependencies_required('pylatex', 'fpdf')
|
|
150
|
-
def _write_pdf_file(
|
|
151
|
-
self, file_path: Path, content: str, use_latex: bool = False
|
|
152
|
-
) -> None:
|
|
153
|
-
r"""Write text content to a PDF file with default formatting.
|
|
154
|
-
|
|
155
|
-
Args:
|
|
156
|
-
file_path (Path): The target file path.
|
|
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`)
|
|
161
|
-
|
|
162
|
-
Raises:
|
|
163
|
-
RuntimeError: If the 'pylatex' or 'fpdf' library is not installed
|
|
164
|
-
when use_latex=True.
|
|
165
|
-
"""
|
|
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
|
-
)
|
|
176
|
-
|
|
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")
|
|
226
|
-
|
|
227
|
-
def _write_csv_file(
|
|
228
|
-
self,
|
|
229
|
-
file_path: Path,
|
|
230
|
-
content: Union[str, List[List]],
|
|
231
|
-
encoding: str = "utf-8",
|
|
232
|
-
) -> None:
|
|
233
|
-
r"""Write CSV content to a file.
|
|
234
|
-
|
|
235
|
-
Args:
|
|
236
|
-
file_path (Path): The target file path.
|
|
237
|
-
content (Union[str, List[List]]): The CSV content as a string or
|
|
238
|
-
list of lists.
|
|
239
|
-
encoding (str): Character encoding to use. (default: :obj: `utf-8`)
|
|
240
|
-
"""
|
|
241
|
-
import csv
|
|
242
|
-
|
|
243
|
-
with file_path.open("w", encoding=encoding, newline='') as f:
|
|
244
|
-
if isinstance(content, str):
|
|
245
|
-
f.write(content)
|
|
246
|
-
else:
|
|
247
|
-
writer = csv.writer(f)
|
|
248
|
-
writer.writerows(content)
|
|
249
|
-
logger.debug(f"Wrote CSV to {file_path} with {encoding} encoding")
|
|
250
|
-
|
|
251
|
-
def _write_json_file(
|
|
252
|
-
self,
|
|
253
|
-
file_path: Path,
|
|
254
|
-
content: str,
|
|
255
|
-
encoding: str = "utf-8",
|
|
256
|
-
) -> None:
|
|
257
|
-
r"""Write JSON content to a file.
|
|
258
|
-
|
|
259
|
-
Args:
|
|
260
|
-
file_path (Path): The target file path.
|
|
261
|
-
content (str): The JSON content as a string.
|
|
262
|
-
encoding (str): Character encoding to use. (default: :obj: `utf-8`)
|
|
263
|
-
"""
|
|
264
|
-
import json
|
|
265
|
-
|
|
266
|
-
with file_path.open("w", encoding=encoding) as f:
|
|
267
|
-
if isinstance(content, str):
|
|
268
|
-
try:
|
|
269
|
-
# Try parsing as JSON string first
|
|
270
|
-
data = json.loads(content)
|
|
271
|
-
json.dump(data, f, ensure_ascii=False)
|
|
272
|
-
except json.JSONDecodeError:
|
|
273
|
-
# If not valid JSON string, write as is
|
|
274
|
-
f.write(content)
|
|
275
|
-
else:
|
|
276
|
-
# If not string, dump as JSON
|
|
277
|
-
json.dump(content, f, ensure_ascii=False)
|
|
278
|
-
logger.debug(f"Wrote JSON to {file_path} with {encoding} encoding")
|
|
279
|
-
|
|
280
|
-
def _write_yaml_file(
|
|
281
|
-
self,
|
|
282
|
-
file_path: Path,
|
|
283
|
-
content: str,
|
|
284
|
-
encoding: str = "utf-8",
|
|
285
|
-
) -> None:
|
|
286
|
-
r"""Write YAML content to a file.
|
|
287
|
-
|
|
288
|
-
Args:
|
|
289
|
-
file_path (Path): The target file path.
|
|
290
|
-
content (str): The YAML content as a string.
|
|
291
|
-
encoding (str): Character encoding to use. (default: :obj: `utf-8`)
|
|
292
|
-
"""
|
|
293
|
-
with file_path.open("w", encoding=encoding) as f:
|
|
294
|
-
f.write(content)
|
|
295
|
-
logger.debug(f"Wrote YAML to {file_path} with {encoding} encoding")
|
|
296
|
-
|
|
297
|
-
def _write_html_file(
|
|
298
|
-
self, file_path: Path, content: str, encoding: str = "utf-8"
|
|
299
|
-
) -> None:
|
|
300
|
-
r"""Write text content to an HTML file.
|
|
301
|
-
|
|
302
|
-
Args:
|
|
303
|
-
file_path (Path): The target file path.
|
|
304
|
-
content (str): The HTML content to write.
|
|
305
|
-
encoding (str): Character encoding to use. (default: :obj: `utf-8`)
|
|
306
|
-
"""
|
|
307
|
-
with file_path.open("w", encoding=encoding) as f:
|
|
308
|
-
f.write(content)
|
|
309
|
-
logger.debug(f"Wrote HTML to {file_path} with {encoding} encoding")
|
|
310
|
-
|
|
311
|
-
def _write_markdown_file(
|
|
312
|
-
self, file_path: Path, content: str, encoding: str = "utf-8"
|
|
313
|
-
) -> None:
|
|
314
|
-
r"""Write text content to a Markdown file.
|
|
315
|
-
|
|
316
|
-
Args:
|
|
317
|
-
file_path (Path): The target file path.
|
|
318
|
-
content (str): The Markdown content to write.
|
|
319
|
-
encoding (str): Character encoding to use. (default: :obj: `utf-8`)
|
|
320
|
-
"""
|
|
321
|
-
with file_path.open("w", encoding=encoding) as f:
|
|
322
|
-
f.write(content)
|
|
323
|
-
logger.debug(f"Wrote Markdown to {file_path} with {encoding} encoding")
|
|
324
|
-
|
|
325
|
-
def write_to_file(
|
|
326
|
-
self,
|
|
327
|
-
content: Union[str, List[List[str]]],
|
|
328
|
-
filename: str,
|
|
329
|
-
encoding: Optional[str] = None,
|
|
330
|
-
use_latex: bool = False,
|
|
331
|
-
) -> str:
|
|
332
|
-
r"""Write the given content to a file.
|
|
333
|
-
|
|
334
|
-
If the file exists, it will be overwritten. Supports multiple formats:
|
|
335
|
-
Markdown (.md, .markdown, default), Plaintext (.txt), CSV (.csv),
|
|
336
|
-
DOC/DOCX (.doc, .docx), PDF (.pdf), JSON (.json), YAML (.yml, .yaml),
|
|
337
|
-
and HTML (.html, .htm).
|
|
338
|
-
|
|
339
|
-
Args:
|
|
340
|
-
content (Union[str, List[List[str]]]): The content to write to the
|
|
341
|
-
file. Content format varies by file type:
|
|
342
|
-
- Text formats (txt, md, html, yaml): string
|
|
343
|
-
- CSV: string or list of lists
|
|
344
|
-
- JSON: string or serializable object
|
|
345
|
-
filename (str): The name or path of the file. If a relative path is
|
|
346
|
-
supplied, it is resolved to self.output_dir.
|
|
347
|
-
encoding (Optional[str]): The character encoding to use. (default:
|
|
348
|
-
:obj: `None`)
|
|
349
|
-
use_latex (bool): For PDF files, whether to use LaTeX rendering
|
|
350
|
-
(True) or simple FPDF rendering (False). (default: :obj:
|
|
351
|
-
`False`)
|
|
352
|
-
|
|
353
|
-
Returns:
|
|
354
|
-
str: A message indicating success or error details.
|
|
355
|
-
"""
|
|
356
|
-
file_path = self._resolve_filepath(filename)
|
|
357
|
-
file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
358
|
-
|
|
359
|
-
# Create backup if file exists
|
|
360
|
-
self._create_backup(file_path)
|
|
361
|
-
|
|
362
|
-
extension = file_path.suffix.lower()
|
|
363
|
-
|
|
364
|
-
# If no extension is provided, use the default format
|
|
365
|
-
if extension == "":
|
|
366
|
-
file_path = file_path.with_suffix(DEFAULT_FORMAT)
|
|
367
|
-
extension = DEFAULT_FORMAT
|
|
368
|
-
|
|
369
|
-
try:
|
|
370
|
-
# Get encoding or use default
|
|
371
|
-
file_encoding = encoding or self.default_encoding
|
|
372
|
-
|
|
373
|
-
if extension in [".doc", ".docx"]:
|
|
374
|
-
self._write_docx_file(file_path, str(content))
|
|
375
|
-
elif extension == ".pdf":
|
|
376
|
-
self._write_pdf_file(
|
|
377
|
-
file_path, str(content), use_latex=use_latex
|
|
378
|
-
)
|
|
379
|
-
elif extension == ".csv":
|
|
380
|
-
self._write_csv_file(
|
|
381
|
-
file_path, content, encoding=file_encoding
|
|
382
|
-
)
|
|
383
|
-
elif extension == ".json":
|
|
384
|
-
self._write_json_file(
|
|
385
|
-
file_path,
|
|
386
|
-
content, # type: ignore[arg-type]
|
|
387
|
-
encoding=file_encoding,
|
|
388
|
-
)
|
|
389
|
-
elif extension in [".yml", ".yaml"]:
|
|
390
|
-
self._write_yaml_file(
|
|
391
|
-
file_path, str(content), encoding=file_encoding
|
|
392
|
-
)
|
|
393
|
-
elif extension in [".html", ".htm"]:
|
|
394
|
-
self._write_html_file(
|
|
395
|
-
file_path, str(content), encoding=file_encoding
|
|
396
|
-
)
|
|
397
|
-
elif extension in [".md", ".markdown"]:
|
|
398
|
-
self._write_markdown_file(
|
|
399
|
-
file_path, str(content), encoding=file_encoding
|
|
400
|
-
)
|
|
401
|
-
else:
|
|
402
|
-
# Fallback to simple text writing for unknown or .txt
|
|
403
|
-
# extensions
|
|
404
|
-
self._write_text_file(
|
|
405
|
-
file_path, str(content), encoding=file_encoding
|
|
406
|
-
)
|
|
407
|
-
|
|
408
|
-
msg = f"Content successfully written to file: {file_path}"
|
|
409
|
-
logger.info(msg)
|
|
410
|
-
return msg
|
|
411
|
-
except Exception as e:
|
|
412
|
-
error_msg = (
|
|
413
|
-
f"Error occurred while writing to file {file_path}: {e}"
|
|
414
|
-
)
|
|
415
|
-
logger.error(error_msg)
|
|
416
|
-
return error_msg
|
|
417
|
-
|
|
418
|
-
def get_tools(self) -> List[FunctionTool]:
|
|
419
|
-
r"""Return a list of FunctionTool objects representing the functions
|
|
420
|
-
in the toolkit.
|
|
421
|
-
|
|
422
|
-
Returns:
|
|
423
|
-
List[FunctionTool]: A list of FunctionTool objects representing
|
|
424
|
-
the available functions in this toolkit.
|
|
425
|
-
"""
|
|
426
|
-
return [
|
|
427
|
-
FunctionTool(self.write_to_file),
|
|
428
|
-
]
|
|
429
|
-
|
|
430
|
-
def _sanitize_filename(self, filename: str) -> str:
|
|
431
|
-
r"""Sanitize a filename by replacing any character that is not
|
|
432
|
-
alphanumeric, a dot (.), hyphen (-), or underscore (_) with an
|
|
433
|
-
underscore (_).
|
|
434
|
-
|
|
435
|
-
Args:
|
|
436
|
-
filename (str): The original filename which may contain spaces or
|
|
437
|
-
special characters.
|
|
438
|
-
|
|
439
|
-
Returns:
|
|
440
|
-
str: The sanitized filename with disallowed characters replaced by
|
|
441
|
-
underscores.
|
|
442
|
-
"""
|
|
443
|
-
safe = re.sub(r'[^\w\-.]', '_', filename)
|
|
444
|
-
return safe
|
|
@@ -1,135 +0,0 @@
|
|
|
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 os
|
|
16
|
-
from typing import List, Optional
|
|
17
|
-
|
|
18
|
-
from openai import OpenAI
|
|
19
|
-
|
|
20
|
-
from camel.logger import get_logger
|
|
21
|
-
from camel.models import BaseModelBackend, ModelFactory
|
|
22
|
-
from camel.toolkits.base import BaseToolkit
|
|
23
|
-
from camel.toolkits.function_tool import FunctionTool
|
|
24
|
-
from camel.types import ModelPlatformType, ModelType
|
|
25
|
-
from camel.utils import api_keys_required
|
|
26
|
-
|
|
27
|
-
logger = get_logger(__name__)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
class OpenAIAgentToolkit(BaseToolkit):
|
|
31
|
-
r"""Toolkit for accessing OpenAI's agent tools including web search and
|
|
32
|
-
file search.
|
|
33
|
-
|
|
34
|
-
Provides access to OpenAI's web search and file search capabilities
|
|
35
|
-
through the Responses API, allowing agents to retrieve information from
|
|
36
|
-
the web and search through uploaded files.
|
|
37
|
-
"""
|
|
38
|
-
|
|
39
|
-
@api_keys_required(
|
|
40
|
-
[
|
|
41
|
-
(None, "OPENAI_API_KEY"),
|
|
42
|
-
]
|
|
43
|
-
)
|
|
44
|
-
def __init__(
|
|
45
|
-
self,
|
|
46
|
-
model: Optional[BaseModelBackend] = None,
|
|
47
|
-
api_key: Optional[str] = None,
|
|
48
|
-
timeout: Optional[float] = None,
|
|
49
|
-
) -> None:
|
|
50
|
-
r"""Initialize the OpenAI agent toolkit.
|
|
51
|
-
|
|
52
|
-
Args:
|
|
53
|
-
model (BaseModelBackend): The OpenAI model to use for responses.
|
|
54
|
-
If None, defaults to gpt-4o-mini. (default: :obj:`None`)
|
|
55
|
-
api_key (str): OpenAI API key. If not provided, will attempt to
|
|
56
|
-
use OPENAI_API_KEY environment variable. (default: :obj:`None`)
|
|
57
|
-
timeout (Optional[float]): The timeout value for API requests
|
|
58
|
-
in seconds. If None, no timeout is applied.
|
|
59
|
-
(default: :obj:`None`)
|
|
60
|
-
"""
|
|
61
|
-
super().__init__(timeout=timeout)
|
|
62
|
-
self.api_key = api_key or os.getenv("OPENAI_API_KEY")
|
|
63
|
-
self.client = OpenAI(api_key=self.api_key)
|
|
64
|
-
self.model = model or ModelFactory.create(
|
|
65
|
-
model_platform=ModelPlatformType.OPENAI,
|
|
66
|
-
model_type=ModelType.GPT_4O_MINI,
|
|
67
|
-
)
|
|
68
|
-
|
|
69
|
-
def web_search(self, query: str) -> str:
|
|
70
|
-
r"""Perform a web search using OpenAI's web search tool.
|
|
71
|
-
|
|
72
|
-
Args:
|
|
73
|
-
query (str): The search query.
|
|
74
|
-
|
|
75
|
-
Returns:
|
|
76
|
-
str: The search result or error message.
|
|
77
|
-
"""
|
|
78
|
-
try:
|
|
79
|
-
response = self.client.responses.create(
|
|
80
|
-
model=str(self.model.model_type),
|
|
81
|
-
tools=[{"type": "web_search_preview"}],
|
|
82
|
-
input=query,
|
|
83
|
-
)
|
|
84
|
-
return response.output_text
|
|
85
|
-
|
|
86
|
-
except Exception as e:
|
|
87
|
-
logger.error(f"Web search failed: {e!s}")
|
|
88
|
-
return f"Web search failed: {e!s}"
|
|
89
|
-
|
|
90
|
-
def file_search(
|
|
91
|
-
self,
|
|
92
|
-
query: str,
|
|
93
|
-
vector_store_id: str,
|
|
94
|
-
) -> str:
|
|
95
|
-
r"""Search through files using OpenAI's file search tool.
|
|
96
|
-
|
|
97
|
-
Args:
|
|
98
|
-
query (str): The search query.
|
|
99
|
-
vector_store_id (str): The vector store ID to search in.
|
|
100
|
-
|
|
101
|
-
Returns:
|
|
102
|
-
str: The search result or error message.
|
|
103
|
-
"""
|
|
104
|
-
if not vector_store_id.strip():
|
|
105
|
-
logger.error("Empty vector store ID provided.")
|
|
106
|
-
return "Empty vector store ID provided, it cannot be empty."
|
|
107
|
-
|
|
108
|
-
try:
|
|
109
|
-
response = self.client.responses.create(
|
|
110
|
-
model=str(self.model.model_type),
|
|
111
|
-
tools=[
|
|
112
|
-
{
|
|
113
|
-
"type": "file_search",
|
|
114
|
-
"vector_store_ids": [vector_store_id],
|
|
115
|
-
}
|
|
116
|
-
],
|
|
117
|
-
input=query,
|
|
118
|
-
)
|
|
119
|
-
return response.output_text
|
|
120
|
-
|
|
121
|
-
except Exception as e:
|
|
122
|
-
logger.error(f"File search failed: {e!s}")
|
|
123
|
-
return f"File search failed: {e!s}"
|
|
124
|
-
|
|
125
|
-
def get_tools(self) -> List[FunctionTool]:
|
|
126
|
-
r"""Retrieve available toolkit functions as FunctionTool objects.
|
|
127
|
-
|
|
128
|
-
Returns:
|
|
129
|
-
List[FunctionTool]: Collection of FunctionTool objects representing
|
|
130
|
-
the available search functions in this toolkit.
|
|
131
|
-
"""
|
|
132
|
-
return [
|
|
133
|
-
FunctionTool(self.web_search),
|
|
134
|
-
FunctionTool(self.file_search),
|
|
135
|
-
]
|