Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,4 +145,4 @@ def main():


if __name__ == "__main__":
main()
main()
152 changes: 152 additions & 0 deletions examples/run_human_control.py
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there's no need to add example just for some specific toolkit, we can add it to current run template, to reduce redundancy, the run_terminal one should also be refactored

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

got it

Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
import sys
import pathlib
from dotenv import load_dotenv
from camel.models import ModelFactory
from camel.toolkits import (
AudioAnalysisToolkit,
CodeExecutionToolkit,
ExcelToolkit,
ImageAnalysisToolkit,
SearchToolkit,
VideoAnalysisToolkit,
BrowserToolkit,
FileWriteToolkit,
HumanToolkit,
TerminalToolkit,
)
from camel.types import ModelPlatformType, ModelType
from camel.logger import set_log_level
from camel.societies import RolePlaying

from owl.utils import run_society, DocumentProcessingToolkit

base_dir = pathlib.Path(__file__).parent.parent
env_path = base_dir / "owl" / ".env"
load_dotenv(dotenv_path=str(env_path))

set_log_level(level="DEBUG")


def construct_society(question: str) -> RolePlaying:
r"""Construct a society of agents based on the given question.

Args:
question (str): The task or question to be addressed by the society.

Returns:
RolePlaying: A configured society of agents ready to address the question.
"""

# Create models for different components
models = {
"user": ModelFactory.create(
model_platform=ModelPlatformType.OPENAI,
model_type=ModelType.GPT_4O,
model_config_dict={"temperature": 0},
),
"assistant": ModelFactory.create(
model_platform=ModelPlatformType.OPENAI,
model_type=ModelType.GPT_4O,
model_config_dict={"temperature": 0},
),
"browsing": ModelFactory.create(
model_platform=ModelPlatformType.OPENAI,
model_type=ModelType.GPT_4O,
model_config_dict={"temperature": 0},
),
"planning": ModelFactory.create(
model_platform=ModelPlatformType.OPENAI,
model_type=ModelType.GPT_4O,
model_config_dict={"temperature": 0},
),
"video": ModelFactory.create(
model_platform=ModelPlatformType.OPENAI,
model_type=ModelType.GPT_4O,
model_config_dict={"temperature": 0},
),
"image": ModelFactory.create(
model_platform=ModelPlatformType.OPENAI,
model_type=ModelType.GPT_4O,
model_config_dict={"temperature": 0},
),
"document": ModelFactory.create(
model_platform=ModelPlatformType.OPENAI,
model_type=ModelType.GPT_4O,
model_config_dict={"temperature": 0},
),
}

# Configure toolkits
tools = [
*HumanToolkit().get_tools(),
*TerminalToolkit().get_tools(),
*BrowserToolkit(
headless=False, # Set to True for headless mode (e.g., on remote servers)
web_agent_model=models["browsing"],
planning_agent_model=models["planning"],
).get_tools(),
*VideoAnalysisToolkit(model=models["video"]).get_tools(),
*AudioAnalysisToolkit().get_tools(), # This requires OpenAI Key
*CodeExecutionToolkit(sandbox="subprocess", verbose=True).get_tools(),
*ImageAnalysisToolkit(model=models["image"]).get_tools(),
SearchToolkit().search_duckduckgo,
SearchToolkit().search_google, # Comment this out if you don't have google search
SearchToolkit().search_wiki,
*ExcelToolkit().get_tools(),
*DocumentProcessingToolkit(model=models["document"]).get_tools(),
*FileWriteToolkit(output_dir="./").get_tools(),
]

# Configure agent roles and parameters
user_agent_kwargs = {"model": models["user"]}
assistant_agent_kwargs = {"model": models["assistant"], "tools": tools}

# Configure task parameters
task_kwargs = {
"task_prompt": question,
"with_task_specify": False,
}

# Create and return the society
society = RolePlaying(
**task_kwargs,
user_role_name="user",
user_agent_kwargs=user_agent_kwargs,
assistant_role_name="assistant",
assistant_agent_kwargs=assistant_agent_kwargs,
)

return society


def main():
r"""Main function to run the OWL system with an example question."""
# Default research question
default_task = "Use plot to draw a heart-shaped graph, save it locally, and ask for my opinion using HumanToolkit before taking each step."

# Override default task if command line argument is provided
task = sys.argv[1] if len(sys.argv) > 1 else default_task

# Construct and run the society
society = construct_society(task)
answer, chat_history, token_count = run_society(society)

# Output the result
print(f"\033[94mAnswer: {answer}\033[0m")


if __name__ == "__main__":
main()
97 changes: 91 additions & 6 deletions owl/webapp_zh.py
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hey @zjrwtx , let's keep current web app as MVP and don't add additional non-core feature to the web app, the current arch is not good for us to do further feature development, we need to redesign the arch is we want to support more features

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okok

Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import threading
import queue
import re # For regular expression operations
import tempfile # 添加此行,用于处理临时文件

os.environ["PYTHONIOENCODING"] = "utf-8"

Expand Down Expand Up @@ -309,12 +310,13 @@ def validate_input(question: str) -> bool:
return True


def run_owl(question: str, example_module: str) -> Tuple[str, str, str]:
def run_owl(question: str, example_module: str, uploaded_file=None) -> Tuple[str, str, str]:
"""运行OWL系统并返回结果

Args:
question: 用户问题
example_module: 要导入的示例模块名(如 "run_terminal_zh" 或 "run_deep")
uploaded_file: 上传的文件(可选)- 当使用type="binary"时,这是字节数据

Returns:
Tuple[...]: 回答、令牌计数、状态
Expand All @@ -330,6 +332,73 @@ def run_owl(question: str, example_module: str) -> Tuple[str, str, str]:
# 确保环境变量已加载
load_dotenv(find_dotenv(), override=True)
logging.info(f"处理问题: '{question}', 使用模块: {example_module}")

# 处理上传的文件
if uploaded_file is not None:
try:
# 创建临时目录保存上传的文件
tmp_dir = tempfile.mkdtemp(prefix="owl_upload_")
logging.info(f"创建临时目录: {tmp_dir}")

# 生成一个唯一的文件名
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")

# 简单地根据文件内容的前几个字节来猜测文件类型
file_ext = ".bin" # 默认扩展名

# 简单的文件类型检测,不依赖magic库
if len(uploaded_file) > 4:
# PDF 文件通常以 %PDF 开头
if uploaded_file[:4] == b'%PDF':
file_ext = ".pdf"
# ZIP 文件 (可能是DOCX, XLSX等)
elif uploaded_file[:4] == b'PK\x03\x04':
# 尝试根据文件名确定具体类型
file_ext = ".zip" # 默认为zip
# 纯文本文件检测 (如果前100个字节都是可打印ASCII或常见Unicode)
elif all(c < 128 and c >= 32 or c in (9, 10, 13) for c in uploaded_file[:100]):
file_ext = ".txt"

file_name = f"uploaded_file_{timestamp}{file_ext}"

# 生成安全的文件路径
file_path = os.path.normpath(os.path.join(tmp_dir, file_name))

logging.info(f"准备保存文件: {file_path}")

# 保存文件内容
try:
with open(file_path, "wb") as f:
f.write(uploaded_file)

# 检查文件是否成功写入
if not os.path.exists(file_path) or os.path.getsize(file_path) == 0:
raise ValueError("文件保存失败或文件为空")

logging.info(f"文件已成功保存: {file_path}, 大小: {os.path.getsize(file_path)} 字节")

# 将文件路径添加到问题中(使用规范化的路径,避免系统差异)
normalized_path = file_path.replace('\\', '/')
if "文件路径:" not in question and "file path:" not in question.lower():
question = f"{question}\n文件路径: {normalized_path}"

logging.info(f"更新后的问题: '{question}'")

except Exception as e:
logging.error(f"保存文件时出错: {str(e)}")
return (
f"保存文件时出错: {str(e)}",
"0",
f"❌ 错误: 文件保存失败 - {str(e)}",
)

except Exception as e:
logging.error(f"处理上传文件时出错: {str(e)}")
return (
f"处理上传文件时出错: {str(e)}",
"0",
f"❌ 错误: 文件处理失败 - {str(e)}",
)

# 检查模块是否在MODULE_DESCRIPTIONS中
if example_module not in MODULE_DESCRIPTIONS:
Expand Down Expand Up @@ -769,8 +838,7 @@ def clear_log_file():
logging.error(f"清空日志文件时出错: {str(e)}")
return ""

# 创建一个实时日志更新函数
def process_with_live_logs(question, module_name):
def process_with_live_logs(question, module_name, uploaded_file=None):
"""处理问题并实时更新日志"""
global CURRENT_PROCESS

Expand All @@ -782,7 +850,7 @@ def process_with_live_logs(question, module_name):

def process_in_background():
try:
result = run_owl(question, module_name)
result = run_owl(question, module_name, uploaded_file)
result_queue.put(result)
except Exception as e:
result_queue.put((f"发生错误: {str(e)}", "0", f"❌ 错误: {str(e)}"))
Expand Down Expand Up @@ -1062,6 +1130,23 @@ def process_in_background():
value="打开百度搜索,总结一下camel-ai的camel框架的github star、fork数目等,并把数字用plot包写成python文件保存到本地,并运行生成的python文件。",
)


file_upload = gr.File(
label="上传文件(可选)",
file_types=["pdf", "docx", "txt", "csv", "xlsx", "json", "py", "ipynb"],
file_count="single",
type="binary", # 使用binary类型
)

# 文件上传说明 - 更新说明以包含Windows兼容性
gr.Markdown("""
<div style="background-color: #e7f3fe; border-left: 6px solid #2196F3; padding: 10px; margin-bottom: 15px; border-radius: 4px;">
<strong>文件上传说明:</strong> 上传文件后,系统会自动将文件保存到临时目录并添加文件路径到您的问题中。
支持多种文件格式,包括PDF、Word文档、文本文件、CSV、Excel、Python代码等。
<br><strong>注意:</strong> 文件会被保存在临时目录,系统重启后可能会被清除。
</div>
""")

# 增强版模块选择下拉菜单
# 只包含MODULE_DESCRIPTIONS中定义的模块
module_dropdown = gr.Dropdown(
Expand Down Expand Up @@ -1205,10 +1290,10 @@ def process_in_background():

refresh_button.click(fn=update_env_table, outputs=[env_table])

# 设置事件处理

run_button.click(
fn=process_with_live_logs,
inputs=[question_input, module_dropdown],
inputs=[question_input, module_dropdown, file_upload],
outputs=[token_count_output, status_output, log_display2],
)

Expand Down