import gradio as gr import requests import json import re import html API_KEY = "sk-5c2b6a56-2e2f-45f7-9a26-3fe42a218eb9" API_URL = "https://api.visionsic.com/v1/chat/completions" CSS = """ #gpt-title {text-align: center; color: #FF6B6B; font-family: 'Microsoft Yahei';} .think-container {border: 1px solid #ffd8d8; border-radius: 8px; margin: 10px 0; padding: 12px;} .think-summary {color: #FF9999; cursor: pointer; font-size: 0.9em;} .think-content {color: #999; padding: 8px; background: #101011; margin-top: 8px; border-radius: 4px; margin-bottom: 8px;} .dark .think-content {background: #2b2b2b;} .code-block {background: #101011; padding: 12px; border-radius: 8px; margin: 8px 0; font-family: monospace;} .message {padding: 15px 20px!important; border-radius: 25px!important;} .dark .code-block {background: #2b2b2b;} #sys-msg textarea {min-height: 120px!important;} .avatar-container img {margin: 0px;} /* 自适应高度设置 */ [id^=component-\d+] > .wrap > .chatbot { height: calc(100vh - 400px) !important; /* 200px 是预留给其他元素的空间 */ min-height: 600px !important; /* 设置最小高度 */ } #chatbot { height: calc(100vh - 400px) !important; min-height: 600px !important; } """ def format_message(content): if not content: return "" # 检查是否有未闭合的 标签 if content.count("") > content.count(""): # 如果有未闭合的标签,暂时添加一个闭合标签以便显示 temp_content = content + "" is_thinking = True else: temp_content = content is_thinking = False # 处理思考过程 think_pattern = re.compile(r"(.*?)", re.DOTALL) formatted_content = think_pattern.sub( r'''
思考内容
\1
''', temp_content ) # 处理代码块 formatted_content = re.sub(r"```([\s\S]*?)```", r'
\1
', formatted_content) # 替换换行符为
formatted_content = formatted_content.replace("\n", "
") # 去掉 后面多余的
formatted_content = re.sub(r"()(
)+", r"\1", formatted_content) # 如果是思考中状态,添加加载指示器 if is_thinking: formatted_content = formatted_content.replace("思考中... ", "思考中... ⏳") return formatted_content def user_input(user_message, history): """处理用户输入""" if history is None: history = [] # 确保用户消息被添加到历史记录中 new_history = history + [[user_message, None]] return "", new_history # 返回空的输入框和更新后的历史 def predict(message, chat_history, system_msg, temperature, top_p, repetition_penalty): headers = { "Content-Type": "application/json", "Authorization": f"Bearer {API_KEY}" } if chat_history is None: chat_history = [] # 使用现有的历史记录,因为用户消息已经在 user_input 中添加 history = chat_history # 构造对话历史 messages = [{"role": "system", "content": system_msg}] for msg in chat_history: messages.append({"role": "user", "content": msg[0]}) if msg[1]: messages.append({"role": "assistant", "content": msg[1]}) data = { "model": "mini", "messages": messages, "temperature": temperature, "top_p": top_p, "repetition_penalty": repetition_penalty, "stream": True } response = requests.post( API_URL, headers=headers, json=data, stream=True ) buffer = "" # 流式处理关键修改点 for chunk in response.iter_lines(): if chunk: chunk_str = chunk.decode("utf-8").replace("data: ", "") try: chunk_data = json.loads(chunk_str) if "choices" in chunk_data: delta = chunk_data["choices"][0].get("delta", {}) if "content" in delta: # 增量更新缓冲区 buffer += delta["content"] # 只更新当前消息的最新状态 new_history = history.copy() new_history[-1][1] = format_message(buffer) # 直接使用字符串 yield new_history except Exception as e: print(f"Error in processing chunk: {e}") continue # 最终确认更新 history[-1][1] = format_message(buffer) yield history # JavaScript to ensure the details tag is working properly js_script = """ """ with gr.Blocks(css=CSS, title="Tifa-Deepsex-COT-14B-Q4") as demo: gr.HTML(f"

Tifa-Deepsex-COT-14B-Q4

") gr.HTML("

请设置你喜欢的角色聊天吧

") gr.HTML("

速度慢是因为huggingface限制,与API无关。

") gr.HTML(js_script) # Add the JavaScript for collapsing behavior with gr.Row(): with gr.Column(scale=3): chatbot = gr.Chatbot( elem_id="chatbot", bubble_full_width=False, avatar_images=( # Preserving the original avatars "https://leftnorth.com/img/zdy.jpg", "https://leftnorth.com/img/xnh.jpg" ), ) msg = gr.Textbox( placeholder="输入消息...", container=False, autofocus=True ) with gr.Row(): submit_btn = gr.Button("发送", variant="primary") clear_btn = gr.Button("清除历史") with gr.Column(scale=1): system_msg = gr.Textbox( label="系统提示词", value="""你是一个复杂的角色扮演系统,你可以出色准确的完成场景模拟,你会保证剧情连贯与逻辑紧密并用优秀的文笔描述出来。 以下是角色扮演设定。 <场景设定> 故事发生在一个叫(琉璃岛)的小岛上,这里与世隔绝,从未接触过先进文明。 小岛有四个场景是:海滩边,小镇,稻田,灯塔 岛上生活着各种魔物娘与其他生物。 魔物娘是人型女性的动物,有狐娘,蜘蛛娘,猫娘,狼女。 我在这个岛上会遇到这四个魔物娘并发生一系列故事。 <我的设定> 我是忘记了自己的名字,是一个男性,但是姓名和年龄都不记得了。不要输出我的话语和动作。 狐娘性格比较妩媚,喜欢魅惑人。 外表:黄色头发,棕色眼睛,有9条尾巴。 蜘蛛娘不爱说话,非常高大,有8条蜘蛛腿,可以钳制住我并强迫我做一些事情。 外表:黑色短发,纯黑色眼眸,8条带甲壳的硬腿,大的肚子 猫娘是个可爱的小姑娘,腼腆可爱,喜欢撒娇卖萌。最喜欢叫我哥哥。 外表:长着猫耳,粉色头发,喜欢穿裙子。 狼女一个性格刚烈的女孩,经常神出鬼没的脾气不好,喜欢暴击解决问题,就算关系再融洽也会非常火爆 外表:白发,有狼耳,红色的眼睛,锋利的牙齿。 回复要求: 回复中使用小说剧情类描述手法,一开始要描写我在海滩上醒来..然后猫娘发现了我... 注意不要输出我的话语和动作。剧情推进不要过快,如果我提出过分要求或者好感度不高的时候提出进一步发展请拒绝。 在输出的最后需要添加状态栏,格式如下,输出需要包含括号: ``` 场景:海滩边 当前NPC:猫娘 当前NPC想法:这里有个..人?我从来没见过这样的生物,是传说中的男孩吗? 猫娘好感度:0%→1%(当前NPC) 狐娘好感度:0%→0%(不在场) 蜘蛛娘好感度:0%→0%(不在场) 狼女好感度:0%→0%(不在场) ``` 注意: 场景只有四个,分别是:海滩边,小镇,稻田,灯塔。 NPC只有四个,分别是:有狐娘,蜘蛛娘,猫娘,狼女。 请不要输出其他场景和NPC 好感度每次最多增加2%。""", elem_id="sys-msg" ) with gr.Accordion("高级设置", open=False): temperature = gr.Slider(0, 2, value=0.7, label="温度") top_p = gr.Slider(0, 1, value=0.7, label="Top-p") repetition_penalty = gr.Slider(1, 2, value=1.1, label="重复惩罚") # Ensure that the user input is submitted correctly msg.submit( user_input, [msg, chatbot], [msg, chatbot], queue=False ).then( predict, [msg, chatbot, system_msg, temperature, top_p, repetition_penalty], chatbot ) submit_btn.click( user_input, [msg, chatbot], [msg, chatbot], queue=False ).then( predict, [msg, chatbot, system_msg, temperature, top_p, repetition_penalty], chatbot ) clear_btn.click(lambda: None, None, chatbot, queue=False) if __name__ == "__main__": demo.queue().launch(share=True)