Wendong-Fan commited on
Commit
d2960de
·
1 Parent(s): a1b6884

update wendong

Browse files
Files changed (1) hide show
  1. owl/webapp_zh.py +122 -296
owl/webapp_zh.py CHANGED
@@ -16,12 +16,13 @@ import signal
16
  import sys
17
  import subprocess
18
  import platform
 
19
 
20
  os.environ['PYTHONIOENCODING'] = 'utf-8'
21
 
22
  # 配置日志系统
23
  def setup_logging():
24
- """配置日志系统,将日志输出到文件和内存队列"""
25
  # 创建logs目录(如果不存在)
26
  logs_dir = os.path.join(os.path.dirname(__file__), "logs")
27
  os.makedirs(logs_dir, exist_ok=True)
@@ -43,14 +44,18 @@ def setup_logging():
43
  file_handler = logging.FileHandler(log_file, encoding='utf-8', mode='a')
44
  file_handler.setLevel(logging.INFO)
45
 
 
 
 
 
46
  # 创建格式化器
47
  formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
48
  file_handler.setFormatter(formatter)
 
49
 
50
  # 添加处理器到根日志记录器
51
  root_logger.addHandler(file_handler)
52
-
53
-
54
 
55
  logging.info("日志系统已初始化,日志文件: %s", log_file)
56
  return log_file
@@ -58,10 +63,10 @@ def setup_logging():
58
  # 全局变量
59
  LOG_FILE = None
60
  LOG_QUEUE = queue.Queue()
 
61
  STOP_LOG_THREAD = threading.Event()
62
  CURRENT_PROCESS = None # 用于跟踪当前运行的进程
63
  STOP_REQUESTED = threading.Event() # 用于标记是否请求停止
64
- CONVERSATION_UPDATE_QUEUE = queue.Queue() # 用于实时更新对话历史的队列
65
 
66
  # 日志读取和更新函数
67
  def log_reader_thread(log_file):
@@ -75,26 +80,29 @@ def log_reader_thread(log_file):
75
  line = f.readline()
76
  if line:
77
  LOG_QUEUE.put(line)
 
78
  else:
79
  # 没有新行,等待一小段时间
80
  time.sleep(0.1)
81
  except Exception as e:
82
  logging.error(f"日志读取线程出错: {str(e)}")
83
 
84
- def get_latest_logs(max_lines=100):
85
  """从队列中获取最新的日志行,如果队列为空则直接从文件读取
86
 
87
  Args:
88
  max_lines: 最大返回行数
 
89
 
90
  Returns:
91
  str: 日志内容
92
  """
93
  logs = []
 
94
  try:
95
  # 尝试从队列中获取所有可用的日志行
96
- while not LOG_QUEUE.empty() and len(logs) < max_lines:
97
- logs.append(LOG_QUEUE.get_nowait())
98
  except queue.Empty:
99
  pass
100
 
@@ -118,7 +126,34 @@ def get_latest_logs(max_lines=100):
118
  if not logs:
119
  return "暂无日志记录或日志系统未正确初始化。"
120
 
121
- return "".join(logs)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
 
123
  # Dictionary containing module descriptions
124
  MODULE_DESCRIPTIONS = {
@@ -209,28 +244,7 @@ FIRECRAWL_API_KEY=""
209
  #FIRECRAWL_API_URL="https://api.firecrawl.dev"
210
  """
211
 
212
- def format_chat_history(chat_history: List[Dict[str, str]]) -> List[List[str]]:
213
- """将聊天历史格式化为Gradio聊天组件可接受的格式
214
-
215
- Args:
216
- chat_history: 原始聊天历史
217
-
218
- Returns:
219
- List[List[str]]: 格式化后的聊天历史
220
- """
221
- formatted_history = []
222
- for message in chat_history:
223
- user_msg = message.get("user", "")
224
- assistant_msg = message.get("assistant", "")
225
-
226
- if user_msg:
227
- formatted_history.append([user_msg, None])
228
- if assistant_msg and formatted_history:
229
- formatted_history[-1][1] = assistant_msg
230
- elif assistant_msg:
231
- formatted_history.append([None, assistant_msg])
232
-
233
- return formatted_history
234
 
235
  def validate_input(question: str) -> bool:
236
  """验证用户输入是否有效
@@ -246,7 +260,7 @@ def validate_input(question: str) -> bool:
246
  return False
247
  return True
248
 
249
- def run_owl(question: str, example_module: str) -> Tuple[str, List[List[str]], str, str]:
250
  """运行OWL系统并返回结果
251
 
252
  Args:
@@ -254,23 +268,15 @@ def run_owl(question: str, example_module: str) -> Tuple[str, List[List[str]], s
254
  example_module: 要导入的示例模块名(如 "run_terminal_zh" 或 "run_deep")
255
 
256
  Returns:
257
- Tuple[...]: 回答、聊天历史、令牌计数、状态
258
  """
259
- global CURRENT_PROCESS, CONVERSATION_UPDATE_QUEUE
260
-
261
- # 清空对话更新队列
262
- while not CONVERSATION_UPDATE_QUEUE.empty():
263
- try:
264
- CONVERSATION_UPDATE_QUEUE.get_nowait()
265
- except queue.Empty:
266
- break
267
 
268
  # 验证输入
269
  if not validate_input(question):
270
  logging.warning("用户提交了无效的输入")
271
  return (
272
  "请输入有效的问题",
273
- [],
274
  "0",
275
  "❌ 错误: 输入无效"
276
  )
@@ -285,7 +291,6 @@ def run_owl(question: str, example_module: str) -> Tuple[str, List[List[str]], s
285
  logging.error(f"用户选择了不支持的模块: {example_module}")
286
  return (
287
  f"所选模块 '{example_module}' 不受支持",
288
- [],
289
  "0",
290
  f"❌ 错误: 不支持的模块"
291
  )
@@ -299,7 +304,6 @@ def run_owl(question: str, example_module: str) -> Tuple[str, List[List[str]], s
299
  logging.error(f"无法导入模块 {module_path}: {str(ie)}")
300
  return (
301
  f"无法导入模块: {module_path}",
302
- [],
303
  "0",
304
  f"❌ 错误: 模块 {example_module} 不存在或无法加载 - {str(ie)}"
305
  )
@@ -307,7 +311,6 @@ def run_owl(question: str, example_module: str) -> Tuple[str, List[List[str]], s
307
  logging.error(f"导入模块 {module_path} 时发生错误: {str(e)}")
308
  return (
309
  f"导入模块时发生错误: {module_path}",
310
- [],
311
  "0",
312
  f"❌ 错误: {str(e)}"
313
  )
@@ -317,7 +320,6 @@ def run_owl(question: str, example_module: str) -> Tuple[str, List[List[str]], s
317
  logging.error(f"模块 {module_path} 中未找到 construct_society 函数")
318
  return (
319
  f"模块 {module_path} 中未找到 construct_society 函数",
320
- [],
321
  "0",
322
  f"❌ 错误: 模块接口不兼容"
323
  )
@@ -327,25 +329,11 @@ def run_owl(question: str, example_module: str) -> Tuple[str, List[List[str]], s
327
  logging.info("正在构建社会模拟...")
328
  society = module.construct_society(question)
329
 
330
- # 添加对话更新回调
331
- if hasattr(society, 'set_message_callback'):
332
- def message_callback(role, content):
333
- """对话消息回调函数"""
334
- try:
335
- # 将消息添加到队列
336
- CONVERSATION_UPDATE_QUEUE.put((role, content))
337
- logging.info(f"对话回调: {role} - {content[:30]}...")
338
- except Exception as e:
339
- logging.error(f"对话回调处理错误: {str(e)}")
340
-
341
- # 设置回调
342
- society.set_message_callback(message_callback)
343
- logging.info("已设置对话更新回调")
344
  except Exception as e:
345
  logging.error(f"构建社会模拟时发生错误: {str(e)}")
346
  return (
347
  f"构建社会模拟时发生错误: {str(e)}",
348
- [],
349
  "0",
350
  f"❌ 错误: 构建失败 - {str(e)}"
351
  )
@@ -359,18 +347,11 @@ def run_owl(question: str, example_module: str) -> Tuple[str, List[List[str]], s
359
  logging.error(f"运行社会模拟时发生错误: {str(e)}")
360
  return (
361
  f"运行社会模拟时发生错误: {str(e)}",
362
- [],
363
  "0",
364
  f"❌ 错误: 运行失败 - {str(e)}"
365
  )
366
 
367
- # 格式化聊天历史
368
- try:
369
- formatted_chat_history = format_chat_history(chat_history)
370
- except Exception as e:
371
- # 如果格式化失败,返回空历史记录但继续处理
372
- logging.error(f"格式化聊天历史时发生错误: {str(e)}")
373
- formatted_chat_history = []
374
 
375
  # 安全地获取令牌计数
376
  if not isinstance(token_info, dict):
@@ -384,7 +365,6 @@ def run_owl(question: str, example_module: str) -> Tuple[str, List[List[str]], s
384
 
385
  return (
386
  answer,
387
- formatted_chat_history,
388
  f"完成令牌: {completion_tokens:,} | 提示令牌: {prompt_tokens:,} | 总计: {total_tokens:,}",
389
  "✅ 成功完成"
390
  )
@@ -393,7 +373,6 @@ def run_owl(question: str, example_module: str) -> Tuple[str, List[List[str]], s
393
  logging.error(f"处理问题时发生未捕获的错误: {str(e)}")
394
  return (
395
  f"发生错误: {str(e)}",
396
- [],
397
  "0",
398
  f"❌ 错误: {str(e)}"
399
  )
@@ -512,6 +491,10 @@ def create_ui():
512
  """获取最新日志并返回给前端显示"""
513
  return get_latest_logs(100)
514
 
 
 
 
 
515
  def clear_log_file():
516
  """清空日志文件内容"""
517
  try:
@@ -525,6 +508,12 @@ def create_ui():
525
  LOG_QUEUE.get_nowait()
526
  except queue.Empty:
527
  break
 
 
 
 
 
 
528
  return "日志文件已清空"
529
  else:
530
  return "日志文件不存在或未设置"
@@ -534,181 +523,43 @@ def create_ui():
534
 
535
  # 创建一个实时日志更新函数
536
  def process_with_live_logs(question, module_name):
537
- """处理问题并实时更新日志和对话历史"""
538
- global CURRENT_PROCESS, CONVERSATION_UPDATE_QUEUE
539
 
540
  # 创建一个后台线程来处理问题
541
  result_queue = queue.Queue()
542
- # 创建一个队列用于实时更新对话历史
543
- chat_history_queue = queue.Queue()
544
-
545
- # 初始化对话历史,添加用户问题
546
- current_chat_history = [[question, None]]
547
-
548
- # 创建一个函数来监听日志中的对话更新
549
- def monitor_logs_for_chat_updates():
550
- """监控日志中的对话更新并将其添加到队列中"""
551
- try:
552
- # 创建一个单独的日志队列用于监控对话
553
- chat_log_queue = queue.Queue()
554
-
555
- # 打开日志文件进行监控
556
- with open(LOG_FILE, 'r', encoding='utf-8') as f:
557
- # 移动到文件末尾
558
- f.seek(0, 2)
559
-
560
- while True:
561
- line = f.readline()
562
- if line:
563
- # 尝试多种模式来检测对话信息
564
-
565
- # 模式1: 检查标准的Agent对话格式
566
- if "Agent:" in line and ":" in line.split("Agent:")[1]:
567
- try:
568
- agent_part = line.split("Agent:")[1].strip()
569
- agent_name = agent_part.split(":")[0].strip()
570
- message = ":".join(agent_part.split(":")[1:]).strip()
571
-
572
- # 将对话信息添加到队列
573
- chat_history_queue.put((agent_name, message))
574
- logging.info(f"检测到对话更新(模式1): {agent_name} - {message[:30]}...")
575
- except Exception as e:
576
- logging.error(f"解析对话信息时出错(模式1): {str(e)}")
577
-
578
- # 模式2: 检查包含角色名和消息的格式
579
- elif " - " in line and any(role in line for role in ["用户", "助手", "系统", "User", "Assistant", "System"]):
580
- try:
581
- parts = line.split(" - ", 1)
582
- if len(parts) >= 2:
583
- # 尝试提取角色名
584
- log_prefix = parts[0]
585
- message_part = parts[1]
586
-
587
- # 尝试从日志前缀中提取角色名
588
- role_candidates = ["用户", "助手", "系统", "User", "Assistant", "System"]
589
- agent_name = None
590
- for role in role_candidates:
591
- if role in log_prefix:
592
- agent_name = role
593
- break
594
-
595
- if agent_name and message_part.strip():
596
- chat_history_queue.put((agent_name, message_part.strip()))
597
- logging.info(f"检测到对话更新(模式2): {agent_name} - {message_part[:30]}...")
598
- except Exception as e:
599
- logging.error(f"解析对话信息时出错(模式2): {str(e)}")
600
-
601
- # 模式3: 检查JSON格式的对话记录
602
- elif '"role"' in line and '"content"' in line and ('"user"' in line.lower() or '"assistant"' in line.lower() or '"system"' in line.lower()):
603
- try:
604
- # 尝试提取JSON部分
605
- json_start = line.find("{")
606
- json_end = line.rfind("}")
607
-
608
- if json_start >= 0 and json_end > json_start:
609
- json_str = line[json_start:json_end+1]
610
- message_data = json.loads(json_str)
611
-
612
- if "role" in message_data and "content" in message_data:
613
- agent_name = message_data["role"].capitalize()
614
- message = message_data["content"]
615
-
616
- chat_history_queue.put((agent_name, message))
617
- logging.info(f"检测到对话更新(模式3): {agent_name} - {message[:30]}...")
618
- except Exception as e:
619
- # JSON解析错误是常见的,所以这里不记录为错误
620
- pass
621
- else:
622
- # 没有新行,等待一小段时间
623
- time.sleep(0.1)
624
- except Exception as e:
625
- logging.error(f"对话监控线程出错: {str(e)}")
626
 
627
  def process_in_background():
628
  try:
629
  result = run_owl(question, module_name)
630
  result_queue.put(result)
631
  except Exception as e:
632
- result_queue.put((f"发生错误: {str(e)}", [], "0", f"❌ 错误: {str(e)}"))
633
-
634
- # 启动对话监控线程
635
- chat_monitor_thread = threading.Thread(target=monitor_logs_for_chat_updates, daemon=True)
636
- chat_monitor_thread.start()
637
 
638
  # 启动后台处理线程
639
  bg_thread = threading.Thread(target=process_in_background)
640
  CURRENT_PROCESS = bg_thread # 记录当前进程
641
  bg_thread.start()
642
 
643
- # 在等待处理完成的同时,每秒更新一次日志和对话历史
644
  while bg_thread.is_alive():
645
- # 检查是否有新的对话更新(从日志解析)
646
- updated = False
647
- while not chat_history_queue.empty():
648
- try:
649
- agent_name, message = chat_history_queue.get_nowait()
650
-
651
- # 如果是新的对话,添加到历史记录
652
- if not current_chat_history or current_chat_history[-1][1] is not None:
653
- # 添加新的对话条目
654
- current_chat_history.append([f"[{agent_name}]", message])
655
- else:
656
- # 更新最后一个对话的回复
657
- current_chat_history[-1][1] = message
658
-
659
- updated = True
660
- except queue.Empty:
661
- break
662
-
663
- # 检查是否有新的对话更新(从回调机制)
664
- while not CONVERSATION_UPDATE_QUEUE.empty():
665
- try:
666
- role, content = CONVERSATION_UPDATE_QUEUE.get_nowait()
667
-
668
- # 格式化角色名称
669
- if role.lower() == "user":
670
- role_display = "用户"
671
- elif role.lower() == "assistant":
672
- role_display = "助手"
673
- else:
674
- role_display = role
675
-
676
- # 如果是新的对话,添加到历史记录
677
- if not current_chat_history or current_chat_history[-1][1] is not None:
678
- # 添加新的对话条目
679
- current_chat_history.append([f"[{role_display}]", content])
680
- else:
681
- # 更新最后一个对话的回复
682
- current_chat_history[-1][1] = content
683
-
684
- updated = True
685
- logging.info(f"从回调更新对话: {role_display} - {content[:30]}...")
686
- except queue.Empty:
687
- break
688
-
689
  # 更新日志显示
690
  logs = get_latest_logs(100)
 
691
 
692
- # 如果有更新或者每秒都要更新,则yield新状态
693
- if updated or True: # 始终更新,可以根据需要调整
694
- yield None, current_chat_history, None, "<span class='status-indicator status-running'></span> 处理中...", logs
695
 
696
  time.sleep(1)
697
 
698
  # 处理完成,获取结果
699
  if not result_queue.empty():
700
  result = result_queue.get()
701
- answer, chat_history, token_count, status = result
702
-
703
- # 如果有完整的聊天历史,使用它替换我们的临时历史
704
- if chat_history and len(chat_history) > 0:
705
- # 但首先确保用户问题已包含在内
706
- if not any(item[0] == question for item in chat_history):
707
- chat_history.insert(0, [question, None])
708
- current_chat_history = chat_history
709
 
710
  # 最后一次更新日志
711
  logs = get_latest_logs(100)
 
712
 
713
  # 根据状态设置不同的指示器
714
  if "错误" in status:
@@ -716,10 +567,11 @@ def create_ui():
716
  else:
717
  status_with_indicator = f"<span class='status-indicator status-success'></span> {status}"
718
 
719
- yield answer, current_chat_history, token_count, status_with_indicator, logs
720
  else:
721
  logs = get_latest_logs(100)
722
- yield "操作未完成", current_chat_history, "0", "<span class='status-indicator status-error'></span> 已终止", logs
 
723
 
724
  with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue")) as app:
725
  gr.Markdown(
@@ -741,28 +593,7 @@ def create_ui():
741
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
742
  }
743
 
744
- /* 用户消息样式 */
745
- .chat-container .user-message {
746
- background-color: #e6f7ff;
747
- border-radius: 18px 18px 0 18px;
748
- padding: 10px 15px;
749
- margin: 5px 0;
750
- }
751
-
752
- /* 助手消息样式 */
753
- .chat-container .assistant-message {
754
- background-color: #f0f0f0;
755
- border-radius: 18px 18px 18px 0;
756
- padding: 10px 15px;
757
- margin: 5px 0;
758
- }
759
-
760
- /* 角色名称样式 */
761
- .chat-container .role-name {
762
- font-weight: bold;
763
- margin-bottom: 5px;
764
- }
765
-
766
  /* 改进标签页样式 */
767
  .tabs .tab-nav {
768
  background-color: #f5f5f5;
@@ -815,26 +646,7 @@ def create_ui():
815
  line-height: 1.4;
816
  }
817
 
818
- /* 聊天头像样式 */
819
- .chat-container .avatar {
820
- display: flex !important;
821
- align-items: center;
822
- justify-content: center;
823
- width: 40px !important;
824
- height: 40px !important;
825
- border-radius: 50%;
826
- font-size: 20px;
827
- margin-right: 10px;
828
- }
829
-
830
- .chat-container .avatar.user {
831
- background-color: #e6f7ff;
832
- }
833
-
834
- .chat-container .avatar.assistant {
835
- background-color: #f0f0f0;
836
- }
837
-
838
  @keyframes pulse {
839
  0% { opacity: 1; }
840
  50% { opacity: 0.5; }
@@ -885,7 +697,29 @@ def create_ui():
885
 
886
 
887
 
888
- with gr.Tabs():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
889
  with gr.TabItem("系统日志"):
890
  # 添加日志显示区域
891
  log_display = gr.Textbox(
@@ -914,38 +748,7 @@ def create_ui():
914
  elem_classes="answer-box"
915
  )
916
 
917
- with gr.TabItem("对话历史", id="chat-history-tab"):
918
- chat_output = gr.Chatbot(
919
- label="完整对话记录",
920
- elem_classes="chat-container",
921
- height=500,
922
-
923
- bubble_full_width=False, # 气泡不占满宽度
924
- show_copy_button=True # 显示复制按钮
925
- )
926
-
927
- # 添加自动滚动到底部的JavaScript
928
- gr.HTML("""
929
- <script>
930
- // 自动滚动聊天记录到底部
931
- function scrollChatToBottom() {
932
- const chatContainer = document.querySelector('.chat-container .chatbot');
933
- if (chatContainer) {
934
- chatContainer.scrollTop = chatContainer.scrollHeight;
935
- }
936
- }
937
-
938
- // 每秒检查并滚动
939
- setInterval(scrollChatToBottom, 1000);
940
-
941
- // 监听标签页切换,当切换到对话历史标签时滚动到底部
942
- document.addEventListener('click', function(e) {
943
- if (e.target && e.target.closest('[id="chat-history-tab"]')) {
944
- setTimeout(scrollChatToBottom, 100);
945
- }
946
- });
947
- </script>
948
- """)
949
 
950
 
951
 
@@ -1047,7 +850,7 @@ def create_ui():
1047
  # 示例问题
1048
  examples = [
1049
  "打开百度搜索,总结一下camel-ai的camel框架的github star、fork数目等,并把数字用plot包写成python文件保存到本地,用本地终端执行python文件显示图出来给我",
1050
- "请分析GitHub上CAMEL-AI项目的最新统计数据。找出该项目的星标数量、贡献者数量和最近的活跃度。",
1051
  "浏览亚马逊并找出一款对程序员有吸引力的产品。请提供产品名称和价格",
1052
  "写一个hello world的python文件,保存到本地",
1053
 
@@ -1075,7 +878,7 @@ def create_ui():
1075
  run_button.click(
1076
  fn=process_with_live_logs,
1077
  inputs=[question_input, module_dropdown],
1078
- outputs=[answer_output, chat_output, token_count_output, status_output, log_display]
1079
  )
1080
 
1081
  # 模块选择更新描述
@@ -1091,11 +894,21 @@ def create_ui():
1091
  outputs=[log_display]
1092
  )
1093
 
 
 
 
 
 
1094
  clear_logs_button.click(
1095
  fn=clear_log_file,
1096
  outputs=[log_display]
1097
  )
1098
 
 
 
 
 
 
1099
  # 自动刷新控制
1100
  def toggle_auto_refresh(enabled):
1101
  if enabled:
@@ -1109,6 +922,12 @@ def create_ui():
1109
  outputs=[log_display]
1110
  )
1111
 
 
 
 
 
 
 
1112
  # 设置自动刷新(默认每3秒刷新一次)
1113
  if auto_refresh_checkbox.value:
1114
  app.load(
@@ -1116,6 +935,13 @@ def create_ui():
1116
  outputs=[log_display],
1117
  every=2
1118
  )
 
 
 
 
 
 
 
1119
 
1120
  return app
1121
 
 
16
  import sys
17
  import subprocess
18
  import platform
19
+ import re
20
 
21
  os.environ['PYTHONIOENCODING'] = 'utf-8'
22
 
23
  # 配置日志系统
24
  def setup_logging():
25
+ """配置日志系统,将日志输出到文件和内存队列以及控制台"""
26
  # 创建logs目录(如果不存在)
27
  logs_dir = os.path.join(os.path.dirname(__file__), "logs")
28
  os.makedirs(logs_dir, exist_ok=True)
 
44
  file_handler = logging.FileHandler(log_file, encoding='utf-8', mode='a')
45
  file_handler.setLevel(logging.INFO)
46
 
47
+ # 创建控制台处理器
48
+ console_handler = logging.StreamHandler()
49
+ console_handler.setLevel(logging.INFO)
50
+
51
  # 创建格式化器
52
  formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
53
  file_handler.setFormatter(formatter)
54
+ console_handler.setFormatter(formatter)
55
 
56
  # 添加处理器到根日志记录器
57
  root_logger.addHandler(file_handler)
58
+ root_logger.addHandler(console_handler)
 
59
 
60
  logging.info("日志系统已初始化,日志文件: %s", log_file)
61
  return log_file
 
63
  # 全局变量
64
  LOG_FILE = None
65
  LOG_QUEUE = queue.Queue()
66
+ LOG_QUEUE2 = queue.Queue() # 对话记录的队列
67
  STOP_LOG_THREAD = threading.Event()
68
  CURRENT_PROCESS = None # 用于跟踪当前运行的进程
69
  STOP_REQUESTED = threading.Event() # 用于标记是否请求停止
 
70
 
71
  # 日志读取和更新函数
72
  def log_reader_thread(log_file):
 
80
  line = f.readline()
81
  if line:
82
  LOG_QUEUE.put(line)
83
+ LOG_QUEUE2.put(line) # 同时添加到第二个队列
84
  else:
85
  # 没有新行,等待一小段时间
86
  time.sleep(0.1)
87
  except Exception as e:
88
  logging.error(f"日志读取线程出错: {str(e)}")
89
 
90
+ def get_latest_logs(max_lines=100, queue_source=None):
91
  """从队列中获取最新的日志行,如果队列为空则直接从文件读取
92
 
93
  Args:
94
  max_lines: 最大返回行数
95
+ queue_source: 指定使用哪个队列,默认为LOG_QUEUE
96
 
97
  Returns:
98
  str: 日志内容
99
  """
100
  logs = []
101
+ log_queue = queue_source if queue_source else LOG_QUEUE
102
  try:
103
  # 尝试从队列中获取所有可用的日志行
104
+ while not log_queue.empty() and len(logs) < max_lines:
105
+ logs.append(log_queue.get_nowait())
106
  except queue.Empty:
107
  pass
108
 
 
126
  if not logs:
127
  return "暂无日志记录或日志系统未正确初始化。"
128
 
129
+ # 格式化日志输出,确保每个日志条目有适当的换行和分隔
130
+ formatted_logs = []
131
+ for log in logs:
132
+ # 移除开头和结尾的多余空白字符
133
+ log = log.strip()
134
+
135
+ # 处理包含JSON或代码片段的日志,确保它们有正确的换行和缩进
136
+ if '"]"\n}' in log or '\n}\n\n' in log:
137
+ # 替换不合理的换行为更清晰的格式
138
+ log = log.replace('"]"\n}', '"]" }').replace('\n}\n\n', ' }\n')
139
+
140
+ # 检测日期时间格式的开头,这通常表示一个新的日志条目
141
+ # 例如:2025-03-14 18:49:31,008 - httpx - INFO
142
+ if re.match(r'^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2},\d{3}', log):
143
+ # 在新的日志条目前添加一个空行,使日志更易读
144
+ formatted_logs.append('\n')
145
+
146
+ # 确保每个日志条目以换行符结束
147
+ if not log.endswith('\n'):
148
+ log += '\n'
149
+
150
+ formatted_logs.append(log)
151
+
152
+ # 移除第一个可能的额外空行
153
+ if formatted_logs and formatted_logs[0] == '\n':
154
+ formatted_logs.pop(0)
155
+
156
+ return "".join(formatted_logs)
157
 
158
  # Dictionary containing module descriptions
159
  MODULE_DESCRIPTIONS = {
 
244
  #FIRECRAWL_API_URL="https://api.firecrawl.dev"
245
  """
246
 
247
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
248
 
249
  def validate_input(question: str) -> bool:
250
  """验证用户输入是否有效
 
260
  return False
261
  return True
262
 
263
+ def run_owl(question: str, example_module: str) -> Tuple[str, str, str]:
264
  """运行OWL系统并返回结果
265
 
266
  Args:
 
268
  example_module: 要导入的示例模块名(如 "run_terminal_zh" 或 "run_deep")
269
 
270
  Returns:
271
+ Tuple[...]: 回答、令牌计数、状态
272
  """
273
+ global CURRENT_PROCESS
 
 
 
 
 
 
 
274
 
275
  # 验证输入
276
  if not validate_input(question):
277
  logging.warning("用户提交了无效的输入")
278
  return (
279
  "请输入有效的问题",
 
280
  "0",
281
  "❌ 错误: 输入无效"
282
  )
 
291
  logging.error(f"用户选择了不支持的模块: {example_module}")
292
  return (
293
  f"所选模块 '{example_module}' 不受支持",
 
294
  "0",
295
  f"❌ 错误: 不支持的模块"
296
  )
 
304
  logging.error(f"无法导入模块 {module_path}: {str(ie)}")
305
  return (
306
  f"无法导入模块: {module_path}",
 
307
  "0",
308
  f"❌ 错误: 模块 {example_module} 不存在或无法加载 - {str(ie)}"
309
  )
 
311
  logging.error(f"导入模块 {module_path} 时发生错误: {str(e)}")
312
  return (
313
  f"导入模块时发生错误: {module_path}",
 
314
  "0",
315
  f"❌ 错误: {str(e)}"
316
  )
 
320
  logging.error(f"模块 {module_path} 中未找到 construct_society 函数")
321
  return (
322
  f"模块 {module_path} 中未找到 construct_society 函数",
 
323
  "0",
324
  f"❌ 错误: 模块接口不兼容"
325
  )
 
329
  logging.info("正在构建社会模拟...")
330
  society = module.construct_society(question)
331
 
332
+
 
 
 
 
 
 
 
 
 
 
 
 
 
333
  except Exception as e:
334
  logging.error(f"构建社会模拟时发生错误: {str(e)}")
335
  return (
336
  f"构建社会模拟时发生错误: {str(e)}",
 
337
  "0",
338
  f"❌ 错误: 构建失败 - {str(e)}"
339
  )
 
347
  logging.error(f"运行社会模拟时发生错误: {str(e)}")
348
  return (
349
  f"运行社会模拟时发生错误: {str(e)}",
 
350
  "0",
351
  f"❌ 错误: 运行失败 - {str(e)}"
352
  )
353
 
354
+
 
 
 
 
 
 
355
 
356
  # 安全地获取令牌计数
357
  if not isinstance(token_info, dict):
 
365
 
366
  return (
367
  answer,
 
368
  f"完成令牌: {completion_tokens:,} | 提示令牌: {prompt_tokens:,} | 总计: {total_tokens:,}",
369
  "✅ 成功完成"
370
  )
 
373
  logging.error(f"处理问题时发生未捕获的错误: {str(e)}")
374
  return (
375
  f"发生错误: {str(e)}",
 
376
  "0",
377
  f"❌ 错误: {str(e)}"
378
  )
 
491
  """获取最新日志并返回给前端显示"""
492
  return get_latest_logs(100)
493
 
494
+ def update_logs2():
495
+ """获取最新对话记录并返回给前端显示"""
496
+ return get_latest_logs(100, LOG_QUEUE2)
497
+
498
  def clear_log_file():
499
  """清空日志文件内容"""
500
  try:
 
508
  LOG_QUEUE.get_nowait()
509
  except queue.Empty:
510
  break
511
+ # 清空第二个日志队列
512
+ while not LOG_QUEUE2.empty():
513
+ try:
514
+ LOG_QUEUE2.get_nowait()
515
+ except queue.Empty:
516
+ break
517
  return "日志文件已清空"
518
  else:
519
  return "日志文件不存在或未设置"
 
523
 
524
  # 创建一个实时日志更新函数
525
  def process_with_live_logs(question, module_name):
526
+ """处理问题并实时更新日志"""
527
+ global CURRENT_PROCESS
528
 
529
  # 创建一个后台线程来处理问题
530
  result_queue = queue.Queue()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
531
 
532
  def process_in_background():
533
  try:
534
  result = run_owl(question, module_name)
535
  result_queue.put(result)
536
  except Exception as e:
537
+ result_queue.put((f"发生错误: {str(e)}", "0", f"❌ 错误: {str(e)}"))
 
 
 
 
538
 
539
  # 启动后台处理线程
540
  bg_thread = threading.Thread(target=process_in_background)
541
  CURRENT_PROCESS = bg_thread # 记录当前进程
542
  bg_thread.start()
543
 
544
+ # 在等待处理完成的同时,每秒更新一次日志
545
  while bg_thread.is_alive():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
546
  # 更新日志显示
547
  logs = get_latest_logs(100)
548
+ logs2 = get_latest_logs(100, LOG_QUEUE2)
549
 
550
+ # 始终更新状态
551
+ yield None, "0", "<span class='status-indicator status-running'></span> 处理中...", logs, logs2
 
552
 
553
  time.sleep(1)
554
 
555
  # 处理完成,获取结果
556
  if not result_queue.empty():
557
  result = result_queue.get()
558
+ answer, token_count, status = result
 
 
 
 
 
 
 
559
 
560
  # 最后一次更新日志
561
  logs = get_latest_logs(100)
562
+ logs2 = get_latest_logs(100, LOG_QUEUE2)
563
 
564
  # 根据状态设置不同的指示器
565
  if "错误" in status:
 
567
  else:
568
  status_with_indicator = f"<span class='status-indicator status-success'></span> {status}"
569
 
570
+ yield answer, token_count, status_with_indicator, logs, logs2
571
  else:
572
  logs = get_latest_logs(100)
573
+ logs2 = get_latest_logs(100, LOG_QUEUE2)
574
+ yield "操作未完成", "0", "<span class='status-indicator status-error'></span> 已终止", logs, logs2
575
 
576
  with gr.Blocks(theme=gr.themes.Soft(primary_hue="blue")) as app:
577
  gr.Markdown(
 
593
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
594
  }
595
 
596
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
597
  /* 改进标签页样式 */
598
  .tabs .tab-nav {
599
  background-color: #f5f5f5;
 
646
  line-height: 1.4;
647
  }
648
 
649
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
650
  @keyframes pulse {
651
  0% { opacity: 1; }
652
  50% { opacity: 0.5; }
 
697
 
698
 
699
 
700
+ with gr.Tabs(selected=1): # 设置对话记录为默认选中的标签页
701
+ with gr.TabItem("对话记录"):
702
+ # 添加对话记录显示区域
703
+ log_display2 = gr.Textbox(
704
+ label="对话记录",
705
+ lines=25,
706
+ max_lines=100,
707
+ interactive=False,
708
+ autoscroll=True,
709
+ show_copy_button=True,
710
+ elem_classes="log-display",
711
+ container=True
712
+ )
713
+
714
+ with gr.Row():
715
+ refresh_logs_button2 = gr.Button("刷新记录")
716
+ auto_refresh_checkbox2 = gr.Checkbox(
717
+ label="自动刷新",
718
+ value=True,
719
+ interactive=True
720
+ )
721
+ clear_logs_button2 = gr.Button("清空记录", variant="secondary")
722
+
723
  with gr.TabItem("系统日志"):
724
  # 添加日志显示区域
725
  log_display = gr.Textbox(
 
748
  elem_classes="answer-box"
749
  )
750
 
751
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
752
 
753
 
754
 
 
850
  # 示例问题
851
  examples = [
852
  "打开百度搜索,总结一下camel-ai的camel框架的github star、fork数目等,并把数字用plot包写成python文件保存到本地,用本地终端执行python文件显示图出来给我",
853
+ "请分析GitHub上CAMEL-AI项目的最新统计数据。找出该项目的星标数量、贡献者名称,把内容整理成一个markdown文件保存到本地",
854
  "浏览亚马逊并找出一款对程序员有吸引力的产品。请提供产品名称和价格",
855
  "写一个hello world的python文件,保存到本地",
856
 
 
878
  run_button.click(
879
  fn=process_with_live_logs,
880
  inputs=[question_input, module_dropdown],
881
+ outputs=[answer_output, token_count_output, status_output, log_display, log_display2]
882
  )
883
 
884
  # 模块选择更新描述
 
894
  outputs=[log_display]
895
  )
896
 
897
+ refresh_logs_button2.click(
898
+ fn=update_logs2,
899
+ outputs=[log_display2]
900
+ )
901
+
902
  clear_logs_button.click(
903
  fn=clear_log_file,
904
  outputs=[log_display]
905
  )
906
 
907
+ clear_logs_button2.click(
908
+ fn=clear_log_file,
909
+ outputs=[log_display2]
910
+ )
911
+
912
  # 自动刷新控制
913
  def toggle_auto_refresh(enabled):
914
  if enabled:
 
922
  outputs=[log_display]
923
  )
924
 
925
+ auto_refresh_checkbox2.change(
926
+ fn=toggle_auto_refresh,
927
+ inputs=[auto_refresh_checkbox2],
928
+ outputs=[log_display2]
929
+ )
930
+
931
  # 设置自动刷新(默认每3秒刷新一次)
932
  if auto_refresh_checkbox.value:
933
  app.load(
 
935
  outputs=[log_display],
936
  every=2
937
  )
938
+
939
+ if auto_refresh_checkbox2.value:
940
+ app.load(
941
+ fn=update_logs2,
942
+ outputs=[log_display2],
943
+ every=2
944
+ )
945
 
946
  return app
947