diff --git a/crazy_functional.py b/crazy_functional.py index 03aaaf551..9935102e4 100644 --- a/crazy_functional.py +++ b/crazy_functional.py @@ -352,6 +352,18 @@ def get_crazy_functions(): }) except: print('Load function plugin failed') + + try: + from crazy_functions.交互功能函数模板 import 交互功能模板函数 + function_plugins.update({ + "交互功能模板函数": { + "Color": "stop", + "AsButton": False, + "Function": HotReload(交互功能模板函数) + } + }) + except: + print('Load function plugin failed') try: from crazy_functions.Latex输出PDF结果 import Latex英文纠错加PDF对比 diff --git "a/crazy_functions/\344\272\244\344\272\222\345\212\237\350\203\275\345\207\275\346\225\260\346\250\241\346\235\277.py" "b/crazy_functions/\344\272\244\344\272\222\345\212\237\350\203\275\345\207\275\346\225\260\346\250\241\346\235\277.py" new file mode 100644 index 000000000..d57fc2b0f --- /dev/null +++ "b/crazy_functions/\344\272\244\344\272\222\345\212\237\350\203\275\345\207\275\346\225\260\346\250\241\346\235\277.py" @@ -0,0 +1,63 @@ +from toolbox import CatchException, update_ui +from .crazy_utils import request_gpt_model_in_new_thread_with_ui_alive + + +@CatchException +def 交互功能模板函数(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port): + """ + txt 输入栏用户输入的文本,例如需要翻译的一段话,再例如一个包含了待处理文件的路径 + llm_kwargs gpt模型参数, 如温度和top_p等, 一般原样传递下去就行 + plugin_kwargs 插件模型的参数, 如温度和top_p等, 一般原样传递下去就行 + chatbot 聊天显示框的句柄,用于显示给用户 + history 聊天历史,前情提要 + system_prompt 给gpt的静默提醒 + web_port 当前软件运行的端口号 + """ + history = [] # 清空历史,以免输入溢出 + chatbot.append(("这是什么功能?", "交互功能函数模板。在执行完成之后, 可以将自身的状态存储到cookie中, 等待用户的再次调用。")) + yield from update_ui(chatbot=chatbot, history=history) # 刷新界面 + + state = chatbot._cookies.get('plugin_state_0001', None) # 初始化插件状态 + + if state is None: + chatbot._cookies['lock_plugin'] = 'crazy_functions.交互功能函数模板->交互功能模板函数' # 赋予插件锁定 锁定插件回调路径,当下一次用户提交时,会直接转到该函数 + chatbot._cookies['plugin_state_0001'] = 'wait_user_keyword' # 赋予插件状态 + + chatbot.append(("第一次调用:", "请输入关键词, 我将为您查找相关壁纸, 建议使用英文单词, 插件锁定中,请直接提交即可。")) + yield from update_ui(chatbot=chatbot, history=history) # 刷新界面 + return + + if state == 'wait_user_keyword': + chatbot._cookies['lock_plugin'] = None # 解除插件锁定,避免遗忘导致死锁 + chatbot._cookies['plugin_state_0001'] = None # 解除插件状态,避免遗忘导致死锁 + + # 解除插件锁定 + chatbot.append((f"获取关键词:{txt}", "")) + yield from update_ui(chatbot=chatbot, history=history) # 刷新界面 + page_return = get_image_page_by_keyword(txt) + inputs=inputs_show_user=f"Extract all image urls in this html page, pick the first 5 images and show them with markdown format: \n\n {page_return}" + gpt_say = yield from request_gpt_model_in_new_thread_with_ui_alive( + inputs=inputs, inputs_show_user=inputs_show_user, + llm_kwargs=llm_kwargs, chatbot=chatbot, history=[], + sys_prompt="When you want to show an image, use markdown format. e.g. ![image_description](image_url). If there are no image url provided, answer 'no image url provided'" + ) + chatbot[-1] = [chatbot[-1][0], gpt_say] + yield from update_ui(chatbot=chatbot, history=history) # 刷新界面 + return + + + +# --------------------------------------------------------------------------------- + +def get_image_page_by_keyword(keyword): + import requests + from bs4 import BeautifulSoup + response = requests.get(f'https://wallhaven.cc/search?q={keyword}', timeout=2) + res = "image urls: \n" + for image_element in BeautifulSoup(response.content, 'html.parser').findAll("img"): + try: + res += image_element["data-src"] + res += "\n" + except: + pass + return res diff --git a/main.py b/main.py index e59915228..f8758580c 100644 --- a/main.py +++ b/main.py @@ -45,10 +45,10 @@ def main(): proxy_info = check_proxy(proxies) gr_L1 = lambda: gr.Row().style() - gr_L2 = lambda scale: gr.Column(scale=scale) + gr_L2 = lambda scale, elem_id: gr.Column(scale=scale, elem_id=elem_id) if LAYOUT == "TOP-DOWN": gr_L1 = lambda: DummyWith() - gr_L2 = lambda scale: gr.Row() + gr_L2 = lambda scale, elem_id: gr.Row() CHATBOT_HEIGHT /= 2 cancel_handles = [] @@ -56,12 +56,12 @@ def main(): gr.HTML(title_html) cookies = gr.State(load_chat_cookies()) with gr_L1(): - with gr_L2(scale=2): - chatbot = gr.Chatbot(label=f"当前模型:{LLM_MODEL}") - chatbot.style(height=CHATBOT_HEIGHT) + with gr_L2(scale=2, elem_id="gpt-chat"): + chatbot = gr.Chatbot(label=f"当前模型:{LLM_MODEL}", elem_id="gpt-chatbot") + if LAYOUT == "TOP-DOWN": chatbot.style(height=CHATBOT_HEIGHT) history = gr.State([]) - with gr_L2(scale=1): - with gr.Accordion("输入区", open=True) as area_input_primary: + with gr_L2(scale=1, elem_id="gpt-panel"): + with gr.Accordion("输入区", open=True, elem_id="input-panel") as area_input_primary: with gr.Row(): txt = gr.Textbox(show_label=False, placeholder="Input question here.").style(container=False) with gr.Row(): @@ -71,14 +71,14 @@ def main(): stopBtn = gr.Button("停止", variant="secondary"); stopBtn.style(size="sm") clearBtn = gr.Button("清除", variant="secondary", visible=False); clearBtn.style(size="sm") with gr.Row(): - status = gr.Markdown(f"Tip: 按Enter提交, 按Shift+Enter换行。当前模型: {LLM_MODEL} \n {proxy_info}") - with gr.Accordion("基础功能区", open=True) as area_basic_fn: + status = gr.Markdown(f"Tip: 按Enter提交, 按Shift+Enter换行。当前模型: {LLM_MODEL} \n {proxy_info}", elem_id="state-panel") + with gr.Accordion("基础功能区", open=True, elem_id="basic-panel") as area_basic_fn: with gr.Row(): for k in functional: if ("Visible" in functional[k]) and (not functional[k]["Visible"]): continue variant = functional[k]["Color"] if "Color" in functional[k] else "secondary" functional[k]["Button"] = gr.Button(k, variant=variant) - with gr.Accordion("函数插件区", open=True) as area_crazy_fn: + with gr.Accordion("函数插件区", open=True, elem_id="plugin-panel") as area_crazy_fn: with gr.Row(): gr.Markdown("注意:以下“红颜色”标识的函数插件需从输入区读取路径作为参数.") with gr.Row(): @@ -100,7 +100,7 @@ def main(): with gr.Row(): with gr.Accordion("点击展开“文件上传区”。上传本地文件可供红色函数插件调用。", open=False) as area_file_up: file_upload = gr.Files(label="任何文件, 但推荐上传压缩文件(zip, tar)", file_count="multiple") - with gr.Accordion("更换模型 & SysPrompt & 交互界面布局", open=(LAYOUT == "TOP-DOWN")): + with gr.Accordion("更换模型 & SysPrompt & 交互界面布局", open=(LAYOUT == "TOP-DOWN"), elem_id="interact-panel"): system_prompt = gr.Textbox(show_label=True, placeholder=f"System Prompt", label="System prompt", value=initial_prompt) top_p = gr.Slider(minimum=-0, maximum=1.0, value=1.0, step=0.01,interactive=True, label="Top-p (nucleus sampling)",) temperature = gr.Slider(minimum=-0, maximum=2.0, value=1.0, step=0.01, interactive=True, label="Temperature",) @@ -109,7 +109,7 @@ def main(): md_dropdown = gr.Dropdown(AVAIL_LLM_MODELS, value=LLM_MODEL, label="更换LLM模型/请求源").style(container=False) gr.Markdown(description) - with gr.Accordion("备选输入区", open=True, visible=False) as area_input_secondary: + with gr.Accordion("备选输入区", open=True, visible=False, elem_id="input-panel2") as area_input_secondary: with gr.Row(): txt2 = gr.Textbox(show_label=False, placeholder="Input question here.", label="输入区2").style(container=False) with gr.Row(): @@ -176,16 +176,17 @@ def on_md_dropdown_changed(k): return {chatbot: gr.update(label="当前模型:"+k)} md_dropdown.select(on_md_dropdown_changed, [md_dropdown], [chatbot] ) # 随变按钮的回调函数注册 - def route(k, *args, **kwargs): + def route(request: gr.Request, k, *args, **kwargs): if k in [r"打开插件列表", r"请先从插件列表中选择"]: return - yield from ArgsGeneralWrapper(crazy_fns[k]["Function"])(*args, **kwargs) + yield from ArgsGeneralWrapper(crazy_fns[k]["Function"])(request, *args, **kwargs) click_handle = switchy_bt.click(route,[switchy_bt, *input_combo, gr.State(PORT)], output_combo) click_handle.then(on_report_generated, [cookies, file_upload, chatbot], [cookies, file_upload, chatbot]) cancel_handles.append(click_handle) # 终止按钮的回调函数注册 stopBtn.click(fn=None, inputs=None, outputs=None, cancels=cancel_handles) stopBtn2.click(fn=None, inputs=None, outputs=None, cancels=cancel_handles) - + demo.load(lambda: 0, inputs=None, outputs=None, _js='()=>{ChatBotHeight();}') + # gradio的inbrowser触发不太稳定,回滚代码到原始的浏览器打开函数 def auto_opentab_delay(): import threading, webbrowser, time diff --git a/theme.py b/theme.py index 5ef7e9605..8a0df2ac6 100644 --- a/theme.py +++ b/theme.py @@ -1,6 +1,6 @@ import gradio as gr from toolbox import get_conf -CODE_HIGHLIGHT, ADD_WAIFU = get_conf('CODE_HIGHLIGHT', 'ADD_WAIFU') +CODE_HIGHLIGHT, ADD_WAIFU, LAYOUT = get_conf('CODE_HIGHLIGHT', 'ADD_WAIFU', 'LAYOUT') # gradio可用颜色列表 # gr.themes.utils.colors.slate (石板色) # gr.themes.utils.colors.gray (灰色) @@ -82,20 +82,76 @@ def adjust_theme(): button_cancel_text_color_dark="white", ) + # Layout = "LEFT-RIGHT" + js = """ + + """ + + if LAYOUT=="TOP-DOWN": + js = "" + # 添加一个萌萌的看板娘 if ADD_WAIFU: - js = """ + js += """ """ - gradio_original_template_fn = gr.routes.templates.TemplateResponse - def gradio_new_template_fn(*args, **kwargs): - res = gradio_original_template_fn(*args, **kwargs) - res.body = res.body.replace(b'', f'{js}'.encode("utf8")) - res.init_headers() - return res - gr.routes.templates.TemplateResponse = gradio_new_template_fn # override gradio template + gradio_original_template_fn = gr.routes.templates.TemplateResponse + def gradio_new_template_fn(*args, **kwargs): + res = gradio_original_template_fn(*args, **kwargs) + res.body = res.body.replace(b'', f'{js}'.encode("utf8")) + res.init_headers() + return res + gr.routes.templates.TemplateResponse = gradio_new_template_fn # override gradio template except: set_theme = None print('gradio版本较旧, 不能自定义字体和颜色') diff --git a/toolbox.py b/toolbox.py index 8a48581f4..4bd84299d 100644 --- a/toolbox.py +++ b/toolbox.py @@ -4,6 +4,7 @@ import inspect import re import os +import gradio from latex2mathml.converter import convert as tex2mathml from functools import wraps, lru_cache pj = os.path.join @@ -40,7 +41,7 @@ def ArgsGeneralWrapper(f): """ 装饰器函数,用于重组输入参数,改变输入参数的顺序与结构。 """ - def decorated(cookies, max_length, llm_model, txt, txt2, top_p, temperature, chatbot, history, system_prompt, plugin_advanced_arg, *args): + def decorated(request: gradio.Request, cookies, max_length, llm_model, txt, txt2, top_p, temperature, chatbot, history, system_prompt, plugin_advanced_arg, *args): txt_passon = txt if txt == "" and txt2 != "": txt_passon = txt2 # 引入一个有cookie的chatbot @@ -54,13 +55,21 @@ def decorated(cookies, max_length, llm_model, txt, txt2, top_p, temperature, cha 'top_p':top_p, 'max_length': max_length, 'temperature':temperature, + 'client_ip': request.client.host, } plugin_kwargs = { "advanced_arg": plugin_advanced_arg, } chatbot_with_cookie = ChatBotWithCookies(cookies) chatbot_with_cookie.write_list(chatbot) - yield from f(txt_passon, llm_kwargs, plugin_kwargs, chatbot_with_cookie, history, system_prompt, *args) + if cookies.get('lock_plugin', None) is None: + # 正常状态 + yield from f(txt_passon, llm_kwargs, plugin_kwargs, chatbot_with_cookie, history, system_prompt, *args) + else: + # 处理个别特殊插件的锁定状态 + module, fn_name = cookies['lock_plugin'].split('->') + f_hot_reload = getattr(importlib.import_module(module, fn_name), fn_name) + yield from f_hot_reload(txt_passon, llm_kwargs, plugin_kwargs, chatbot_with_cookie, history, system_prompt, *args) return decorated @@ -68,8 +77,21 @@ def update_ui(chatbot, history, msg='正常', **kwargs): # 刷新界面 """ 刷新用户界面 """ - assert isinstance(chatbot, ChatBotWithCookies), "在传递chatbot的过程中不要将其丢弃。必要时,可用clear将其清空,然后用for+append循环重新赋值。" - yield chatbot.get_cookies(), chatbot, history, msg + assert isinstance(chatbot, ChatBotWithCookies), "在传递chatbot的过程中不要将其丢弃。必要时, 可用clear将其清空, 然后用for+append循环重新赋值。" + cookies = chatbot.get_cookies() + + # 解决插件锁定时的界面显示问题 + if cookies.get('lock_plugin', None): + label = cookies.get('llm_model', "") + " | " + "正在锁定插件" + cookies.get('lock_plugin', None) + chatbot_gr = gradio.update(value=chatbot, label=label) + if cookies.get('label', "") != label: cookies['label'] = label # 记住当前的label + elif cookies.get('label', None): + chatbot_gr = gradio.update(value=chatbot, label=cookies.get('llm_model', "")) + cookies['label'] = None # 清空label + else: + chatbot_gr = chatbot + + yield cookies, chatbot_gr, history, msg def update_ui_lastest_msg(lastmsg, chatbot, history, delay=1): # 刷新界面 """ diff --git a/version b/version index 489d2f8af..0a33d385e 100644 --- a/version +++ b/version @@ -1,5 +1,5 @@ { - "version": 3.43, + "version": 3.44, "show_feature": true, - "new_feature": "修复Azure接口的BUG <-> 完善多语言模块 <-> 完善本地Latex矫错和翻译功能 <-> 增加gpt-3.5-16k的支持 <-> 新增最强Arxiv论文翻译插件 <-> 修复gradio复制按钮BUG <-> 修复PDF翻译的BUG, 新增HTML中英双栏对照 <-> 添加了OpenAI图片生成插件" + "new_feature": "改善UI <-> 修复Azure接口的BUG <-> 完善多语言模块 <-> 完善本地Latex矫错和翻译功能 <-> 增加gpt-3.5-16k的支持 <-> 新增最强Arxiv论文翻译插件 <-> 修复gradio复制按钮BUG <-> 修复PDF翻译的BUG, 新增HTML中英双栏对照 <-> 添加了OpenAI图片生成插件" }