Merge branch 'master' into frontier

这个提交包含在:
binary-husky
2025-01-29 00:00:08 +08:00
当前提交 722a055879
共有 16 个文件被更改,包括 352 次插入55 次删除

查看文件

@@ -1,9 +1,9 @@
> [!IMPORTANT] > [!IMPORTANT]
> `master主分支`最新动态(2025.1.28): 增加字体自定义功能
> `frontier开发分支`最新动态(2024.12.9): 更新对话时间线功能,优化xelatex论文翻译 > `frontier开发分支`最新动态(2024.12.9): 更新对话时间线功能,优化xelatex论文翻译
> `wiki文档`最新动态(2024.12.5): 更新ollama接入指南 > `wiki文档`最新动态(2024.12.5): 更新ollama接入指南
> `master主分支`最新动态(2024.12.19): 更新3.91版本,更新release页一键安装脚本
> >
> 2024.10.10: 突发停电,紧急恢复了提供[whl包](https://drive.google.com/file/d/19U_hsLoMrjOlQSzYS3pzWX9fTzyusArP/view?usp=sharing)的文件服务器 > 2024.10.10: 突发停电,紧急恢复了提供[whl包](https://drive.google.com/drive/folders/14kR-3V-lIbvGxri4AHc8TpiA1fqsw7SK?usp=sharing)的文件服务器
> 2024.10.8: 版本3.90加入对llama-index的初步支持,版本3.80加入插件二级菜单功能详见wiki > 2024.10.8: 版本3.90加入对llama-index的初步支持,版本3.80加入插件二级菜单功能详见wiki
> 2024.5.1: 加入Doc2x翻译PDF论文的功能,[查看详情](https://github.com/binary-husky/gpt_academic/wiki/Doc2x) > 2024.5.1: 加入Doc2x翻译PDF论文的功能,[查看详情](https://github.com/binary-husky/gpt_academic/wiki/Doc2x)
> 2024.3.11: 全力支持Qwen、GLM、DeepseekCoder等中文大语言模型 SoVits语音克隆模块,[查看详情](https://www.bilibili.com/video/BV1Rp421S7tF/) > 2024.3.11: 全力支持Qwen、GLM、DeepseekCoder等中文大语言模型 SoVits语音克隆模块,[查看详情](https://www.bilibili.com/video/BV1Rp421S7tF/)

查看文件

@@ -85,6 +85,30 @@ DEFAULT_WORKER_NUM = 3
THEME = "Default" THEME = "Default"
AVAIL_THEMES = ["Default", "Chuanhu-Small-and-Beautiful", "High-Contrast", "Gstaff/Xkcd", "NoCrypt/Miku"] AVAIL_THEMES = ["Default", "Chuanhu-Small-and-Beautiful", "High-Contrast", "Gstaff/Xkcd", "NoCrypt/Miku"]
FONT = "Theme-Default-Font"
AVAIL_FONTS = [
"默认值(Theme-Default-Font)",
"宋体(SimSun)",
"黑体(SimHei)",
"楷体(KaiTi)",
"仿宋(FangSong)",
"华文细黑(STHeiti Light)",
"华文楷体(STKaiti)",
"华文仿宋(STFangsong)",
"华文宋体(STSong)",
"华文中宋(STZhongsong)",
"华文新魏(STXinwei)",
"华文隶书(STLiti)",
"思源宋体(Source Han Serif CN VF@https://chinese-fonts-cdn.deno.dev/packages/syst/dist/SourceHanSerifCN/result.css)",
"月星楷(Moon Stars Kai HW@https://chinese-fonts-cdn.deno.dev/packages/moon-stars-kai/dist/MoonStarsKaiHW-Regular/result.css)",
"珠圆体(MaokenZhuyuanTi@https://chinese-fonts-cdn.deno.dev/packages/mkzyt/dist/猫啃珠圆体/result.css)",
"平方萌萌哒(PING FANG MENG MNEG DA@https://chinese-fonts-cdn.deno.dev/packages/pfmmd/dist/平方萌萌哒/result.css)",
"Helvetica",
"ui-sans-serif",
"sans-serif",
"system-ui"
]
# 默认的系统提示词system prompt # 默认的系统提示词system prompt
INIT_SYS_PROMPT = "Serve me as a writing and programming assistant." INIT_SYS_PROMPT = "Serve me as a writing and programming assistant."

查看文件

@@ -373,7 +373,7 @@ def 编译Latex(chatbot, history, main_file_original, main_file_modified, work_f
# 根据编译器类型返回编译命令 # 根据编译器类型返回编译命令
def get_compile_command(compiler, filename): def get_compile_command(compiler, filename):
compile_command = f'{compiler} -interaction=batchmode -file-line-error {filename}.tex' compile_command = f'{compiler} -interaction=batchmode -file-line-error {filename}.tex'
logger.info('Latex 编译指令: ', compile_command) logger.info('Latex 编译指令: ' + compile_command)
return compile_command return compile_command
# 确定使用的编译器 # 确定使用的编译器

查看文件

@@ -5,6 +5,9 @@ FROM fuqingxu/11.3.1-runtime-ubuntu20.04-with-texlive:latest
# edge-tts需要的依赖,某些pip包所需的依赖 # edge-tts需要的依赖,某些pip包所需的依赖
RUN apt update && apt install ffmpeg build-essential -y RUN apt update && apt install ffmpeg build-essential -y
RUN apt-get install -y fontconfig
RUN ln -s /usr/local/texlive/2023/texmf-dist/fonts/truetype /usr/share/fonts/truetype/texlive
RUN fc-cache -fv
RUN apt-get clean RUN apt-get clean
# use python3 as the system default python # use python3 as the system default python

查看文件

@@ -49,7 +49,7 @@ def main():
# 读取配置 # 读取配置
proxies, WEB_PORT, LLM_MODEL, CONCURRENT_COUNT, AUTHENTICATION = get_conf('proxies', 'WEB_PORT', 'LLM_MODEL', 'CONCURRENT_COUNT', 'AUTHENTICATION') proxies, WEB_PORT, LLM_MODEL, CONCURRENT_COUNT, AUTHENTICATION = get_conf('proxies', 'WEB_PORT', 'LLM_MODEL', 'CONCURRENT_COUNT', 'AUTHENTICATION')
CHATBOT_HEIGHT, LAYOUT, AVAIL_LLM_MODELS, AUTO_CLEAR_TXT = get_conf('CHATBOT_HEIGHT', 'LAYOUT', 'AVAIL_LLM_MODELS', 'AUTO_CLEAR_TXT') CHATBOT_HEIGHT, LAYOUT, AVAIL_LLM_MODELS, AUTO_CLEAR_TXT = get_conf('CHATBOT_HEIGHT', 'LAYOUT', 'AVAIL_LLM_MODELS', 'AUTO_CLEAR_TXT')
ENABLE_AUDIO, AUTO_CLEAR_TXT, PATH_LOGGING, AVAIL_THEMES, THEME, ADD_WAIFU = get_conf('ENABLE_AUDIO', 'AUTO_CLEAR_TXT', 'PATH_LOGGING', 'AVAIL_THEMES', 'THEME', 'ADD_WAIFU') ENABLE_AUDIO, AUTO_CLEAR_TXT, AVAIL_FONTS, AVAIL_THEMES, THEME, ADD_WAIFU = get_conf('ENABLE_AUDIO', 'AUTO_CLEAR_TXT', 'AVAIL_FONTS', 'AVAIL_THEMES', 'THEME', 'ADD_WAIFU')
NUM_CUSTOM_BASIC_BTN, SSL_KEYFILE, SSL_CERTFILE = get_conf('NUM_CUSTOM_BASIC_BTN', 'SSL_KEYFILE', 'SSL_CERTFILE') NUM_CUSTOM_BASIC_BTN, SSL_KEYFILE, SSL_CERTFILE = get_conf('NUM_CUSTOM_BASIC_BTN', 'SSL_KEYFILE', 'SSL_CERTFILE')
DARK_MODE, INIT_SYS_PROMPT, ADD_WAIFU, TTS_TYPE = get_conf('DARK_MODE', 'INIT_SYS_PROMPT', 'ADD_WAIFU', 'TTS_TYPE') DARK_MODE, INIT_SYS_PROMPT, ADD_WAIFU, TTS_TYPE = get_conf('DARK_MODE', 'INIT_SYS_PROMPT', 'ADD_WAIFU', 'TTS_TYPE')
if LLM_MODEL not in AVAIL_LLM_MODELS: AVAIL_LLM_MODELS += [LLM_MODEL] if LLM_MODEL not in AVAIL_LLM_MODELS: AVAIL_LLM_MODELS += [LLM_MODEL]
@@ -178,7 +178,7 @@ def main():
# 左上角工具栏定义 # 左上角工具栏定义
from themes.gui_toolbar import define_gui_toolbar from themes.gui_toolbar import define_gui_toolbar
checkboxes, checkboxes_2, max_length_sl, theme_dropdown, system_prompt, file_upload_2, md_dropdown, top_p, temperature = \ checkboxes, checkboxes_2, max_length_sl, theme_dropdown, system_prompt, file_upload_2, md_dropdown, top_p, temperature = \
define_gui_toolbar(AVAIL_LLM_MODELS, LLM_MODEL, INIT_SYS_PROMPT, THEME, AVAIL_THEMES, ADD_WAIFU, help_menu_description, js_code_for_toggle_darkmode) define_gui_toolbar(AVAIL_LLM_MODELS, LLM_MODEL, INIT_SYS_PROMPT, THEME, AVAIL_THEMES, AVAIL_FONTS, ADD_WAIFU, help_menu_description, js_code_for_toggle_darkmode)
# 浮动菜单定义 # 浮动菜单定义
from themes.gui_floating_menu import define_gui_floating_menu from themes.gui_floating_menu import define_gui_floating_menu
@@ -228,9 +228,6 @@ def main():
cancel_handles.append(submit_btn.click(**predict_args)) cancel_handles.append(submit_btn.click(**predict_args))
resetBtn.click(None, None, [chatbot, history, status], _js= """clear_conversation""") # 先在前端快速清除chatbot&status resetBtn.click(None, None, [chatbot, history, status], _js= """clear_conversation""") # 先在前端快速清除chatbot&status
resetBtn2.click(None, None, [chatbot, history, status], _js="""clear_conversation""") # 先在前端快速清除chatbot&status resetBtn2.click(None, None, [chatbot, history, status], _js="""clear_conversation""") # 先在前端快速清除chatbot&status
# reset_server_side_args = (lambda history: ([], [], "已重置"), [history], [chatbot, history, status])
# resetBtn.click(*reset_server_side_args) # 再在后端清除history
# resetBtn2.click(*reset_server_side_args) # 再在后端清除history
clearBtn.click(None, None, [txt, txt2], _js=js_code_clear) clearBtn.click(None, None, [txt, txt2], _js=js_code_clear)
clearBtn2.click(None, None, [txt, txt2], _js=js_code_clear) clearBtn2.click(None, None, [txt, txt2], _js=js_code_clear)
if AUTO_CLEAR_TXT: if AUTO_CLEAR_TXT:

查看文件

@@ -13,7 +13,7 @@ scipdf_parser>=0.52
spacy==3.7.4 spacy==3.7.4
anthropic>=0.18.1 anthropic>=0.18.1
python-markdown-math python-markdown-math
pymdown-extensions pymdown-extensions>=10.14
websocket-client websocket-client
beautifulsoup4 beautifulsoup4
prompt_toolkit prompt_toolkit

查看文件

@@ -2,6 +2,7 @@ import markdown
import re import re
import os import os
import math import math
import html
from loguru import logger from loguru import logger
from textwrap import dedent from textwrap import dedent
@@ -384,6 +385,24 @@ def markdown_convertion(txt):
) )
def code_block_title_replace_format(match):
lang = match.group(1)
filename = match.group(2)
return f"```{lang} {{title=\"{filename}\"}}\n"
def get_last_backticks_indent(text):
# 从后向前查找最后一个 ```
lines = text.splitlines()
for line in reversed(lines):
if '```' in line:
# 计算前面的空格数量
indent = len(line) - len(line.lstrip())
return indent
return 0 # 如果没找到返回0
@lru_cache(maxsize=16) # 使用lru缓存
def close_up_code_segment_during_stream(gpt_reply): def close_up_code_segment_during_stream(gpt_reply):
""" """
在gpt输出代码的中途输出了前面的```,但还没输出完后面的```),补上后面的``` 在gpt输出代码的中途输出了前面的```,但还没输出完后面的```),补上后面的```
@@ -397,6 +416,12 @@ def close_up_code_segment_during_stream(gpt_reply):
""" """
if "```" not in gpt_reply: if "```" not in gpt_reply:
return gpt_reply return gpt_reply
# replace [```python:warp.py] to [```python {title="warp.py"}]
pattern = re.compile(r"```([a-z]{1,12}):([^:\n]{1,35}\.([a-zA-Z^:\n]{1,3}))\n")
if pattern.search(gpt_reply):
gpt_reply = pattern.sub(code_block_title_replace_format, gpt_reply)
if gpt_reply.endswith("```"): if gpt_reply.endswith("```"):
return gpt_reply return gpt_reply
@@ -404,7 +429,11 @@ def close_up_code_segment_during_stream(gpt_reply):
segments = gpt_reply.split("```") segments = gpt_reply.split("```")
n_mark = len(segments) - 1 n_mark = len(segments) - 1
if n_mark % 2 == 1: if n_mark % 2 == 1:
return gpt_reply + "\n```" # 输出代码片段中! try:
num_padding = get_last_backticks_indent(gpt_reply)
except:
num_padding = 0
return gpt_reply + "\n" + " "*num_padding + "```" # 输出代码片段中!
else: else:
return gpt_reply return gpt_reply
@@ -421,6 +450,19 @@ def special_render_issues_for_mermaid(text):
return text return text
def contain_html_tag(text):
"""
判断文本中是否包含HTML标签。
"""
pattern = r'</?([a-zA-Z0-9_]{3,16})>|<script\s+[^>]*src=["\']([^"\']+)["\'][^>]*>'
return re.search(pattern, text) is not None
def contain_image(text):
pattern = r'<br/><br/><div align="center"><img src="file=(.*?)" base64="(.*?)"></div>'
return re.search(pattern, text) is not None
def compat_non_markdown_input(text): def compat_non_markdown_input(text):
""" """
改善非markdown输入的显示效果,例如将空格转换为&nbsp;,将换行符转换为</br>等。 改善非markdown输入的显示效果,例如将空格转换为&nbsp;,将换行符转换为</br>等。
@@ -429,9 +471,13 @@ def compat_non_markdown_input(text):
# careful inputmarkdown输入 # careful inputmarkdown输入
text = special_render_issues_for_mermaid(text) # 处理特殊的渲染问题 text = special_render_issues_for_mermaid(text) # 处理特殊的渲染问题
return text return text
elif "</div>" in text: elif ("<" in text) and (">" in text) and contain_html_tag(text):
# careful inputhtml输入 # careful inputhtml输入
return text if contain_image(text):
return text
else:
escaped_text = html.escape(text)
return escaped_text
else: else:
# whatever input非markdown输入 # whatever input非markdown输入
lines = text.split("\n") lines = text.split("\n")

查看文件

@@ -20,7 +20,7 @@ Replace 'Tex/' with the actual directory path where your files are located befor
md = """ md = """
Following code including wrapper Following code including wrapper
```mermaid ```python:wrapper.py
graph TD graph TD
A[Enter Chart Definition] --> B(Preview) A[Enter Chart Definition] --> B(Preview)
B --> C{decide} B --> C{decide}
@@ -41,6 +41,33 @@ Any folded content here. It requires an empty line just above it.
</details> </details>
"""
md ="""
在这种场景中,您希望机器 B 能够通过轮询机制来间接地“请求”机器 A,而实际上机器 A 只能主动向机器 B 发出请求。这是一种典型的客户端-服务器轮询模式。下面是如何实现这种机制的详细步骤:
### 机器 B 的实现
1. **安装 FastAPI 和必要的依赖库**
```bash
pip install fastapi uvicorn
```
2. **创建 FastAPI 服务**
```python
from fastapi import FastAPI
from fastapi.responses import JSONResponse
from uuid import uuid4
from threading import Lock
import time
app = FastAPI()
# 字典用于存储请求和状态
requests = {}
process_lock = Lock()
""" """
def validate_path(): def validate_path():
import os, sys import os, sys
@@ -53,10 +80,12 @@ def validate_path():
validate_path() # validate path so you can run from base directory validate_path() # validate path so you can run from base directory
from toolbox import markdown_convertion from toolbox import markdown_convertion
from shared_utils.advanced_markdown_format import markdown_convertion_for_file # from shared_utils.advanced_markdown_format import markdown_convertion_for_file
from shared_utils.advanced_markdown_format import close_up_code_segment_during_stream
# with open("gpt_log/default_user/shared/2024-04-22-01-27-43.zip.extract/translated_markdown.md", "r", encoding="utf-8") as f: # with open("gpt_log/default_user/shared/2024-04-22-01-27-43.zip.extract/translated_markdown.md", "r", encoding="utf-8") as f:
# md = f.read() # md = f.read()
html = markdown_convertion_for_file(md) md = close_up_code_segment_during_stream(md)
html = markdown_convertion(md)
# print(html) # print(html)
with open("test.html", "w", encoding="utf-8") as f: with open("test.html", "w", encoding="utf-8") as f:
f.write(html) f.write(html)

查看文件

@@ -1,9 +1,16 @@
:root {
--gpt-academic-message-font-size: 15px;
}
.message {
font-size: var(--gpt-academic-message-font-size) !important;
}
#plugin_arg_menu { #plugin_arg_menu {
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
border: dashed; border: dashed;
} }
/* hide remove all button */ /* hide remove all button */
.remove-all.svelte-aqlk7e.svelte-aqlk7e.svelte-aqlk7e { .remove-all.svelte-aqlk7e.svelte-aqlk7e.svelte-aqlk7e {
visibility: hidden; visibility: hidden;
@@ -25,7 +32,6 @@
visibility: hidden; visibility: hidden;
} }
/* height of the upload box */ /* height of the upload box */
.wrap.svelte-xwlu1w { .wrap.svelte-xwlu1w {
min-height: var(--size-32); min-height: var(--size-32);
@@ -97,13 +103,9 @@
min-width: min(80px, 100%); min-width: min(80px, 100%);
} }
#cbs,
#cbs {
background-color: var(--block-background-fill) !important;
}
#cbsc { #cbsc {
background-color: var(--block-background-fill) !important; background-color: rgba(var(--block-background-fill), 0.5) !important;
} }
#interact-panel .form { #interact-panel .form {
@@ -207,6 +209,7 @@
.welcome-content { .welcome-content {
text-wrap: balance; text-wrap: balance;
height: 55px; height: 55px;
font-size: 13px;
display: flex; display: flex;
align-items: center; align-items: center;
} }
@@ -275,4 +278,36 @@
.tooltip.svelte-p2nen8.svelte-p2nen8 { .tooltip.svelte-p2nen8.svelte-p2nen8 {
box-shadow: 10px 10px 15px rgba(0, 0, 0, 0.5); box-shadow: 10px 10px 15px rgba(0, 0, 0, 0.5);
left: 10px; left: 10px;
} }
#tooltip .hidden {
/* display: none; */
opacity: 0;
transition: opacity 0.5s ease;
}
#tooltip .visible {
/* display: block; */
opacity: 1;
transition: opacity 0.5s ease;
}
#elem_fontsize,
#elem_top_p,
#elem_temperature,
#elem_max_length_sl,
#elem_prompt {
/* 左右为0;顶部为0,底部为2px */
padding: 0 0 4px 0;
backdrop-filter: blur(10px);
background-color: rgba(var(--block-background-fill), 0.5);
}
#tooltip #cbs,
#tooltip #cbsc,
#tooltip .svelte-b6y5bg,
#tooltip .tabitem {
backdrop-filter: blur(10px);
background-color: rgba(var(--block-background-fill), 0.5);
}

查看文件

@@ -750,10 +750,24 @@ function minor_ui_adjustment() {
var bar_btn_width = []; var bar_btn_width = [];
// 自动隐藏超出范围的toolbar按钮 // 自动隐藏超出范围的toolbar按钮
function auto_hide_toolbar() { function auto_hide_toolbar() {
var qq = document.getElementById('tooltip'); // if chatbot hit upper page boarder, hide all
var tab_nav = qq.getElementsByClassName('tab-nav'); const elem_chatbot = document.getElementById('gpt-chatbot');
const chatbot_top = elem_chatbot.getBoundingClientRect().top;
var tooltip = document.getElementById('tooltip');
var tab_nav = tooltip.getElementsByClassName('tab-nav')[0];
// 20 px 大概是一个字的高度
if (chatbot_top < 20) {
// tab_nav.style.display = 'none';
if (tab_nav.classList.contains('visible')) {tab_nav.classList.remove('visible');}
if (!tab_nav.classList.contains('hidden')) {tab_nav.classList.add('hidden');}
return;
}
if (tab_nav.classList.contains('hidden')) {tab_nav.classList.remove('hidden');}
if (!tab_nav.classList.contains('visible')) {tab_nav.classList.add('visible');}
// tab_nav.style.display = '';
if (tab_nav.length == 0) { return; } if (tab_nav.length == 0) { return; }
var btn_list = tab_nav[0].getElementsByTagName('button') var btn_list = tab_nav.getElementsByTagName('button')
if (btn_list.length == 0) { return; } if (btn_list.length == 0) { return; }
// 获取页面宽度 // 获取页面宽度
var page_width = document.documentElement.clientWidth; var page_width = document.documentElement.clientWidth;
@@ -1034,7 +1048,7 @@ async function save_conversation_history() {
return timeB - timeA; return timeB - timeA;
}); });
const max_chat_preserve = 4; const max_chat_preserve = 10;
if (conversation_history.length >= max_chat_preserve + 1) { if (conversation_history.length >= max_chat_preserve + 1) {
toast_push('对话时间线记录已满,正在移除最早的对话记录。您也可以点击左侧的记录点进行手动清理。', 3000); toast_push('对话时间线记录已满,正在移除最早的对话记录。您也可以点击左侧的记录点进行手动清理。', 3000);

查看文件

@@ -567,7 +567,6 @@ ul:not(.options) {
border-radius: var(--radius-xl) !important; border-radius: var(--radius-xl) !important;
border: none; border: none;
padding: var(--spacing-xl) !important; padding: var(--spacing-xl) !important;
font-size: 15px !important;
line-height: var(--line-md) !important; line-height: var(--line-md) !important;
min-height: calc(var(--text-md)*var(--line-md) + 2*var(--spacing-xl)); min-height: calc(var(--text-md)*var(--line-md) + 2*var(--spacing-xl));
min-width: calc(var(--text-md)*var(--line-md) + 2*var(--spacing-xl)); min-width: calc(var(--text-md)*var(--line-md) + 2*var(--spacing-xl));

查看文件

@@ -10,6 +10,14 @@ theme_dir = os.path.dirname(__file__)
def adjust_theme(): def adjust_theme():
try: try:
set_theme = gr.themes.Soft( set_theme = gr.themes.Soft(
font=[
"Helvetica",
"Microsoft YaHei",
"ui-sans-serif",
"sans-serif",
"system-ui",
],
font_mono=["ui-monospace", "Consolas", "monospace"],
primary_hue=gr.themes.Color( primary_hue=gr.themes.Color(
c50="#EBFAF2", c50="#EBFAF2",
c100="#CFF3E1", c100="#CFF3E1",

查看文件

@@ -1,6 +1,7 @@
import gradio as gr import gradio as gr
from toolbox import get_conf
def define_gui_toolbar(AVAIL_LLM_MODELS, LLM_MODEL, INIT_SYS_PROMPT, THEME, AVAIL_THEMES, ADD_WAIFU, help_menu_description, js_code_for_toggle_darkmode): def define_gui_toolbar(AVAIL_LLM_MODELS, LLM_MODEL, INIT_SYS_PROMPT, THEME, AVAIL_THEMES, AVAIL_FONTS, ADD_WAIFU, help_menu_description, js_code_for_toggle_darkmode):
with gr.Floating(init_x="0%", init_y="0%", visible=True, width=None, drag="forbidden", elem_id="tooltip"): with gr.Floating(init_x="0%", init_y="0%", visible=True, width=None, drag="forbidden", elem_id="tooltip"):
with gr.Row(): with gr.Row():
with gr.Tab("上传文件", elem_id="interact-panel"): with gr.Tab("上传文件", elem_id="interact-panel"):
@@ -9,12 +10,12 @@ def define_gui_toolbar(AVAIL_LLM_MODELS, LLM_MODEL, INIT_SYS_PROMPT, THEME, AVAI
with gr.Tab("更换模型", elem_id="interact-panel"): with gr.Tab("更换模型", elem_id="interact-panel"):
md_dropdown = gr.Dropdown(AVAIL_LLM_MODELS, value=LLM_MODEL, elem_id="elem_model_sel", label="更换LLM模型/请求源").style(container=False) md_dropdown = gr.Dropdown(AVAIL_LLM_MODELS, value=LLM_MODEL, elem_id="elem_model_sel", label="更换LLM模型/请求源").style(container=False)
top_p = gr.Slider(minimum=-0, maximum=1.0, value=1.0, step=0.01,interactive=True, label="Top-p (nucleus sampling)",) top_p = gr.Slider(minimum=-0, maximum=1.0, value=1.0, step=0.01,interactive=True, label="Top-p (nucleus sampling)", elem_id="elem_top_p")
temperature = gr.Slider(minimum=-0, maximum=2.0, value=1.0, step=0.01, interactive=True, label="Temperature", elem_id="elem_temperature") temperature = gr.Slider(minimum=-0, maximum=2.0, value=1.0, step=0.01, interactive=True, label="Temperature", elem_id="elem_temperature")
max_length_sl = gr.Slider(minimum=256, maximum=1024*32, value=4096, step=128, interactive=True, label="Local LLM MaxLength",) max_length_sl = gr.Slider(minimum=256, maximum=1024*32, value=4096, step=128, interactive=True, label="Local LLM MaxLength", elem_id="elem_max_length_sl")
system_prompt = gr.Textbox(show_label=True, lines=2, placeholder=f"System Prompt", label="System prompt", value=INIT_SYS_PROMPT, elem_id="elem_prompt") system_prompt = gr.Textbox(show_label=True, lines=2, placeholder=f"System Prompt", label="System prompt", value=INIT_SYS_PROMPT, elem_id="elem_prompt")
temperature.change(None, inputs=[temperature], outputs=None, temperature.change(None, inputs=[temperature], outputs=None,
_js="""(temperature)=>gpt_academic_gradio_saveload("save", "elem_prompt", "js_temperature_cookie", temperature)""") _js="""(temperature)=>gpt_academic_gradio_saveload("save", "elem_temperature", "js_temperature_cookie", temperature)""")
system_prompt.change(None, inputs=[system_prompt], outputs=None, system_prompt.change(None, inputs=[system_prompt], outputs=None,
_js="""(system_prompt)=>gpt_academic_gradio_saveload("save", "elem_prompt", "js_system_prompt_cookie", system_prompt)""") _js="""(system_prompt)=>gpt_academic_gradio_saveload("save", "elem_prompt", "js_system_prompt_cookie", system_prompt)""")
md_dropdown.change(None, inputs=[md_dropdown], outputs=None, md_dropdown.change(None, inputs=[md_dropdown], outputs=None,
@@ -22,6 +23,8 @@ def define_gui_toolbar(AVAIL_LLM_MODELS, LLM_MODEL, INIT_SYS_PROMPT, THEME, AVAI
with gr.Tab("界面外观", elem_id="interact-panel"): with gr.Tab("界面外观", elem_id="interact-panel"):
theme_dropdown = gr.Dropdown(AVAIL_THEMES, value=THEME, label="更换UI主题").style(container=False) theme_dropdown = gr.Dropdown(AVAIL_THEMES, value=THEME, label="更换UI主题").style(container=False)
fontfamily_dropdown = gr.Dropdown(AVAIL_FONTS, value=get_conf("FONT"), elem_id="elem_fontfamily", label="更换字体类型").style(container=False)
fontsize_slider = gr.Slider(minimum=5, maximum=25, value=15, step=1, interactive=True, label="字体大小(默认15)", elem_id="elem_fontsize")
checkboxes = gr.CheckboxGroup(["基础功能区", "函数插件区", "浮动输入区", "输入清除键", "插件参数区"], value=["基础功能区", "函数插件区"], label="显示/隐藏功能区", elem_id='cbs').style(container=False) checkboxes = gr.CheckboxGroup(["基础功能区", "函数插件区", "浮动输入区", "输入清除键", "插件参数区"], value=["基础功能区", "函数插件区"], label="显示/隐藏功能区", elem_id='cbs').style(container=False)
opt = ["自定义菜单"] opt = ["自定义菜单"]
value=[] value=[]
@@ -31,7 +34,10 @@ def define_gui_toolbar(AVAIL_LLM_MODELS, LLM_MODEL, INIT_SYS_PROMPT, THEME, AVAI
dark_mode_btn.click(None, None, None, _js=js_code_for_toggle_darkmode) dark_mode_btn.click(None, None, None, _js=js_code_for_toggle_darkmode)
open_new_tab = gr.Button("打开新对话", variant="secondary").style(size="sm") open_new_tab = gr.Button("打开新对话", variant="secondary").style(size="sm")
open_new_tab.click(None, None, None, _js=f"""()=>duplicate_in_new_window()""") open_new_tab.click(None, None, None, _js=f"""()=>duplicate_in_new_window()""")
fontfamily_dropdown.select(None, inputs=[fontfamily_dropdown], outputs=None,
_js="""(fontfamily)=>{gpt_academic_gradio_saveload("save", "elem_fontfamily", "js_fontfamily", fontfamily); gpt_academic_change_chatbot_font(fontfamily, null, null);}""")
fontsize_slider.change(None, inputs=[fontsize_slider], outputs=None,
_js="""(fontsize)=>{gpt_academic_gradio_saveload("save", "elem_fontsize", "js_fontsize", fontsize); gpt_academic_change_chatbot_font(null, fontsize, null);}""")
with gr.Tab("帮助", elem_id="interact-panel"): with gr.Tab("帮助", elem_id="interact-panel"):
gr.Markdown(help_menu_description) gr.Markdown(help_menu_description)

查看文件

@@ -5,6 +5,129 @@ function remove_legacy_cookie() {
} }
function processFontFamily(fontfamily) {
// 检查是否包含括号
if (fontfamily.includes('(')) {
// 分割字符串
const parts = fontfamily.split('(');
const fontNamePart = parts[1].split(')')[0].trim(); // 获取括号内的部分
// 检查是否包含 @
if (fontNamePart.includes('@')) {
const [fontName, fontUrl] = fontNamePart.split('@').map(part => part.trim());
return { fontName, fontUrl };
} else {
return { fontName: fontNamePart, fontUrl: null };
}
} else {
return { fontName: fontfamily, fontUrl: null };
}
}
// 检查字体是否存在
function checkFontAvailability(fontfamily) {
return new Promise((resolve) => {
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
// 设置两个不同的字体进行比较
const testText = 'abcdefghijklmnopqrstuvwxyz0123456789';
context.font = `16px ${fontfamily}, sans-serif`;
const widthWithFont = context.measureText(testText).width;
context.font = '16px sans-serif';
const widthWithFallback = context.measureText(testText).width;
// 如果宽度相同,说明字体不存在
resolve(widthWithFont !== widthWithFallback);
});
}
async function checkFontAvailabilityV2(fontfamily) {
fontName = fontfamily;
console.log('Checking font availability:', fontName);
if ('queryLocalFonts' in window) {
try {
const fonts = await window.queryLocalFonts();
const fontExists = fonts.some(font => font.family === fontName);
console.log(`Local Font "${fontName}" exists:`, fontExists);
return fontExists;
} catch (error) {
console.error('Error querying local fonts:', error);
return false;
}
} else {
console.error('queryLocalFonts is not supported in this browser.');
return false;
}
}
// 动态加载字体
function loadFont(fontfamily, fontUrl) {
return new Promise((resolve, reject) => {
// 使用 Google Fonts 或其他字体来源
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = fontUrl;
link.onload = () => {
toast_push(`字体 "${fontfamily}" 已成功加载`, 3000);
resolve();
};
link.onerror = (error) => {
reject(error);
};
document.head.appendChild(link);
});
}
function gpt_academic_change_chatbot_font(fontfamily, fontsize, fontcolor) {
const chatbot = document.querySelector('#gpt-chatbot');
// 检查元素是否存在
if (chatbot) {
if (fontfamily != null) {
// 更改字体
const result = processFontFamily(fontfamily);
if (result.fontName == "Theme-Default-Font") {
chatbot.style.fontFamily = result.fontName;
return;
}
// 检查字体是否存在
checkFontAvailability(result.fontName).then((isAvailable) => {
if (isAvailable) {
// 如果字体存在,直接应用
chatbot.style.fontFamily = result.fontName;
} else {
if (result.fontUrl == null) {
// toast_push('无法加载字体,本地字体不存在,且URL未提供', 3000);
// 直接把失效的字体放上去,让系统自动fallback
chatbot.style.fontFamily = result.fontName;
return;
} else {
toast_push('正在下载字体', 3000);
// 如果字体不存在,尝试加载字体
loadFont(result.fontName, result.fontUrl).then(() => {
chatbot.style.fontFamily = result.fontName;
}).catch((error) => {
console.error(`无法加载字体 "${result.fontName}":`, error);
});
}
}
});
}
if (fontsize != null) {
// 修改字体大小
document.documentElement.style.setProperty(
'--gpt-academic-message-font-size',
`${fontsize}px`
);
}
if (fontcolor != null) {
// 更改字体颜色
chatbot.style.color = fontcolor;
}
} else {
console.error('#gpt-chatbot is missing');
}
}
async function GptAcademicJavaScriptInit(dark, prompt, live2d, layout, tts) { async function GptAcademicJavaScriptInit(dark, prompt, live2d, layout, tts) {
// 第一部分,布局初始化 // 第一部分,布局初始化
remove_legacy_cookie(); remove_legacy_cookie();
@@ -13,7 +136,7 @@ async function GptAcademicJavaScriptInit(dark, prompt, live2d, layout, tts) {
ButtonWithDropdown_init(); ButtonWithDropdown_init();
update_conversation_metadata(); update_conversation_metadata();
window.addEventListener("gptac_restore_chat_from_local_storage", restore_chat_from_local_storage); window.addEventListener("gptac_restore_chat_from_local_storage", restore_chat_from_local_storage);
// 加载欢迎页面 // 加载欢迎页面
const welcomeMessage = new WelcomeMessage(); const welcomeMessage = new WelcomeMessage();
welcomeMessage.begin_render(); welcomeMessage.begin_render();
@@ -23,7 +146,7 @@ async function GptAcademicJavaScriptInit(dark, prompt, live2d, layout, tts) {
welcomeMessage.update(); welcomeMessage.update();
}); });
chatbotObserver.observe(chatbotIndicator, { attributes: true, childList: true, subtree: true }); chatbotObserver.observe(chatbotIndicator, { attributes: true, childList: true, subtree: true });
if (layout === "LEFT-RIGHT") { chatbotAutoHeight(); } if (layout === "LEFT-RIGHT") { chatbotAutoHeight(); }
if (layout === "LEFT-RIGHT") { limit_scroll_position(); } if (layout === "LEFT-RIGHT") { limit_scroll_position(); }
@@ -46,7 +169,7 @@ async function GptAcademicJavaScriptInit(dark, prompt, live2d, layout, tts) {
} }
// 自动朗读 // 自动朗读
if (tts != "DISABLE"){ if (tts != "DISABLE") {
enable_tts = true; enable_tts = true;
if (getCookie("js_auto_read_cookie")) { if (getCookie("js_auto_read_cookie")) {
auto_read_tts = getCookie("js_auto_read_cookie") auto_read_tts = getCookie("js_auto_read_cookie")
@@ -56,7 +179,11 @@ async function GptAcademicJavaScriptInit(dark, prompt, live2d, layout, tts) {
} }
} }
} }
// 字体
gpt_academic_gradio_saveload("load", "elem_fontfamily", "js_fontfamily", null, "str");
gpt_academic_change_chatbot_font(getCookie("js_fontfamily"), null, null);
gpt_academic_gradio_saveload("load", "elem_fontsize", "js_fontsize", null, "str");
gpt_academic_change_chatbot_font(null, getCookie("js_fontsize"), null);
// SysPrompt 系统静默提示词 // SysPrompt 系统静默提示词
gpt_academic_gradio_saveload("load", "elem_prompt", "js_system_prompt_cookie", null, "str"); gpt_academic_gradio_saveload("load", "elem_prompt", "js_system_prompt_cookie", null, "str");
// Temperature 大模型温度参数 // Temperature 大模型温度参数
@@ -66,7 +193,7 @@ async function GptAcademicJavaScriptInit(dark, prompt, live2d, layout, tts) {
const cached_model = getCookie("js_md_dropdown_cookie"); const cached_model = getCookie("js_md_dropdown_cookie");
var model_sel = await get_gradio_component("elem_model_sel"); var model_sel = await get_gradio_component("elem_model_sel");
// determine whether the cached model is in the choices // determine whether the cached model is in the choices
if (model_sel.props.choices.includes(cached_model)){ if (model_sel.props.choices.includes(cached_model)) {
// change dropdown // change dropdown
gpt_academic_gradio_saveload("load", "elem_model_sel", "js_md_dropdown_cookie", null, "str"); gpt_academic_gradio_saveload("load", "elem_model_sel", "js_md_dropdown_cookie", null, "str");
// 连锁修改chatbot的label // 连锁修改chatbot的label

查看文件

@@ -85,7 +85,7 @@ class WelcomeMessage {
this.card_array = []; this.card_array = [];
this.static_welcome_message_previous = []; this.static_welcome_message_previous = [];
this.reflesh_time_interval = 15 * 1000; this.reflesh_time_interval = 15 * 1000;
this.major_title = "欢迎使用GPT-Academic";
const reflesh_render_status = () => { const reflesh_render_status = () => {
for (let index = 0; index < this.card_array.length; index++) { for (let index = 0; index < this.card_array.length; index++) {
@@ -99,6 +99,8 @@ class WelcomeMessage {
// call update when page size change, call this.update when page size change // call update when page size change, call this.update when page size change
window.addEventListener('resize', this.update.bind(this)); window.addEventListener('resize', this.update.bind(this));
// add a loop to reflesh cards
this.startRefleshCards();
} }
begin_render() { begin_render() {
@@ -106,9 +108,12 @@ class WelcomeMessage {
} }
async startRefleshCards() { async startRefleshCards() {
// sleep certain time
await new Promise(r => setTimeout(r, this.reflesh_time_interval)); await new Promise(r => setTimeout(r, this.reflesh_time_interval));
await this.reflesh_cards(); // checkout visible status
if (this.visible) { if (this.visible) {
// if visible, then reflesh cards
await this.reflesh_cards();
setTimeout(() => { setTimeout(() => {
this.startRefleshCards.call(this); this.startRefleshCards.call(this);
}, 1); }, 1);
@@ -194,35 +199,38 @@ class WelcomeMessage {
} }
async update() { async update() {
// console.log('update') // update the card visibility
const elem_chatbot = document.getElementById('gpt-chatbot'); const elem_chatbot = document.getElementById('gpt-chatbot');
const chatbot_top = elem_chatbot.getBoundingClientRect().top; const chatbot_top = elem_chatbot.getBoundingClientRect().top;
const welcome_card_container = document.getElementsByClassName('welcome-card-container')[0]; const welcome_card_container = document.getElementsByClassName('welcome-card-container')[0];
// detect if welcome card overflow
let welcome_card_overflow = false; let welcome_card_overflow = false;
if (welcome_card_container) { if (welcome_card_container) {
const welcome_card_top = welcome_card_container.getBoundingClientRect().top; const welcome_card_top = welcome_card_container.getBoundingClientRect().top;
if (welcome_card_top < chatbot_top) { if (welcome_card_top < chatbot_top) {
welcome_card_overflow = true; welcome_card_overflow = true;
// console.log("welcome_card_overflow");
} }
} }
var page_width = document.documentElement.clientWidth; var page_width = document.documentElement.clientWidth;
const width_to_hide_welcome = 1200; const width_to_hide_welcome = 1200;
if (!await this.isChatbotEmpty() || page_width < width_to_hide_welcome || welcome_card_overflow) { if (!await this.isChatbotEmpty() || page_width < width_to_hide_welcome || welcome_card_overflow) {
// overflow !
if (this.visible) { if (this.visible) {
console.log("remove welcome"); // console.log("remove welcome");
this.removeWelcome(); this.visible = false; // this two lines must always be together this.removeWelcome();
this.card_array = []; this.card_array = [];
this.static_welcome_message_previous = []; this.static_welcome_message_previous = [];
} }
return; return;
} }
if (this.visible) { if (this.visible) {
// console.log("already visible");
return; return;
} }
console.log("show welcome"); // not overflow, not yet shown, then create and display welcome card
this.showWelcome(); this.visible = true; // this two lines must always be together // console.log("show welcome");
this.startRefleshCards(); this.showWelcome();
} }
showCard(message) { showCard(message) {
@@ -263,7 +271,7 @@ class WelcomeMessage {
} }
async showWelcome() { async showWelcome() {
this.visible = true;
// 首先,找到想要添加子元素的父元素 // 首先,找到想要添加子元素的父元素
const elem_chatbot = document.getElementById('gpt-chatbot'); const elem_chatbot = document.getElementById('gpt-chatbot');
@@ -274,7 +282,7 @@ class WelcomeMessage {
// 创建主标题 // 创建主标题
const major_title = document.createElement('div'); const major_title = document.createElement('div');
major_title.classList.add('welcome-title'); major_title.classList.add('welcome-title');
major_title.textContent = "欢迎使用GPT-Academic"; major_title.textContent = this.major_title;
welcome_card_container.appendChild(major_title) welcome_card_container.appendChild(major_title)
// 创建卡片 // 创建卡片
@@ -297,16 +305,17 @@ class WelcomeMessage {
} }
async removeWelcome() { async removeWelcome() {
this.visible = false;
// remove welcome-card-container // remove welcome-card-container
const elem_chatbot = document.getElementById('gpt-chatbot'); const elem_chatbot = document.getElementById('gpt-chatbot');
const welcome_card_container = document.getElementsByClassName('welcome-card-container')[0]; const welcome_card_container = document.getElementsByClassName('welcome-card-container')[0];
// 添加隐藏动画 // begin hide animation
welcome_card_container.classList.add('hide'); welcome_card_container.classList.add('hide');
// 等待动画结束后再移除元素
welcome_card_container.addEventListener('transitionend', () => { welcome_card_container.addEventListener('transitionend', () => {
elem_chatbot.removeChild(welcome_card_container); elem_chatbot.removeChild(welcome_card_container);
}, { once: true }); }, { once: true });
const timeout = 600; // 与CSS中transition的时间保持一致(1s) // add a fail safe timeout
const timeout = 600; // 与 CSS 中 transition 的时间保持一致(1s)
setTimeout(() => { setTimeout(() => {
if (welcome_card_container.parentNode) { if (welcome_card_container.parentNode) {
elem_chatbot.removeChild(welcome_card_container); elem_chatbot.removeChild(welcome_card_container);

查看文件

@@ -1,5 +1,5 @@
{ {
"version": 3.91, "version": 3.92,
"show_feature": true, "show_feature": true,
"new_feature": "优化前端并修复TTS的BUG <-> 添加时间线回溯功能 <-> 支持chatgpt-4o-latest <-> 增加RAG组件 <-> 升级多合一主提交键" "new_feature": "字体和字体大小自定义 <-> 优化前端并修复TTS的BUG <-> 添加时间线回溯功能 <-> 支持chatgpt-4o-latest <-> 增加RAG组件 <-> 升级多合一主提交键"
} }