镜像自地址
https://github.com/binary-husky/gpt_academic.git
已同步 2025-12-06 22:46:48 +00:00
Merge branch 'master' into frontier
这个提交包含在:
@@ -43,7 +43,8 @@ AVAIL_LLM_MODELS = ["qwen-max", "o1-mini", "o1-mini-2024-09-12", "o1", "o1-2024-
|
|||||||
"gpt-3.5-turbo-1106", "gpt-3.5-turbo-16k", "gpt-3.5-turbo", "azure-gpt-3.5",
|
"gpt-3.5-turbo-1106", "gpt-3.5-turbo-16k", "gpt-3.5-turbo", "azure-gpt-3.5",
|
||||||
"gpt-4", "gpt-4-32k", "azure-gpt-4", "glm-4", "glm-4v", "glm-3-turbo",
|
"gpt-4", "gpt-4-32k", "azure-gpt-4", "glm-4", "glm-4v", "glm-3-turbo",
|
||||||
"gemini-1.5-pro", "chatglm3", "chatglm4",
|
"gemini-1.5-pro", "chatglm3", "chatglm4",
|
||||||
"deepseek-chat", "deepseek-coder", "deepseek-reasoner"
|
"deepseek-chat", "deepseek-coder", "deepseek-reasoner",
|
||||||
|
"volcengine-deepseek-r1-250120", "volcengine-deepseek-v3-241226",
|
||||||
]
|
]
|
||||||
|
|
||||||
EMBEDDING_MODEL = "text-embedding-3-small"
|
EMBEDDING_MODEL = "text-embedding-3-small"
|
||||||
@@ -267,6 +268,10 @@ MOONSHOT_API_KEY = ""
|
|||||||
YIMODEL_API_KEY = ""
|
YIMODEL_API_KEY = ""
|
||||||
|
|
||||||
|
|
||||||
|
# 接入火山引擎的在线大模型),api-key获取地址 https://console.volcengine.com/ark/region:ark+cn-beijing/endpoint
|
||||||
|
ARK_API_KEY = "00000000-0000-0000-0000-000000000000" # 火山引擎 API KEY
|
||||||
|
|
||||||
|
|
||||||
# 紫东太初大模型 https://ai-maas.wair.ac.cn
|
# 紫东太初大模型 https://ai-maas.wair.ac.cn
|
||||||
TAICHU_API_KEY = ""
|
TAICHU_API_KEY = ""
|
||||||
|
|
||||||
|
|||||||
2
main.py
2
main.py
@@ -34,7 +34,7 @@ def encode_plugin_info(k, plugin)->str:
|
|||||||
|
|
||||||
def main():
|
def main():
|
||||||
import gradio as gr
|
import gradio as gr
|
||||||
if gr.__version__ not in ['3.32.12']:
|
if gr.__version__ not in ['3.32.12', '3.32.13']:
|
||||||
raise ModuleNotFoundError("使用项目内置Gradio获取最优体验! 请运行 `pip install -r requirements.txt` 指令安装内置Gradio及其他依赖, 详情信息见requirements.txt.")
|
raise ModuleNotFoundError("使用项目内置Gradio获取最优体验! 请运行 `pip install -r requirements.txt` 指令安装内置Gradio及其他依赖, 详情信息见requirements.txt.")
|
||||||
|
|
||||||
# 一些基础工具
|
# 一些基础工具
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ ollama_endpoint = "http://localhost:11434/api/chat"
|
|||||||
yimodel_endpoint = "https://api.lingyiwanwu.com/v1/chat/completions"
|
yimodel_endpoint = "https://api.lingyiwanwu.com/v1/chat/completions"
|
||||||
deepseekapi_endpoint = "https://api.deepseek.com/v1/chat/completions"
|
deepseekapi_endpoint = "https://api.deepseek.com/v1/chat/completions"
|
||||||
grok_model_endpoint = "https://api.x.ai/v1/chat/completions"
|
grok_model_endpoint = "https://api.x.ai/v1/chat/completions"
|
||||||
|
volcengine_endpoint = "https://ark.cn-beijing.volces.com/api/v3/chat/completions"
|
||||||
|
|
||||||
if not AZURE_ENDPOINT.endswith('/'): AZURE_ENDPOINT += '/'
|
if not AZURE_ENDPOINT.endswith('/'): AZURE_ENDPOINT += '/'
|
||||||
azure_endpoint = AZURE_ENDPOINT + f'openai/deployments/{AZURE_ENGINE}/chat/completions?api-version=2023-05-15'
|
azure_endpoint = AZURE_ENDPOINT + f'openai/deployments/{AZURE_ENGINE}/chat/completions?api-version=2023-05-15'
|
||||||
@@ -102,6 +103,7 @@ if ollama_endpoint in API_URL_REDIRECT: ollama_endpoint = API_URL_REDIRECT[ollam
|
|||||||
if yimodel_endpoint in API_URL_REDIRECT: yimodel_endpoint = API_URL_REDIRECT[yimodel_endpoint]
|
if yimodel_endpoint in API_URL_REDIRECT: yimodel_endpoint = API_URL_REDIRECT[yimodel_endpoint]
|
||||||
if deepseekapi_endpoint in API_URL_REDIRECT: deepseekapi_endpoint = API_URL_REDIRECT[deepseekapi_endpoint]
|
if deepseekapi_endpoint in API_URL_REDIRECT: deepseekapi_endpoint = API_URL_REDIRECT[deepseekapi_endpoint]
|
||||||
if grok_model_endpoint in API_URL_REDIRECT: grok_model_endpoint = API_URL_REDIRECT[grok_model_endpoint]
|
if grok_model_endpoint in API_URL_REDIRECT: grok_model_endpoint = API_URL_REDIRECT[grok_model_endpoint]
|
||||||
|
if volcengine_endpoint in API_URL_REDIRECT: volcengine_endpoint = API_URL_REDIRECT[volcengine_endpoint]
|
||||||
|
|
||||||
# 获取tokenizer
|
# 获取tokenizer
|
||||||
tokenizer_gpt35 = LazyloadTiktoken("gpt-3.5-turbo")
|
tokenizer_gpt35 = LazyloadTiktoken("gpt-3.5-turbo")
|
||||||
@@ -1089,8 +1091,10 @@ if "deepseekcoder" in AVAIL_LLM_MODELS: # deepseekcoder
|
|||||||
})
|
})
|
||||||
except:
|
except:
|
||||||
logger.error(trimmed_format_exc())
|
logger.error(trimmed_format_exc())
|
||||||
|
|
||||||
# -=-=-=-=-=-=- 幻方-深度求索大模型在线API -=-=-=-=-=-=-
|
# -=-=-=-=-=-=- 幻方-深度求索大模型在线API -=-=-=-=-=-=-
|
||||||
if "deepseek-chat" in AVAIL_LLM_MODELS or "deepseek-coder" in AVAIL_LLM_MODELS or "deepseek-reasoner" in AVAIL_LLM_MODELS:
|
claude_models = ["deepseek-chat", "deepseek-coder", "deepseek-reasoner"]
|
||||||
|
if any(item in claude_models for item in AVAIL_LLM_MODELS):
|
||||||
try:
|
try:
|
||||||
deepseekapi_noui, deepseekapi_ui = get_predict_function(
|
deepseekapi_noui, deepseekapi_ui = get_predict_function(
|
||||||
api_key_conf_name="DEEPSEEK_API_KEY", max_output_token=4096, disable_proxy=False
|
api_key_conf_name="DEEPSEEK_API_KEY", max_output_token=4096, disable_proxy=False
|
||||||
@@ -1127,6 +1131,60 @@ if "deepseek-chat" in AVAIL_LLM_MODELS or "deepseek-coder" in AVAIL_LLM_MODELS o
|
|||||||
})
|
})
|
||||||
except:
|
except:
|
||||||
logger.error(trimmed_format_exc())
|
logger.error(trimmed_format_exc())
|
||||||
|
|
||||||
|
# -=-=-=-=-=-=- 火山引擎 对齐支持 -=-=-=-=-=-=-
|
||||||
|
for model in [m for m in AVAIL_LLM_MODELS if m.startswith("volcengine-")]:
|
||||||
|
# 为了更灵活地接入volcengine多模型管理界面,设计了此接口,例子:AVAIL_LLM_MODELS = ["volcengine-deepseek-r1-250120(max_token=6666)"]
|
||||||
|
# 其中
|
||||||
|
# "volcengine-" 是前缀(必要)
|
||||||
|
# "deepseek-r1-250120" 是模型名(必要)
|
||||||
|
# "(max_token=6666)" 是配置(非必要)
|
||||||
|
model_info_extend = model_info
|
||||||
|
model_info_extend.update({
|
||||||
|
"deepseek-r1-250120": {
|
||||||
|
"max_token": 16384,
|
||||||
|
"enable_reasoning": True,
|
||||||
|
"can_multi_thread": True,
|
||||||
|
"endpoint": volcengine_endpoint,
|
||||||
|
"tokenizer": tokenizer_gpt35,
|
||||||
|
"token_cnt": get_token_num_gpt35,
|
||||||
|
},
|
||||||
|
"deepseek-v3-241226": {
|
||||||
|
"max_token": 16384,
|
||||||
|
"enable_reasoning": False,
|
||||||
|
"can_multi_thread": True,
|
||||||
|
"endpoint": volcengine_endpoint,
|
||||||
|
"tokenizer": tokenizer_gpt35,
|
||||||
|
"token_cnt": get_token_num_gpt35,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
try:
|
||||||
|
origin_model_name, max_token_tmp = read_one_api_model_name(model)
|
||||||
|
# 如果是已知模型,则尝试获取其信息
|
||||||
|
original_model_info = model_info_extend.get(origin_model_name.replace("volcengine-", "", 1), None)
|
||||||
|
except:
|
||||||
|
logger.error(f"volcengine模型 {model} 的 max_token 配置不是整数,请检查配置文件。")
|
||||||
|
continue
|
||||||
|
|
||||||
|
volcengine_noui, volcengine_ui = get_predict_function(api_key_conf_name="ARK_API_KEY", max_output_token=8192, disable_proxy=True, model_remove_prefix = ["volcengine-"])
|
||||||
|
|
||||||
|
this_model_info = {
|
||||||
|
"fn_with_ui": volcengine_ui,
|
||||||
|
"fn_without_ui": volcengine_noui,
|
||||||
|
"endpoint": volcengine_endpoint,
|
||||||
|
"can_multi_thread": True,
|
||||||
|
"max_token": 64000,
|
||||||
|
"tokenizer": tokenizer_gpt35,
|
||||||
|
"token_cnt": get_token_num_gpt35,
|
||||||
|
}
|
||||||
|
|
||||||
|
# 同步已知模型的其他信息
|
||||||
|
attribute = "has_multimodal_capacity"
|
||||||
|
if original_model_info is not None and original_model_info.get(attribute, None) is not None: this_model_info.update({attribute: original_model_info.get(attribute, None)})
|
||||||
|
attribute = "enable_reasoning"
|
||||||
|
if original_model_info is not None and original_model_info.get(attribute, None) is not None: this_model_info.update({attribute: original_model_info.get(attribute, None)})
|
||||||
|
model_info.update({model: this_model_info})
|
||||||
|
|
||||||
# -=-=-=-=-=-=- one-api 对齐支持 -=-=-=-=-=-=-
|
# -=-=-=-=-=-=- one-api 对齐支持 -=-=-=-=-=-=-
|
||||||
for model in [m for m in AVAIL_LLM_MODELS if m.startswith("one-api-")]:
|
for model in [m for m in AVAIL_LLM_MODELS if m.startswith("one-api-")]:
|
||||||
# 为了更灵活地接入one-api多模型管理界面,设计了此接口,例子:AVAIL_LLM_MODELS = ["one-api-mixtral-8x7b(max_token=6666)"]
|
# 为了更灵活地接入one-api多模型管理界面,设计了此接口,例子:AVAIL_LLM_MODELS = ["one-api-mixtral-8x7b(max_token=6666)"]
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ def decode_chunk(chunk):
|
|||||||
finish_reason = chunk["error"]["code"]
|
finish_reason = chunk["error"]["code"]
|
||||||
except:
|
except:
|
||||||
finish_reason = "API_ERROR"
|
finish_reason = "API_ERROR"
|
||||||
return response, reasoning_content, finish_reason
|
return response, reasoning_content, finish_reason, str(chunk)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if chunk["choices"][0]["delta"]["content"] is not None:
|
if chunk["choices"][0]["delta"]["content"] is not None:
|
||||||
@@ -122,7 +122,8 @@ def generate_message(input, model, key, history, max_output_token, system_prompt
|
|||||||
def get_predict_function(
|
def get_predict_function(
|
||||||
api_key_conf_name,
|
api_key_conf_name,
|
||||||
max_output_token,
|
max_output_token,
|
||||||
disable_proxy = False
|
disable_proxy = False,
|
||||||
|
model_remove_prefix = [],
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
为openai格式的API生成响应函数,其中传入参数:
|
为openai格式的API生成响应函数,其中传入参数:
|
||||||
@@ -137,6 +138,16 @@ def get_predict_function(
|
|||||||
|
|
||||||
APIKEY = get_conf(api_key_conf_name)
|
APIKEY = get_conf(api_key_conf_name)
|
||||||
|
|
||||||
|
def remove_prefix(model_name):
|
||||||
|
# 去除模型名字的前缀,输入 volcengine-deepseek-r1-250120 会返回 deepseek-r1-250120
|
||||||
|
if not model_remove_prefix:
|
||||||
|
return model_name
|
||||||
|
model_without_prefix = model_name
|
||||||
|
for prefix in model_remove_prefix:
|
||||||
|
if model_without_prefix.startswith(prefix):
|
||||||
|
model_without_prefix = model_without_prefix[len(prefix):]
|
||||||
|
return model_without_prefix
|
||||||
|
|
||||||
def predict_no_ui_long_connection(
|
def predict_no_ui_long_connection(
|
||||||
inputs,
|
inputs,
|
||||||
llm_kwargs,
|
llm_kwargs,
|
||||||
@@ -164,9 +175,11 @@ def get_predict_function(
|
|||||||
raise RuntimeError(f"APIKEY为空,请检查配置文件的{APIKEY}")
|
raise RuntimeError(f"APIKEY为空,请检查配置文件的{APIKEY}")
|
||||||
if inputs == "":
|
if inputs == "":
|
||||||
inputs = "你好👋"
|
inputs = "你好👋"
|
||||||
|
|
||||||
|
|
||||||
headers, payload = generate_message(
|
headers, payload = generate_message(
|
||||||
input=inputs,
|
input=inputs,
|
||||||
model=llm_kwargs["llm_model"],
|
model=remove_prefix(llm_kwargs["llm_model"]),
|
||||||
key=APIKEY,
|
key=APIKEY,
|
||||||
history=history,
|
history=history,
|
||||||
max_output_token=max_output_token,
|
max_output_token=max_output_token,
|
||||||
@@ -302,7 +315,7 @@ def get_predict_function(
|
|||||||
|
|
||||||
headers, payload = generate_message(
|
headers, payload = generate_message(
|
||||||
input=inputs,
|
input=inputs,
|
||||||
model=llm_kwargs["llm_model"],
|
model=remove_prefix(llm_kwargs["llm_model"]),
|
||||||
key=APIKEY,
|
key=APIKEY,
|
||||||
history=history,
|
history=history,
|
||||||
max_output_token=max_output_token,
|
max_output_token=max_output_token,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
https://public.agent-matrix.com/publish/gradio-3.32.12-py3-none-any.whl
|
https://public.agent-matrix.com/publish/gradio-3.32.13-py3-none-any.whl
|
||||||
fastapi==0.110
|
fastapi==0.110
|
||||||
gradio-client==0.8
|
gradio-client==0.8
|
||||||
pypdf2==2.12.1
|
pypdf2==2.12.1
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ import re
|
|||||||
import os
|
import os
|
||||||
import math
|
import math
|
||||||
import html
|
import html
|
||||||
|
import base64
|
||||||
|
import gzip
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
from textwrap import dedent
|
from textwrap import dedent
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
@@ -325,6 +326,14 @@ def markdown_convertion_for_file(txt):
|
|||||||
# cat them together
|
# cat them together
|
||||||
return pre + convert_stage_5 + suf
|
return pre + convert_stage_5 + suf
|
||||||
|
|
||||||
|
def compress_string(s):
|
||||||
|
compress_string = gzip.compress(s.encode('utf-8'))
|
||||||
|
return base64.b64encode(compress_string).decode()
|
||||||
|
|
||||||
|
def decompress_string(s):
|
||||||
|
decoded_string = base64.b64decode(s)
|
||||||
|
return gzip.decompress(decoded_string).decode('utf-8')
|
||||||
|
|
||||||
@lru_cache(maxsize=128) # 使用 lru缓存 加快转换速度
|
@lru_cache(maxsize=128) # 使用 lru缓存 加快转换速度
|
||||||
def markdown_convertion(txt):
|
def markdown_convertion(txt):
|
||||||
"""
|
"""
|
||||||
@@ -336,6 +345,12 @@ def markdown_convertion(txt):
|
|||||||
# print('警告,输入了已经经过转化的字符串,二次转化可能出问题')
|
# print('警告,输入了已经经过转化的字符串,二次转化可能出问题')
|
||||||
return txt # 已经被转化过,不需要再次转化
|
return txt # 已经被转化过,不需要再次转化
|
||||||
|
|
||||||
|
# 在文本中插入一个base64编码的原始文本,以便在复制时能够获得原始文本
|
||||||
|
raw_text_encoded = compress_string(txt)
|
||||||
|
raw_text_node = f'<div class="raw_text">{raw_text_encoded}</div>'
|
||||||
|
suf = raw_text_node + "</div>"
|
||||||
|
|
||||||
|
# 用于查找数学公式的正则表达式
|
||||||
find_equation_pattern = r'<script type="math/tex(?:.*?)>(.*?)</script>'
|
find_equation_pattern = r'<script type="math/tex(?:.*?)>(.*?)</script>'
|
||||||
|
|
||||||
txt = fix_markdown_indent(txt)
|
txt = fix_markdown_indent(txt)
|
||||||
@@ -493,6 +508,7 @@ def simple_markdown_convertion(text):
|
|||||||
suf = "</div>"
|
suf = "</div>"
|
||||||
if text.startswith(pre) and text.endswith(suf):
|
if text.startswith(pre) and text.endswith(suf):
|
||||||
return text # 已经被转化过,不需要再次转化
|
return text # 已经被转化过,不需要再次转化
|
||||||
|
|
||||||
text = compat_non_markdown_input(text) # 兼容非markdown输入
|
text = compat_non_markdown_input(text) # 兼容非markdown输入
|
||||||
text = markdown.markdown(
|
text = markdown.markdown(
|
||||||
text,
|
text,
|
||||||
|
|||||||
@@ -332,3 +332,7 @@
|
|||||||
text-wrap: wrap;
|
text-wrap: wrap;
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.raw_text {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
@@ -259,7 +259,24 @@ function cancel_loading_status() {
|
|||||||
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
||||||
// 第 2 部分: 复制按钮
|
// 第 2 部分: 复制按钮
|
||||||
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
||||||
|
// 解压缩函数
|
||||||
|
function decompressString(compressedString) {
|
||||||
|
// 第1步:Base64解码
|
||||||
|
const binaryString = atob(compressedString);
|
||||||
|
|
||||||
|
// 第2步:将二进制字符串转换为Uint8Array
|
||||||
|
const bytes = new Uint8Array(binaryString.length);
|
||||||
|
for (let i = 0; i < binaryString.length; i++) {
|
||||||
|
bytes[i] = binaryString.charCodeAt(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 第3步:使用DecompressionStream (基于Web Streams API)进行gzip解压缩
|
||||||
|
const ds = new DecompressionStream('gzip');
|
||||||
|
const decompressedStream = new Response(new Blob([bytes])).body.pipeThrough(ds);
|
||||||
|
|
||||||
|
// 第4步:获取解压后的数据并转换为字符串
|
||||||
|
return new Response(decompressedStream).text();
|
||||||
|
}
|
||||||
|
|
||||||
var allow_auto_read_continously = true;
|
var allow_auto_read_continously = true;
|
||||||
var allow_auto_read_tts_flag = false;
|
var allow_auto_read_tts_flag = false;
|
||||||
@@ -283,19 +300,56 @@ function addCopyButton(botElement, index, is_last_in_arr) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var copyButton = document.createElement('button');
|
// var copyButton = document.createElement('button');
|
||||||
copyButton.classList.add('copy-bot-btn');
|
// copyButton.classList.add('copy-bot-btn');
|
||||||
copyButton.setAttribute('aria-label', 'Copy');
|
// copyButton.setAttribute('aria-label', 'Copy');
|
||||||
copyButton.innerHTML = copyIcon;
|
// copyButton.innerHTML = copyIcon;
|
||||||
copyButton.addEventListener('click', async () => {
|
// copyButton.addEventListener('click', async () => {
|
||||||
const textToCopy = botElement.innerText;
|
// const textToCopy = botElement.innerText;
|
||||||
|
// try {
|
||||||
|
// // push_text_to_audio(textToCopy).catch(console.error);
|
||||||
|
// if ("clipboard" in navigator) {
|
||||||
|
// await navigator.clipboard.writeText(textToCopy);
|
||||||
|
// copyButton.innerHTML = copiedIcon;
|
||||||
|
// setTimeout(() => {
|
||||||
|
// copyButton.innerHTML = copyIcon;
|
||||||
|
// }, 1500);
|
||||||
|
// } else {
|
||||||
|
// const textArea = document.createElement("textarea");
|
||||||
|
// textArea.value = textToCopy;
|
||||||
|
// document.body.appendChild(textArea);
|
||||||
|
// textArea.select();
|
||||||
|
// try {
|
||||||
|
// document.execCommand('copy');
|
||||||
|
// copyButton.innerHTML = copiedIcon;
|
||||||
|
// setTimeout(() => {
|
||||||
|
// copyButton.innerHTML = copyIcon;
|
||||||
|
// }, 1500);
|
||||||
|
// } catch (error) {
|
||||||
|
// console.error("Copy failed: ", error);
|
||||||
|
// }
|
||||||
|
// document.body.removeChild(textArea);
|
||||||
|
// }
|
||||||
|
// } catch (error) {
|
||||||
|
// console.error("Copy failed: ", error);
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
// 原始文本拷贝
|
||||||
|
var copyButtonOrig = document.createElement('button');
|
||||||
|
copyButtonOrig.classList.add('copy-bot-btn');
|
||||||
|
copyButtonOrig.setAttribute('aria-label', 'Copy');
|
||||||
|
copyButtonOrig.innerHTML = copyIcon;
|
||||||
|
copyButtonOrig.addEventListener('click', async () => {
|
||||||
try {
|
try {
|
||||||
|
const base64gzipcode = botElement.getElementsByClassName('raw_text')[0].innerText;
|
||||||
|
const textToCopy = await decompressString(base64gzipcode);
|
||||||
// push_text_to_audio(textToCopy).catch(console.error);
|
// push_text_to_audio(textToCopy).catch(console.error);
|
||||||
if ("clipboard" in navigator) {
|
if ("clipboard" in navigator) {
|
||||||
await navigator.clipboard.writeText(textToCopy);
|
await navigator.clipboard.writeText(textToCopy);
|
||||||
copyButton.innerHTML = copiedIcon;
|
copyButtonOrig.innerHTML = copiedIcon;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
copyButton.innerHTML = copyIcon;
|
copyButtonOrig.innerHTML = copyIcon;
|
||||||
}, 1500);
|
}, 1500);
|
||||||
} else {
|
} else {
|
||||||
const textArea = document.createElement("textarea");
|
const textArea = document.createElement("textarea");
|
||||||
@@ -304,9 +358,9 @@ function addCopyButton(botElement, index, is_last_in_arr) {
|
|||||||
textArea.select();
|
textArea.select();
|
||||||
try {
|
try {
|
||||||
document.execCommand('copy');
|
document.execCommand('copy');
|
||||||
copyButton.innerHTML = copiedIcon;
|
copyButtonOrig.innerHTML = copiedIcon;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
copyButton.innerHTML = copyIcon;
|
copyButtonOrig.innerHTML = copyIcon;
|
||||||
}, 1500);
|
}, 1500);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Copy failed: ", error);
|
console.error("Copy failed: ", error);
|
||||||
@@ -345,7 +399,8 @@ function addCopyButton(botElement, index, is_last_in_arr) {
|
|||||||
|
|
||||||
var messageBtnColumn = document.createElement('div');
|
var messageBtnColumn = document.createElement('div');
|
||||||
messageBtnColumn.classList.add('message-btn-row');
|
messageBtnColumn.classList.add('message-btn-row');
|
||||||
messageBtnColumn.appendChild(copyButton);
|
// messageBtnColumn.appendChild(copyButton);
|
||||||
|
messageBtnColumn.appendChild(copyButtonOrig);
|
||||||
if (enable_tts) {
|
if (enable_tts) {
|
||||||
messageBtnColumn.appendChild(audioButton);
|
messageBtnColumn.appendChild(audioButton);
|
||||||
}
|
}
|
||||||
|
|||||||
在新工单中引用
屏蔽一个用户