import gradio as gr import os import httpx import openai from openai import OpenAI from openai import AsyncOpenAI from datasets import load_dataset dataset = load_dataset("silk-road/50-Chinese-Novel-Characters") novel_list = [] novel2roles = {} role2datas = {} from tqdm import tqdm for data in tqdm(dataset['train']): novel = data['book'] role = data['role'] if novel not in novel_list: novel_list.append(novel) if novel not in novel2roles: novel2roles[novel] = [] if role not in novel2roles[novel]: novel2roles[novel].append(role) role_tuple = (novel, role) if role_tuple not in role2datas: role2datas[role_tuple] = [] role2datas[role_tuple].append(data) from ChatHaruhi.utils import base64_to_float_array from tqdm import tqdm for novel in tqdm(novel_list): for role in novel2roles[novel]: for data in role2datas[(novel, role)]: data["vec"] = base64_to_float_array(data["bge_zh_s15"]) def conv2story( role, conversations ): lines = [conv["value"] if conv["from"] == "human" else role + ": " + conv["value"] for conv in conversations] return "\n".join(lines) for novel in tqdm(novel_list): for role in novel2roles[novel]: for data in role2datas[(novel, role)]: data["story"] = conv2story( role, data["conversations"] ) from ChatHaruhi import ChatHaruhi from ChatHaruhi.response_openai import get_response as get_response_openai from ChatHaruhi.response_zhipu import get_response as get_response_zhipu get_response = get_response_zhipu narrators = ["叙述者", "旁白","文章作者","作者","Narrator","narrator"] def package_persona( role_name, world_name ): if role_name in narrators: return package_persona_for_narrator( role_name, world_name ) return f"""I want you to act like {role_name} from {world_name}. If others‘ questions are related with the novel, please try to reuse the original lines from the novel. I want you to respond and answer like {role_name} using the tone, manner and vocabulary {role_name} would use.""" def package_persona_for_narrator( role_name, world_name ): return f"""I want you to act like narrator {role_name} from {world_name}. 当角色行动之后,继续交代和推进新的剧情.""" role_tuple2chatbot = {} def initialize_chatbot( novel, role ): global role_tuple2chatbot if (novel, role) not in role_tuple2chatbot: persona = package_persona( role, novel ) persona += "\n{{RAG对话}}\n{{RAG对话}}\n{{RAG对话}}\n" stories = [data["story"] for data in role2datas[(novel, role)] ] vecs = [data["vec"] for data in role2datas[(novel, role)] ] chatbot = ChatHaruhi( role_name = role, persona = persona , stories = stories, story_vecs= vecs,\ llm = get_response) chatbot.verbose = False role_tuple2chatbot[(novel, role)] = chatbot from tqdm import tqdm for novel in tqdm(novel_list): for role in novel2roles[novel]: initialize_chatbot( novel, role ) readme_text = """# 使用说明 选择小说角色 如果你有什么附加信息,添加到附加信息里面就可以 比如"韩立会炫耀自己刚刚学会了Python" 然后就可以开始聊天了 因为这些角色还没有增加Greeting信息,所以之后再开发个随机乱聊功能 # 开发细节 - 采用ChatHaruhi3.0的接口进行prompting - 这里的数据是用一个7B的tuned qwen模型进行抽取的 - 想看数据可以去看第三个tab - 抽取模型用了40k左右的GLM蒸馏数据 - 抽取模型是腾讯大哥BPSK训练的 # 总结人物性格 第三个Tab里面,可以显示一个prompt总结人物的性格 复制到openai或者GLM或者Claude进行人物总结 # 这些小说数据从HaruhiZero 0.4模型开始,被加入训练 openai太慢了 今天试试GLM的 不过当前demo是openai的 """ from transformers import AutoTokenizer, AutoModel, AutoModelForCausalLM tokenizer = AutoTokenizer.from_pretrained("silk-road/Haruhi-Zero-1_8B", trust_remote_code=True) model = AutoModelForCausalLM.from_pretrained("silk-road/Haruhi-Zero-1_8B", device_map="auto", trust_remote_code=True) model = model.eval() def get_response_qwen18(message): from ChatHaruhi.utils import normalize2uaua message_ua = normalize2uaua(message, if_replace_system = True) import json message_tuples = [] for i in range(0, len(message_ua)-1, 2): message_tuple = (message_ua[i]["content"], message_ua[i+1]["content"]) message_tuples.append(message_tuple) response, _ = model.chat(tokenizer, message_ua[-1]["content"], history=message_tuples) return response from ChatHaruhi.response_openai import get_response, async_get_response import gradio as gr def get_role_list( novel ): new_list = novel2roles[novel] new_value = new_list[0] return gr.update(choices = new_list, value = new_value) save_log = "/content/output.txt" def get_chatbot( novel, role ): if (novel, role) not in role_tuple2chatbot: initialize_chatbot( novel, role ) return role_tuple2chatbot[(novel, role)] import json def random_chat_callback( novel, role, chat_history): datas = role2datas[(novel, role)] reesponse_set = set() for chat_tuple in chat_history: if chat_tuple[1] is not None: reesponse_set.add(chat_tuple[1]) for _ in range(5): random_data = random.choice(datas) convs = random_data["conversations"] n = len(convs) index = [x for x in range(0,n,2)] for i in index: query = convs[i]['value'] response = convs[i+1]['value'] if response not in reesponse_set: chat_history.append( (query, response) ) return chat_history return chat_history async def submit_chat( novel, role, user_name, user_text, chat_history, persona_addition_info,model_sel): if len(user_text) > 400: user_text = user_text[:400] if_user_in_text = True chatbot = get_chatbot( novel, role ) chatbot.persona = initialize_persona( novel, role, persona_addition_info) # chatbot.llm_async = async_get_response if model_sel == "openai": chatbot.llm = get_response_openai elif model_sel == "Zhipu": chatbot.llm = get_response_zhipu else: chatbot.llm = get_response_qwen18 history = [] for chat_tuple in chat_history: if chat_tuple[0] is not None: history.append( {"speaker":"{{user}}","content":chat_tuple[0]} ) if chat_tuple[1] is not None: history.append( {"speaker":"{{role}}","content":chat_tuple[1]} ) chatbot.history = history input_text = user_text if if_user_in_text: input_text = user_name + " : " + user_text response = chatbot.chat(user = "", text = input_text ) # response = await chatbot.async_chat(user = "", text = input_text ) else: response = chatbot.chat(user = user_name, text = input_text) # response = await chatbot.async_chat(user = user_name, text = input_text) chat_history.append( (input_text, response) ) print_data = {"novel":novel, "role":role, "user_text":input_text, "response":response} print(json.dumps(print_data, ensure_ascii=False)) with open(save_log, "a",encoding = "utf-8") as f: f.write(json.dumps(print_data, ensure_ascii=False) + "\n") return chat_history def initialize_persona( novel, role, persona_addition_info): whole_persona = package_persona( role, novel ) whole_persona += "\n" + persona_addition_info whole_persona += "\n{{RAG对话}}\n{{RAG对话}}\n{{RAG对话}}\n" return whole_persona def clean_history( ): return [] def clean_input(): return "" import random def generate_summarize_prompt( novel, role_name ): whole_prompt = f''' 你在分析小说{novel}中的角色{role_name} 结合小说{novel}中的内容,以及下文中角色{role_name}的对话 判断{role_name}的人物设定、人物特点以及语言风格 {role_name}的对话: ''' stories = [data["story"] for data in role2datas[(novel, role_name)] ] sample_n = 5 sample_stories = random.sample(stories, sample_n) for story in sample_stories: whole_prompt += story + "\n\n" return whole_prompt.strip() with gr.Blocks() as demo: gr.Markdown("""# 50本小说的人物测试 这个interface由李鲁鲁实现,主要是用来看语料的 增加了随机聊天,支持GLM,openai切换 米唯实接入了qwen1.8B并布置于huggingface上""") with gr.Tab("聊天"): with gr.Row(): novel_sel = gr.Dropdown( novel_list, label = "小说", value = "悟空传" , interactive = True) role_sel = gr.Dropdown( novel2roles[novel_sel.value], label = "角色", value = "孙悟空", interactive = True ) with gr.Row(): chat_history = gr.Chatbot(height = 600) with gr.Row(): user_name = gr.Textbox(label="user_name", scale = 1, value = "鲁鲁", interactive = True) user_text = gr.Textbox(label="user_text", scale = 20) submit = gr.Button("submit", scale = 1) with gr.Row(): random_chat = gr.Button("随机聊天", scale = 1) clean_message = gr.Button("清空聊天", scale = 1) with gr.Row(): persona_addition_info = gr.TextArea( label = "额外人物设定", value = "", interactive = True ) with gr.Row(): update_persona = gr.Button("补充人物设定到prompt", scale = 1) model_sel = gr.Radio(["Zhipu","openai","qwen1.8B"], interactive = True, scale = 5, value = "qwen1.8B", label = "模型选择") with gr.Row(): whole_persona = gr.TextArea( label = "完整的system prompt", value = "", interactive = False ) novel_sel.change(fn = get_role_list, inputs = [novel_sel], outputs = [role_sel]).then(fn = initialize_persona, inputs = [novel_sel, role_sel, persona_addition_info], outputs = [whole_persona]) role_sel.change(fn = initialize_persona, inputs = [novel_sel, role_sel, persona_addition_info], outputs = [whole_persona]) update_persona.click(fn = initialize_persona, inputs = [novel_sel, role_sel, persona_addition_info], outputs = [whole_persona]) random_chat.click(fn = random_chat_callback, inputs = [novel_sel, role_sel, chat_history], outputs = [chat_history]) user_text.submit(fn = submit_chat, inputs = [novel_sel, role_sel, user_name, user_text, chat_history, persona_addition_info,model_sel], outputs = [chat_history]).then(fn = clean_input, inputs = [], outputs = [user_text]) submit.click(fn = submit_chat, inputs = [novel_sel, role_sel, user_name, user_text, chat_history, persona_addition_info,model_sel], outputs = [chat_history]).then(fn = clean_input, inputs = [], outputs = [user_text]) clean_message.click(fn = clean_history, inputs = [], outputs = [chat_history]) with gr.Tab("README"): gr.Markdown(readme_text) with gr.Tab("辅助人物总结"): with gr.Row(): generate_prompt = gr.Button("生成人物总结prompt", scale = 1) with gr.Row(): whole_prompt = gr.TextArea( label = "复制这个prompt到Openai或者GLM或者Claude进行总结", value = "", interactive = False ) generate_prompt.click(fn = generate_summarize_prompt, inputs = [novel_sel, role_sel], outputs = [whole_prompt]) demo.launch(share=True, debug = True)