194130157a commited on
Commit
9165dcf
·
verified ·
1 Parent(s): c5c67e3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +88 -29
app.py CHANGED
@@ -64,7 +64,7 @@ else:
64
  ROOT_BASE = os.getcwd()
65
 
66
  TASKS_ROOT_DIR = os.path.join(ROOT_BASE, "nihaisha_tasks_v14_final")
67
- CORPUS_ROOT_DIR = os.path.join(ROOT_BASE, "nihaisha_corpus_lib")
68
  PERSONAS_FILE = os.path.join(ROOT_BASE, "personas_config.json")
69
 
70
  os.makedirs(TASKS_ROOT_DIR, exist_ok=True)
@@ -81,11 +81,13 @@ class PersonaManager:
81
  "user_template": "【任务】:请根据参考资料总结核心内容。\n【主题】:{topic}"
82
  }
83
  }
84
- self.personas = self.load_personas()
 
 
85
 
86
  def load_personas(self):
 
87
  if not os.path.exists(self.filepath):
88
- self.save_personas(self.default_personas)
89
  return self.default_personas
90
  try:
91
  with open(self.filepath, 'r', encoding='utf-8') as f:
@@ -96,22 +98,27 @@ class PersonaManager:
96
  return self.default_personas
97
 
98
  def save_personas(self, new_data):
 
99
  try:
100
  with open(self.filepath, 'w', encoding='utf-8') as f:
101
  json.dump(new_data, f, indent=4, ensure_ascii=False)
102
- self.personas = new_data
 
 
103
  return True
104
  except Exception as e:
105
  print(f"❌ 保存失败: {e}")
106
  return False
107
 
108
  def get_persona(self, name):
109
- if name not in self.personas and self.personas:
110
- return list(self.personas.values())[0]
111
- return self.personas.get(name)
 
112
 
113
  def get_all_names(self):
114
- return list(self.personas.keys())
 
115
 
116
  persona_manager = PersonaManager()
117
 
@@ -532,31 +539,57 @@ def start_execution(topic_text, persona_name, p_sys, p_user, selected_refs):
532
  threading.Thread(target=worker, args=(tid, topics, persona_name, p_sys, p_user, selected_refs, manual_topic_name)).start()
533
  return f"任务已启动: {tid}", tid
534
 
535
- # --- UI Helper ---
536
- def update_persona_dropdown():
537
- names = persona_manager.get_all_names()
538
- return gr.Dropdown(choices=names, value=names[0] if names else None)
 
 
 
 
 
 
 
 
 
 
539
 
540
  def on_select_persona_config(name):
 
 
541
  p = persona_manager.get_persona(name)
 
 
542
  return p.get("system"), p.get("user_template")
543
 
544
  def save_new_persona(name, sys, user):
545
- if not name: return "❌ 名字不能为空", gr.update()
 
 
 
546
  current = persona_manager.load_personas()
547
  current[name] = {"system": sys, "user_template": user}
 
548
  if persona_manager.save_personas(current):
549
- return f"✅ 角色 '{name}' 已保存", update_persona_dropdown()
 
 
550
  else:
551
- return f"❌ 保存失败", gr.update()
552
 
553
  def delete_persona(name):
554
- if not name: return "❌ 未选择", gr.update()
 
 
 
555
  current = persona_manager.load_personas()
556
  if name in current:
557
  del current[name]
558
  persona_manager.save_personas(current)
559
- return f"🗑️ 已删除", update_persona_dropdown()
 
 
 
560
 
561
  # --- 界面布局 ---
562
 
@@ -571,10 +604,11 @@ with gr.Blocks(title="AI 灵活内容工厂 (V14 Final)") as demo:
571
 
572
  with gr.Tabs():
573
  # --- Tab 1: 创作中心 ---
574
- with gr.Tab("🚀 1. 创作中心"):
575
  with gr.Row():
576
  with gr.Column(scale=1):
577
  gr.Markdown("### 配置与启动")
 
578
  persona_select = gr.Dropdown(label="加载预设角色 (选填)", choices=persona_manager.get_all_names(), interactive=True)
579
  topic_input = gr.Textbox(label="输入主题 (一行一个)", lines=3, placeholder="留空则自动拟题...")
580
 
@@ -591,10 +625,11 @@ with gr.Blocks(title="AI 灵活内容工厂 (V14 Final)") as demo:
591
  start_msg = gr.Label(show_label=False)
592
 
593
  # --- Tab 2: 角色配置 ---
594
- with gr.Tab("🎭 2. 角色配置"):
595
  with gr.Row():
596
  with gr.Column(scale=1):
597
  gr.Markdown("### 角色管理")
 
598
  p_config_select = gr.Dropdown(label="选择角色编辑", choices=persona_manager.get_all_names(), interactive=True)
599
  p_name_input = gr.Textbox(label="新建/重命名", placeholder="角色名")
600
  p_save_btn = gr.Button("💾 保存/新建", variant="primary")
@@ -636,37 +671,61 @@ with gr.Blocks(title="AI 灵活内容工厂 (V14 Final)") as demo:
636
  outputs=task_dd
637
  )
638
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
639
  # 角色配置联动
640
  persona_select.select(on_select_persona_config, persona_select, [p_sys_input, p_user_input])
 
 
641
  p_config_select.select(on_select_persona_config, p_config_select, [p_sys_input, p_user_input])
642
  p_config_select.select(lambda x: x, p_config_select, p_name_input)
643
 
644
- p_save_btn.click(save_new_persona, [p_name_input, p_sys_input, p_user_input], [p_status, p_config_select])
645
- p_save_btn.click(update_persona_dropdown, outputs=persona_select)
646
- p_del_btn.click(delete_persona, p_config_select, [p_status, p_config_select])
647
- p_del_btn.click(update_persona_dropdown, outputs=persona_select)
 
 
 
 
 
 
 
 
 
648
 
649
  analyze_btn.click(analyze_matches, topic_input, [ref_checkboxes, analyze_status, state_all_files])
650
  select_all_btn.click(toggle_select_all, inputs=[ref_checkboxes, state_all_files], outputs=ref_checkboxes)
651
  upload_btn.click(save_to_corpus, files, [corpus_display, upload_res])
652
  clear_btn.click(clear_corpus, outputs=[corpus_display, upload_res])
653
 
654
- # 【关键修复】自动刷新逻辑
655
- # 每2秒从后台读取日志和结果文件路径
656
  timer.tick(
657
  lambda tid: (task_manager.get_task_info(tid)["log"], task_manager.get_task_info(tid)["result"]) if tid else ("", None),
658
  inputs=task_dd,
659
  outputs=[log_box, result_dl]
660
  )
661
 
662
- # 手动刷新按钮
663
  refresh_task.click(lambda: gr.Dropdown(choices=task_manager.list_tasks(), value=task_manager.list_tasks()[0] if task_manager.list_tasks() else None), outputs=task_dd)
664
  task_dd.change(lambda tid: (task_manager.get_task_info(tid)["log"], task_manager.get_task_info(tid)["result"]) if tid else ("", None), task_dd, [log_box, result_dl])
665
 
666
- # 自动加载
667
  demo.load(list_corpus_files, outputs=corpus_display)
668
- demo.load(update_persona_dropdown, outputs=persona_select)
669
- demo.load(update_persona_dropdown, outputs=p_config_select)
 
670
 
671
  if __name__ == "__main__":
672
  demo.queue().launch()
 
64
  ROOT_BASE = os.getcwd()
65
 
66
  TASKS_ROOT_DIR = os.path.join(ROOT_BASE, "nihaisha_tasks_v14_final")
67
+ CORPUS_ROOT_DIR = os.path.join(ROOT_BASE, "nihaisha_corpus_lib")
68
  PERSONAS_FILE = os.path.join(ROOT_BASE, "personas_config.json")
69
 
70
  os.makedirs(TASKS_ROOT_DIR, exist_ok=True)
 
81
  "user_template": "【任务】:请根据参考资料总结核心内容。\n【主题】:{topic}"
82
  }
83
  }
84
+ # 启动时确保文件存在
85
+ if not os.path.exists(self.filepath):
86
+ self.save_personas(self.default_personas)
87
 
88
  def load_personas(self):
89
+ """强制从硬盘读取,不使用内存缓存"""
90
  if not os.path.exists(self.filepath):
 
91
  return self.default_personas
92
  try:
93
  with open(self.filepath, 'r', encoding='utf-8') as f:
 
98
  return self.default_personas
99
 
100
  def save_personas(self, new_data):
101
+ """保存并强制刷入磁盘"""
102
  try:
103
  with open(self.filepath, 'w', encoding='utf-8') as f:
104
  json.dump(new_data, f, indent=4, ensure_ascii=False)
105
+ f.flush()
106
+ os.fsync(f.fileno()) # 物理写入
107
+ time.sleep(0.05) # 微小延迟确保系统文件锁释放
108
  return True
109
  except Exception as e:
110
  print(f"❌ 保存失败: {e}")
111
  return False
112
 
113
  def get_persona(self, name):
114
+ data = self.load_personas()
115
+ if name not in data and data:
116
+ return list(data.values())[0]
117
+ return data.get(name)
118
 
119
  def get_all_names(self):
120
+ data = self.load_personas()
121
+ return list(data.keys())
122
 
123
  persona_manager = PersonaManager()
124
 
 
539
  threading.Thread(target=worker, args=(tid, topics, persona_name, p_sys, p_user, selected_refs, manual_topic_name)).start()
540
  return f"任务已启动: {tid}", tid
541
 
542
+ # --- UI Helper (核心修复部分) ---
543
+
544
+ def global_refresh_dropdowns(target_value=None):
545
+ """
546
+ 通用刷新函数:强制从硬盘读取最新列表,并生成两个gr.update对象。
547
+ """
548
+ all_names = persona_manager.get_all_names()
549
+
550
+ # 确定选中的值:如果指定值在列表里,就选它;否则选第一个;如果列表为空,则None
551
+ final_val = target_value if (target_value and target_value in all_names) else (all_names[0] if all_names else None)
552
+
553
+ # 返回两个更新指令,分别对应 Tab1 和 Tab2 的下拉框
554
+ # 注意:使用 gr.update(choices=..., value=...) 是解决不同步的终极方案
555
+ return gr.update(choices=all_names, value=final_val), gr.update(choices=all_names, value=final_val)
556
 
557
  def on_select_persona_config(name):
558
+ if not name:
559
+ return "", ""
560
  p = persona_manager.get_persona(name)
561
+ if not p:
562
+ return "", ""
563
  return p.get("system"), p.get("user_template")
564
 
565
  def save_new_persona(name, sys, user):
566
+ """保存角色并原子化更新两个下拉框"""
567
+ if not name:
568
+ return "❌ 名字不能为空", gr.update(), gr.update()
569
+
570
  current = persona_manager.load_personas()
571
  current[name] = {"system": sys, "user_template": user}
572
+
573
  if persona_manager.save_personas(current):
574
+ # 保存成功后,直接调用通用刷新函数,选中新创建的角色
575
+ u1, u2 = global_refresh_dropdowns(target_value=name)
576
+ return f"✅ 角色 '{name}' 已保存", u1, u2
577
  else:
578
+ return f"❌ 保存失败", gr.update(), gr.update()
579
 
580
  def delete_persona(name):
581
+ """删除角色并原子化更新两个下拉框"""
582
+ if not name:
583
+ return "❌ 未选择", gr.update(), gr.update()
584
+
585
  current = persona_manager.load_personas()
586
  if name in current:
587
  del current[name]
588
  persona_manager.save_personas(current)
589
+
590
+ # 删除后,刷新列表,默认选中第一个
591
+ u1, u2 = global_refresh_dropdowns(target_value=None)
592
+ return f"🗑️ 已删除", u1, u2
593
 
594
  # --- 界面布局 ---
595
 
 
604
 
605
  with gr.Tabs():
606
  # --- Tab 1: 创作中心 ---
607
+ with gr.Tab("🚀 1. 创作中心") as tab_create:
608
  with gr.Row():
609
  with gr.Column(scale=1):
610
  gr.Markdown("### 配置与启动")
611
+ # 给组件定义变量名,方便后续更新
612
  persona_select = gr.Dropdown(label="加载预设角色 (选填)", choices=persona_manager.get_all_names(), interactive=True)
613
  topic_input = gr.Textbox(label="输入主题 (一行一个)", lines=3, placeholder="留空则自动拟题...")
614
 
 
625
  start_msg = gr.Label(show_label=False)
626
 
627
  # --- Tab 2: 角色配置 ---
628
+ with gr.Tab("🎭 2. 角色配置") as tab_config:
629
  with gr.Row():
630
  with gr.Column(scale=1):
631
  gr.Markdown("### 角色管理")
632
+ # 这里定义变量名
633
  p_config_select = gr.Dropdown(label="选择角色编辑", choices=persona_manager.get_all_names(), interactive=True)
634
  p_name_input = gr.Textbox(label="新建/重命名", placeholder="角色名")
635
  p_save_btn = gr.Button("💾 保存/新建", variant="primary")
 
671
  outputs=task_dd
672
  )
673
 
674
+ # ================= 核心修复逻辑:Tab 切换自动刷新 =================
675
+ # 原理:只要用户点击 Tab 1 或 Tab 2,就强制刷新该页面下的下拉框
676
+ # 这确保了即使按钮更新失败,切换一下页面也能立刻修复
677
+
678
+ def tab_refresh_handler():
679
+ u1, u2 = global_refresh_dropdowns()
680
+ return u1 # 返回给当前Tab的组件
681
+
682
+ # 绑定 Tab 选中事件 (Gradio 4.x+)
683
+ tab_create.select(fn=lambda: global_refresh_dropdowns()[0], inputs=None, outputs=persona_select)
684
+ tab_config.select(fn=lambda: global_refresh_dropdowns()[1], inputs=None, outputs=p_config_select)
685
+
686
+ # =============================================================
687
+
688
  # 角色配置联动
689
  persona_select.select(on_select_persona_config, persona_select, [p_sys_input, p_user_input])
690
+
691
+ # 关键逻辑:选择角色配置时,自动填充名字输入框
692
  p_config_select.select(on_select_persona_config, p_config_select, [p_sys_input, p_user_input])
693
  p_config_select.select(lambda x: x, p_config_select, p_name_input)
694
 
695
+ # 核心修复:保存按钮同时更新两个下拉框,并设置Value,防止报错
696
+ p_save_btn.click(
697
+ save_new_persona,
698
+ inputs=[p_name_input, p_sys_input, p_user_input],
699
+ outputs=[p_status, persona_select, p_config_select] # 同时指向 Tab1 和 Tab2 的下拉框
700
+ )
701
+
702
+ # 核心修复:删除按钮同时更新两个下拉框
703
+ p_del_btn.click(
704
+ delete_persona,
705
+ inputs=p_config_select,
706
+ outputs=[p_status, persona_select, p_config_select]
707
+ )
708
 
709
  analyze_btn.click(analyze_matches, topic_input, [ref_checkboxes, analyze_status, state_all_files])
710
  select_all_btn.click(toggle_select_all, inputs=[ref_checkboxes, state_all_files], outputs=ref_checkboxes)
711
  upload_btn.click(save_to_corpus, files, [corpus_display, upload_res])
712
  clear_btn.click(clear_corpus, outputs=[corpus_display, upload_res])
713
 
714
+ # 自动刷新逻辑
 
715
  timer.tick(
716
  lambda tid: (task_manager.get_task_info(tid)["log"], task_manager.get_task_info(tid)["result"]) if tid else ("", None),
717
  inputs=task_dd,
718
  outputs=[log_box, result_dl]
719
  )
720
 
 
721
  refresh_task.click(lambda: gr.Dropdown(choices=task_manager.list_tasks(), value=task_manager.list_tasks()[0] if task_manager.list_tasks() else None), outputs=task_dd)
722
  task_dd.change(lambda tid: (task_manager.get_task_info(tid)["log"], task_manager.get_task_info(tid)["result"]) if tid else ("", None), task_dd, [log_box, result_dl])
723
 
724
+ # 初始加载
725
  demo.load(list_corpus_files, outputs=corpus_display)
726
+ # 初始化时确保两个下拉框都有值
727
+ demo.load(lambda: global_refresh_dropdowns()[0], outputs=persona_select)
728
+ demo.load(lambda: global_refresh_dropdowns()[1], outputs=p_config_select)
729
 
730
  if __name__ == "__main__":
731
  demo.queue().launch()