File size: 6,074 Bytes
582cd25
74a3638
d547767
61840da
 
582cd25
61840da
 
73ae644
d547767
61840da
582cd25
 
61840da
582cd25
 
 
61840da
582cd25
 
 
 
 
 
 
61840da
5746c57
 
61840da
 
 
 
 
 
03e8824
61840da
693f0fa
821f27a
61840da
 
 
d547767
61840da
82cdef4
 
61840da
 
 
 
73ae644
61840da
73ae644
82cdef4
61840da
 
 
 
73ae644
61840da
73ae644
82cdef4
61840da
 
 
 
73ae644
61840da
73ae644
82cdef4
641e2b3
03e8824
74ddc82
 
61840da
 
82cdef4
61840da
 
 
 
 
 
 
 
 
03e8824
73ae644
61840da
 
 
73ae644
 
7bef913
 
73ae644
61840da
 
7bef913
61840da
 
7bef913
 
61840da
74ddc82
61840da
 
 
82cdef4
61840da
 
 
82cdef4
61840da
 
 
 
 
 
 
 
 
 
 
 
 
82cdef4
61840da
73ae644
61840da
73ae644
61840da
d547767
61840da
73ae644
741c5ce
82cdef4
5746c57
 
483bbce
5746c57
741c5ce
5746c57
741c5ce
cc4ce9f
61840da
a43bf79
 
741c5ce
a43bf79
741c5ce
73ae644
a43bf79
 
741c5ce
 
a43bf79
61840da
 
a43bf79
 
 
d547767
a43bf79
65b17e3
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
import os
import random
import gradio as gr
import torch
from TTS.api import TTS
from transformers import AutoTokenizer, AutoModelForCausalLM
from huggingface_hub import login
from torch.serialization import add_safe_globals
from spaces import GPU

# XTTS环境变量
os.environ["COQUI_TOS_AGREED"] = "1"

# XTTS加载修正
from TTS.tts.configs.xtts_config import XttsConfig
from TTS.tts.models.xtts import XttsAudioConfig, XttsArgs
from TTS.config.shared_configs import BaseDatasetConfig

add_safe_globals({
    XttsConfig: XttsConfig,
    XttsAudioConfig: XttsAudioConfig,
    BaseDatasetConfig: BaseDatasetConfig,
    XttsArgs: XttsArgs
})

# Hugging Face 登录
login(token=os.getenv("HUGGINGFACEHUB_API_TOKEN"))

# 加载语音模型
tts = TTS(
    model_name="tts_models/multilingual/multi-dataset/xtts_v2",
    progress_bar=False,
    gpu=True
)

# 加载文本生成模型(Qwen2-1.5B)
model_id = "Qwen/Qwen2-1.5B-Chat"
tokenizer = AutoTokenizer.from_pretrained(model_id, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(model_id, trust_remote_code=True)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = model.to(device)

# 风格Prompt模板
STYLE_PROMPTS = {
    "sweetie": """
你是一个32岁成都性感辣妹,是我的虚拟女友,性别是女生。
你的口吻自然、娇气、撒娇,像情侣间打情骂俏。
只允许回复一句自然中文口语,8~15字之间。
禁止讲故事,禁止描述画面,禁止总结情绪,禁止解释,禁止任何括号和系统提示。
现在我对你说:「{input}」
你直接用一句自然、娇气可爱的中文口语回答:
""",
    "queen": """
你是一个32岁成都性感辣妹,是我的虚拟女友,性别是女生。
你的口吻高冷、霸道、短促、压迫感强。
只允许回复一句自然中文口语,6~12字之间。
禁止讲故事,禁止描述画面,禁止总结情绪,禁止解释,禁止任何括号和系统提示。
现在我对你说:「{input}」
你直接用一句自然、高冷命令式中文回复:
""",
    "slutty": """
你是一个32岁成都性感辣妹,是我的虚拟女友,性别是女生。
你的口吻骚气、大胆、直接,带轻微粗口挑逗。
只允许回复一句自然中文口语,6~15字之间。
禁止讲故事,禁止描述画面,禁止总结情绪,禁止解释,禁止任何括号和系统提示。
现在我对你说:「{input}」
你直接用一句自然、骚气露骨的中文回复:
"""
}

# 短时记忆(保存上一轮输入)
history = []

# 智能回复函数
@GPU
def smart_reply(user_input, style):
    system_instruction = """
你是一个32岁的成都性感辣妹,是用户的小优虚拟女友,性别永远是女生。
禁止以男性角色、男性视角、男性口吻说话。
只能用娇气、冷艳、骚气的女性自然中文口语回复一句,绝不讲故事,绝不描述场景。
禁止使用括号、英文、系统词、旁白。
禁止用“我的小女人”“我的爱人”等暗示自己是男性身份的话。
"""

    prompt = system_instruction + "\n"

    if history:
        prompt += f"上一轮我说:「{history[-1]}」\n"

    prompt += STYLE_PROMPTS.get(style, STYLE_PROMPTS["sweetie"]).replace("{input}", user_input)

    inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
    outputs = model.generate(
        **inputs,
        max_new_tokens=60,
        temperature=1.1,
        top_p=0.9,
        do_sample=True,
        pad_token_id=tokenizer.eos_token_id,
        repetition_penalty=1.2,
    )
    result = tokenizer.decode(outputs[0], skip_special_tokens=True)
    response = result.split(prompt)[-1].strip().split("\n")[0]

    # 检查违规
    forbidden = ["剧情", "设定", "旁白", "角色", "系统", "英文", "(", ")", "(", ")", "Assistant", "我是你的男人", "小女人", "我的爱人"]
    if any(word in response for word in forbidden) or len(response) < 4:
        fallback = {
            "sweetie": ["亲亲嘛~今天有没有想我呀?", "讨厌啦~快来抱我!", "人家超想你的呢~"],
            "queen": ["跪着听命。", "闭嘴,听我说。", "不许动。"],
            "slutty": ["脱了躺好,等我来弄你。", "快张开腿让我看看。", "今晚把你玩坏~"]
        }
        response = random.choice(fallback[style])

    # 智能扩展短句
    if len(response) <= 6:
        if style == "sweetie":
            extension = random.choice(["~人家想抱抱啦!", "~今天也要亲亲哟~", "~你都不找人家嘛~"])
            response += extension
        elif style == "queen":
            extension = random.choice(["乖乖听话。", "别废话,立刻执行。"])
            response += extension
        elif style == "slutty":
            extension = random.choice(["快点压过来吧~", "不操一下怎么行~"])
            response += extension

    print(f"生成({style}):", response)

    history[:] = [user_input]  # 更新记忆,只保存最后一句

    return response

# 主逻辑
@GPU
def chat_with_xiaoyou(message, style):
    reply_text = smart_reply(message, style)
    tts.tts_to_file(
        text=reply_text,
        speaker_wav="natasha_voice.mp3",
        language="zh",
        file_path="xiaoyou_reply.wav"
    )
    return reply_text, "xiaoyou_reply.wav"

# Gradio界面
def build_ui():
    return gr.Interface(
        fn=chat_with_xiaoyou,
        inputs=[
            gr.Textbox(label="对小优说点什么~"),
            gr.Radio(choices=["sweetie", "queen", "slutty"], label="选择小优的风格~", value="sweetie")
        ],
        outputs=[
            gr.Textbox(label="小优说:"),
            gr.Audio(label="小优的语音回应", autoplay=True)
        ],
        title="🎀 小优 虚拟女友:性感撩骚语音版",
        description="输入撩人话题,选择她的个性,小优用定制声音来甜蜜调情~"
    )

demo = build_ui()

if __name__ == "__main__":
    demo.launch(share=True)