Spaces:
Sleeping
Sleeping
Upload app.py
Browse files
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:
|
| 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:
|
| 444 |
}
|
| 445 |
@media (max-width: 768px) {
|
| 446 |
body:has(.main-title-guest) .block-container {
|
| 447 |
-
top:
|
| 448 |
padding: 0.5rem !important;
|
| 449 |
}
|
| 450 |
}
|
| 451 |
-
|
| 452 |
/* 登录模式:标题较矮,内容区 top 更小 */
|
| 453 |
body:has(.main-title-user) .block-container {
|
| 454 |
-
top:
|
| 455 |
-
padding-top: 0 !important;
|
| 456 |
}
|
| 457 |
@media (max-width: 768px) {
|
| 458 |
body:has(.main-title-user) .block-container {
|
| 459 |
-
top:
|
| 460 |
-
padding-top: 0 !important;
|
| 461 |
padding: 0.5rem !important;
|
| 462 |
}
|
| 463 |
}
|
| 464 |
|
| 465 |
/* 欢迎页:垂直居中,禁止滚动 */
|
| 466 |
-
.block-container:has(.welcome-marker)
|
| 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 |
-
), "
|
| 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=
|
| 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(
|
| 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 |
-
|
| 1869 |
-
|
| 1870 |
-
|
| 1871 |
-
|
| 1872 |
-
|
| 1873 |
-
|
| 1874 |
-
|
| 1875 |
-
|
| 1876 |
-
|
| 1877 |
-
|
| 1878 |
-
|
| 1879 |
-
|
| 1880 |
-
|
| 1881 |
-
|
| 1882 |
-
|
| 1883 |
-
|
| 1884 |
-
<div style="font-size:14px; line-height:1.8; margin-bottom:20px;">
|
| 1885 |
-
🌐 <b>联网模式</b> —— 大模型 + 网络搜索,实时回答<br>
|
| 1886 |
-
📚 <b>知识库模式</b> —— 上传文档,基于私有知识回答
|
| 1887 |
</div>
|
| 1888 |
-
|
| 1889 |
-
|
| 1890 |
-
|
| 1891 |
-
|
| 1892 |
-
|
| 1893 |
-
|
| 1894 |
-
|
| 1895 |
-
|
| 1896 |
-
|
| 1897 |
-
|
| 1898 |
-
|
| 1899 |
-
|
| 1900 |
-
|
| 1901 |
-
|
| 1902 |
-
|
| 1903 |
-
|
| 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 |
-
|
| 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 |
# =========================
|