From 785a7555ca394158b3c65b7913acc3aa8ee407c0 Mon Sep 17 00:00:00 2001 From: "yifeng.wang" <3038880699@qq.com> Date: Sun, 9 Mar 2025 21:57:18 +0800 Subject: [PATCH 1/7] first web demo --- owl/app.py | 302 ++++++++++++++++++++++++++++++++++++++++++ owl/script_adapter.py | 83 ++++++++++++ requirements.txt | 6 +- run_app.py | 43 ++++++ 4 files changed, 433 insertions(+), 1 deletion(-) create mode 100644 owl/app.py create mode 100644 owl/script_adapter.py create mode 100644 run_app.py diff --git a/owl/app.py b/owl/app.py new file mode 100644 index 0000000..3ca4cc1 --- /dev/null +++ b/owl/app.py @@ -0,0 +1,302 @@ +import os +import sys +import gradio as gr +import subprocess +import threading +import time +from datetime import datetime +import queue +import re +from pathlib import Path +import json + +# 设置日志队列 +log_queue = queue.Queue() + +# 脚本选项 +SCRIPTS = { + "Qwen Mini (中文)": "run_qwen_mini_zh.py", + "Qwen": "run_qwen.py", + "Mini": "run_mini.py", + "DeepSeek": "run_deepseek.py", + "默认": "run.py", + "GAIA Roleplaying": "run_gaia_roleplaying.py" +} + +# 脚本描述 +SCRIPT_DESCRIPTIONS = { + "Qwen Mini (中文)": "使用阿里云Qwen模型的中文版本,适合中文问答和任务", + "Qwen": "使用阿里云Qwen模型,支持多种工具和功能", + "Mini": "轻量级版本,使用OpenAI GPT-4o模型", + "DeepSeek": "使用DeepSeek模型,适合复杂推理任务", + "默认": "默认OWL实现,使用OpenAI GPT-4o模型和全套工具", + "GAIA Roleplaying": "GAIA基准测试实现,用于评估模型能力" +} + +def get_script_info(script_name): + """获取脚本的详细信息""" + return SCRIPT_DESCRIPTIONS.get(script_name, "无描述信息") + +def run_script(script_dropdown, question, progress=gr.Progress()): + """运行选定的脚本并返回输出""" + script_name = SCRIPTS[script_dropdown] + + if not question.strip(): + return "请输入问题!", "", "", "", None + + # 清空日志队列 + while not log_queue.empty(): + log_queue.get() + + # 创建日志目录 + log_dir = Path("logs") + log_dir.mkdir(exist_ok=True) + + # 创建带时间戳的日志文件 + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + log_file = log_dir / f"{script_name.replace('.py', '')}_{timestamp}.log" + + # 构建命令 + cmd = [sys.executable, os.path.join("owl", "script_adapter.py"), os.path.join("owl", script_name)] + + # 创建环境变量副本并添加问题 + env = os.environ.copy() + env["OWL_QUESTION"] = question + + # 启动进程 + process = subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + bufsize=1, + env=env + ) + + # 创建线程来读取输出 + def read_output(): + with open(log_file, "w", encoding="utf-8") as f: + for line in iter(process.stdout.readline, ""): + if line: + # 写入日志文件 + f.write(line) + f.flush() + # 添加到队列 + log_queue.put(line) + + # 启动读取线程 + threading.Thread(target=read_output, daemon=True).start() + + # 收集日志 + logs = [] + progress(0, desc="正在运行...") + + # 等待进程完成或超时 + start_time = time.time() + timeout = 1800 # 30分钟超时 + + while process.poll() is None: + # 检查是否超时 + if time.time() - start_time > timeout: + process.terminate() + log_queue.put("执行超时,已终止进程\n") + break + + # 从队列获取日志 + while not log_queue.empty(): + log = log_queue.get() + logs.append(log) + + # 更新进度 + elapsed = time.time() - start_time + progress(min(elapsed / 300, 0.99), desc="正在运行...") + + # 短暂休眠以减少CPU使用 + time.sleep(0.1) + + # 每秒更新一次日志显示 + yield status_message(process), extract_answer(logs), "".join(logs), str(log_file), None + + # 获取剩余日志 + while not log_queue.empty(): + logs.append(log_queue.get()) + + # 提取聊天历史(如果有) + chat_history = extract_chat_history(logs) + + # 返回最终状态和日志 + return status_message(process), extract_answer(logs), "".join(logs), str(log_file), chat_history + +def status_message(process): + """根据进程状态返回状态消息""" + if process.poll() is None: + return "⏳ 正在运行..." + elif process.returncode == 0: + return "✅ 执行成功" + else: + return f"❌ 执行失败 (返回码: {process.returncode})" + +def extract_answer(logs): + """从日志中提取答案""" + answer = "" + for log in logs: + if "Answer:" in log: + answer = log.split("Answer:", 1)[1].strip() + break + return answer + +def extract_chat_history(logs): + """尝试从日志中提取聊天历史""" + try: + for i, log in enumerate(logs): + if "chat_history" in log: + # 尝试找到JSON格式的聊天历史 + start_idx = log.find("[") + if start_idx != -1: + # 尝试解析JSON + json_str = log[start_idx:].strip() + # 查找下一行中可能的结束括号 + if json_str[-1] != "]" and i+1 < len(logs): + for j in range(i+1, min(i+10, len(logs))): + end_idx = logs[j].find("]") + if end_idx != -1: + json_str += logs[j][:end_idx+1] + break + + try: + chat_data = json.loads(json_str) + # 格式化为Gradio聊天组件可用的格式 + formatted_chat = [] + for msg in chat_data: + if "role" in msg and "content" in msg: + role = "用户" if msg["role"] == "user" else "助手" + formatted_chat.append([role, msg["content"]]) + return formatted_chat + except json.JSONDecodeError: + pass + except Exception: + pass + return None + +def modify_script(script_name, question): + """修改脚本以使用提供的问题""" + script_path = os.path.join("owl", script_name) + + with open(script_path, "r", encoding="utf-8") as f: + content = f.read() + + # 查找并替换问题变量 + if "question = " in content: + # 使用正则表达式替换问题字符串 + modified_content = re.sub( + r'question\s*=\s*["\'].*?["\']', + f'question = "{question}"', + content + ) + + with open(script_path, "w", encoding="utf-8") as f: + f.write(modified_content) + + return True + + return False + +def create_ui(): + """创建Gradio界面""" + with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue")) as app: + gr.Markdown( + """ + # 🦉 OWL 智能助手运行平台 + + 选择一个模型并输入您的问题,系统将运行相应的脚本并显示结果。 + """ + ) + + with gr.Row(): + with gr.Column(scale=1): + script_dropdown = gr.Dropdown( + choices=list(SCRIPTS.keys()), + value=list(SCRIPTS.keys())[0], + label="选择模型" + ) + + script_info = gr.Textbox( + value=get_script_info(list(SCRIPTS.keys())[0]), + label="模型描述", + interactive=False + ) + + script_dropdown.change( + fn=lambda x: get_script_info(x), + inputs=script_dropdown, + outputs=script_info + ) + + question_input = gr.Textbox( + lines=5, + placeholder="请输入您的问题...", + label="问题" + ) + + run_button = gr.Button("运行", variant="primary") + + with gr.Column(scale=2): + with gr.Tabs(): + with gr.TabItem("结果"): + status_output = gr.Textbox(label="状态") + answer_output = gr.Textbox(label="回答", lines=10) + log_file_output = gr.Textbox(label="日志文件路径") + + with gr.TabItem("运行日志"): + log_output = gr.Textbox(label="完整日志", lines=25) + + with gr.TabItem("聊天历史"): + chat_output = gr.Chatbot(label="对话历史") + + run_button.click( + fn=run_script, + inputs=[ + script_dropdown, + question_input + ], + outputs=[status_output, answer_output, log_output, log_file_output, chat_output], + show_progress=True + ) + + # 示例问题 + examples = [ + ["Qwen Mini (中文)", "打开小红书上浏览推荐栏目下的前三个笔记内容,不要登陆,之后给我一个总结报告"], + ["Mini", "What was the volume in m^3 of the fish bag that was calculated in the University of Leicester paper `Can Hiccup Supply Enough Fish to Maintain a Dragon's Diet?`"], + ["默认", "What is the current weather in New York?"] + ] + + gr.Examples( + examples=examples, + inputs=[script_dropdown, question_input] + ) + + # 添加页脚 + gr.Markdown( + """ + ### 📝 使用说明 + + - 选择一个模型并输入您的问题 + - 点击"运行"按钮开始执行 + - 在"结果"标签页查看执行状态和回答 + - 在"运行日志"标签页查看完整日志 + - 在"聊天历史"标签页查看对话历史(如果有) + + ### ⚠️ 注意事项 + + - 运行某些模型可能需要API密钥,请确保在`.env`文件中设置了相应的环境变量 + - 某些脚本可能需要较长时间运行,请耐心等待 + - 如果运行超过30分钟,进程将自动终止 + """ + ) + + return app + +if __name__ == "__main__": + # 创建并启动应用 + app = create_ui() + app.queue().launch(share=True) \ No newline at end of file diff --git a/owl/script_adapter.py b/owl/script_adapter.py new file mode 100644 index 0000000..3356fb0 --- /dev/null +++ b/owl/script_adapter.py @@ -0,0 +1,83 @@ +import os +import sys +import importlib.util +import re +from pathlib import Path + +def load_module_from_path(module_name, file_path): + """从文件路径加载Python模块""" + spec = importlib.util.spec_from_file_location(module_name, file_path) + module = importlib.util.module_from_spec(spec) + sys.modules[module_name] = module + spec.loader.exec_module(module) + return module + +def run_script_with_env_question(script_name): + """使用环境变量中的问题运行脚本""" + # 获取环境变量中的问题 + question = os.environ.get("OWL_QUESTION") + if not question: + print("错误: 未设置OWL_QUESTION环境变量") + sys.exit(1) + + # 脚本路径 + script_path = Path(script_name).resolve() + if not script_path.exists(): + print(f"错误: 脚本 {script_path} 不存在") + sys.exit(1) + + # 加载脚本模块 + module_name = script_path.stem + try: + # 将脚本目录添加到sys.path + script_dir = script_path.parent + if str(script_dir) not in sys.path: + sys.path.insert(0, str(script_dir)) + + # 读取脚本内容 + with open(script_path, "r", encoding="utf-8") as f: + content = f.read() + + # 检查脚本是否有main函数 + has_main = re.search(r'def\s+main\s*\(\s*\)\s*:', content) is not None + + if has_main: + # 如果有main函数,加载模块并调用main + module = load_module_from_path(module_name, script_path) + + # 修改模块中的question变量 + if hasattr(module, "question"): + setattr(module, "question", question) + + # 调用main函数 + if hasattr(module, "main"): + module.main() + else: + print(f"错误: 脚本 {script_path} 中没有main函数") + sys.exit(1) + else: + # 如果没有main函数,直接执行脚本内容 + # 替换question变量 + modified_content = re.sub( + r'question\s*=\s*["\'].*?["\']', + f'question = "{question}"', + content + ) + + # 执行修改后的脚本 + exec(modified_content, {"__file__": str(script_path)}) + + except Exception as e: + print(f"执行脚本时出错: {e}") + import traceback + traceback.print_exc() + sys.exit(1) + +if __name__ == "__main__": + # 检查命令行参数 + if len(sys.argv) < 2: + print("用法: python script_adapter.py ") + sys.exit(1) + + # 运行指定的脚本 + run_script_with_env_question(sys.argv[1]) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 625257a..024d776 100644 --- a/requirements.txt +++ b/requirements.txt @@ -129,7 +129,7 @@ ruff>=0.7.0 mypy>=1.5.1 toml>=0.10.2 pre-commit>=3.0.0 -gradio>=3.0.0 +gradio>=4.0.0 # Type stubs types-Pillow @@ -141,3 +141,7 @@ types-tqdm types-colorama>=0.0.0 types-requests>=2.0.0 types-PyYAML>=6.0.0 + +python-dotenv +tqdm +pathlib diff --git a/run_app.py b/run_app.py new file mode 100644 index 0000000..cfb71c6 --- /dev/null +++ b/run_app.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +OWL 智能助手运行平台启动脚本 +""" + +import os +import sys +from pathlib import Path + +def main(): + """主函数,启动OWL智能助手运行平台""" + # 确保当前目录是项目根目录 + project_root = Path(__file__).resolve().parent + os.chdir(project_root) + + # 创建日志目录 + log_dir = project_root / "logs" + log_dir.mkdir(exist_ok=True) + + # 导入并运行应用 + sys.path.insert(0, str(project_root)) + + try: + from owl.app import create_ui + + # 创建并启动应用 + app = create_ui() + app.launch(share=False) + + except ImportError as e: + print(f"错误: 无法导入必要的模块。请确保已安装所有依赖项: {e}") + print("提示: 运行 'pip install -r requirements.txt' 安装所有依赖项") + sys.exit(1) + except Exception as e: + print(f"启动应用程序时出错: {e}") + import traceback + traceback.print_exc() + sys.exit(1) + +if __name__ == "__main__": + main() \ No newline at end of file From 08e3e8ee4ddde403f8e6c1d6e99ff8d7a1db9716 Mon Sep 17 00:00:00 2001 From: "yifeng.wang" <3038880699@qq.com> Date: Sun, 9 Mar 2025 22:14:00 +0800 Subject: [PATCH 2/7] add env --- owl/app.py | 272 ++++++++++++++++++++++++++++++++---------- owl/script_adapter.py | 109 ++++++++++++----- 2 files changed, 291 insertions(+), 90 deletions(-) diff --git a/owl/app.py b/owl/app.py index 3ca4cc1..a8b89d1 100644 --- a/owl/app.py +++ b/owl/app.py @@ -9,10 +9,16 @@ import queue import re from pathlib import Path import json +import signal +import dotenv # 设置日志队列 log_queue = queue.Queue() +# 当前运行的进程 +current_process = None +process_lock = threading.Lock() + # 脚本选项 SCRIPTS = { "Qwen Mini (中文)": "run_qwen_mini_zh.py", @@ -33,12 +39,98 @@ SCRIPT_DESCRIPTIONS = { "GAIA Roleplaying": "GAIA基准测试实现,用于评估模型能力" } +# 环境变量分组 +ENV_GROUPS = { + "模型API": [ + {"name": "OPENAI_API_KEY", "label": "OpenAI API密钥", "type": "password", "required": True}, + {"name": "OPENAI_API_BASE_URL", "label": "OpenAI API基础URL", "type": "text", "required": False}, + {"name": "QWEN_API_KEY", "label": "阿里云Qwen API密钥", "type": "password", "required": False}, + {"name": "DEEPSEEK_API_KEY", "label": "DeepSeek API密钥", "type": "password", "required": False}, + ], + "搜索工具": [ + {"name": "GOOGLE_API_KEY", "label": "Google API密钥", "type": "password", "required": False}, + {"name": "SEARCH_ENGINE_ID", "label": "搜索引擎ID", "type": "text", "required": False}, + ], + "其他工具": [ + {"name": "HF_TOKEN", "label": "Hugging Face令牌", "type": "password", "required": False}, + {"name": "CHUNKR_API_KEY", "label": "Chunkr API密钥", "type": "password", "required": False}, + {"name": "FIRECRAWL_API_KEY", "label": "Firecrawl API密钥", "type": "password", "required": False}, + ] +} + def get_script_info(script_name): """获取脚本的详细信息""" return SCRIPT_DESCRIPTIONS.get(script_name, "无描述信息") +def load_env_vars(): + """加载环境变量""" + env_vars = {} + # 尝试从.env文件加载 + dotenv.load_dotenv() + + # 获取所有环境变量 + for group in ENV_GROUPS.values(): + for var in group: + env_vars[var["name"]] = os.environ.get(var["name"], "") + + return env_vars + +def save_env_vars(env_vars): + """保存环境变量到.env文件""" + # 读取现有的.env文件内容 + env_path = Path(".env") + existing_content = {} + + if env_path.exists(): + with open(env_path, "r", encoding="utf-8") as f: + for line in f: + line = line.strip() + if line and not line.startswith("#") and "=" in line: + key, value = line.split("=", 1) + existing_content[key.strip()] = value.strip() + + # 更新环境变量 + for key, value in env_vars.items(): + if value: # 只保存非空值 + existing_content[key] = value + # 同时更新当前进程的环境变量 + os.environ[key] = value + + # 写入.env文件 + with open(env_path, "w", encoding="utf-8") as f: + for key, value in existing_content.items(): + f.write(f"{key}={value}\n") + + return "环境变量已保存" + +def terminate_process(): + """终止当前运行的进程""" + global current_process + + with process_lock: + if current_process is not None and current_process.poll() is None: + # 在Windows上使用CTRL_BREAK_EVENT,在Unix上使用SIGTERM + if os.name == 'nt': + current_process.send_signal(signal.CTRL_BREAK_EVENT) + else: + current_process.terminate() + + # 等待进程终止 + try: + current_process.wait(timeout=5) + except subprocess.TimeoutExpired: + # 如果进程没有在5秒内终止,强制终止 + current_process.kill() + + log_queue.put("进程已终止\n") + return "✅ 进程已终止" + else: + return "❌ 没有正在运行的进程" + def run_script(script_dropdown, question, progress=gr.Progress()): """运行选定的脚本并返回输出""" + global current_process + script_name = SCRIPTS[script_dropdown] if not question.strip(): @@ -64,19 +156,20 @@ def run_script(script_dropdown, question, progress=gr.Progress()): env["OWL_QUESTION"] = question # 启动进程 - process = subprocess.Popen( - cmd, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - text=True, - bufsize=1, - env=env - ) + with process_lock: + current_process = subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + bufsize=1, + env=env + ) # 创建线程来读取输出 def read_output(): with open(log_file, "w", encoding="utf-8") as f: - for line in iter(process.stdout.readline, ""): + for line in iter(current_process.stdout.readline, ""): if line: # 写入日志文件 f.write(line) @@ -95,11 +188,13 @@ def run_script(script_dropdown, question, progress=gr.Progress()): start_time = time.time() timeout = 1800 # 30分钟超时 - while process.poll() is None: + while current_process.poll() is None: # 检查是否超时 if time.time() - start_time > timeout: - process.terminate() - log_queue.put("执行超时,已终止进程\n") + with process_lock: + if current_process.poll() is None: + current_process.terminate() + log_queue.put("执行超时,已终止进程\n") break # 从队列获取日志 @@ -115,7 +210,7 @@ def run_script(script_dropdown, question, progress=gr.Progress()): time.sleep(0.1) # 每秒更新一次日志显示 - yield status_message(process), extract_answer(logs), "".join(logs), str(log_file), None + yield status_message(current_process), extract_answer(logs), "".join(logs), str(log_file), None # 获取剩余日志 while not log_queue.empty(): @@ -125,7 +220,7 @@ def run_script(script_dropdown, question, progress=gr.Progress()): chat_history = extract_chat_history(logs) # 返回最终状态和日志 - return status_message(process), extract_answer(logs), "".join(logs), str(log_file), chat_history + return status_message(current_process), extract_answer(logs), "".join(logs), str(log_file), chat_history def status_message(process): """根据进程状态返回状态消息""" @@ -203,6 +298,9 @@ def modify_script(script_name, question): def create_ui(): """创建Gradio界面""" + # 加载环境变量 + env_vars = load_env_vars() + with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue")) as app: gr.Markdown( """ @@ -212,47 +310,101 @@ def create_ui(): """ ) - with gr.Row(): - with gr.Column(scale=1): - script_dropdown = gr.Dropdown( - choices=list(SCRIPTS.keys()), - value=list(SCRIPTS.keys())[0], - label="选择模型" - ) + with gr.Tabs() as tabs: + with gr.TabItem("运行模型"): + with gr.Row(): + with gr.Column(scale=1): + script_dropdown = gr.Dropdown( + choices=list(SCRIPTS.keys()), + value=list(SCRIPTS.keys())[0], + label="选择模型" + ) + + script_info = gr.Textbox( + value=get_script_info(list(SCRIPTS.keys())[0]), + label="模型描述", + interactive=False + ) + + script_dropdown.change( + fn=lambda x: get_script_info(x), + inputs=script_dropdown, + outputs=script_info + ) + + question_input = gr.Textbox( + lines=5, + placeholder="请输入您的问题...", + label="问题" + ) + + gr.Markdown( + """ + > **注意**: 您输入的问题将替换脚本中的默认问题。系统会自动处理问题的替换,确保您的问题被正确使用。 + """ + ) + + with gr.Row(): + run_button = gr.Button("运行", variant="primary") + stop_button = gr.Button("终止", variant="stop") + + with gr.Column(scale=2): + with gr.Tabs(): + with gr.TabItem("结果"): + status_output = gr.Textbox(label="状态") + answer_output = gr.Textbox(label="回答", lines=10) + log_file_output = gr.Textbox(label="日志文件路径") + + with gr.TabItem("运行日志"): + log_output = gr.Textbox(label="完整日志", lines=25) + + with gr.TabItem("聊天历史"): + chat_output = gr.Chatbot(label="对话历史") - script_info = gr.Textbox( - value=get_script_info(list(SCRIPTS.keys())[0]), - label="模型描述", - interactive=False - ) + # 示例问题 + examples = [ + ["Qwen Mini (中文)", "打开小红书上浏览推荐栏目下的前三个笔记内容,不要登陆,之后给我一个总结报告"], + ["Mini", "What was the volume in m^3 of the fish bag that was calculated in the University of Leicester paper `Can Hiccup Supply Enough Fish to Maintain a Dragon's Diet?`"], + ["默认", "What is the current weather in New York?"] + ] - script_dropdown.change( - fn=lambda x: get_script_info(x), - inputs=script_dropdown, - outputs=script_info + gr.Examples( + examples=examples, + inputs=[script_dropdown, question_input] ) - - question_input = gr.Textbox( - lines=5, - placeholder="请输入您的问题...", - label="问题" - ) - - run_button = gr.Button("运行", variant="primary") - with gr.Column(scale=2): - with gr.Tabs(): - with gr.TabItem("结果"): - status_output = gr.Textbox(label="状态") - answer_output = gr.Textbox(label="回答", lines=10) - log_file_output = gr.Textbox(label="日志文件路径") - - with gr.TabItem("运行日志"): - log_output = gr.Textbox(label="完整日志", lines=25) - - with gr.TabItem("聊天历史"): - chat_output = gr.Chatbot(label="对话历史") + with gr.TabItem("环境变量配置"): + env_inputs = {} + save_status = gr.Textbox(label="保存状态", interactive=False) + + for group_name, vars in ENV_GROUPS.items(): + with gr.Accordion(group_name, open=True): + for var in vars: + if var["type"] == "password": + env_inputs[var["name"]] = gr.Textbox( + value=env_vars.get(var["name"], ""), + label=var["label"] + (" (必填)" if var.get("required", False) else ""), + placeholder=f"请输入{var['label']}", + type="password" + ) + else: + env_inputs[var["name"]] = gr.Textbox( + value=env_vars.get(var["name"], ""), + label=var["label"] + (" (必填)" if var.get("required", False) else ""), + placeholder=f"请输入{var['label']}" + ) + + save_button = gr.Button("保存环境变量", variant="primary") + + # 保存环境变量 + save_inputs = [env_inputs[var_name] for group in ENV_GROUPS.values() for var in group for var_name in [var["name"]]] + save_button.click( + fn=lambda *values: save_env_vars(dict(zip([var["name"] for group in ENV_GROUPS.values() for var in group], values))), + inputs=save_inputs, + outputs=save_status + ) + # 运行脚本 run_button.click( fn=run_script, inputs=[ @@ -263,16 +415,11 @@ def create_ui(): show_progress=True ) - # 示例问题 - examples = [ - ["Qwen Mini (中文)", "打开小红书上浏览推荐栏目下的前三个笔记内容,不要登陆,之后给我一个总结报告"], - ["Mini", "What was the volume in m^3 of the fish bag that was calculated in the University of Leicester paper `Can Hiccup Supply Enough Fish to Maintain a Dragon's Diet?`"], - ["默认", "What is the current weather in New York?"] - ] - - gr.Examples( - examples=examples, - inputs=[script_dropdown, question_input] + # 终止运行 + stop_button.click( + fn=terminate_process, + inputs=[], + outputs=[status_output] ) # 添加页脚 @@ -282,15 +429,18 @@ def create_ui(): - 选择一个模型并输入您的问题 - 点击"运行"按钮开始执行 + - 如需终止运行,点击"终止"按钮 - 在"结果"标签页查看执行状态和回答 - 在"运行日志"标签页查看完整日志 - 在"聊天历史"标签页查看对话历史(如果有) + - 在"环境变量配置"标签页配置API密钥和其他环境变量 ### ⚠️ 注意事项 - - 运行某些模型可能需要API密钥,请确保在`.env`文件中设置了相应的环境变量 + - 运行某些模型可能需要API密钥,请确保在"环境变量配置"标签页中设置了相应的环境变量 - 某些脚本可能需要较长时间运行,请耐心等待 - 如果运行超过30分钟,进程将自动终止 + - 您输入的问题将替换脚本中的默认问题,确保问题与所选模型兼容 """ ) diff --git a/owl/script_adapter.py b/owl/script_adapter.py index 3356fb0..1395155 100644 --- a/owl/script_adapter.py +++ b/owl/script_adapter.py @@ -26,45 +26,96 @@ def run_script_with_env_question(script_name): print(f"错误: 脚本 {script_path} 不存在") sys.exit(1) - # 加载脚本模块 - module_name = script_path.stem + # 读取脚本内容 + with open(script_path, "r", encoding="utf-8") as f: + content = f.read() + + # 检查脚本是否有main函数 + has_main = re.search(r'def\s+main\s*\(\s*\)\s*:', content) is not None + + # 尝试查找并替换question变量 + # 匹配多种可能的question定义模式 + patterns = [ + r'question\s*=\s*["\'].*?["\']', # 简单字符串赋值 + r'question\s*=\s*\(\s*["\'].*?["\']\s*\)', # 带括号的字符串赋值 + r'question\s*=\s*f["\'].*?["\']', # f-string赋值 + r'question\s*=\s*r["\'].*?["\']', # 原始字符串赋值 + ] + + question_replaced = False + for pattern in patterns: + if re.search(pattern, content): + # 转义问题中的特殊字符 + escaped_question = question.replace("\\", "\\\\").replace("\"", "\\\"").replace("'", "\\'") + # 替换问题 + modified_content = re.sub( + pattern, + f'question = "{escaped_question}"', + content + ) + question_replaced = True + break + + if not question_replaced: + # 如果没有找到question变量,尝试在main函数前插入 + if has_main: + # 在main函数前插入question变量 + main_match = re.search(r'def\s+main\s*\(\s*\)\s*:', content) + if main_match: + insert_pos = main_match.start() + # 转义问题中的特殊字符 + escaped_question = question.replace("\\", "\\\\").replace("\"", "\\\"").replace("'", "\\'") + modified_content = content[:insert_pos] + f'\n# 用户输入的问题\nquestion = "{escaped_question}"\n\n' + content[insert_pos:] + question_replaced = True + + if not question_replaced: + # 如果仍然无法替换,尝试在文件末尾添加代码来使用用户的问题 + modified_content = content + f'\n\n# 用户输入的问题\nquestion = "{question}"\n\n' + modified_content += ''' +# 如果脚本中有construct_society函数,使用用户问题运行 +if "construct_society" in globals(): + try: + society = construct_society(question) + from utils import run_society + answer, chat_history, token_count = run_society(society) + print(f"Answer: {answer}") + except Exception as e: + print(f"运行时出错: {e}") + import traceback + traceback.print_exc() +''' + + # 执行修改后的脚本 try: # 将脚本目录添加到sys.path script_dir = script_path.parent if str(script_dir) not in sys.path: sys.path.insert(0, str(script_dir)) - # 读取脚本内容 - with open(script_path, "r", encoding="utf-8") as f: - content = f.read() - - # 检查脚本是否有main函数 - has_main = re.search(r'def\s+main\s*\(\s*\)\s*:', content) is not None - if has_main: # 如果有main函数,加载模块并调用main - module = load_module_from_path(module_name, script_path) + # 创建临时文件 + temp_script_path = script_path.with_name(f"temp_{script_path.name}") + with open(temp_script_path, "w", encoding="utf-8") as f: + f.write(modified_content) - # 修改模块中的question变量 - if hasattr(module, "question"): - setattr(module, "question", question) - - # 调用main函数 - if hasattr(module, "main"): - module.main() - else: - print(f"错误: 脚本 {script_path} 中没有main函数") - sys.exit(1) + try: + # 加载临时模块 + module_name = f"temp_{script_path.stem}" + module = load_module_from_path(module_name, temp_script_path) + + # 调用main函数 + if hasattr(module, "main"): + module.main() + else: + print(f"错误: 脚本 {script_path} 中没有main函数") + sys.exit(1) + finally: + # 删除临时文件 + if temp_script_path.exists(): + temp_script_path.unlink() else: - # 如果没有main函数,直接执行脚本内容 - # 替换question变量 - modified_content = re.sub( - r'question\s*=\s*["\'].*?["\']', - f'question = "{question}"', - content - ) - - # 执行修改后的脚本 + # 如果没有main函数,直接执行修改后的脚本 exec(modified_content, {"__file__": str(script_path)}) except Exception as e: From 7cd436d5594cb29f9430009ea89a6b2d6c21d8b3 Mon Sep 17 00:00:00 2001 From: "yifeng.wang" <3038880699@qq.com> Date: Sun, 9 Mar 2025 23:47:47 +0800 Subject: [PATCH 3/7] polish --- owl/app.py | 82 +++++++++++++++++++++++++----- owl/temp_run.py | 129 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 200 insertions(+), 11 deletions(-) create mode 100644 owl/temp_run.py diff --git a/owl/app.py b/owl/app.py index a8b89d1..cff79c8 100644 --- a/owl/app.py +++ b/owl/app.py @@ -42,19 +42,73 @@ SCRIPT_DESCRIPTIONS = { # 环境变量分组 ENV_GROUPS = { "模型API": [ - {"name": "OPENAI_API_KEY", "label": "OpenAI API密钥", "type": "password", "required": True}, - {"name": "OPENAI_API_BASE_URL", "label": "OpenAI API基础URL", "type": "text", "required": False}, - {"name": "QWEN_API_KEY", "label": "阿里云Qwen API密钥", "type": "password", "required": False}, - {"name": "DEEPSEEK_API_KEY", "label": "DeepSeek API密钥", "type": "password", "required": False}, + { + "name": "OPENAI_API_KEY", + "label": "OpenAI API密钥", + "type": "password", + "required": True, + "help": "OpenAI API密钥,用于访问GPT模型。获取方式:https://platform.openai.com/api-keys" + }, + { + "name": "OPENAI_API_BASE_URL", + "label": "OpenAI API基础URL", + "type": "text", + "required": False, + "help": "OpenAI API的基础URL,可选。如果使用代理或自定义端点,请设置此项。" + }, + { + "name": "QWEN_API_KEY", + "label": "阿里云Qwen API密钥", + "type": "password", + "required": False, + "help": "阿里云Qwen API密钥,用于访问Qwen模型。获取方式:https://help.aliyun.com/zh/model-studio/developer-reference/get-api-key" + }, + { + "name": "DEEPSEEK_API_KEY", + "label": "DeepSeek API密钥", + "type": "password", + "required": False, + "help": "DeepSeek API密钥,用于访问DeepSeek模型。获取方式:https://platform.deepseek.com/api_keys" + }, ], "搜索工具": [ - {"name": "GOOGLE_API_KEY", "label": "Google API密钥", "type": "password", "required": False}, - {"name": "SEARCH_ENGINE_ID", "label": "搜索引擎ID", "type": "text", "required": False}, + { + "name": "GOOGLE_API_KEY", + "label": "Google API密钥", + "type": "password", + "required": False, + "help": "Google搜索API密钥,用于网络搜索功能。获取方式:https://developers.google.com/custom-search/v1/overview" + }, + { + "name": "SEARCH_ENGINE_ID", + "label": "搜索引擎ID", + "type": "text", + "required": False, + "help": "Google自定义搜索引擎ID,与Google API密钥配合使用。获取方式:https://developers.google.com/custom-search/v1/overview" + }, ], "其他工具": [ - {"name": "HF_TOKEN", "label": "Hugging Face令牌", "type": "password", "required": False}, - {"name": "CHUNKR_API_KEY", "label": "Chunkr API密钥", "type": "password", "required": False}, - {"name": "FIRECRAWL_API_KEY", "label": "Firecrawl API密钥", "type": "password", "required": False}, + { + "name": "HF_TOKEN", + "label": "Hugging Face令牌", + "type": "password", + "required": False, + "help": "Hugging Face API令牌,用于访问Hugging Face模型和数据集。获取方式:https://huggingface.co/join" + }, + { + "name": "CHUNKR_API_KEY", + "label": "Chunkr API密钥", + "type": "password", + "required": False, + "help": "Chunkr API密钥,用于文档处理功能。获取方式:https://chunkr.ai/" + }, + { + "name": "FIRECRAWL_API_KEY", + "label": "Firecrawl API密钥", + "type": "password", + "required": False, + "help": "Firecrawl API密钥,用于网页爬取功能。获取方式:https://www.firecrawl.dev/" + }, ] } @@ -92,16 +146,19 @@ def save_env_vars(env_vars): # 更新环境变量 for key, value in env_vars.items(): if value: # 只保存非空值 + # 确保值是字符串形式,并用引号包裹 + if not (value.startswith('"') and value.endswith('"')) and not (value.startswith("'") and value.endswith("'")): + value = f'"{value}"' existing_content[key] = value # 同时更新当前进程的环境变量 - os.environ[key] = value + os.environ[key] = value.strip('"\'') # 写入.env文件 with open(env_path, "w", encoding="utf-8") as f: for key, value in existing_content.items(): f.write(f"{key}={value}\n") - return "环境变量已保存" + return "✅ 环境变量已保存" def terminate_process(): """终止当前运行的进程""" @@ -380,6 +437,9 @@ def create_ui(): for group_name, vars in ENV_GROUPS.items(): with gr.Accordion(group_name, open=True): for var in vars: + # 添加帮助信息 + gr.Markdown(f"**{var['help']}**") + if var["type"] == "password": env_inputs[var["name"]] = gr.Textbox( value=env_vars.get(var["name"], ""), diff --git a/owl/temp_run.py b/owl/temp_run.py new file mode 100644 index 0000000..1300617 --- /dev/null +++ b/owl/temp_run.py @@ -0,0 +1,129 @@ +from dotenv import load_dotenv +load_dotenv() + +from camel.models import ModelFactory +from camel.toolkits import ( + AudioAnalysisToolkit, + CodeExecutionToolkit, + DocumentProcessingToolkit, + ExcelToolkit, + ImageAnalysisToolkit, + SearchToolkit, + VideoAnalysisToolkit, + WebToolkit, +) +from camel.types import ModelPlatformType, ModelType + +from utils import OwlRolePlaying, run_society + + +def construct_society(question: str) -> OwlRolePlaying: + 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: + OwlRolePlaying: 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}, + ), + "web": 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}, + ), + "search": ModelFactory.create( + model_platform=ModelPlatformType.OPENAI, + model_type=ModelType.GPT_4O, + model_config_dict={"temperature": 0}, + ), + } + + # Configure toolkits + tools = [ + *WebToolkit( + headless=False, # Set to True for headless mode (e.g., on remote servers) + web_agent_model=models["web"], + planning_agent_model=models["planning"], + ).get_tools(), + *DocumentProcessingToolkit().get_tools(), + *VideoAnalysisToolkit(model=models["video"]).get_tools(), # This requires OpenAI Key + *AudioAnalysisToolkit().get_tools(), # This requires OpenAI Key + *CodeExecutionToolkit(sandbox="subprocess", verbose=True).get_tools(), + *ImageAnalysisToolkit(model=models["image"]).get_tools(), + *SearchToolkit(model=models["search"]).get_tools(), + *ExcelToolkit().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 = OwlRolePlaying( + **task_kwargs, + user_role_name="user", + user_agent_kwargs=user_agent_kwargs, + assistant_role_name="assistant", + assistant_agent_kwargs=assistant_agent_kwargs, + ) + + return society + + + +# 用户输入的问题 +question = "What is the current weather in New York?" + +def main(): + r"""Main function to run the OWL system with an example question.""" + # Example research question + question = ( + "What was the volume in m^3 of the fish bag that was calculated in " + "the University of Leicester paper `Can Hiccup Supply Enough Fish " + "to Maintain a Dragon's Diet?`" + ) + + # Construct and run the society + society = construct_society(question) + answer, chat_history, token_count = run_society(society) + + # Output the result + print(f"Answer: {answer}") + + +if __name__ == "__main__": + main() From e38ce058b3bd966db05a5a51dc3f2accf970a808 Mon Sep 17 00:00:00 2001 From: "yifeng.wang" <3038880699@qq.com> Date: Mon, 10 Mar 2025 00:39:08 +0800 Subject: [PATCH 4/7] fix input question --- owl/app.py | 151 ++++++++++++++++++++++++++++++------------ owl/script_adapter.py | 151 ++++++++++++++++++++++++++++-------------- owl/temp_run.py | 129 ------------------------------------ 3 files changed, 207 insertions(+), 224 deletions(-) delete mode 100644 owl/temp_run.py diff --git a/owl/app.py b/owl/app.py index cff79c8..86d2a6e 100644 --- a/owl/app.py +++ b/owl/app.py @@ -109,7 +109,8 @@ ENV_GROUPS = { "required": False, "help": "Firecrawl API密钥,用于网页爬取功能。获取方式:https://www.firecrawl.dev/" }, - ] + ], + "自定义环境变量": [] # 用户自定义的环境变量将存储在这里 } def get_script_info(script_name): @@ -127,6 +128,34 @@ def load_env_vars(): for var in group: env_vars[var["name"]] = os.environ.get(var["name"], "") + # 加载.env文件中可能存在的其他环境变量 + if Path(".env").exists(): + with open(".env", "r", encoding="utf-8") as f: + for line in f: + line = line.strip() + if line and not line.startswith("#") and "=" in line: + key, value = line.split("=", 1) + key = key.strip() + value = value.strip().strip('"\'') + + # 检查是否是已知的环境变量 + known_var = False + for group in ENV_GROUPS.values(): + if any(var["name"] == key for var in group): + known_var = True + break + + # 如果不是已知的环境变量,添加到自定义环境变量组 + if not known_var and key not in env_vars: + ENV_GROUPS["自定义环境变量"].append({ + "name": key, + "label": key, + "type": "text", + "required": False, + "help": "用户自定义环境变量" + }) + env_vars[key] = value + return env_vars def save_env_vars(env_vars): @@ -160,6 +189,32 @@ def save_env_vars(env_vars): return "✅ 环境变量已保存" +def add_custom_env_var(name, value, var_type): + """添加自定义环境变量""" + if not name: + return "❌ 环境变量名不能为空", None + + # 检查是否已存在同名环境变量 + for group in ENV_GROUPS.values(): + if any(var["name"] == name for var in group): + return f"❌ 环境变量 {name} 已存在", None + + # 添加到自定义环境变量组 + ENV_GROUPS["自定义环境变量"].append({ + "name": name, + "label": name, + "type": var_type, + "required": False, + "help": "用户自定义环境变量" + }) + + # 保存环境变量 + env_vars = {name: value} + save_env_vars(env_vars) + + # 返回成功消息和更新后的环境变量组 + return f"✅ 已添加环境变量 {name}", ENV_GROUPS["自定义环境变量"] + def terminate_process(): """终止当前运行的进程""" global current_process @@ -330,29 +385,6 @@ def extract_chat_history(logs): pass return None -def modify_script(script_name, question): - """修改脚本以使用提供的问题""" - script_path = os.path.join("owl", script_name) - - with open(script_path, "r", encoding="utf-8") as f: - content = f.read() - - # 查找并替换问题变量 - if "question = " in content: - # 使用正则表达式替换问题字符串 - modified_content = re.sub( - r'question\s*=\s*["\'].*?["\']', - f'question = "{question}"', - content - ) - - with open(script_path, "w", encoding="utf-8") as f: - f.write(modified_content) - - return True - - return False - def create_ui(): """创建Gradio界面""" # 加载环境变量 @@ -434,32 +466,62 @@ def create_ui(): env_inputs = {} save_status = gr.Textbox(label="保存状态", interactive=False) + # 添加自定义环境变量部分 + with gr.Accordion("添加自定义环境变量", open=True): + with gr.Row(): + new_var_name = gr.Textbox(label="环境变量名", placeholder="例如:MY_CUSTOM_API_KEY") + new_var_value = gr.Textbox(label="环境变量值", placeholder="输入值") + new_var_type = gr.Dropdown( + choices=["text", "password"], + value="text", + label="类型" + ) + + add_var_button = gr.Button("添加环境变量", variant="primary") + add_var_status = gr.Textbox(label="添加状态", interactive=False) + + # 自定义环境变量列表 + custom_vars_list = gr.JSON( + value=ENV_GROUPS["自定义环境变量"], + label="已添加的自定义环境变量", + visible=len(ENV_GROUPS["自定义环境变量"]) > 0 + ) + + # 添加环境变量按钮点击事件 + add_var_button.click( + fn=add_custom_env_var, + inputs=[new_var_name, new_var_value, new_var_type], + outputs=[add_var_status, custom_vars_list] + ) + + # 现有环境变量配置 for group_name, vars in ENV_GROUPS.items(): - with gr.Accordion(group_name, open=True): - for var in vars: - # 添加帮助信息 - gr.Markdown(f"**{var['help']}**") - - if var["type"] == "password": - env_inputs[var["name"]] = gr.Textbox( - value=env_vars.get(var["name"], ""), - label=var["label"] + (" (必填)" if var.get("required", False) else ""), - placeholder=f"请输入{var['label']}", - type="password" - ) - else: - env_inputs[var["name"]] = gr.Textbox( - value=env_vars.get(var["name"], ""), - label=var["label"] + (" (必填)" if var.get("required", False) else ""), - placeholder=f"请输入{var['label']}" - ) + if group_name != "自定义环境变量" or len(vars) > 0: # 只显示非空的自定义环境变量组 + with gr.Accordion(group_name, open=(group_name != "自定义环境变量")): + for var in vars: + # 添加帮助信息 + gr.Markdown(f"**{var['help']}**") + + if var["type"] == "password": + env_inputs[var["name"]] = gr.Textbox( + value=env_vars.get(var["name"], ""), + label=var["label"] + (" (必填)" if var.get("required", False) else ""), + placeholder=f"请输入{var['label']}", + type="password" + ) + else: + env_inputs[var["name"]] = gr.Textbox( + value=env_vars.get(var["name"], ""), + label=var["label"] + (" (必填)" if var.get("required", False) else ""), + placeholder=f"请输入{var['label']}" + ) save_button = gr.Button("保存环境变量", variant="primary") # 保存环境变量 - save_inputs = [env_inputs[var_name] for group in ENV_GROUPS.values() for var in group for var_name in [var["name"]]] + save_inputs = [env_inputs[var_name] for group in ENV_GROUPS.values() for var in group for var_name in [var["name"]] if var_name in env_inputs] save_button.click( - fn=lambda *values: save_env_vars(dict(zip([var["name"] for group in ENV_GROUPS.values() for var in group], values))), + fn=lambda *values: save_env_vars(dict(zip([var["name"] for group in ENV_GROUPS.values() for var in group if var["name"] in env_inputs], values))), inputs=save_inputs, outputs=save_status ) @@ -494,6 +556,7 @@ def create_ui(): - 在"运行日志"标签页查看完整日志 - 在"聊天历史"标签页查看对话历史(如果有) - 在"环境变量配置"标签页配置API密钥和其他环境变量 + - 您可以添加自定义环境变量,满足特殊需求 ### ⚠️ 注意事项 diff --git a/owl/script_adapter.py b/owl/script_adapter.py index 1395155..4f0a7a3 100644 --- a/owl/script_adapter.py +++ b/owl/script_adapter.py @@ -3,6 +3,7 @@ import sys import importlib.util import re from pathlib import Path +import traceback def load_module_from_path(module_name, file_path): """从文件路径加载Python模块""" @@ -33,57 +34,81 @@ def run_script_with_env_question(script_name): # 检查脚本是否有main函数 has_main = re.search(r'def\s+main\s*\(\s*\)\s*:', content) is not None - # 尝试查找并替换question变量 - # 匹配多种可能的question定义模式 - patterns = [ - r'question\s*=\s*["\'].*?["\']', # 简单字符串赋值 - r'question\s*=\s*\(\s*["\'].*?["\']\s*\)', # 带括号的字符串赋值 - r'question\s*=\s*f["\'].*?["\']', # f-string赋值 - r'question\s*=\s*r["\'].*?["\']', # 原始字符串赋值 - ] + # 转义问题中的特殊字符 + escaped_question = question.replace("\\", "\\\\").replace("\"", "\\\"").replace("'", "\\'") - question_replaced = False - for pattern in patterns: - if re.search(pattern, content): - # 转义问题中的特殊字符 - escaped_question = question.replace("\\", "\\\\").replace("\"", "\\\"").replace("'", "\\'") - # 替换问题 - modified_content = re.sub( - pattern, - f'question = "{escaped_question}"', - content + # 查找脚本中所有的question赋值 + question_assignments = re.findall(r'question\s*=\s*(?:["\'].*?["\']|\(.*?\))', content) + print(f"在脚本中找到 {len(question_assignments)} 个question赋值") + + # 修改脚本内容,替换所有的question赋值 + modified_content = content + + # 如果脚本中有question赋值,替换所有的赋值 + if question_assignments: + for assignment in question_assignments: + modified_content = modified_content.replace( + assignment, + f'question = "{escaped_question}"' ) - question_replaced = True - break - - if not question_replaced: - # 如果没有找到question变量,尝试在main函数前插入 + print(f"已替换脚本中的所有question赋值为: {question}") + else: + # 如果没有找到question赋值,尝试在main函数前插入 if has_main: - # 在main函数前插入question变量 main_match = re.search(r'def\s+main\s*\(\s*\)\s*:', content) if main_match: insert_pos = main_match.start() - # 转义问题中的特殊字符 - escaped_question = question.replace("\\", "\\\\").replace("\"", "\\\"").replace("'", "\\'") modified_content = content[:insert_pos] + f'\n# 用户输入的问题\nquestion = "{escaped_question}"\n\n' + content[insert_pos:] - question_replaced = True + print(f"已在main函数前插入问题: {question}") + else: + # 如果没有main函数,在文件开头插入 + modified_content = f'# 用户输入的问题\nquestion = "{escaped_question}"\n\n' + content + print(f"已在文件开头插入问题: {question}") - if not question_replaced: - # 如果仍然无法替换,尝试在文件末尾添加代码来使用用户的问题 - modified_content = content + f'\n\n# 用户输入的问题\nquestion = "{question}"\n\n' + # 添加monkey patch代码,确保construct_society函数使用用户的问题 + monkey_patch_code = f''' +# 确保construct_society函数使用用户的问题 +original_construct_society = globals().get('construct_society') +if original_construct_society: + def patched_construct_society(*args, **kwargs): + # 忽略传入的参数,始终使用用户的问题 + return original_construct_society("{escaped_question}") + + # 替换原始函数 + globals()['construct_society'] = patched_construct_society + print("已修补construct_society函数,确保使用用户问题") +''' + + # 在文件末尾添加monkey patch代码 + modified_content += monkey_patch_code + + # 如果脚本没有调用main函数,添加调用代码 + if has_main and "__main__" not in content: modified_content += ''' -# 如果脚本中有construct_society函数,使用用户问题运行 -if "construct_society" in globals(): + +# 确保调用main函数 +if __name__ == "__main__": + main() +''' + print("已添加main函数调用代码") + + # 如果脚本没有construct_society调用,添加调用代码 + if "construct_society" in content and "run_society" in content and "Answer:" not in content: + modified_content += f''' + +# 确保执行construct_society和run_society +if "construct_society" in globals() and "run_society" in globals(): try: - society = construct_society(question) + society = construct_society("{escaped_question}") from utils import run_society answer, chat_history, token_count = run_society(society) - print(f"Answer: {answer}") + print(f"Answer: {{answer}}") except Exception as e: - print(f"运行时出错: {e}") + print(f"运行时出错: {{e}}") import traceback traceback.print_exc() ''' + print("已添加construct_society和run_society调用代码") # 执行修改后的脚本 try: @@ -92,35 +117,59 @@ if "construct_society" in globals(): if str(script_dir) not in sys.path: sys.path.insert(0, str(script_dir)) - if has_main: - # 如果有main函数,加载模块并调用main - # 创建临时文件 - temp_script_path = script_path.with_name(f"temp_{script_path.name}") - with open(temp_script_path, "w", encoding="utf-8") as f: - f.write(modified_content) + # 创建临时文件 + temp_script_path = script_path.with_name(f"temp_{script_path.name}") + with open(temp_script_path, "w", encoding="utf-8") as f: + f.write(modified_content) + + print(f"已创建临时脚本文件: {temp_script_path}") + + try: + # 直接执行临时脚本 + print(f"开始执行脚本...") - try: + # 如果有main函数,加载模块并调用main + if has_main: # 加载临时模块 module_name = f"temp_{script_path.stem}" module = load_module_from_path(module_name, temp_script_path) + # 确保模块中有question变量,并且值是用户输入的问题 + setattr(module, "question", question) + + # 如果模块中有construct_society函数,修补它 + if hasattr(module, "construct_society"): + original_func = module.construct_society + def patched_func(*args, **kwargs): + return original_func(question) + module.construct_society = patched_func + print("已在模块级别修补construct_society函数") + # 调用main函数 if hasattr(module, "main"): + print("调用main函数...") module.main() else: print(f"错误: 脚本 {script_path} 中没有main函数") sys.exit(1) - finally: - # 删除临时文件 - if temp_script_path.exists(): - temp_script_path.unlink() - else: - # 如果没有main函数,直接执行修改后的脚本 - exec(modified_content, {"__file__": str(script_path)}) + else: + # 如果没有main函数,直接执行修改后的脚本 + print("直接执行脚本内容...") + exec(modified_content, {"__file__": str(temp_script_path)}) + + except Exception as e: + print(f"执行脚本时出错: {e}") + traceback.print_exc() + sys.exit(1) + + finally: + # 删除临时文件 + if temp_script_path.exists(): + temp_script_path.unlink() + print(f"已删除临时脚本文件: {temp_script_path}") except Exception as e: - print(f"执行脚本时出错: {e}") - import traceback + print(f"处理脚本时出错: {e}") traceback.print_exc() sys.exit(1) diff --git a/owl/temp_run.py b/owl/temp_run.py deleted file mode 100644 index 1300617..0000000 --- a/owl/temp_run.py +++ /dev/null @@ -1,129 +0,0 @@ -from dotenv import load_dotenv -load_dotenv() - -from camel.models import ModelFactory -from camel.toolkits import ( - AudioAnalysisToolkit, - CodeExecutionToolkit, - DocumentProcessingToolkit, - ExcelToolkit, - ImageAnalysisToolkit, - SearchToolkit, - VideoAnalysisToolkit, - WebToolkit, -) -from camel.types import ModelPlatformType, ModelType - -from utils import OwlRolePlaying, run_society - - -def construct_society(question: str) -> OwlRolePlaying: - 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: - OwlRolePlaying: 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}, - ), - "web": 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}, - ), - "search": ModelFactory.create( - model_platform=ModelPlatformType.OPENAI, - model_type=ModelType.GPT_4O, - model_config_dict={"temperature": 0}, - ), - } - - # Configure toolkits - tools = [ - *WebToolkit( - headless=False, # Set to True for headless mode (e.g., on remote servers) - web_agent_model=models["web"], - planning_agent_model=models["planning"], - ).get_tools(), - *DocumentProcessingToolkit().get_tools(), - *VideoAnalysisToolkit(model=models["video"]).get_tools(), # This requires OpenAI Key - *AudioAnalysisToolkit().get_tools(), # This requires OpenAI Key - *CodeExecutionToolkit(sandbox="subprocess", verbose=True).get_tools(), - *ImageAnalysisToolkit(model=models["image"]).get_tools(), - *SearchToolkit(model=models["search"]).get_tools(), - *ExcelToolkit().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 = OwlRolePlaying( - **task_kwargs, - user_role_name="user", - user_agent_kwargs=user_agent_kwargs, - assistant_role_name="assistant", - assistant_agent_kwargs=assistant_agent_kwargs, - ) - - return society - - - -# 用户输入的问题 -question = "What is the current weather in New York?" - -def main(): - r"""Main function to run the OWL system with an example question.""" - # Example research question - question = ( - "What was the volume in m^3 of the fish bag that was calculated in " - "the University of Leicester paper `Can Hiccup Supply Enough Fish " - "to Maintain a Dragon's Diet?`" - ) - - # Construct and run the society - society = construct_society(question) - answer, chat_history, token_count = run_society(society) - - # Output the result - print(f"Answer: {answer}") - - -if __name__ == "__main__": - main() From 8f6534e7e7c3c65a6fe63a936ff27d7f2a56411a Mon Sep 17 00:00:00 2001 From: Wendong Date: Mon, 10 Mar 2025 04:03:56 +0800 Subject: [PATCH 5/7] update gradio to uv --- pyproject.toml | 1 + uv.lock | 2 ++ 2 files changed, 3 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 20800e6..92bd475 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,6 +24,7 @@ dependencies = [ "camel-ai[all]==0.2.23", "chunkr-ai>=0.0.41", "docx2markdown>=0.1.1", + "gradio>=3.50.2", ] [project.urls] diff --git a/uv.lock b/uv.lock index 98ba229..633a78e 100644 --- a/uv.lock +++ b/uv.lock @@ -3510,6 +3510,7 @@ dependencies = [ { name = "camel-ai", extra = ["all"] }, { name = "chunkr-ai" }, { name = "docx2markdown" }, + { name = "gradio" }, ] [package.metadata] @@ -3517,6 +3518,7 @@ requires-dist = [ { name = "camel-ai", extras = ["all"], specifier = "==0.2.23" }, { name = "chunkr-ai", specifier = ">=0.0.41" }, { name = "docx2markdown", specifier = ">=0.1.1" }, + { name = "gradio", specifier = ">=3.50.2" }, ] [[package]] From 36dcd3b87ba82a95367e094709e59360d04cb290 Mon Sep 17 00:00:00 2001 From: Wendong Date: Mon, 10 Mar 2025 04:22:08 +0800 Subject: [PATCH 6/7] update wendong --- owl/app.py | 119 +++++++++++-------- owl/script_adapter.py | 260 +++++++++++++++++++++++++----------------- run_app.py | 2 +- 3 files changed, 223 insertions(+), 158 deletions(-) diff --git a/owl/app.py b/owl/app.py index 86d2a6e..d4631a8 100644 --- a/owl/app.py +++ b/owl/app.py @@ -6,7 +6,6 @@ import threading import time from datetime import datetime import queue -import re from pathlib import Path import json import signal @@ -22,20 +21,20 @@ process_lock = threading.Lock() # 脚本选项 SCRIPTS = { "Qwen Mini (中文)": "run_qwen_mini_zh.py", - "Qwen": "run_qwen.py", + "Qwen (中文)": "run_qwen_zh.py", "Mini": "run_mini.py", - "DeepSeek": "run_deepseek.py", - "默认": "run.py", + "DeepSeek (中文)": "run_deepseek_zh.py", + "Default": "run.py", "GAIA Roleplaying": "run_gaia_roleplaying.py" } # 脚本描述 SCRIPT_DESCRIPTIONS = { "Qwen Mini (中文)": "使用阿里云Qwen模型的中文版本,适合中文问答和任务", - "Qwen": "使用阿里云Qwen模型,支持多种工具和功能", + "Qwen (中文)": "使用阿里云Qwen模型,支持多种工具和功能", "Mini": "轻量级版本,使用OpenAI GPT-4o模型", - "DeepSeek": "使用DeepSeek模型,适合复杂推理任务", - "默认": "默认OWL实现,使用OpenAI GPT-4o模型和全套工具", + "DeepSeek (中文)": "使用DeepSeek模型,适合非多模态任务", + "Default": "默认OWL实现,使用OpenAI GPT-4o模型和全套工具", "GAIA Roleplaying": "GAIA基准测试实现,用于评估模型能力" } @@ -46,7 +45,7 @@ ENV_GROUPS = { "name": "OPENAI_API_KEY", "label": "OpenAI API密钥", "type": "password", - "required": True, + "required": False, "help": "OpenAI API密钥,用于访问GPT模型。获取方式:https://platform.openai.com/api-keys" }, { @@ -176,6 +175,7 @@ def save_env_vars(env_vars): for key, value in env_vars.items(): if value: # 只保存非空值 # 确保值是字符串形式,并用引号包裹 + value = str(value) # 确保值是字符串 if not (value.startswith('"') and value.endswith('"')) and not (value.startswith("'") and value.endswith("'")): value = f'"{value}"' existing_content[key] = value @@ -243,7 +243,9 @@ def run_script(script_dropdown, question, progress=gr.Progress()): """运行选定的脚本并返回输出""" global current_process - script_name = SCRIPTS[script_dropdown] + script_name = SCRIPTS.get(script_dropdown) + if not script_name: + return "❌ 无效的脚本选择", "", "", "", None if not question.strip(): return "请输入问题!", "", "", "", None @@ -280,14 +282,17 @@ def run_script(script_dropdown, question, progress=gr.Progress()): # 创建线程来读取输出 def read_output(): - with open(log_file, "w", encoding="utf-8") as f: - for line in iter(current_process.stdout.readline, ""): - if line: - # 写入日志文件 - f.write(line) - f.flush() - # 添加到队列 - log_queue.put(line) + try: + with open(log_file, "w", encoding="utf-8") as f: + for line in iter(current_process.stdout.readline, ""): + if line: + # 写入日志文件 + f.write(line) + f.flush() + # 添加到队列 + log_queue.put(line) + except Exception as e: + log_queue.put(f"读取输出时出错: {str(e)}\n") # 启动读取线程 threading.Thread(target=read_output, daemon=True).start() @@ -305,7 +310,10 @@ def run_script(script_dropdown, question, progress=gr.Progress()): if time.time() - start_time > timeout: with process_lock: if current_process.poll() is None: - current_process.terminate() + if os.name == 'nt': + current_process.send_signal(signal.CTRL_BREAK_EVENT) + else: + current_process.terminate() log_queue.put("执行超时,已终止进程\n") break @@ -355,32 +363,41 @@ def extract_answer(logs): def extract_chat_history(logs): """尝试从日志中提取聊天历史""" try: - for i, log in enumerate(logs): + chat_json_str = "" + capture_json = False + + for log in logs: if "chat_history" in log: - # 尝试找到JSON格式的聊天历史 + # 开始捕获JSON start_idx = log.find("[") if start_idx != -1: - # 尝试解析JSON - json_str = log[start_idx:].strip() - # 查找下一行中可能的结束括号 - if json_str[-1] != "]" and i+1 < len(logs): - for j in range(i+1, min(i+10, len(logs))): - end_idx = logs[j].find("]") - if end_idx != -1: - json_str += logs[j][:end_idx+1] - break - - try: - chat_data = json.loads(json_str) - # 格式化为Gradio聊天组件可用的格式 - formatted_chat = [] - for msg in chat_data: - if "role" in msg and "content" in msg: - role = "用户" if msg["role"] == "user" else "助手" - formatted_chat.append([role, msg["content"]]) - return formatted_chat - except json.JSONDecodeError: - pass + capture_json = True + chat_json_str = log[start_idx:] + elif capture_json: + # 继续捕获JSON直到找到匹配的结束括号 + chat_json_str += log + if "]" in log: + # 找到结束括号,尝试解析JSON + end_idx = chat_json_str.rfind("]") + 1 + if end_idx > 0: + try: + # 清理可能的额外文本 + json_str = chat_json_str[:end_idx].strip() + chat_data = json.loads(json_str) + + # 格式化为Gradio聊天组件可用的格式 + formatted_chat = [] + for msg in chat_data: + if "role" in msg and "content" in msg: + role = "用户" if msg["role"] == "user" else "助手" + formatted_chat.append([role, msg["content"]]) + return formatted_chat + except json.JSONDecodeError: + # 如果解析失败,继续捕获 + pass + except Exception: + # 其他错误,停止捕获 + capture_json = False except Exception: pass return None @@ -400,17 +417,19 @@ def create_ui(): ) with gr.Tabs() as tabs: - with gr.TabItem("运行模型"): + with gr.TabItem("运行模式"): with gr.Row(): with gr.Column(scale=1): + # 确保默认值是SCRIPTS中存在的键 + default_script = list(SCRIPTS.keys())[0] if SCRIPTS else None script_dropdown = gr.Dropdown( choices=list(SCRIPTS.keys()), - value=list(SCRIPTS.keys())[0], - label="选择模型" + value=default_script, + label="选择模式" ) script_info = gr.Textbox( - value=get_script_info(list(SCRIPTS.keys())[0]), + value=get_script_info(default_script) if default_script else "", label="模型描述", interactive=False ) @@ -452,9 +471,9 @@ def create_ui(): # 示例问题 examples = [ - ["Qwen Mini (中文)", "打开小红书上浏览推荐栏目下的前三个笔记内容,不要登陆,之后给我一个总结报告"], - ["Mini", "What was the volume in m^3 of the fish bag that was calculated in the University of Leicester paper `Can Hiccup Supply Enough Fish to Maintain a Dragon's Diet?`"], - ["默认", "What is the current weather in New York?"] + ["Qwen Mini (中文)", "浏览亚马逊并找出一款对程序员有吸引力的产品。请提供产品名称和价格"], + ["DeepSeek (中文)", "请分析GitHub上CAMEL-AI项目的最新统计数据。找出该项目的星标数量、贡献者数量和最近的活跃度。然后,创建一个简单的Excel表格来展示这些数据,并生成一个柱状图来可视化这些指标。最后,总结CAMEL项目的受欢迎程度和发展趋势。"], + ["Default", "Navigate to Amazon.com and identify one product that is attractive to coders. Please provide me with the product name and price. No need to verify your answer."] ] gr.Examples( @@ -505,14 +524,14 @@ def create_ui(): if var["type"] == "password": env_inputs[var["name"]] = gr.Textbox( value=env_vars.get(var["name"], ""), - label=var["label"] + (" (必填)" if var.get("required", False) else ""), + label=var["label"], placeholder=f"请输入{var['label']}", type="password" ) else: env_inputs[var["name"]] = gr.Textbox( value=env_vars.get(var["name"], ""), - label=var["label"] + (" (必填)" if var.get("required", False) else ""), + label=var["label"], placeholder=f"请输入{var['label']}" ) diff --git a/owl/script_adapter.py b/owl/script_adapter.py index 4f0a7a3..db285c5 100644 --- a/owl/script_adapter.py +++ b/owl/script_adapter.py @@ -7,11 +7,20 @@ import traceback def load_module_from_path(module_name, file_path): """从文件路径加载Python模块""" - spec = importlib.util.spec_from_file_location(module_name, file_path) - module = importlib.util.module_from_spec(spec) - sys.modules[module_name] = module - spec.loader.exec_module(module) - return module + try: + spec = importlib.util.spec_from_file_location(module_name, file_path) + if spec is None: + print(f"错误: 无法从 {file_path} 创建模块规范") + return None + + module = importlib.util.module_from_spec(spec) + sys.modules[module_name] = module + spec.loader.exec_module(module) + return module + except Exception as e: + print(f"加载模块时出错: {e}") + traceback.print_exc() + return None def run_script_with_env_question(script_name): """使用环境变量中的问题运行脚本""" @@ -27,46 +36,55 @@ def run_script_with_env_question(script_name): print(f"错误: 脚本 {script_path} 不存在") sys.exit(1) - # 读取脚本内容 - with open(script_path, "r", encoding="utf-8") as f: - content = f.read() + # 创建临时文件路径 + temp_script_path = script_path.with_name(f"temp_{script_path.name}") - # 检查脚本是否有main函数 - has_main = re.search(r'def\s+main\s*\(\s*\)\s*:', content) is not None - - # 转义问题中的特殊字符 - escaped_question = question.replace("\\", "\\\\").replace("\"", "\\\"").replace("'", "\\'") - - # 查找脚本中所有的question赋值 - question_assignments = re.findall(r'question\s*=\s*(?:["\'].*?["\']|\(.*?\))', content) - print(f"在脚本中找到 {len(question_assignments)} 个question赋值") - - # 修改脚本内容,替换所有的question赋值 - modified_content = content - - # 如果脚本中有question赋值,替换所有的赋值 - if question_assignments: - for assignment in question_assignments: - modified_content = modified_content.replace( - assignment, - f'question = "{escaped_question}"' - ) - print(f"已替换脚本中的所有question赋值为: {question}") - else: - # 如果没有找到question赋值,尝试在main函数前插入 - if has_main: - main_match = re.search(r'def\s+main\s*\(\s*\)\s*:', content) - if main_match: - insert_pos = main_match.start() - modified_content = content[:insert_pos] + f'\n# 用户输入的问题\nquestion = "{escaped_question}"\n\n' + content[insert_pos:] - print(f"已在main函数前插入问题: {question}") + try: + # 读取脚本内容 + try: + with open(script_path, "r", encoding="utf-8") as f: + content = f.read() + except Exception as e: + print(f"读取脚本文件时出错: {e}") + sys.exit(1) + + # 检查脚本是否有main函数 + has_main = re.search(r'def\s+main\s*\(\s*\)\s*:', content) is not None + + # 转义问题中的特殊字符 + escaped_question = question.replace("\\", "\\\\").replace("\"", "\\\"").replace("'", "\\'") + + # 查找脚本中所有的question赋值 - 改进的正则表达式 + # 匹配单行和多行字符串赋值 + question_assignments = re.findall(r'question\s*=\s*(?:["\'].*?["\']|""".*?"""|\'\'\'.*?\'\'\'|\(.*?\))', content, re.DOTALL) + print(f"在脚本中找到 {len(question_assignments)} 个question赋值") + + # 修改脚本内容,替换所有的question赋值 + modified_content = content + + # 如果脚本中有question赋值,替换所有的赋值 + if question_assignments: + for assignment in question_assignments: + modified_content = modified_content.replace( + assignment, + f'question = "{escaped_question}"' + ) + print(f"已替换脚本中的所有question赋值为: {question}") else: - # 如果没有main函数,在文件开头插入 - modified_content = f'# 用户输入的问题\nquestion = "{escaped_question}"\n\n' + content - print(f"已在文件开头插入问题: {question}") - - # 添加monkey patch代码,确保construct_society函数使用用户的问题 - monkey_patch_code = f''' + # 如果没有找到question赋值,尝试在main函数前插入 + if has_main: + main_match = re.search(r'def\s+main\s*\(\s*\)\s*:', content) + if main_match: + insert_pos = main_match.start() + modified_content = content[:insert_pos] + f'\n# 用户输入的问题\nquestion = "{escaped_question}"\n\n' + content[insert_pos:] + print(f"已在main函数前插入问题: {question}") + else: + # 如果没有main函数,在文件开头插入 + modified_content = f'# 用户输入的问题\nquestion = "{escaped_question}"\n\n' + content + print(f"已在文件开头插入问题: {question}") + + # 添加monkey patch代码,确保construct_society函数使用用户的问题 + monkey_patch_code = f''' # 确保construct_society函数使用用户的问题 original_construct_society = globals().get('construct_society') if original_construct_society: @@ -78,23 +96,23 @@ if original_construct_society: globals()['construct_society'] = patched_construct_society print("已修补construct_society函数,确保使用用户问题") ''' - - # 在文件末尾添加monkey patch代码 - modified_content += monkey_patch_code - - # 如果脚本没有调用main函数,添加调用代码 - if has_main and "__main__" not in content: - modified_content += ''' + + # 在文件末尾添加monkey patch代码 + modified_content += monkey_patch_code + + # 如果脚本没有调用main函数,添加调用代码 + if has_main and "__main__" not in content: + modified_content += ''' # 确保调用main函数 if __name__ == "__main__": main() ''' - print("已添加main函数调用代码") - - # 如果脚本没有construct_society调用,添加调用代码 - if "construct_society" in content and "run_society" in content and "Answer:" not in content: - modified_content += f''' + print("已添加main函数调用代码") + + # 如果脚本没有construct_society调用,添加调用代码 + if "construct_society" in content and "run_society" in content and "Answer:" not in content: + modified_content += f''' # 确保执行construct_society和run_society if "construct_society" in globals() and "run_society" in globals(): @@ -108,70 +126,98 @@ if "construct_society" in globals() and "run_society" in globals(): import traceback traceback.print_exc() ''' - print("已添加construct_society和run_society调用代码") - - # 执行修改后的脚本 - try: - # 将脚本目录添加到sys.path - script_dir = script_path.parent - if str(script_dir) not in sys.path: - sys.path.insert(0, str(script_dir)) - - # 创建临时文件 - temp_script_path = script_path.with_name(f"temp_{script_path.name}") - with open(temp_script_path, "w", encoding="utf-8") as f: - f.write(modified_content) - - print(f"已创建临时脚本文件: {temp_script_path}") + print("已添加construct_society和run_society调用代码") + # 执行修改后的脚本 try: - # 直接执行临时脚本 - print(f"开始执行脚本...") + # 将脚本目录添加到sys.path + script_dir = script_path.parent + if str(script_dir) not in sys.path: + sys.path.insert(0, str(script_dir)) - # 如果有main函数,加载模块并调用main - if has_main: - # 加载临时模块 - module_name = f"temp_{script_path.stem}" - module = load_module_from_path(module_name, temp_script_path) + # 创建临时文件 + try: + with open(temp_script_path, "w", encoding="utf-8") as f: + f.write(modified_content) + print(f"已创建临时脚本文件: {temp_script_path}") + except Exception as e: + print(f"创建临时脚本文件时出错: {e}") + sys.exit(1) + + try: + # 直接执行临时脚本 + print(f"开始执行脚本...") - # 确保模块中有question变量,并且值是用户输入的问题 - setattr(module, "question", question) - - # 如果模块中有construct_society函数,修补它 - if hasattr(module, "construct_society"): - original_func = module.construct_society - def patched_func(*args, **kwargs): - return original_func(question) - module.construct_society = patched_func - print("已在模块级别修补construct_society函数") - - # 调用main函数 - if hasattr(module, "main"): - print("调用main函数...") - module.main() + # 如果有main函数,加载模块并调用main + if has_main: + # 加载临时模块 + module_name = f"temp_{script_path.stem}" + module = load_module_from_path(module_name, temp_script_path) + + if module is None: + print(f"错误: 无法加载模块 {module_name}") + sys.exit(1) + + # 确保模块中有question变量,并且值是用户输入的问题 + setattr(module, "question", question) + + # 如果模块中有construct_society函数,修补它 + if hasattr(module, "construct_society"): + original_func = module.construct_society + def patched_func(*args, **kwargs): + return original_func(question) + module.construct_society = patched_func + print("已在模块级别修补construct_society函数") + + # 调用main函数 + if hasattr(module, "main"): + print("调用main函数...") + module.main() + else: + print(f"错误: 脚本 {script_path} 中没有main函数") + sys.exit(1) else: - print(f"错误: 脚本 {script_path} 中没有main函数") - sys.exit(1) - else: - # 如果没有main函数,直接执行修改后的脚本 - print("直接执行脚本内容...") - exec(modified_content, {"__file__": str(temp_script_path)}) - + # 如果没有main函数,直接执行修改后的脚本 + print("直接执行脚本内容...") + # 使用更安全的方式执行脚本 + with open(temp_script_path, "r", encoding="utf-8") as f: + script_code = f.read() + + # 创建一个安全的全局命名空间 + safe_globals = { + "__file__": str(temp_script_path), + "__name__": "__main__" + } + # 添加内置函数 + safe_globals.update({k: v for k, v in globals().items() + if k in ['__builtins__']}) + + # 执行脚本 + exec(script_code, safe_globals) + + except Exception as e: + print(f"执行脚本时出错: {e}") + traceback.print_exc() + sys.exit(1) + except Exception as e: - print(f"执行脚本时出错: {e}") + print(f"处理脚本时出错: {e}") traceback.print_exc() sys.exit(1) - finally: - # 删除临时文件 - if temp_script_path.exists(): - temp_script_path.unlink() - print(f"已删除临时脚本文件: {temp_script_path}") - except Exception as e: print(f"处理脚本时出错: {e}") traceback.print_exc() sys.exit(1) + + finally: + # 删除临时文件 + if temp_script_path.exists(): + try: + temp_script_path.unlink() + print(f"已删除临时脚本文件: {temp_script_path}") + except Exception as e: + print(f"删除临时脚本文件时出错: {e}") if __name__ == "__main__": # 检查命令行参数 @@ -180,4 +226,4 @@ if __name__ == "__main__": sys.exit(1) # 运行指定的脚本 - run_script_with_env_question(sys.argv[1]) \ No newline at end of file + run_script_with_env_question(sys.argv[1]) \ No newline at end of file diff --git a/run_app.py b/run_app.py index cfb71c6..89eee1b 100644 --- a/run_app.py +++ b/run_app.py @@ -27,7 +27,7 @@ def main(): # 创建并启动应用 app = create_ui() - app.launch(share=False) + app.queue().launch(share=False) except ImportError as e: print(f"错误: 无法导入必要的模块。请确保已安装所有依赖项: {e}") From dab0db90af629bd0ad860caaf443a5a604f8f35f Mon Sep 17 00:00:00 2001 From: Wendong Date: Mon, 10 Mar 2025 04:25:53 +0800 Subject: [PATCH 7/7] update readme --- README.md | 18 ++++++++++++++++++ README_zh.md | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/README.md b/README.md index 26f0f97..0fab4bf 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,7 @@ Our vision is to revolutionize how AI agents collaborate to solve real-world tas - [**Running with Docker**](#running-with-docker) - [🚀 Quick Start](#-quick-start) +- [🌐 Web Interface](#-web-interface) - [🧪 Experiments](#-experiments) - [⏱️ Future Plans](#️-future-plans) - [📄 License](#-license) @@ -228,6 +229,23 @@ Example tasks you can try: - "Help me debug this Python code: [your code here]" - "Summarize the main points from this research paper: [paper URL]" +# 🌐 Web Interface + +OWL now includes a web-based user interface that makes it easier to interact with the system. To start the web interface, run: + +```bash +python run_app.py +``` + +The web interface provides the following features: + +- **Easy Model Selection**: Choose between different models (OpenAI, Qwen, DeepSeek, etc.) +- **Environment Variable Management**: Configure your API keys and other settings directly from the UI +- **Interactive Chat Interface**: Communicate with OWL agents through a user-friendly interface +- **Task History**: View the history and results of your interactions + +The web interface is built using Gradio and runs locally on your machine. No data is sent to external servers beyond what's required for the model API calls you configure. + # 🧪 Experiments We provided a script to reproduce the results on GAIA. diff --git a/README_zh.md b/README_zh.md index 6a24680..0792660 100644 --- a/README_zh.md +++ b/README_zh.md @@ -73,6 +73,7 @@ - [**设置环境变量**](#设置环境变量) - [**使用Docker运行**](#使用docker运行) - [🚀 快速开始](#-快速开始) +- [🌐 网页界面](#-网页界面) - [🧪 实验](#-实验) - [⏱️ 未来计划](#️-未来计划) - [📄 许可证](#-许可证) @@ -223,6 +224,23 @@ OWL 将自动调用与文档相关的工具来处理文件并提取答案。 - "帮我调试这段 Python 代码:[在此粘贴你的代码]" - "总结这篇研究论文的主要观点:[论文URL]" +# 🌐 网页界面 + +OWL 现在包含一个基于网页的用户界面,使与系统交互变得更加容易。要启动网页界面,请运行: + +```bash +python run_app.py +``` + +网页界面提供以下功能: + +- **便捷的模型选择**:选择不同的模型(OpenAI、Qwen、DeepSeek等) +- **环境变量管理**:直接从界面配置API密钥和其他设置 +- **交互式聊天界面**:通过用户友好的界面与OWL智能体交流 +- **任务历史**:查看交互的历史记录和结果 + +网页界面使用Gradio构建,在您的本地机器上运行。除了您配置的模型API调用所需的数据外,不会向外部服务器发送任何数据。 + # 🧪 实验 我们提供了一个脚本用于复现 GAIA 上的实验结果。