From 87b3f79ae913518e02077056610a0c5b05d7f08f Mon Sep 17 00:00:00 2001 From: binary-husky Date: Wed, 4 Jun 2025 01:53:29 +0800 Subject: [PATCH] setup nv --- Dockerfile | 21 ++- crazy_functions/Conversation_To_File.py | 170 ++++++++++++++++++++++-- toolbox.py | 2 +- 3 files changed, 178 insertions(+), 15 deletions(-) diff --git a/Dockerfile b/Dockerfile index 27755a35..b5ae77c1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ # - 如何构建: 先修改 `config.py`, 然后 `docker build -t gpt-academic . ` # - 如何运行(Linux下): `docker run --rm -it --net=host gpt-academic ` # - 如何运行(其他操作系统,选择任意一个固定端口50923): `docker run --rm -it -e WEB_PORT=50923 -p 50923:50923 gpt-academic ` -FROM python:3.11 +FROM ghcr.io/astral-sh/uv:python3.12-bookworm # 非必要步骤,更换pip源 (以下三行,可以删除) @@ -12,8 +12,15 @@ RUN echo '[global]' > /etc/pip.conf && \ echo 'trusted-host = mirrors.aliyun.com' >> /etc/pip.conf -# 语音输出功能(以下两行,第一行更换阿里源,第二行安装ffmpeg,都可以删除) -RUN UBUNTU_VERSION=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release); echo "deb https://mirrors.aliyun.com/debian/ $UBUNTU_VERSION main non-free contrib" > /etc/apt/sources.list; apt-get update +# 语音输出功能(以下1,2行更换阿里源,第3,4行安装ffmpeg,都可以删除) +# RUN echo "deb https://mirrors.aliyun.com/debian/ bookworm main contrib non-free non-free-firmware" > /etc/apt/sources.list +# RUN echo "deb https://mirrors.aliyun.com/debian/ bookworm-updates main contrib non-free non-free-firmware" >> /etc/apt/sources.list +# RUN echo "deb https://mirrors.aliyun.com/debian/ bookworm-backports main contrib non-free non-free-firmware" >> /etc/apt/sources.list +# RUN echo "deb https://mirrors.aliyun.com/debian-security/ bookworm-security main contrib non-free non-free-firmware" >> /etc/apt/sources.list +# COPY /usr/share/doc/apt/examples/sources.list /etc/apt/sources.list +RUN sed -i 's/deb.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list.d/debian.sources && \ + sed -i 's/security.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list.d/debian.sources && \ + apt-get update RUN apt-get install ffmpeg -y RUN apt-get clean @@ -24,13 +31,15 @@ WORKDIR /gpt # 安装大部分依赖,利用Docker缓存加速以后的构建 (以下两行,可以删除) COPY requirements.txt ./ -RUN pip3 install -r requirements.txt +RUN uv venv --python=3.12 && uv pip install --verbose -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/ # 装载项目文件,安装剩余依赖(必要) COPY . . -RUN pip3 install -r requirements.txt +RUN uv venv --python=3.12 && uv pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/ +RUN ln -sf .venv/bin/python3.12 /usr/local/bin/python3 && \ + ln -sf .venv/bin/python3.12 /usr/local/bin/python # 非必要步骤,用于预热模块(可以删除) RUN python3 -c 'from check_proxy import warm_up_modules; warm_up_modules()' @@ -38,4 +47,4 @@ RUN python3 -m pip cache purge # 启动(必要) -CMD ["python3", "-u", "main.py"] +CMD ["bash", "-c", "source .venv/bin/activate && python main.py"] diff --git a/crazy_functions/Conversation_To_File.py b/crazy_functions/Conversation_To_File.py index b8408748..2e1bf47f 100644 --- a/crazy_functions/Conversation_To_File.py +++ b/crazy_functions/Conversation_To_File.py @@ -1,10 +1,11 @@ -from toolbox import CatchException, update_ui, promote_file_to_downloadzone, get_log_folder, get_user -from crazy_functions.plugin_template.plugin_class_template import GptAcademicPluginTemplate, ArgProperty import re +from toolbox import CatchException, update_ui, promote_file_to_downloadzone, get_log_folder, get_user, update_ui_latest_msg +from crazy_functions.plugin_template.plugin_class_template import GptAcademicPluginTemplate, ArgProperty +from loguru import logger f_prefix = 'GPT-Academic对话存档' -def write_chat_to_file(chatbot, history=None, file_name=None): +def write_chat_to_file_legacy(chatbot, history=None, file_name=None): """ 将对话记录history以Markdown格式写入文件中。如果没有指定文件名,则使用当前时间生成文件名。 """ @@ -12,6 +13,9 @@ def write_chat_to_file(chatbot, history=None, file_name=None): import time from themes.theme import advanced_css + if (file_name is not None) and (file_name != "") and (not file_name.endswith('.html')): file_name += '.html' + else: file_name = None + if file_name is None: file_name = f_prefix + time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime()) + '.html' fp = os.path.join(get_log_folder(get_user(chatbot), plugin_name='chat_history'), file_name) @@ -68,6 +72,147 @@ def write_chat_to_file(chatbot, history=None, file_name=None): promote_file_to_downloadzone(fp, rename_file=file_name, chatbot=chatbot) return '对话历史写入:' + fp +def write_chat_to_file(chatbot, history=None, file_name=None): + """ + 将对话记录history以多种格式(HTML、Word、Markdown)写入文件中。如果没有指定文件名,则使用当前时间生成文件名。 + + Args: + chatbot: 聊天机器人对象,包含对话内容 + history: 对话历史记录 + file_name: 指定的文件名,如果为None则使用时间戳 + + Returns: + str: 提示信息,包含文件保存路径 + """ + import os + import time + import asyncio + import aiofiles + from toolbox import promote_file_to_downloadzone + from crazy_functions.doc_fns.conversation_doc.excel_doc import save_chat_tables + from crazy_functions.doc_fns.conversation_doc.html_doc import HtmlFormatter + from crazy_functions.doc_fns.conversation_doc.markdown_doc import MarkdownFormatter + from crazy_functions.doc_fns.conversation_doc.word_doc import WordFormatter + from crazy_functions.doc_fns.conversation_doc.txt_doc import TxtFormatter + from crazy_functions.doc_fns.conversation_doc.word2pdf import WordToPdfConverter + + async def save_html(): + try: + html_formatter = HtmlFormatter(chatbot, history) + html_content = html_formatter.create_document() + html_file = os.path.join(save_dir, base_name + '.html') + async with aiofiles.open(html_file, 'w', encoding='utf8') as f: + await f.write(html_content) + return html_file + except Exception as e: + print(f"保存HTML格式失败: {str(e)}") + return None + + async def save_word(): + try: + word_formatter = WordFormatter() + doc = word_formatter.create_document(history) + docx_file = os.path.join(save_dir, base_name + '.docx') + # 由于python-docx不支持异步,使用线程池执行 + loop = asyncio.get_event_loop() + await loop.run_in_executor(None, doc.save, docx_file) + return docx_file + except Exception as e: + print(f"保存Word格式失败: {str(e)}") + return None + async def save_pdf(docx_file): + try: + if docx_file: + # 获取文件名和保存路径 + pdf_file = os.path.join(save_dir, base_name + '.pdf') + + # 在线程池中执行转换 + loop = asyncio.get_event_loop() + pdf_file = await loop.run_in_executor( + None, + WordToPdfConverter.convert_to_pdf, + docx_file + # save_dir + ) + + return pdf_file + + except Exception as e: + print(f"保存PDF格式失败: {str(e)}") + return None + + async def save_markdown(): + try: + md_formatter = MarkdownFormatter() + md_content = md_formatter.create_document(history) + md_file = os.path.join(save_dir, base_name + '.md') + async with aiofiles.open(md_file, 'w', encoding='utf8') as f: + await f.write(md_content) + return md_file + except Exception as e: + print(f"保存Markdown格式失败: {str(e)}") + return None + + async def save_txt(): + try: + txt_formatter = TxtFormatter() + txt_content = txt_formatter.create_document(history) + txt_file = os.path.join(save_dir, base_name + '.txt') + async with aiofiles.open(txt_file, 'w', encoding='utf8') as f: + await f.write(txt_content) + return txt_file + except Exception as e: + print(f"保存TXT格式失败: {str(e)}") + return None + + async def main(): + # 并发执行所有保存任务 + html_task = asyncio.create_task(save_html()) + word_task = asyncio.create_task(save_word()) + md_task = asyncio.create_task(save_markdown()) + txt_task = asyncio.create_task(save_txt()) + + # 等待所有任务完成 + html_file = await html_task + docx_file = await word_task + md_file = await md_task + txt_file = await txt_task + + # PDF转换需要等待word文件生成完成 + pdf_file = await save_pdf(docx_file) + # 收集所有成功生成的文件 + result_files = [f for f in [html_file, docx_file, md_file, txt_file, pdf_file] if f] + + # 保存Excel表格 + excel_files = save_chat_tables(history, save_dir, base_name) + result_files.extend(excel_files) + + return result_files + + # 生成时间戳 + timestamp = time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime()) + + # 获取保存目录 + save_dir = get_log_folder(get_user(chatbot), plugin_name='chat_history') + + # 处理文件名 + base_name = file_name if file_name else f"聊天记录_{timestamp}" + + # 运行异步任务 + result_files = asyncio.run(main()) + + # 将生成的文件添加到下载区 + for file in result_files: + promote_file_to_downloadzone(file, rename_file=os.path.basename(file), chatbot=chatbot) + + # 如果没有成功保存任何文件,返回错误信息 + if not result_files: + return "保存对话记录失败,请检查错误日志" + + ext_list = [os.path.splitext(f)[1] for f in result_files] + # 返回成功信息和文件路径 + return f"对话历史已保存至以下格式文件:" + "、".join(ext_list) + def gen_file_preview(file_name): try: with open(file_name, 'r', encoding='utf8') as f: @@ -119,12 +264,21 @@ def 对话历史存档(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_ user_request 当前用户的请求信息(IP地址等) """ file_name = plugin_kwargs.get("file_name", None) - if (file_name is not None) and (file_name != "") and (not file_name.endswith('.html')): file_name += '.html' - else: file_name = None - - chatbot.append((None, f"[Local Message] {write_chat_to_file(chatbot, history, file_name)},您可以调用下拉菜单中的“载入对话历史存档”还原当下的对话。")) - yield from update_ui(chatbot=chatbot, history=history) # 刷新界面 # 由于请求gpt需要一段时间,我们先及时地做一次界面更新 + chatbot.append((None, f"[Local Message] {write_chat_to_file_legacy(chatbot, history, file_name)},您可以调用下拉菜单中的“载入对话历史存档”还原当下的对话。")) + try: + chatbot.append((None, f"[Local Message] 正在尝试生成pdf以及word格式的对话存档,请稍等...")) + yield from update_ui(chatbot=chatbot, history=history) # 刷新界面 # 由于请求需要一段时间,我们先及时地做一次界面更新 + lastmsg = f"[Local Message] {write_chat_to_file(chatbot, history, file_name)}。" \ + f"您可以调用下拉菜单中的“载入对话历史会话”还原当下的对话,请注意,目前只支持html格式载入历史。" \ + f"当模型回答中存在表格,将提取表格内容存储为Excel的xlsx格式,如果你提供一些数据,然后输入指令要求模型帮你整理为表格" \ + f"(如“请帮我将下面的数据整理为表格:”),再利用此插件就可以获取到Excel表格。" + yield from update_ui_latest_msg(lastmsg, chatbot, history) # 刷新界面 # 由于请求需要一段时间,我们先及时地做一次界面更新 + except Exception as e: + logger.exception(f"已完成对话存档(pdf和word格式的对话存档生成未成功)。{str(e)}") + lastmsg = "已完成对话存档(pdf和word格式的对话存档生成未成功)。" + yield from update_ui_latest_msg(lastmsg, chatbot, history) # 刷新界面 # 由于请求需要一段时间,我们先及时地做一次界面更新 + return class Conversation_To_File_Wrap(GptAcademicPluginTemplate): def __init__(self): diff --git a/toolbox.py b/toolbox.py index 3f0ca5cf..d2583905 100644 --- a/toolbox.py +++ b/toolbox.py @@ -594,7 +594,7 @@ def on_report_generated(cookies:dict, files:List[str], chatbot:ChatBotWithCookie file_links += ( f'
{f}' ) - chatbot.append(["报告如何远程获取?", f"报告已经添加到右侧“文件下载区”(可能处于折叠状态),请查收。{file_links}"]) + chatbot.append([None, f"已经添加到右侧“文件下载区”(可能处于折叠状态),请查收。您也可以点击以下链接直接下载:{file_links}"]) return cookies, report_files, chatbot