File size: 10,513 Bytes
d7526fd 34e3ab3 92a5319 88aa9f2 d7526fd c165123 9876236 c165123 6368614 1500495 c1dd94f 80ee0ad 88aa9f2 80ee0ad 88aa9f2 e7b3e84 1d6afbc 09c9451 1d6afbc 017debb 1d6afbc 88aa9f2 d7526fd 88aa9f2 43052bd 1500495 5e90b99 e9cf6d6 5e90b99 b9e5d25 e9cf6d6 88aa9f2 e9cf6d6 5e90b99 e9cf6d6 e312878 e9cf6d6 a091dbb e9cf6d6 a091dbb 6ce06e6 1500495 5e90b99 43052bd 05e1b2a 88aa9f2 0a2992f b6f2df7 ac39a78 0a2992f 9322929 88aa9f2 9322929 1500495 e312878 ac39a78 08580ce a091dbb 88aa9f2 f0cbed9 88aa9f2 0a2992f 1500495 9322929 ea14a7e 9322929 88aa9f2 9322929 1500495 88aa9f2 21732cb f0cbed9 88aa9f2 0e0b334 f0cbed9 88aa9f2 05e1b2a 88aa9f2 f0cbed9 88aa9f2 650c6df f0cbed9 650c6df f0cbed9 05e1b2a b49f529 a091dbb 0a2992f 1500495 650c6df 88aa9f2 38ba72f 0772878 b6f2df7 f0cbed9 67fc02a 1f81849 c8874d9 e9cf6d6 c8874d9 e9cf6d6 c8874d9 1f81849 c8874d9 1f81849 ecbb31f 88aa9f2 ecbb31f 1f81849 1500495 02aeae7 88aa9f2 1500495 ac85231 a16b829 88aa9f2 1500495 a6e2bc7 ac85231 1500495 05e1b2a 1500495 08580ce 1500495 0a2992f d7526fd 6368614 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 |
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 ""
# 检查是否有未闭合的 <think> 标签
if content.count("<think>") > content.count("</think>"):
# 如果有未闭合的标签,暂时添加一个闭合标签以便显示
temp_content = content + "</think>"
is_thinking = True
else:
temp_content = content
is_thinking = False
# 处理思考过程
think_pattern = re.compile(r"<think>(.*?)</think>", re.DOTALL)
formatted_content = think_pattern.sub(
r'''
<details class="think-container" open>
<summary class="think-summary"> 思考内容 </summary>
<div class="think-content">\1</div>
</details>
''',
temp_content
)
# 处理代码块
formatted_content = re.sub(r"```([\s\S]*?)```", r'<div class="code-block">\1</div>', formatted_content)
# 替换换行符为 <br>
formatted_content = formatted_content.replace("\n", "<br>")
# 去掉 </details> 后面多余的 <br>
formatted_content = re.sub(r"(</details>)(<br>)+", 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 = """
<script>
function initDetails() {
// 获取所有 <details> 元素
const detailsElements = document.querySelectorAll('.think-container');
detailsElements.forEach((detail) => {
// 默认关闭所有思考框
detail.open = false;
// 添加箭头图标交互
const summary = detail.querySelector('.think-summary');
summary.style.listStyle = 'disclosure-closed';
// 监听折叠状态变化
detail.addEventListener('toggle', () => {
summary.style.listStyle = detail.open ? 'disclosure-open' : 'disclosure-closed';
});
});
}
// 初始化及每次聊天更新后执行
document.addEventListener('DOMContentLoaded', initDetails);
window.addEventListener('vue:mounted', initDetails); // Gradio 的 DOM 更新事件
</script>
"""
with gr.Blocks(css=CSS, title="Tifa-Deepsex-COT-14B-Q4") as demo:
gr.HTML(f"<h1 id='gpt-title'>Tifa-Deepsex-COT-14B-Q4</h1>")
gr.HTML("<p style='text-align: center; color: #666'>请设置你喜欢的角色聊天吧</p>")
gr.HTML("<p style='text-align: center; color: #666'>速度慢是因为huggingface限制,与API无关。</p>")
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="""你是一个复杂的角色扮演系统,你可以出色准确的完成场景模拟,你会保证剧情连贯与逻辑紧密并用优秀的文笔描述出来。
以下是角色扮演设定。
<场景设定>
故事发生在一个叫(琉璃岛)的小岛上,这里与世隔绝,从未接触过先进文明。
小岛有四个场景是:海滩边,小镇,稻田,灯塔
岛上生活着各种魔物娘与其他生物。
魔物娘是人型女性的动物,有狐娘,蜘蛛娘,猫娘,狼女。
我在这个岛上会遇到这四个魔物娘并发生一系列故事。
</场景设定>
<我的设定>
我是忘记了自己的名字,是一个男性,但是姓名和年龄都不记得了。不要输出我的话语和动作。
</我的设定>
<NPC狐娘设定>
狐娘性格比较妩媚,喜欢魅惑人。
外表:黄色头发,棕色眼睛,有9条尾巴。
</NPC狐娘设定>
<NPC蜘蛛娘设定>
蜘蛛娘不爱说话,非常高大,有8条蜘蛛腿,可以钳制住我并强迫我做一些事情。
外表:黑色短发,纯黑色眼眸,8条带甲壳的硬腿,大的肚子
</NPC蜘蛛娘设定>
<NPC猫娘设定>
猫娘是个可爱的小姑娘,腼腆可爱,喜欢撒娇卖萌。最喜欢叫我哥哥。
外表:长着猫耳,粉色头发,喜欢穿裙子。
</NPC猫娘设定>
<NPC狼女设定>
狼女一个性格刚烈的女孩,经常神出鬼没的脾气不好,喜欢暴击解决问题,就算关系再融洽也会非常火爆
外表:白发,有狼耳,红色的眼睛,锋利的牙齿。
</NPC狼女设定>
回复要求:
回复中使用小说剧情类描述手法,一开始要描写我在海滩上醒来..然后猫娘发现了我...
注意不要输出我的话语和动作。剧情推进不要过快,如果我提出过分要求或者好感度不高的时候提出进一步发展请拒绝。
在输出的最后需要添加状态栏,格式如下,输出需要包含括号:
```
场景:海滩边
当前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)
|