langchain-ChatGLM源码阅读:webui.py
样式定制
使用gradio设置页面的视觉组件和交互逻辑
import gradio as gr
import shutilfrom chains.local_doc_qa import LocalDocQA
from configs.model_config import *
import nltk
import models.shared as shared
from models.loader.args import parser
from models.loader import LoaderCheckPoint
import osnltk.data.path = [NLTK_DATA_PATH] + nltk.data.path
embedding_model_dict_list = list(embedding_model_dict.keys())llm_model_dict_list = list(llm_model_dict.keys())local_doc_qa = LocalDocQA()
# 记录运行日志到 CSV文件
flag_csv_logger = gr.CSVLogger()block_css = """.importantButton {background: linear-gradient(45deg, #7e0570,#5d1c99, #6e00ff) !important;border: none !important;
}
.importantButton:hover {background: linear-gradient(45deg, #ff00e0,#8500ff, #6e00ff) !important;border: none !important;
}"""webui_title = """
# 🎉langchain-ChatGLM WebUI🎉
👍 [https://github.com/imClumsyPanda/langchain-ChatGLM](https://github.com/imClumsyPanda/langchain-ChatGLM)
"""
# 检索知识库
default_vs = get_vs_list()[0] if len(get_vs_list()) > 1 else "为空"
init_message = f"""欢迎使用 langchain-ChatGLM Web UI!请在右侧切换模式,目前支持直接与 LLM 模型对话或基于本地知识库问答。知识库问答模式,选择知识库名称后,即可开始问答,当前知识库{default_vs},如有需要可以在选择知识库名称后上传文件/文件夹至知识库。知识库暂不支持文件删除,该功能将在后续版本中推出。
"""# 初始化消息
model_status = init_model()default_theme_args = dict(font=["Source Sans Pro", 'ui-sans-serif', 'system-ui', 'sans-serif'],font_mono=['IBM Plex Mono', 'ui-monospace', 'Consolas', 'monospace'],
)# Blocks API用于构建交互式界面,创建一个名为demo的块。该块包含三个状态变量:
# vs_path、file_status和model_status
with gr.Blocks(css=block_css, theme=gr.themes.Default(**default_theme_args)) as demo:
# gr.State是Gradio提供的一种不可见控件,用于存储同一用户运行演示时的会话状态。
# 当用户刷新页面时,State变量的值被清除。它的目的是在后台存储一些变量方便访问和交互。vs_path, file_status, model_status = gr.State(os.path.join(KB_ROOT_PATH, get_vs_list()[0], "vector_store") if len(get_vs_list()) > 1 else ""), gr.State(""), gr.State(model_status)gr.Markdown(webui_title)# Tab用来控制功能切换的标签with gr.Tab("对话"):# gr.Row() 会创建一个水平方向的行,之后的组件默认会按顺序水平排列在这一行内# 这里是将对话框和选择框放在同一行,做了一个大对齐with gr.Row():# 这一列的宽度是默认列宽的 10 倍with gr.Column(scale=10):# 最上面的框chatbot = gr.Chatbot([[None, init_message], [None, model_status.value]],elem_id="chat-box",show_label=False).style(height=750)# 最下面的输入框,注意可通过调用.style()方法来设置组件的 CSS 样式query = gr.Textbox(show_label=False,placeholder="请输入提问内容,按回车进行提交").style(container=False)with gr.Column(scale=5):# Radio 组件实现了一个单选按钮组,可以通过mode变量得到用户的选择mode = gr.Radio(["LLM 对话", "知识库问答", "Bing搜索问答"],label="请选择使用模式",value="知识库问答", )# 使用 Grace 的 Accordion 组件创建了一个折叠面板# visible 属性来显示或隐藏这个面板knowledge_set = gr.Accordion("知识库设定", visible=False)vs_setting = gr.Accordion("配置知识库")# 为单选按钮绑定 change 事件处理函数 change_modemode.change(fn=change_mode,# change_mode函数的输入输出inputs=[mode, chatbot],outputs=[vs_setting, knowledge_set, chatbot])with vs_setting:vs_refresh = gr.Button("更新已有知识库选项")# 下拉框# interactive=True时,组件变为交互可用状态,用户可以修改或选择select_vs = gr.Dropdown(get_vs_list(),label="请选择要加载的知识库",interactive=True,value=get_vs_list()[0] if len(get_vs_list()) > 0 else None)vs_name = gr.Textbox(label="请输入新建知识库名称,当前知识库命名暂不支持中文",lines=1,interactive=True,visible=True)vs_add = gr.Button(value="添加至知识库选项", visible=True)vs_delete = gr.Button("删除本知识库", visible=False)file2vs = gr.Column(visible=False)with file2vs:# load_vs = gr.Button("加载知识库")gr.Markdown("向知识库中添加文件")sentence_size = gr.Number(value=SENTENCE_SIZE, precision=0,label="文本入库分句长度限制",interactive=True, visible=True)with gr.Tab("上传文件"):files = gr.File(label="添加文件",file_types=['.txt', '.md', '.docx', '.pdf', '.png', '.jpg', ".csv"],file_count="multiple",show_label=False)load_file_button = gr.Button("上传文件并加载知识库")with gr.Tab("上传文件夹"):folder_files = gr.File(label="添加文件",file_count="directory",show_label=False)load_folder_button = gr.Button("上传文件夹并加载知识库")with gr.Tab("删除文件"):files_to_delete = gr.CheckboxGroup(choices=[],label="请从知识库已有文件中选择要删除的文件",interactive=True)delete_file_button = gr.Button("从知识库中删除选中文件")# 这里绑定了select_vs,后面还有一个test,因此绑定的refresh_vs_list函数要返回两个值# select_vs此时的值被改变了,因此触发了change动作绑定的change_vs_name_input函数vs_refresh.click(fn=refresh_vs_list,inputs=[],outputs=select_vs)vs_add.click(fn=add_vs_name,inputs=[vs_name, chatbot],outputs=[select_vs, vs_name, vs_add, file2vs, chatbot, vs_delete])vs_delete.click(fn=delete_vs,inputs=[select_vs, chatbot],outputs=[select_vs, vs_name, vs_add, file2vs, chatbot, vs_delete])select_vs.change(fn=change_vs_name_input,inputs=[select_vs, chatbot],outputs=[vs_name, vs_add, file2vs, vs_path, chatbot, files_to_delete, vs_delete])load_file_button.click(get_vector_store,show_progress=True,inputs=[select_vs, files, sentence_size, chatbot, vs_add, vs_add],outputs=[vs_path, files, chatbot, files_to_delete], )load_folder_button.click(get_vector_store,show_progress=True,inputs=[select_vs, folder_files, sentence_size, chatbot, vs_add,vs_add],outputs=[vs_path, folder_files, chatbot, files_to_delete], )flag_csv_logger.setup([query, vs_path, chatbot, mode], "flagged")query.submit(get_answer,[query, vs_path, chatbot, mode],[chatbot, query])delete_file_button.click(delete_file,show_progress=True,inputs=[select_vs, files_to_delete, chatbot],outputs=[files_to_delete, chatbot])with gr.Tab("知识库测试 Beta"):# gr.Row() 会创建一个水平方向的行,之后的组件默认会按顺序水平排列在这一行内# 这里是将对话框和选择框放在同一行,做了一个大对齐with gr.Row():# 这一列的宽度是默认列宽的 10 倍with gr.Column(scale=10):# 最上面的框chatbot = gr.Chatbot([[None, knowledge_base_test_mode_info]],elem_id="chat-box",show_label=False).style(height=750)# 最下面的输入框query = gr.Textbox(show_label=False,placeholder="请输入提问内容,按回车进行提交").style(container=False)with gr.Column(scale=5):# Radio 组件实现了一个单选按钮组,可以通过mode变量得到用户的选择,这里直接设不可见mode = gr.Radio(["知识库测试"], # "知识库问答",label="请选择使用模式",value="知识库测试",visible=False)# 使用 Grace 的 Accordion 组件创建了一个折叠面板knowledge_set = gr.Accordion("知识库设定", visible=True)vs_setting = gr.Accordion("配置知识库", visible=True)# 为单选按钮绑定 change 事件处理函数 change_modemode.change(fn=change_mode,inputs=[mode, chatbot],outputs=[vs_setting, knowledge_set, chatbot])with knowledge_set:# 数字输入组件score_threshold = gr.Number(value=VECTOR_SEARCH_SCORE_THRESHOLD,label="知识相关度 Score 阈值,分值越低匹配度越高",precision=0,interactive=True)vector_search_top_k = gr.Number(value=VECTOR_SEARCH_TOP_K, precision=0,label="获取知识库内容条数", interactive=True)chunk_conent = gr.Checkbox(value=False,label="是否启用上下文关联",interactive=True)chunk_sizes = gr.Number(value=CHUNK_SIZE, precision=0,label="匹配单段内容的连接上下文后最大长度",interactive=True, visible=False)chunk_conent.change(fn=change_chunk_conent,inputs=[chunk_conent, gr.Textbox(value="chunk_conent", visible=False), chatbot],outputs=[chunk_sizes, chatbot])with vs_setting:vs_refresh = gr.Button("更新已有知识库选项")# 不同Tab下标志同一动作开始的刷新键可以共用一个名字,但是各Tab下动作影响的# 组件需要起不同的名字,并且在函数返回时依次赋值给各个组件并更新select_vs_test = gr.Dropdown(get_vs_list(),label="请选择要加载的知识库",interactive=True,value=get_vs_list()[0] if len(get_vs_list()) > 0 else None)vs_name = gr.Textbox(label="请输入新建知识库名称,当前知识库命名暂不支持中文",lines=1,interactive=True,visible=True)vs_add = gr.Button(value="添加至知识库选项", visible=True)file2vs = gr.Column(visible=False)with file2vs:# load_vs = gr.Button("加载知识库")gr.Markdown("向知识库中添加单条内容或文件")sentence_size = gr.Number(value=SENTENCE_SIZE, precision=0,label="文本入库分句长度限制",interactive=True, visible=True)with gr.Tab("上传文件"):files = gr.File(label="添加文件",file_types=['.txt', '.md', '.docx', '.pdf'],file_count="multiple",show_label=False)load_file_button = gr.Button("上传文件并加载知识库")with gr.Tab("上传文件夹"):folder_files = gr.File(label="添加文件",# file_types=['.txt', '.md', '.docx', '.pdf'],file_count="directory",show_label=False)load_folder_button = gr.Button("上传文件夹并加载知识库")with gr.Tab("添加单条内容"):one_title = gr.Textbox(label="标题", placeholder="请输入要添加单条段落的标题", lines=1)one_conent = gr.Textbox(label="内容", placeholder="请输入要添加单条段落的内容", lines=5)one_content_segmentation = gr.Checkbox(value=True, label="禁止内容分句入库",interactive=True)load_conent_button = gr.Button("添加内容并加载知识库")# 将上传的文件保存到content文件夹下,并更新下拉框,注意这里是select_vs_testvs_refresh.click(fn=refresh_vs_list,inputs=[],outputs=select_vs_test)vs_add.click(fn=add_vs_name,inputs=[vs_name, chatbot],outputs=[select_vs_test, vs_name, vs_add, file2vs, chatbot])select_vs_test.change(fn=change_vs_name_input,inputs=[select_vs_test, chatbot],outputs=[vs_name, vs_add, file2vs, vs_path, chatbot])load_file_button.click(get_vector_store,show_progress=True,inputs=[select_vs_test, files, sentence_size, chatbot, vs_add, vs_add],outputs=[vs_path, files, chatbot], )load_folder_button.click(get_vector_store,show_progress=True,inputs=[select_vs_test, folder_files, sentence_size, chatbot, vs_add,vs_add],outputs=[vs_path, folder_files, chatbot], )load_conent_button.click(get_vector_store,show_progress=True,inputs=[select_vs_test, one_title, sentence_size, chatbot,one_conent, one_content_segmentation],outputs=[vs_path, files, chatbot], )flag_csv_logger.setup([query, vs_path, chatbot, mode], "flagged")query.submit(get_answer,[query, vs_path, chatbot, mode, score_threshold, vector_search_top_k, chunk_conent,chunk_sizes],[chatbot, query])with gr.Tab("模型配置"):llm_model = gr.Radio(llm_model_dict_list,label="LLM 模型",value=LLM_MODEL,interactive=True)no_remote_model = gr.Checkbox(shared.LoaderCheckPoint.no_remote_model,label="加载本地模型",interactive=True)llm_history_len = gr.Slider(0, 10,value=LLM_HISTORY_LEN,step=1,label="LLM 对话轮数",interactive=True)use_ptuning_v2 = gr.Checkbox(USE_PTUNING_V2,label="使用p-tuning-v2微调过的模型",interactive=True)use_lora = gr.Checkbox(USE_LORA,label="使用lora微调的权重",interactive=True)embedding_model = gr.Radio(embedding_model_dict_list,label="Embedding 模型",value=EMBEDDING_MODEL,interactive=True)top_k = gr.Slider(1, 20, value=VECTOR_SEARCH_TOP_K, step=1,label="向量匹配 top k", interactive=True)load_model_button = gr.Button("重新加载模型")load_model_button.click(reinit_model, show_progress=True,inputs=[llm_model, embedding_model, llm_history_len, no_remote_model, use_ptuning_v2,use_lora, top_k, chatbot], outputs=chatbot)# load_knowlege_button = gr.Button("重新构建知识库")# load_knowlege_button.click(reinit_vector_store, show_progress=True,# inputs=[select_vs, chatbot], outputs=chatbot)demo.load(# 加载初始化逻辑refresh_vs_list,并传入输出组件fn=refresh_vs_list,inputs=None,outputs=[select_vs, select_vs_test],# 加入后台执行队列queue=True,show_progress=False,)(demo# 限制回调函数的最多并发执行数为3以避免应用过载.queue(concurrency_count=3).launch(server_name='172.20.63.134',server_port=7860,show_api=False,share=False,inbrowser=False))
回调函数具体实现
监听到前端的事件后调用的回调函数,负责实现前后端交互。需要注意的一点是,chatbot中显示新的聊天内容并不是在原来的基础上添加,而是从头到尾的重新打印,所以基本上每个函数都要传旧的history和返回新的history。
获取知识库列表
def get_vs_list():# 返回当前最新的经排序后的知识库列表lst_default = ["新建知识库"]if not os.path.exists(KB_ROOT_PATH):return lst_defaultlst = os.listdir(KB_ROOT_PATH)if not lst:return lst_defaultlst.sort()return lst_default + lst
获取不同模式下的回答
- query.submit动作绑定的函数
def get_answer(query, vs_path, history, mode, score_threshold=VECTOR_SEARCH_SCORE_THRESHOLD,vector_search_top_k=VECTOR_SEARCH_TOP_K, chunk_conent: bool = True,chunk_size=CHUNK_SIZE, streaming: bool = STREAMING):if mode == "Bing搜索问答":for resp, history in local_doc_qa.get_search_result_based_answer(query=query, chat_history=history, streaming=streaming):source = "\n\n"source += "".join([f"""<details> <summary>出处 [{i + 1}] <a href="{doc.metadata["source"]}" target="_blank">{doc.metadata["source"]}</a> </summary>\n"""f"""{doc.page_content}\n"""f"""</details>"""for i, doc inenumerate(resp["source_documents"])])history[-1][-1] += sourceyield history, ""# "index.faiss"是向量索引的文件名,可以根据文件存在性,来判断向量索引是否需要构建。elif mode == "知识库问答" and vs_path is not None and os.path.exists(vs_path) and "index.faiss" in os.listdir(vs_path):# 注意score_threshold, vector_search_top_k, chunk_conent,chunk_size# 这几个参数压根没传进get_knowledge_based_answerfor resp, history in local_doc_qa.get_knowledge_based_answer(query=query, vs_path=vs_path, chat_history=history, streaming=streaming):source = "\n\n"source += "".join([f"""<details> <summary>出处 [{i + 1}] {os.path.split(doc.metadata["source"])[-1]}</summary>\n"""f"""{doc.page_content}\n"""f"""</details>"""for i, doc inenumerate(resp["source_documents"])])history[-1][-1] += source# 分别赋值给chatbot和queryyield history, ""elif mode == "知识库测试":if os.path.exists(vs_path):# 使用了全部传入参数,但是只是用于测试文件匹配度不能回答resp, prompt = local_doc_qa.get_knowledge_based_conent_test(query=query, vs_path=vs_path,score_threshold=score_threshold,vector_search_top_k=vector_search_top_k,chunk_conent=chunk_conent,chunk_size=chunk_size)if not resp["source_documents"]:yield history + [[query,"根据您的设定,没有匹配到任何内容,请确认您设置的知识相关度 Score 阈值是否过小或其他参数是否正确。"]], ""else:source = "\n".join([f"""<details open> <summary>【知识相关度 Score】:{doc.metadata["score"]} - 【出处{i + 1}】: {os.path.split(doc.metadata["source"])[-1]} </summary>\n"""f"""{doc.page_content}\n"""f"""</details>"""for i, doc inenumerate(resp["source_documents"])])history.append([query, "以下内容为知识库中满足设置条件的匹配结果:\n\n" + source])yield history, ""else:yield history + [[query,"请选择知识库后进行测试,当前未选择知识库。"]], ""else:answer_result_stream_result = local_doc_qa.llm_model_chain({"prompt": query, "history": history, "streaming": streaming})for answer_result in answer_result_stream_result['answer_result_stream']:resp = answer_result.llm_output["answer"]history = answer_result.historyhistory[-1][-1] = respyield history, ""logger.info(f"flagging: username={FLAG_USER_NAME},query={query},vs_path={vs_path},mode={mode},history={history}")flag_csv_logger.flag([query, vs_path, history, mode], username=FLAG_USER_NAME)
模型初始化
- 初始化model_status
def init_model():args = parser.parse_args()args_dict = vars(args)shared.loaderCheckPoint = LoaderCheckPoint(args_dict)llm_model_ins = shared.loaderLLM()llm_model_ins.history_len = LLM_HISTORY_LENtry:local_doc_qa.init_cfg(llm_model=llm_model_ins)answer_result_stream_result = local_doc_qa.llm_model_chain({"prompt": "你好", "history": [], "streaming": False})for answer_result in answer_result_stream_result['answer_result_stream']:print(answer_result.llm_output)reply = """模型已成功加载,可以开始对话,或从右侧选择模式后开始对话"""logger.info(reply)return replyexcept Exception as e:logger.error(e)reply = """模型未成功加载,请到页面左上角"模型配置"选项卡中重新选择后点击"加载模型"按钮"""if str(e) == "Unknown platform: darwin":logger.info("该报错可能因为您使用的是 macOS 操作系统,需先下载模型至本地后执行 Web UI,具体方法请参考项目 README 中本地部署方法及常见问题:"" https://github.com/imClumsyPanda/langchain-ChatGLM")else:logger.info(reply)return reply
模型重加载
- load_model_button.click动作绑定的函数
def reinit_model(llm_model, embedding_model, llm_history_len, no_remote_model, use_ptuning_v2, use_lora, top_k,history):try:llm_model_ins = shared.loaderLLM(llm_model, no_remote_model, use_ptuning_v2)llm_model_ins.history_len = llm_history_lenlocal_doc_qa.init_cfg(llm_model=llm_model_ins,embedding_model=embedding_model,top_k=top_k)model_status = """模型已成功重新加载,可以开始对话,或从右侧选择模式后开始对话"""logger.info(model_status)except Exception as e:logger.error(e)model_status = """模型未成功重新加载,请到页面左上角"模型配置"选项卡中重新选择后点击"加载模型"按钮"""logger.info(model_status)# 更新chatbot的值return history + [[None, model_status]]
文件向量化
- load_file_button.click和load_folder_button.click动作绑定的函数
def get_vector_store(vs_id, files, sentence_size, history, one_conent, one_content_segmentation):vs_path = os.path.join(KB_ROOT_PATH, vs_id, "vector_store")filelist = []if local_doc_qa.llm_model_chain and local_doc_qa.embeddings:if isinstance(files, list):for file in files:filename = os.path.split(file.name)[-1]shutil.move(file.name, os.path.join(KB_ROOT_PATH, vs_id, "content", filename))filelist.append(os.path.join(KB_ROOT_PATH, vs_id, "content", filename))vs_path, loaded_files = local_doc_qa.init_knowledge_vector_store(filelist, vs_path, sentence_size)else:vs_path, loaded_files = local_doc_qa.one_knowledge_add(vs_path, files, one_conent, one_content_segmentation,sentence_size)if len(loaded_files):file_status = f"已添加 {'、'.join([os.path.split(i)[-1] for i in loaded_files if i])} 内容至知识库,并已加载知识库,请开始提问"else:file_status = "文件未成功加载,请重新上传文件"else:file_status = "模型未完成加载,请先在加载模型后再导入文件"vs_path = Nonelogger.info(file_status)return vs_path, None, history + [[None, file_status]], \gr.update(choices=local_doc_qa.list_file_from_vector_store(vs_path) if vs_path else [])
选择知识库
- select_vs.change和select_vs_test.change动作所绑定的函数,如果刷新知识库会有bug
def change_vs_name_input(vs_id, history):if vs_id == "新建知识库":return gr.update(visible=True), gr.update(visible=True), gr.update(visible=False), None, history, \gr.update(choices=[]), gr.update(visible=False)else:# 刷新时这地方有bug,直接传了个列表过去,逆天vs_path = os.path.join(KB_ROOT_PATH, vs_id, "vector_store")if "index.faiss" in os.listdir(vs_path):file_status = f"已加载知识库{vs_id},请开始提问"return gr.update(visible=False), gr.update(visible=False), gr.update(visible=True), \vs_path, history + [[None, file_status]], \gr.update(choices=local_doc_qa.list_file_from_vector_store(vs_path), value=[]), \gr.update(visible=True)else:file_status = f"已选择知识库{vs_id},当前知识库中未上传文件,请先上传文件后,再开始提问"return gr.update(visible=False), gr.update(visible=False), gr.update(visible=True), \vs_path, history + [[None, file_status]], \gr.update(choices=[], value=[]), gr.update(visible=True, value=[])knowledge_base_test_mode_info = ("【注意】\n\n""1. 您已进入知识库测试模式,您输入的任何对话内容都将用于进行知识库查询,""并仅输出知识库匹配出的内容及相似度分值和及输入的文本源路径,查询的内容并不会进入模型查询。\n\n""2. 知识相关度 Score 经测试,建议设置为 500 或更低,具体设置情况请结合实际使用调整。""""3. 使用"添加单条数据"添加文本至知识库时,内容如未分段,则内容越多越会稀释各查询内容与之关联的score阈值。\n\n""""4. 单条内容长度建议设置在100-150左右。\n\n""5. 本界面用于知识入库及知识匹配相关参数设定,但当前版本中,""本界面中修改的参数并不会直接修改对话界面中参数,仍需前往`configs/model_config.py`修改后生效。""相关参数将在后续版本中支持本界面直接修改。")
切换模型
- mode.change动作绑定的函数
def change_mode(mode, history):# 调整vs_setting, knowledge_set, chatbot的可见性if mode == "知识库问答":return gr.update(visible=True), gr.update(visible=False), history# + [[None, "【注意】:您已进入知识库问答模式,您输入的任何查询都将进行知识库查询,然后会自动整理知识库关联内容进入模型查询!!!"]]elif mode == "知识库测试":return gr.update(visible=True), gr.update(visible=True), [[None,knowledge_base_test_mode_info]]else:return gr.update(visible=False), gr.update(visible=False), history
启用上下文
- chunk_conent.change动作绑定的函数
def change_chunk_conent(mode, label_conent, history):# 更新chunk_sizes, chatbotconent = ""if "chunk_conent" in label_conent:conent = "搜索结果上下文关联"elif "one_content_segmentation" in label_conent: # 这里没用上,可以先留着conent = "内容分段入库"if mode:return gr.update(visible=True), history + [[None, f"【已开启{conent}】"]]else:return gr.update(visible=False), history + [[None, f"【已关闭{conent}】"]]
创建新的知识库
- vs_add.click动作所绑定的函数
def add_vs_name(vs_name, chatbot):if vs_name is None or vs_name.strip() == "":vs_status = "知识库名称不能为空,请重新填写知识库名称"chatbot = chatbot + [[None, vs_status]]return gr.update(visible=True), gr.update(visible=True), gr.update(visible=True), gr.update(visible=False), chatbot, gr.update(visible=False)elif vs_name in get_vs_list():vs_status = "与已有知识库名称冲突,请重新选择其他名称后提交"chatbot = chatbot + [[None, vs_status]]return gr.update(visible=True), gr.update(visible=True), gr.update(visible=True), gr.update(visible=False), chatbot, gr.update(visible=False)else:# 新建上传文件存储路径if not os.path.exists(os.path.join(KB_ROOT_PATH, vs_name, "content")):os.makedirs(os.path.join(KB_ROOT_PATH, vs_name, "content"))# 新建向量库存储路径if not os.path.exists(os.path.join(KB_ROOT_PATH, vs_name, "vector_store")):os.makedirs(os.path.join(KB_ROOT_PATH, vs_name, "vector_store"))vs_status = f"""已新增知识库"{vs_name}",将在上传文件并载入成功后进行存储。请在开始对话前,先完成文件上传。 """chatbot = chatbot + [[None, vs_status]]# 更新select_vs, vs_name, vs_add, file2vs, chatbot, vs_delete这几个组件return gr.update(visible=True, choices=get_vs_list(), value=vs_name), gr.update(visible=False), gr.update(visible=False), gr.update(visible=True), chatbot, gr.update(visible=True)
更新知识库
- vs_refresh.click和demo.load动作绑定的事件
def refresh_vs_list():# 更新select_vs, select_vs_test两个组件return gr.update(choices=get_vs_list()), gr.update(choices=get_vs_list())
删除文件及整个知识库
def delete_file(vs_id, files_to_delete, chatbot):vs_path = os.path.join(KB_ROOT_PATH, vs_id, "vector_store")content_path = os.path.join(KB_ROOT_PATH, vs_id, "content")docs_path = [os.path.join(content_path, file) for file in files_to_delete]status = local_doc_qa.delete_file_from_vector_store(vs_path=vs_path,filepath=docs_path)if "fail" not in status:for doc_path in docs_path:if os.path.exists(doc_path):os.remove(doc_path)rested_files = local_doc_qa.list_file_from_vector_store(vs_path)if "fail" in status:vs_status = "文件删除失败。"elif len(rested_files) > 0:vs_status = "文件删除成功。"else:vs_status = f"文件删除成功,知识库{vs_id}中无已上传文件,请先上传文件后,再开始提问。"logger.info(",".join(files_to_delete) + vs_status)chatbot = chatbot + [[None, vs_status]]return gr.update(choices=local_doc_qa.list_file_from_vector_store(vs_path), value=[]), chatbotdef delete_vs(vs_id, chatbot):try:shutil.rmtree(os.path.join(KB_ROOT_PATH, vs_id))status = f"成功删除知识库{vs_id}"logger.info(status)chatbot = chatbot + [[None, status]]return gr.update(choices=get_vs_list(), value=get_vs_list()[0]), gr.update(visible=True), gr.update(visible=True), \gr.update(visible=False), chatbot, gr.update(visible=False)except Exception as e:logger.error(e)status = f"删除知识库{vs_id}失败"chatbot = chatbot + [[None, status]]return gr.update(visible=True), gr.update(visible=False), gr.update(visible=False), \gr.update(visible=True), chatbot, gr.update(visible=True)
相关文章:
langchain-ChatGLM源码阅读:webui.py
样式定制 使用gradio设置页面的视觉组件和交互逻辑 import gradio as gr import shutilfrom chains.local_doc_qa import LocalDocQA from configs.model_config import * import nltk import models.shared as shared from models.loader.args import parser from models.load…...
<C++>二、 类和对象
1.面向对象和面向过程 C语言是面向过程的,关注的是过程,分析出求解问题的步骤, 通过函数调用逐步解决问题。 C是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。 2. C类 C…...
【HttpRunnerManager】搭建接口自动化测试平台实战
目录 一、需要准备的知识点 二、我搭建的环境 三、搭建过程 四、访问链接 五、两个问题点 【整整200集】超超超详细的Python接口自动化测试进阶教程,真实模拟企业项目实战!! 一、需要准备的知识点 1. linux: 安装 python3、nginx 安装和…...
【adb】adb常用命令
Android Debug Bridge (adb) Android 调试桥 (adb) 是一种功能多样的命令行工具,可让您与设备进行通信。adb 命令可用于执行各种设备操作,例如安装和调试应用。adb 提供对 Unix shell(可用来在设备上运行各种命令)的访问权限。它…...
SAP 委外副产品业务
SAP 委外副产品业务 1.订单bom设置数量为负 2.采购收货时,副产品O库存增加,545 O 借:原材料 贷:委外加工-发出材料 3.从O库存调拨回本地库存,542...
高并发编程-2. 并发级别
此文章为笔记,为阅读其他文章的感受、补充、记录、练习、汇总,非原创,感谢每个知识分享者。 原文 文章目录 阻塞无饥饿(Starvation-Free)无障碍(Obstruction-Free)无锁(Lock-Free)无等待 由于临界区的存在,多线程之间的并发必须受…...
牛客网Verilog刷题——VL47
牛客网Verilog刷题——VL47 题目答案 题目 实现4bit位宽的格雷码计数器。 电路的接口如下图所示: 输入输出描述: 信号类型输入/输出位宽描述clkwireIntput1时钟信号rst_nwireIntput1异步复位信号,低电平有效gray_outregOutput4输出格雷码计数…...
Redis以及Java使用Redis
一、Redis的安装 Redis是一个基于内存的 key-value 结构数据库。 基于内存存储,读写性能高 适合存储热点数据(热点商品、资讯、新闻) 企业应用广泛 官网:https://redis.io 中文网:https://www.redis.net.cn/ Redis…...
Apipost教程?一篇文章玩转Apipost
你是否经常遇到接口开发过程中的各种问题?或许你曾为接口测试与调试的繁琐流程而烦恼。不要担心!今天我将向大家介绍一款功能强大、易于上手的接口测试工具——Apipost,并带你深入了解如何玩转它,轻松实现接口测试与调试。 什么是…...
微信小程序开发学习之--地图绘制行政区域图
不知道大家有没有感觉就是在做微信小程序地图功能时刚刚接触时候真的感觉好迷茫呀,文档看不懂,资料找不到,就很难受呀,比如我现在的功能就想想绘制出一个区域的轮廓图,主要是为了显眼,效果图如下࿱…...
在windows下安装ruby使用gem
在windows下安装ruby使用gem 1.下载安装ruby环境2.使用gem3.gem换源 1.下载安装ruby环境 ruby下载地址 选择合适的版本进行下载和安装: 在安装的时候,请勾选Add Ruby executables to your PATH这个选项,添加环境变量: 安装Ruby成…...
【Ajax】笔记-设置CORS响应头实现跨域
CORS CORS CORS是什么? CORS(Cross-Origin Resource Sharing),跨域资源共享。CORS是官方的跨域解决方案,它的特点是不需要在客户端做任何特殊的操作,完全在服务器中进行处理,支持get和post请求。跨域资源共享标准新增了一组HTTP首…...
实现Feed流的三种模式:拉模式、推模式和推拉结合模式
在互联网产品中,Feed流是一种常见的功能,它可以帮助我们实时获取我们关注的用户的最新动态。Feed流的实现有多种模式,包括拉模式、推模式和推拉结合模式。在本文中,我们将详细介绍这三种模式,并通过Java代码示例来实现…...
Vue中使用Typescript及Typescript基础
准备工作 新建一个基于ts的vue项目 通过官方脚手架构建安装 # 1. 如果没有安装 Vue CLI 就先安装 npm install --global vue/cli最新的Vue CLI工具允许开发者 使用 TypeScript 集成环境 创建新项目。 只需运行vue create my-app 然后选择选项,箭头键选择 Manuall…...
MySQL数据库 【索引事务】
目录 一、概念 二、索引的优缺点 1、索引的优点 2、索引的缺陷 三、索引的使用 1、查看索引 2、创建索引 3、删除索引 四、索引底层的数据结构 1、B树 2、B树 五、索引事务 1、概念和回滚 2、事务的使用 3、事务的基本特性 4、并发会遇到的问题 (…...
源码阅读:classnames
源码阅读:classnames 源码阅读:classnames简介源码解读indexdedupebind类型声明 学习与收获 源码阅读:classnames 简介 classnames 一个简单的 JavaScript 实用程序,用于有条件地将类名连接在一起。 可以通过 npm 包管理器从 n…...
【解惑笔记】树莓派+OpenCV+YOLOv5目标检测(Pytorch框架)
【学习资料】 子豪兄的零基础树莓派教程https://github.com/TommyZihao/ZihaoTutorialOfRaspberryPi/blob/master/%E7%AC%AC2%E8%AE%B2%EF%BC%9A%E6%A0%91%E8%8E%93%E6%B4%BE%E6%96%B0%E6%89%8B%E6%97%A0%E7%97%9B%E5%BC%80%E6%9C%BA%E6%8C%87%E5%8D%97.md#%E7%83%A7%E5%BD%95…...
PostgreSQL中如何配置Huge page的数量
在了解如在PG中如何配置大页之前,我们先要对大页进行一定的了解,为什么要配置大页,配置大页的好处有哪些。 我们日常的操作系统中,程序不直接使用内存,而是使用虚拟内存地址来处理内存分配,避免计算的复杂…...
Mysql之binlog日志浅析
一、binlog日志简介 Binlog是MySQL数据库中的二进制日志,用于记录数据库中所有修改操作,包括增删改等操作。binlog以二进制格式保存,可以通过解析binlog文件来查看数据库的操作历史记录。binlog日志可以用于数据恢复、数据备份、数据同步等场…...
js 生成器函数
生成器函数(Generator Function):生成器函数是一种特殊的函数,可以通过yield关键字来暂停和恢复函数的执行,从而实现惰性计算和迭代器的功能。在例子中,我们定义了一个fibonacci生成器函数,它使…...
Linux简单的操作
ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...
2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面
代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口(适配服务端返回 Token) export const login async (code, avatar) > {const res await http…...
CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云
目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...
Unity | AmplifyShaderEditor插件基础(第七集:平面波动shader)
目录 一、👋🏻前言 二、😈sinx波动的基本原理 三、😈波动起来 1.sinx节点介绍 2.vertexPosition 3.集成Vector3 a.节点Append b.连起来 4.波动起来 a.波动的原理 b.时间节点 c.sinx的处理 四、🌊波动优化…...
Java编程之桥接模式
定义 桥接模式(Bridge Pattern)属于结构型设计模式,它的核心意图是将抽象部分与实现部分分离,使它们可以独立地变化。这种模式通过组合关系来替代继承关系,从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...
Go 并发编程基础:通道(Channel)的使用
在 Go 中,Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式,用于在多个 Goroutine 之间传递数据,从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...
快刀集(1): 一刀斩断视频片头广告
一刀流:用一个简单脚本,秒杀视频片头广告,还你清爽观影体验。 1. 引子 作为一个爱生活、爱学习、爱收藏高清资源的老码农,平时写代码之余看看电影、补补片,是再正常不过的事。 电影嘛,要沉浸,…...
三分算法与DeepSeek辅助证明是单峰函数
前置 单峰函数有唯一的最大值,最大值左侧的数值严格单调递增,最大值右侧的数值严格单调递减。 单谷函数有唯一的最小值,最小值左侧的数值严格单调递减,最小值右侧的数值严格单调递增。 三分的本质 三分和二分一样都是通过不断缩…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
人工智能--安全大模型训练计划:基于Fine-tuning + LLM Agent
安全大模型训练计划:基于Fine-tuning LLM Agent 1. 构建高质量安全数据集 目标:为安全大模型创建高质量、去偏、符合伦理的训练数据集,涵盖安全相关任务(如有害内容检测、隐私保护、道德推理等)。 1.1 数据收集 描…...
