Spaces:
Runtime error
A newer version of the Gradio SDK is available:
5.7.1
实现带二级菜单的插件
一、如何写带有二级菜单的插件
声明一个
Class
,继承父类GptAcademicPluginTemplate
from crazy_functions.plugin_template.plugin_class_template import GptAcademicPluginTemplate from crazy_functions.plugin_template.plugin_class_template import ArgProperty class Demo_Wrap(GptAcademicPluginTemplate): def __init__(self): ...
声明二级菜单中需要的变量,覆盖父类的
define_arg_selection_menu
函数。class Demo_Wrap(GptAcademicPluginTemplate): ... def define_arg_selection_menu(self): """ 定义插件的二级选项菜单 第一个参数,名称`main_input`,参数`type`声明这是一个文本框,文本框上方显示`title`,文本框内部显示`description`,`default_value`为默认值; 第二个参数,名称`advanced_arg`,参数`type`声明这是一个文本框,文本框上方显示`title`,文本框内部显示`description`,`default_value`为默认值; 第三个参数,名称`allow_cache`,参数`type`声明这是一个下拉菜单,下拉菜单上方显示`title`+`description`,下拉菜单的选项为`options`,`default_value`为下拉菜单默认值; """ gui_definition = { "main_input": ArgProperty(title="ArxivID", description="输入Arxiv的ID或者网址", default_value="", type="string").model_dump_json(), "advanced_arg": ArgProperty(title="额外的翻译提示词", description=r"如果有必要, 请在此处给出自定义翻译命令", default_value="", type="string").model_dump_json(), "allow_cache": ArgProperty(title="是否允许从缓存中调取结果", options=["允许缓存", "从头执行"], default_value="允许缓存", description="无", type="dropdown").model_dump_json(), } return gui_definition ...
ArgProperty 中每个条目对应一个参数,
type == "string"
时,使用文本块,type == dropdown
时,使用下拉菜单。注意:
main_input
和advanced_arg
是两个特殊的参数。main_input
会自动与界面右上角的输入区
进行同步,而advanced_arg
会自动与界面右下角的高级参数输入区
同步。除此之外,参数名称可以任意选取。其他细节详见crazy_functions/plugin_template/plugin_class_template.py
。编写插件程序,覆盖父类的
execute
函数。例如:
class Demo_Wrap(GptAcademicPluginTemplate): ... ... def execute(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, user_request): """ 执行插件 plugin_kwargs字典中会包含用户的选择,与上述 `define_arg_selection_menu` 一一对应 """ allow_cache = plugin_kwargs["allow_cache"] advanced_arg = plugin_kwargs["advanced_arg"] if allow_cache == "从头执行": plugin_kwargs["advanced_arg"] = "--no-cache " + plugin_kwargs["advanced_arg"] yield from Latex翻译中文并重新编译PDF(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, user_request)
注册插件
将以下条目插入
crazy_functional.py
即可。注意,与旧插件不同的是,Function
键值应该为None,而Class
键值为上述插件的类名称(Demo_Wrap
)。"新插件": { "Group": "学术", "Color": "stop", "AsButton": True, "Info": "插件说明", "Function": None, "Class": Demo_Wrap, },
已经结束了,启动程序测试吧~!
二、背后的原理(需要JavaScript的前置知识)
(I) 首先介绍三个Gradio官方没有的重要前端函数
主javascript程序common.js
中有三个Gradio官方没有的重要API
get_data_from_gradio_component
这个函数可以获取任意gradio组件的当前值,例如textbox中的字符,dropdown中的当前选项,chatbot当前的对话等等。调用方法举例:// 获取当前的对话 let chatbot = await get_data_from_gradio_component('gpt-chatbot');
get_gradio_component
有时候我们不仅需要gradio组件的当前值,还需要它的label值、是否隐藏、下拉菜单其他可选选项等等,而通过这个函数可以直接获取这个组件的句柄。举例:// 获取下拉菜单组件的句柄 var model_sel = await get_gradio_component("elem_model_sel"); // 获取它的所有属性,包括其所有可选选项 console.log(model_sel.props)
push_data_to_gradio_component
这个函数可以将数据推回gradio组件,例如textbox中的字符,dropdown中的当前选项等等。调用方法举例:// 修改一个按钮上面的文本 push_data_to_gradio_component("btnName", "gradio_element_id", "string"); // 隐藏一个组件 push_data_to_gradio_component({ visible: false, __type__: 'update' }, "plugin_arg_menu", "obj"); // 修改组件label push_data_to_gradio_component({ label: '新label的值', __type__: 'update' }, "gpt-chatbot", "obj") // 第一个参数是value, // - 可以是字符串(调整textbox的文本,按钮的文本); // - 还可以是 { visible: false, __type__: 'update' } 这样的字典(调整visible, label, choices) // 第二个参数是elem_id // 第三个参数是"string" 或者 "obj"
(II) 从点击插件到执行插件的逻辑过程
简述:程序启动时把每个插件的二级菜单编码为BASE64,存储在用户的浏览器前端,用户调用对应功能时,会按照插件的BASE64编码,将平时隐藏的菜单(有选择性地)显示出来。
启动阶段(主函数
main.py
中),遍历每个插件,生成二级菜单的BASE64编码,存入变量register_advanced_plugin_init_code_arr
。def get_js_code_for_generating_menu(self, btnName): define_arg_selection = self.define_arg_selection_menu() DEFINE_ARG_INPUT_INTERFACE = json.dumps(define_arg_selection) return base64.b64encode(DEFINE_ARG_INPUT_INTERFACE.encode('utf-8')).decode('utf-8')
用户加载阶段(主javascript程序
common.js
中),浏览器加载register_advanced_plugin_init_code_arr
,存入本地的字典advanced_plugin_init_code_lib
:advanced_plugin_init_code_lib = {} function register_advanced_plugin_init_code(key, code){ advanced_plugin_init_code_lib[key] = code; }
用户点击插件按钮(主函数
main.py
中)时,仅执行以下javascript代码,唤醒隐藏的二级菜单(生成菜单的代码在common.js
中的generate_menu
函数上):// 生成高级插件的选择菜单 function run_advanced_plugin_launch_code(key){ generate_menu(advanced_plugin_init_code_lib[key], key); } function on_flex_button_click(key){ run_advanced_plugin_launch_code(key); }
click_handle = plugins[k]["Button"].click(None, inputs=[], outputs=None, _js=f"""()=>run_advanced_plugin_launch_code("{k}")""")
当用户点击二级菜单的执行键时,通过javascript脚本模拟点击一个隐藏按钮,触发后续程序(
common.js
中的execute_current_pop_up_plugin
,会把二级菜单中的参数缓存到invisible_current_pop_up_plugin_arg_final
,然后模拟点击invisible_callback_btn_for_plugin_exe
按钮)。隐藏按钮的定义在(主函数main.py
),该隐藏按钮会最终触发route_switchy_bt_with_arg
函数(定义于themes/gui_advanced_plugin_class.py
):click_handle_ng = new_plugin_callback.click(route_switchy_bt_with_arg, [ gr.State(["new_plugin_callback", "usr_confirmed_arg"] + input_combo_order), new_plugin_callback, usr_confirmed_arg, *input_combo ], output_combo)
最后,
route_switchy_bt_with_arg
中,会搜集所有用户参数,统一集中到plugin_kwargs
参数中,并执行对应插件的execute
函数。