zjrwtx commited on
Commit
e6c66d5
·
1 Parent(s): f95eedb
Files changed (2) hide show
  1. owl/app.py +210 -60
  2. owl/script_adapter.py +80 -29
owl/app.py CHANGED
@@ -9,10 +9,16 @@ import queue
9
  import re
10
  from pathlib import Path
11
  import json
 
 
12
 
13
  # 设置日志队列
14
  log_queue = queue.Queue()
15
 
 
 
 
 
16
  # 脚本选项
17
  SCRIPTS = {
18
  "Qwen Mini (中文)": "run_qwen_mini_zh.py",
@@ -33,12 +39,98 @@ SCRIPT_DESCRIPTIONS = {
33
  "GAIA Roleplaying": "GAIA基准测试实现,用于评估模型能力"
34
  }
35
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  def get_script_info(script_name):
37
  """获取脚本的详细信息"""
38
  return SCRIPT_DESCRIPTIONS.get(script_name, "无描述信息")
39
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  def run_script(script_dropdown, question, progress=gr.Progress()):
41
  """运行选定的脚本并返回输出"""
 
 
42
  script_name = SCRIPTS[script_dropdown]
43
 
44
  if not question.strip():
@@ -64,19 +156,20 @@ def run_script(script_dropdown, question, progress=gr.Progress()):
64
  env["OWL_QUESTION"] = question
65
 
66
  # 启动进程
67
- process = subprocess.Popen(
68
- cmd,
69
- stdout=subprocess.PIPE,
70
- stderr=subprocess.STDOUT,
71
- text=True,
72
- bufsize=1,
73
- env=env
74
- )
 
75
 
76
  # 创建线程来读取输出
77
  def read_output():
78
  with open(log_file, "w", encoding="utf-8") as f:
79
- for line in iter(process.stdout.readline, ""):
80
  if line:
81
  # 写入日志文件
82
  f.write(line)
@@ -95,11 +188,13 @@ def run_script(script_dropdown, question, progress=gr.Progress()):
95
  start_time = time.time()
96
  timeout = 1800 # 30分钟超时
97
 
98
- while process.poll() is None:
99
  # 检查是否超时
100
  if time.time() - start_time > timeout:
101
- process.terminate()
102
- log_queue.put("执行超时,已终止进程\n")
 
 
103
  break
104
 
105
  # 从队列获取日志
@@ -115,7 +210,7 @@ def run_script(script_dropdown, question, progress=gr.Progress()):
115
  time.sleep(0.1)
116
 
117
  # 每秒更新一次日志显示
118
- yield status_message(process), extract_answer(logs), "".join(logs), str(log_file), None
119
 
120
  # 获取剩余日志
121
  while not log_queue.empty():
@@ -125,7 +220,7 @@ def run_script(script_dropdown, question, progress=gr.Progress()):
125
  chat_history = extract_chat_history(logs)
126
 
127
  # 返回最终状态和日志
128
- return status_message(process), extract_answer(logs), "".join(logs), str(log_file), chat_history
129
 
130
  def status_message(process):
131
  """根据进程状态返回状态消息"""
@@ -203,6 +298,9 @@ def modify_script(script_name, question):
203
 
204
  def create_ui():
205
  """创建Gradio界面"""
 
 
 
206
  with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue")) as app:
207
  gr.Markdown(
208
  """
@@ -212,47 +310,101 @@ def create_ui():
212
  """
213
  )
214
 
215
- with gr.Row():
216
- with gr.Column(scale=1):
217
- script_dropdown = gr.Dropdown(
218
- choices=list(SCRIPTS.keys()),
219
- value=list(SCRIPTS.keys())[0],
220
- label="选择模型"
221
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
222
 
223
- script_info = gr.Textbox(
224
- value=get_script_info(list(SCRIPTS.keys())[0]),
225
- label="模型描述",
226
- interactive=False
227
- )
 
228
 
229
- script_dropdown.change(
230
- fn=lambda x: get_script_info(x),
231
- inputs=script_dropdown,
232
- outputs=script_info
233
  )
 
 
 
 
234
 
235
- question_input = gr.Textbox(
236
- lines=5,
237
- placeholder="请输入您的问题...",
238
- label="问题"
239
- )
 
 
 
 
 
 
 
 
 
 
 
240
 
241
- run_button = gr.Button("运行", variant="primary")
242
-
243
- with gr.Column(scale=2):
244
- with gr.Tabs():
245
- with gr.TabItem("结果"):
246
- status_output = gr.Textbox(label="状态")
247
- answer_output = gr.Textbox(label="回答", lines=10)
248
- log_file_output = gr.Textbox(label="日志文件路径")
249
-
250
- with gr.TabItem("运行日志"):
251
- log_output = gr.Textbox(label="完整日志", lines=25)
252
-
253
- with gr.TabItem("聊天历史"):
254
- chat_output = gr.Chatbot(label="对话历史")
255
 
 
256
  run_button.click(
257
  fn=run_script,
258
  inputs=[
@@ -263,16 +415,11 @@ def create_ui():
263
  show_progress=True
264
  )
265
 
266
- # 示例问题
267
- examples = [
268
- ["Qwen Mini (中文)", "打开小红书上浏览推荐栏目下的前三个笔记内容,不要登陆,之后给我一个总结报告"],
269
- ["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?`"],
270
- ["默认", "What is the current weather in New York?"]
271
- ]
272
-
273
- gr.Examples(
274
- examples=examples,
275
- inputs=[script_dropdown, question_input]
276
  )
277
 
278
  # 添加页脚
@@ -282,15 +429,18 @@ def create_ui():
282
 
283
  - 选择一个模型并输入您的问题
284
  - 点击"运行"按钮开始执行
 
285
  - 在"结果"标签页查看执行状态和回答
286
  - 在"运行日志"标签页查看完整日志
287
  - 在"聊天历史"标签页查看对话历史(如果有)
 
288
 
289
  ### ⚠️ 注意事项
290
 
291
- - 运行某些模型可能需要API密钥,请确保在`.env`文件中设置了相应的环境变量
292
  - 某些脚本可能需要较长时间运行,请耐心等待
293
  - 如果运行超过30分钟,进程将自动终止
 
294
  """
295
  )
296
 
 
9
  import re
10
  from pathlib import Path
11
  import json
12
+ import signal
13
+ import dotenv
14
 
15
  # 设置日志队列
16
  log_queue = queue.Queue()
17
 
18
+ # 当前运行的进程
19
+ current_process = None
20
+ process_lock = threading.Lock()
21
+
22
  # 脚本选项
23
  SCRIPTS = {
24
  "Qwen Mini (中文)": "run_qwen_mini_zh.py",
 
39
  "GAIA Roleplaying": "GAIA基准测试实现,用于评估模型能力"
40
  }
41
 
42
+ # 环境变量分组
43
+ ENV_GROUPS = {
44
+ "模型API": [
45
+ {"name": "OPENAI_API_KEY", "label": "OpenAI API密钥", "type": "password", "required": True},
46
+ {"name": "OPENAI_API_BASE_URL", "label": "OpenAI API基础URL", "type": "text", "required": False},
47
+ {"name": "QWEN_API_KEY", "label": "阿里云Qwen API密钥", "type": "password", "required": False},
48
+ {"name": "DEEPSEEK_API_KEY", "label": "DeepSeek API密钥", "type": "password", "required": False},
49
+ ],
50
+ "搜索工具": [
51
+ {"name": "GOOGLE_API_KEY", "label": "Google API密钥", "type": "password", "required": False},
52
+ {"name": "SEARCH_ENGINE_ID", "label": "搜索引擎ID", "type": "text", "required": False},
53
+ ],
54
+ "其他工具": [
55
+ {"name": "HF_TOKEN", "label": "Hugging Face令牌", "type": "password", "required": False},
56
+ {"name": "CHUNKR_API_KEY", "label": "Chunkr API密钥", "type": "password", "required": False},
57
+ {"name": "FIRECRAWL_API_KEY", "label": "Firecrawl API密钥", "type": "password", "required": False},
58
+ ]
59
+ }
60
+
61
  def get_script_info(script_name):
62
  """获取脚本的详细信息"""
63
  return SCRIPT_DESCRIPTIONS.get(script_name, "无描述信息")
64
 
65
+ def load_env_vars():
66
+ """加载环境变量"""
67
+ env_vars = {}
68
+ # 尝试从.env文件加载
69
+ dotenv.load_dotenv()
70
+
71
+ # 获取所有环境变量
72
+ for group in ENV_GROUPS.values():
73
+ for var in group:
74
+ env_vars[var["name"]] = os.environ.get(var["name"], "")
75
+
76
+ return env_vars
77
+
78
+ def save_env_vars(env_vars):
79
+ """保存环境变量到.env文件"""
80
+ # 读取现有的.env文件内容
81
+ env_path = Path(".env")
82
+ existing_content = {}
83
+
84
+ if env_path.exists():
85
+ with open(env_path, "r", encoding="utf-8") as f:
86
+ for line in f:
87
+ line = line.strip()
88
+ if line and not line.startswith("#") and "=" in line:
89
+ key, value = line.split("=", 1)
90
+ existing_content[key.strip()] = value.strip()
91
+
92
+ # 更新环境变量
93
+ for key, value in env_vars.items():
94
+ if value: # 只保存非空值
95
+ existing_content[key] = value
96
+ # 同时更新当前进程的环境变量
97
+ os.environ[key] = value
98
+
99
+ # 写入.env文件
100
+ with open(env_path, "w", encoding="utf-8") as f:
101
+ for key, value in existing_content.items():
102
+ f.write(f"{key}={value}\n")
103
+
104
+ return "环境变量已保存"
105
+
106
+ def terminate_process():
107
+ """终止当前运行的进程"""
108
+ global current_process
109
+
110
+ with process_lock:
111
+ if current_process is not None and current_process.poll() is None:
112
+ # 在Windows上使用CTRL_BREAK_EVENT,在Unix上使用SIGTERM
113
+ if os.name == 'nt':
114
+ current_process.send_signal(signal.CTRL_BREAK_EVENT)
115
+ else:
116
+ current_process.terminate()
117
+
118
+ # 等待进程终止
119
+ try:
120
+ current_process.wait(timeout=5)
121
+ except subprocess.TimeoutExpired:
122
+ # 如果进程没有在5秒内终止,强制终止
123
+ current_process.kill()
124
+
125
+ log_queue.put("进程已终止\n")
126
+ return "✅ 进程已终止"
127
+ else:
128
+ return "❌ 没有正在运行的进程"
129
+
130
  def run_script(script_dropdown, question, progress=gr.Progress()):
131
  """运行选定的脚本并返回输出"""
132
+ global current_process
133
+
134
  script_name = SCRIPTS[script_dropdown]
135
 
136
  if not question.strip():
 
156
  env["OWL_QUESTION"] = question
157
 
158
  # 启动进程
159
+ with process_lock:
160
+ current_process = subprocess.Popen(
161
+ cmd,
162
+ stdout=subprocess.PIPE,
163
+ stderr=subprocess.STDOUT,
164
+ text=True,
165
+ bufsize=1,
166
+ env=env
167
+ )
168
 
169
  # 创建线程来读取输出
170
  def read_output():
171
  with open(log_file, "w", encoding="utf-8") as f:
172
+ for line in iter(current_process.stdout.readline, ""):
173
  if line:
174
  # 写入日志文件
175
  f.write(line)
 
188
  start_time = time.time()
189
  timeout = 1800 # 30分钟超时
190
 
191
+ while current_process.poll() is None:
192
  # 检查是否超时
193
  if time.time() - start_time > timeout:
194
+ with process_lock:
195
+ if current_process.poll() is None:
196
+ current_process.terminate()
197
+ log_queue.put("执行超时,已终止进程\n")
198
  break
199
 
200
  # 从队列获取日志
 
210
  time.sleep(0.1)
211
 
212
  # 每秒更新一次日志显示
213
+ yield status_message(current_process), extract_answer(logs), "".join(logs), str(log_file), None
214
 
215
  # 获取剩余日志
216
  while not log_queue.empty():
 
220
  chat_history = extract_chat_history(logs)
221
 
222
  # 返回最终状态和日志
223
+ return status_message(current_process), extract_answer(logs), "".join(logs), str(log_file), chat_history
224
 
225
  def status_message(process):
226
  """根据进程状态返回状态消息"""
 
298
 
299
  def create_ui():
300
  """创建Gradio界面"""
301
+ # 加载环境变量
302
+ env_vars = load_env_vars()
303
+
304
  with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue")) as app:
305
  gr.Markdown(
306
  """
 
310
  """
311
  )
312
 
313
+ with gr.Tabs() as tabs:
314
+ with gr.TabItem("运行模型"):
315
+ with gr.Row():
316
+ with gr.Column(scale=1):
317
+ script_dropdown = gr.Dropdown(
318
+ choices=list(SCRIPTS.keys()),
319
+ value=list(SCRIPTS.keys())[0],
320
+ label="选择模型"
321
+ )
322
+
323
+ script_info = gr.Textbox(
324
+ value=get_script_info(list(SCRIPTS.keys())[0]),
325
+ label="模型描述",
326
+ interactive=False
327
+ )
328
+
329
+ script_dropdown.change(
330
+ fn=lambda x: get_script_info(x),
331
+ inputs=script_dropdown,
332
+ outputs=script_info
333
+ )
334
+
335
+ question_input = gr.Textbox(
336
+ lines=5,
337
+ placeholder="请输入您的问题...",
338
+ label="问题"
339
+ )
340
+
341
+ gr.Markdown(
342
+ """
343
+ > **注意**: 您输入的问题将替换脚本中的默认问题。系统会自动处理问题的替换,确保您的问题被正确使用。
344
+ """
345
+ )
346
+
347
+ with gr.Row():
348
+ run_button = gr.Button("运行", variant="primary")
349
+ stop_button = gr.Button("终止", variant="stop")
350
+
351
+ with gr.Column(scale=2):
352
+ with gr.Tabs():
353
+ with gr.TabItem("结果"):
354
+ status_output = gr.Textbox(label="状态")
355
+ answer_output = gr.Textbox(label="回答", lines=10)
356
+ log_file_output = gr.Textbox(label="日志文件路径")
357
+
358
+ with gr.TabItem("运行日志"):
359
+ log_output = gr.Textbox(label="完整日志", lines=25)
360
+
361
+ with gr.TabItem("聊天历史"):
362
+ chat_output = gr.Chatbot(label="对话历史")
363
 
364
+ # 示例问题
365
+ examples = [
366
+ ["Qwen Mini (中文)", "打开小红书上浏览推荐栏目下的前三个笔记内容,不要登陆,之后给我一个��结报告"],
367
+ ["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?`"],
368
+ ["默认", "What is the current weather in New York?"]
369
+ ]
370
 
371
+ gr.Examples(
372
+ examples=examples,
373
+ inputs=[script_dropdown, question_input]
 
374
  )
375
+
376
+ with gr.TabItem("环境变量配置"):
377
+ env_inputs = {}
378
+ save_status = gr.Textbox(label="保存状态", interactive=False)
379
 
380
+ for group_name, vars in ENV_GROUPS.items():
381
+ with gr.Accordion(group_name, open=True):
382
+ for var in vars:
383
+ if var["type"] == "password":
384
+ env_inputs[var["name"]] = gr.Textbox(
385
+ value=env_vars.get(var["name"], ""),
386
+ label=var["label"] + (" (必填)" if var.get("required", False) else ""),
387
+ placeholder=f"请输入{var['label']}",
388
+ type="password"
389
+ )
390
+ else:
391
+ env_inputs[var["name"]] = gr.Textbox(
392
+ value=env_vars.get(var["name"], ""),
393
+ label=var["label"] + (" (必填)" if var.get("required", False) else ""),
394
+ placeholder=f"请输入{var['label']}"
395
+ )
396
 
397
+ save_button = gr.Button("保存环境变量", variant="primary")
398
+
399
+ # 保存环境变量
400
+ save_inputs = [env_inputs[var_name] for group in ENV_GROUPS.values() for var in group for var_name in [var["name"]]]
401
+ save_button.click(
402
+ fn=lambda *values: save_env_vars(dict(zip([var["name"] for group in ENV_GROUPS.values() for var in group], values))),
403
+ inputs=save_inputs,
404
+ outputs=save_status
405
+ )
 
 
 
 
 
406
 
407
+ # 运行脚本
408
  run_button.click(
409
  fn=run_script,
410
  inputs=[
 
415
  show_progress=True
416
  )
417
 
418
+ # 终止运行
419
+ stop_button.click(
420
+ fn=terminate_process,
421
+ inputs=[],
422
+ outputs=[status_output]
 
 
 
 
 
423
  )
424
 
425
  # 添加页脚
 
429
 
430
  - 选择一个模型并输入您的问题
431
  - 点击"运行"按钮开始执行
432
+ - 如需终止运行,点击"终止"按钮
433
  - 在"结果"标签页查看执行状态和回答
434
  - 在"运行日志"标签页查看完整日志
435
  - 在"聊天历史"标签页查看对话历史(如果有)
436
+ - 在"环境变量配置"标签页配置API密钥和其他环境变量
437
 
438
  ### ⚠️ 注意事项
439
 
440
+ - 运行某些模型可能需要API密钥,请确保在"��境变量配置"标签页中设置了相应的环境变量
441
  - 某些脚本可能需要较长时间运行,请耐心等待
442
  - 如果运行超过30分钟,进程将自动终止
443
+ - 您输入的问题将替换脚本中的默认问题,确保问题与所选模型兼容
444
  """
445
  )
446
 
owl/script_adapter.py CHANGED
@@ -26,45 +26,96 @@ def run_script_with_env_question(script_name):
26
  print(f"错误: 脚本 {script_path} 不存在")
27
  sys.exit(1)
28
 
29
- # 加载脚本模块
30
- module_name = script_path.stem
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  try:
32
  # 将脚本目录添加到sys.path
33
  script_dir = script_path.parent
34
  if str(script_dir) not in sys.path:
35
  sys.path.insert(0, str(script_dir))
36
 
37
- # 读取脚本内容
38
- with open(script_path, "r", encoding="utf-8") as f:
39
- content = f.read()
40
-
41
- # 检查脚本是否有main函数
42
- has_main = re.search(r'def\s+main\s*\(\s*\)\s*:', content) is not None
43
-
44
  if has_main:
45
  # 如果有main函数,加载模块并调用main
46
- module = load_module_from_path(module_name, script_path)
 
 
 
47
 
48
- # 修改模块中的question变量
49
- if hasattr(module, "question"):
50
- setattr(module, "question", question)
51
-
52
- # 调用main函数
53
- if hasattr(module, "main"):
54
- module.main()
55
- else:
56
- print(f"错误: 脚本 {script_path} 中没有main函数")
57
- sys.exit(1)
 
 
 
 
 
58
  else:
59
- # 如果没有main函数,直接执行脚本内容
60
- # 替换question变量
61
- modified_content = re.sub(
62
- r'question\s*=\s*["\'].*?["\']',
63
- f'question = "{question}"',
64
- content
65
- )
66
-
67
- # 执行修改后的脚本
68
  exec(modified_content, {"__file__": str(script_path)})
69
 
70
  except Exception as e:
 
26
  print(f"错误: 脚本 {script_path} 不存在")
27
  sys.exit(1)
28
 
29
+ # 读取脚本内容
30
+ with open(script_path, "r", encoding="utf-8") as f:
31
+ content = f.read()
32
+
33
+ # 检查脚本是否有main函数
34
+ has_main = re.search(r'def\s+main\s*\(\s*\)\s*:', content) is not None
35
+
36
+ # 尝试查找并替换question变量
37
+ # 匹配多种可能的question定义模式
38
+ patterns = [
39
+ r'question\s*=\s*["\'].*?["\']', # 简单字符串赋值
40
+ r'question\s*=\s*\(\s*["\'].*?["\']\s*\)', # 带括号的字符串赋值
41
+ r'question\s*=\s*f["\'].*?["\']', # f-string赋值
42
+ r'question\s*=\s*r["\'].*?["\']', # 原始字符串赋值
43
+ ]
44
+
45
+ question_replaced = False
46
+ for pattern in patterns:
47
+ if re.search(pattern, content):
48
+ # 转义问题中的特殊字符
49
+ escaped_question = question.replace("\\", "\\\\").replace("\"", "\\\"").replace("'", "\\'")
50
+ # 替换问题
51
+ modified_content = re.sub(
52
+ pattern,
53
+ f'question = "{escaped_question}"',
54
+ content
55
+ )
56
+ question_replaced = True
57
+ break
58
+
59
+ if not question_replaced:
60
+ # 如果没有找到question变量,尝试在main函数前插入
61
+ if has_main:
62
+ # 在main函数前插入question变量
63
+ main_match = re.search(r'def\s+main\s*\(\s*\)\s*:', content)
64
+ if main_match:
65
+ insert_pos = main_match.start()
66
+ # 转义问题中的特殊字符
67
+ escaped_question = question.replace("\\", "\\\\").replace("\"", "\\\"").replace("'", "\\'")
68
+ modified_content = content[:insert_pos] + f'\n# 用户输入的问题\nquestion = "{escaped_question}"\n\n' + content[insert_pos:]
69
+ question_replaced = True
70
+
71
+ if not question_replaced:
72
+ # 如果仍然无法替换,尝试在文件末尾添加代码来使用用户的问题
73
+ modified_content = content + f'\n\n# 用户输入的问题\nquestion = "{question}"\n\n'
74
+ modified_content += '''
75
+ # 如果脚本中有construct_society函数,使用用户问题运行
76
+ if "construct_society" in globals():
77
+ try:
78
+ society = construct_society(question)
79
+ from utils import run_society
80
+ answer, chat_history, token_count = run_society(society)
81
+ print(f"Answer: {answer}")
82
+ except Exception as e:
83
+ print(f"运行时出错: {e}")
84
+ import traceback
85
+ traceback.print_exc()
86
+ '''
87
+
88
+ # 执行修改后的脚本
89
  try:
90
  # 将脚本目录添加到sys.path
91
  script_dir = script_path.parent
92
  if str(script_dir) not in sys.path:
93
  sys.path.insert(0, str(script_dir))
94
 
 
 
 
 
 
 
 
95
  if has_main:
96
  # 如果有main函数,加载模块并调用main
97
+ # 创建临时文件
98
+ temp_script_path = script_path.with_name(f"temp_{script_path.name}")
99
+ with open(temp_script_path, "w", encoding="utf-8") as f:
100
+ f.write(modified_content)
101
 
102
+ try:
103
+ # 加载临时模块
104
+ module_name = f"temp_{script_path.stem}"
105
+ module = load_module_from_path(module_name, temp_script_path)
106
+
107
+ # 调用main函数
108
+ if hasattr(module, "main"):
109
+ module.main()
110
+ else:
111
+ print(f"错误: 脚本 {script_path} 中没有main函数")
112
+ sys.exit(1)
113
+ finally:
114
+ # 删除临时文件
115
+ if temp_script_path.exists():
116
+ temp_script_path.unlink()
117
  else:
118
+ # 如果没有main函数,直接执行修改后的脚本
 
 
 
 
 
 
 
 
119
  exec(modified_content, {"__file__": str(script_path)})
120
 
121
  except Exception as e: