AlauStone commited on
Commit
2be886d
·
verified ·
1 Parent(s): 90190b7

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +44 -92
app.py CHANGED
@@ -153,16 +153,7 @@ def _get_custom_css():
153
  [data-testid="stSidebarContent"] { padding-top: 1.5rem !important; }
154
  [data-testid="stVerticalBlock"] > div { gap: 0.2rem !important; }
155
  /* 聊天消息间距控制 */
156
- [data-testid="stChatMessage"] {
157
- margin-bottom: 0.1rem !important;
158
- margin-top: 0.1rem !important;
159
- padding-top: 0.5rem !important;
160
- padding-bottom: 0.5rem !important;
161
- }
162
- /* 聊天消息内容区域顶部对齐头像 */
163
- [data-testid="stChatMessageContent"] {
164
- padding-top: 0.25rem !important;
165
- }
166
  /* 聊天消息内部 gap 归零 */
167
  [data-testid="stChatMessageContent"] > [data-testid="stVerticalBlock"] > div { gap: 0 !important; }
168
  /* 聊天消息内标题字体大小限制 */
@@ -256,7 +247,7 @@ def _get_custom_css():
256
  /* 主内容区域:限制在标题和搜索框之间 */
257
  .block-container {
258
  position: fixed !important;
259
- top: 3.5rem !important;
260
  bottom: 7rem !important;
261
  left: 0 !important;
262
  right: 0 !important;
@@ -440,48 +431,33 @@ def _get_custom_css():
440
 
441
  /* 游客模式:标题区域 */
442
  body:has(.main-title-guest) .block-container {
443
- top: 3.5rem !important;
444
  }
445
  @media (max-width: 768px) {
446
  body:has(.main-title-guest) .block-container {
447
- top: 3.5rem !important;
448
  padding: 0.5rem !important;
449
  }
450
  }
451
-
452
  /* 登录模式:标题较矮,内容区 top 更小 */
453
  body:has(.main-title-user) .block-container {
454
- top: 0 !important;
455
- padding-top: 0 !important;
456
  }
457
  @media (max-width: 768px) {
458
  body:has(.main-title-user) .block-container {
459
- top: 0 !important;
460
- padding-top: 0 !important;
461
  padding: 0.5rem !important;
462
  }
463
  }
464
 
465
  /* 欢迎页:垂直居中,禁止滚动 */
466
- .block-container:has(.welcome-marker):not(:has([data-testid="stChatMessage"])) {
467
  overflow: hidden !important;
468
  display: flex !important;
469
  align-items: center !important;
470
  justify-content: center !important;
471
  }
472
- /* 有聊天消息时:恢复滚动,隐藏欢迎页所有内容 */
473
- .block-container:has([data-testid="stChatMessage"]) {
474
- overflow-y: auto !important;
475
- }
476
- .block-container:has([data-testid="stChatMessage"]) .welcome-marker,
477
- .block-container:has([data-testid="stChatMessage"]) .welcome-hint,
478
- .block-container:has([data-testid="stChatMessage"]) div:has(> .welcome-marker) {
479
- display: none !important;
480
- }
481
- /* 有消息时隐藏快捷问题按钮区域 */
482
- .block-container:has([data-testid="stChatMessage"]) [data-testid="stHorizontalBlock"]:has([data-testid="stBaseButton-secondary"]):not(:has([data-testid="stPopover"])) {
483
- display: none !important;
484
- }
485
 
486
  /* 欢迎页文字颜色 - 浅色模式 */
487
  .welcome-marker {
@@ -494,16 +470,6 @@ def _get_custom_css():
494
  color: #999 !important;
495
  }
496
 
497
- /* 快捷问题按钮区域:延迟显示防止闪烁 */
498
- .block-container:has(.welcome-marker) [data-testid="stHorizontalBlock"]:has([data-testid="stBaseButton-secondary"]):not(:has([data-testid="stPopover"])) {
499
- animation: fadeInDelayed 0.3s ease-in-out;
500
- animation-fill-mode: both;
501
- }
502
- @keyframes fadeInDelayed {
503
- 0%, 50% { opacity: 0; }
504
- 100% { opacity: 1; }
505
- }
506
-
507
  /* 快捷问题按钮:无边框链接风格 */
508
  .block-container:has(.welcome-marker) [data-testid="stBaseButton-secondary"] {
509
  background: transparent !important;
@@ -909,7 +875,7 @@ def _get_embedding_client():
909
  api_key=BAIDU_TOKEN,
910
  base_url="https://qianfan.baidubce.com/v2",
911
  default_headers={"appid": BAIDU_APP_ID},
912
- ), "qwen3-embedding-0.6b"
913
  if OR_KEY:
914
  return OpenAI(
915
  api_key=OR_KEY,
@@ -1815,7 +1781,7 @@ def llm_answer(query, context_docs, selected_display_name, web_enabled, is_guest
1815
  messages=messages,
1816
  stream=True,
1817
  extra_headers=extra_h,
1818
- timeout=15, # 缩短超时,加快失败切换
1819
  )
1820
 
1821
  full_text = ""
@@ -1823,9 +1789,6 @@ def llm_answer(query, context_docs, selected_display_name, web_enabled, is_guest
1823
  for chunk in response:
1824
  if chunk.choices and chunk.choices[0].delta.content:
1825
  content = chunk.choices[0].delta.content
1826
- # 过滤OpenRouter状态消息(connecting等)
1827
- if content.strip().lower() in ("connecting", "connected", "waiting"):
1828
- continue
1829
  full_text += content
1830
  has_content = True
1831
  yield content
@@ -1841,7 +1804,7 @@ def llm_answer(query, context_docs, selected_display_name, web_enabled, is_guest
1841
  logger.warning(f"{label} 失败: {err_msg[:100]}")
1842
  if "429" in err_msg:
1843
  st.toast(f"{label} 拥堵,切换备选...", icon="⏳")
1844
- time.sleep(0.8) # 缩短等待
1845
  continue
1846
 
1847
  yield "❌ 抱歉,所有免费和收费线路均暂时不可用。"
@@ -1865,46 +1828,39 @@ if "messages" not in st.session_state:
1865
  elif st.session_state.get("_chat_cleared"):
1866
  # 用户主动清空,不从数据库恢复
1867
  del st.session_state["_chat_cleared"]
1868
- st.session_state.pop("_has_chatted", None) # 重置,允许显示欢迎页
1869
-
1870
- # 欢迎页占位(有消息时、生成中、快捷问题待处理时、已开始对话 都隐藏)
1871
- _show_welcome = (
1872
- not st.session_state.messages
1873
- and not st.session_state.get("_generating")
1874
- and not st.session_state.get("_quick_question")
1875
- and not st.session_state.get("_has_chatted") # 一旦开始对话就不再显示
1876
- )
1877
- if _show_welcome:
1878
- # 无消息且未生成中时显示欢迎
1879
- st.markdown(
1880
- """
1881
- <div class="welcome-marker" style="text-align:center;">
1882
- <div style="font-size:48px; margin-bottom:12px;">🤖</div>
1883
- <div style="font-size:18px; font-weight:600; margin-bottom:8px;">欢迎使用智答 AI 助手</div>
1884
- <div style="font-size:14px; line-height:1.8; margin-bottom:20px;">
1885
- 🌐 <b>联网模式</b> —— 大模型 + 网络搜索,实时回答<br>
1886
- 📚 <b>知识库模式</b> —— 上传文档,基于私有知识回答
1887
  </div>
1888
- <div class="welcome-hint" style="font-size:13px; margin-bottom:12px;">💡 试试这些问题</div>
1889
- </div>
1890
- """,
1891
- unsafe_allow_html=True,
1892
- )
1893
- # 快捷问题 - 电脑端横向3个,手机端通过CSS控制
1894
- _quick_questions = [
1895
- "今天有什么热点新闻",
1896
- "用Python写快速排序",
1897
- "解释RAG技术是什么",
1898
- ]
1899
- # 电脑端横向3列
1900
- _q_cols = st.columns(3)
1901
- for i, qq in enumerate(_quick_questions):
1902
- with _q_cols[i]:
1903
- if st.button(f"💬 {qq}", key=f"quick_q{i}", use_container_width=True, type="secondary"):
1904
- st.session_state["_quick_question"] = qq
1905
- st.session_state["_generating"] = True
1906
- st.session_state["_has_chatted"] = True # 标记已开始对话
1907
- st.rerun()
1908
 
1909
 
1910
  @st.fragment
@@ -1922,7 +1878,6 @@ def _chat_fragment():
1922
  # 检查快捷问题
1923
  if st.session_state.get("_quick_question"):
1924
  q = st.session_state.pop("_quick_question")
1925
- st.session_state["_generating"] = True # 立即标记,防止欢迎页闪烁
1926
 
1927
  with chat_box:
1928
  for m in st.session_state.messages:
@@ -1936,8 +1891,7 @@ def _chat_fragment():
1936
  st.markdown(m["content"])
1937
 
1938
  if q:
1939
- st.session_state["_generating"] = True
1940
- st.session_state["_has_chatted"] = True # 标记已开始对话
1941
  st.session_state.messages.append({"role": "user", "content": q})
1942
  if not IS_GUEST:
1943
  _save_chat_message(CURRENT_USER, "user", q)
@@ -1993,11 +1947,9 @@ def _chat_fragment():
1993
  )
1994
  if not IS_GUEST:
1995
  _save_chat_message(CURRENT_USER, "assistant", full_response, meta_info)
1996
- st.session_state.pop("_generating", None) # 清除生成标记
1997
  except Exception as e:
1998
  logger.error(f"模型调用异常: {e}")
1999
  response_container.error(f"❌ 抱歉,连接模型时出错了: {str(e)}")
2000
- st.session_state.pop("_generating", None) # 清除生成标记
2001
 
2002
 
2003
  # =========================
 
153
  [data-testid="stSidebarContent"] { padding-top: 1.5rem !important; }
154
  [data-testid="stVerticalBlock"] > div { gap: 0.2rem !important; }
155
  /* 聊天消息间距控制 */
156
+ [data-testid="stChatMessage"] { margin-bottom: 0.1rem !important; margin-top: 0.1rem !important; padding-top: 0.5rem !important; padding-bottom: 0.5rem !important; }
 
 
 
 
 
 
 
 
 
157
  /* 聊天消息内部 gap 归零 */
158
  [data-testid="stChatMessageContent"] > [data-testid="stVerticalBlock"] > div { gap: 0 !important; }
159
  /* 聊天消息内标题字体大小限制 */
 
247
  /* 主内容区域:限制在标题和搜索框之间 */
248
  .block-container {
249
  position: fixed !important;
250
+ top: 7rem !important;
251
  bottom: 7rem !important;
252
  left: 0 !important;
253
  right: 0 !important;
 
431
 
432
  /* 游客模式:标题区域 */
433
  body:has(.main-title-guest) .block-container {
434
+ top: 6.5rem !important;
435
  }
436
  @media (max-width: 768px) {
437
  body:has(.main-title-guest) .block-container {
438
+ top: 6.5rem !important;
439
  padding: 0.5rem !important;
440
  }
441
  }
442
+
443
  /* 登录模式:标题较矮,内容区 top 更小 */
444
  body:has(.main-title-user) .block-container {
445
+ top: 6rem !important;
 
446
  }
447
  @media (max-width: 768px) {
448
  body:has(.main-title-user) .block-container {
449
+ top: 4.5rem !important;
 
450
  padding: 0.5rem !important;
451
  }
452
  }
453
 
454
  /* 欢迎页:垂直居中,禁止滚动 */
455
+ .block-container:has(.welcome-marker) {
456
  overflow: hidden !important;
457
  display: flex !important;
458
  align-items: center !important;
459
  justify-content: center !important;
460
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
461
 
462
  /* 欢迎页文字颜色 - 浅色模式 */
463
  .welcome-marker {
 
470
  color: #999 !important;
471
  }
472
 
 
 
 
 
 
 
 
 
 
 
473
  /* 快捷问题按钮:无边框链接风格 */
474
  .block-container:has(.welcome-marker) [data-testid="stBaseButton-secondary"] {
475
  background: transparent !important;
 
875
  api_key=BAIDU_TOKEN,
876
  base_url="https://qianfan.baidubce.com/v2",
877
  default_headers={"appid": BAIDU_APP_ID},
878
+ ), "bge-large-zh"
879
  if OR_KEY:
880
  return OpenAI(
881
  api_key=OR_KEY,
 
1781
  messages=messages,
1782
  stream=True,
1783
  extra_headers=extra_h,
1784
+ timeout=25,
1785
  )
1786
 
1787
  full_text = ""
 
1789
  for chunk in response:
1790
  if chunk.choices and chunk.choices[0].delta.content:
1791
  content = chunk.choices[0].delta.content
 
 
 
1792
  full_text += content
1793
  has_content = True
1794
  yield content
 
1804
  logger.warning(f"{label} 失败: {err_msg[:100]}")
1805
  if "429" in err_msg:
1806
  st.toast(f"{label} 拥堵,切换备选...", icon="⏳")
1807
+ time.sleep(1.5)
1808
  continue
1809
 
1810
  yield "❌ 抱歉,所有免费和收费线路均暂时不可用。"
 
1828
  elif st.session_state.get("_chat_cleared"):
1829
  # 用户主动清空,不从数据库恢复
1830
  del st.session_state["_chat_cleared"]
1831
+
1832
+ # 欢迎页占位(有消息时隐藏)
1833
+ _welcome_hero = st.empty()
1834
+ if not st.session_state.messages:
1835
+ # 无消息时显示欢迎页
1836
+ with _welcome_hero.container():
1837
+ st.markdown(
1838
+ """
1839
+ <div class="welcome-marker" style="text-align:center;">
1840
+ <div style="font-size:48px; margin-bottom:12px;">🤖</div>
1841
+ <div style="font-size:18px; font-weight:600; margin-bottom:8px;">欢迎使用智答 AI 助手</div>
1842
+ <div style="font-size:14px; line-height:1.8; margin-bottom:20px;">
1843
+ 🌐 <b>联网模式</b> —— 大模型 + 网络搜索,实时回答<br>
1844
+ 📚 <b>知识库模式</b> —— 上传文档,基于私有知识回答
1845
+ </div>
1846
+ <div class="welcome-hint" style="font-size:13px; margin-bottom:12px;">💡 试试这些问题</div>
 
 
 
1847
  </div>
1848
+ """,
1849
+ unsafe_allow_html=True,
1850
+ )
1851
+ # 快捷问题 - 电脑端横向3个,手机端通过CSS控制
1852
+ _quick_questions = [
1853
+ "今天有什么热点新闻",
1854
+ "用Python写快速排序",
1855
+ "解释RAG技术是什么",
1856
+ ]
1857
+ # 电脑端横向3列
1858
+ _q_cols = st.columns(3)
1859
+ for i, qq in enumerate(_quick_questions):
1860
+ with _q_cols[i]:
1861
+ if st.button(f"💬 {qq}", key=f"quick_q{i}", use_container_width=True, type="secondary"):
1862
+ st.session_state["_quick_question"] = qq
1863
+ st.rerun()
 
 
 
 
1864
 
1865
 
1866
  @st.fragment
 
1878
  # 检查快捷问题
1879
  if st.session_state.get("_quick_question"):
1880
  q = st.session_state.pop("_quick_question")
 
1881
 
1882
  with chat_box:
1883
  for m in st.session_state.messages:
 
1891
  st.markdown(m["content"])
1892
 
1893
  if q:
1894
+ _welcome_hero.empty() # 发送消息后清空欢迎页
 
1895
  st.session_state.messages.append({"role": "user", "content": q})
1896
  if not IS_GUEST:
1897
  _save_chat_message(CURRENT_USER, "user", q)
 
1947
  )
1948
  if not IS_GUEST:
1949
  _save_chat_message(CURRENT_USER, "assistant", full_response, meta_info)
 
1950
  except Exception as e:
1951
  logger.error(f"模型调用异常: {e}")
1952
  response_container.error(f"❌ 抱歉,连接模型时出错了: {str(e)}")
 
1953
 
1954
 
1955
  # =========================