初始化项目,由ModelHub XC社区提供模型

Model: evildevlive/Qwen3-Sex
Source: Original Platform
This commit is contained in:
ModelHub XC
2026-06-27 09:07:15 +08:00
commit bb6612bce2
16 changed files with 1410 additions and 0 deletions

190
infer.py Normal file
View File

@@ -0,0 +1,190 @@
import signal
import torch
from threading import Thread, Event
from transformers import (
AutoModelForCausalLM,
AutoTokenizer,
GenerationConfig,
StoppingCriteria,
StoppingCriteriaList,
TextIteratorStreamer,
)
from pathlib import Path
MODEL_PATH = Path(__file__).resolve().parent
DEFAULT_SYSTEM = ""
class StopOnEvent(StoppingCriteria):
def __init__(self, event):
self.event = event
def __call__(self, input_ids, scores, **kwargs):
return self.event.is_set()
def load_model():
print(f"Loading model from: {MODEL_PATH}")
tokenizer = AutoTokenizer.from_pretrained(
MODEL_PATH,
trust_remote_code=True,
)
model = AutoModelForCausalLM.from_pretrained(
MODEL_PATH,
dtype=torch.bfloat16 if torch.cuda.is_available() else torch.float32,
device_map="auto",
trust_remote_code=True,
)
model.eval()
gen_config = GenerationConfig.from_pretrained(
MODEL_PATH,
trust_remote_code=True,
)
print(f"Loaded generation config:\n{gen_config}")
return tokenizer, model, gen_config
def build_messages(history, user_input, system=DEFAULT_SYSTEM):
messages = []
if system:
messages.append({"role": "system", "content": system})
for u, a in history:
messages.append({"role": "user", "content": u})
messages.append({"role": "assistant", "content": a})
messages.append({"role": "user", "content": user_input})
return messages
def join_thread(thread, stop_event):
stop_event.set()
# 不再 break: 确保 daemon 线程真正结束, 避免 shutdown 阶段残留线程
while thread.is_alive():
try:
thread.join(timeout=0.1)
except KeyboardInterrupt:
stop_event.set()
@torch.inference_mode()
def stream_chat(tokenizer, model, gen_config, history, user_input):
messages = build_messages(history, user_input)
inputs = tokenizer.apply_chat_template(
messages,
add_generation_prompt=True,
return_tensors="pt",
return_dict=True,
).to(model.device)
streamer = TextIteratorStreamer(
tokenizer,
skip_prompt=True,
skip_special_tokens=True,
)
stop_event = Event()
stopping_criteria = StoppingCriteriaList([StopOnEvent(stop_event)])
generate_kwargs = dict(
**inputs,
streamer=streamer,
generation_config=gen_config,
stopping_criteria=stopping_criteria,
)
thread = Thread(
target=model.generate,
kwargs=generate_kwargs,
daemon=True,
)
thread.start()
response = ""
interrupted = False
try:
for new_text in streamer:
print(new_text, end="", flush=True)
response += new_text
except KeyboardInterrupt:
interrupted = True
stop_event.set()
print("\n[Interrupted. Type exit to quit or continue chatting.]")
finally:
join_thread(thread, stop_event)
if not interrupted:
print()
return response, interrupted
def main():
try:
tokenizer, model, gen_config = load_model()
except KeyboardInterrupt:
print("\nBye.")
return
print("\n=== Qwen3-Sex Chat ===")
print("Type 'exit' / 'quit' to quit, or type 'clear' to clear history.")
history = []
while True:
try:
user_input = input("User: ").strip()
except (EOFError, KeyboardInterrupt):
print("\nBye.")
break
if not user_input:
continue
if user_input.lower() in {"exit", "quit"}:
print("Bye.")
break
if user_input.lower() == "clear":
history = []
print("[History cleared]\n")
continue
print("Assistant: ", end="", flush=True)
response, interrupted = stream_chat(
tokenizer,
model,
gen_config,
history,
user_input,
)
if response.strip() and not interrupted:
history.append((user_input, response))
if __name__ == "__main__":
try:
main()
finally:
while True:
try:
signal.signal(signal.SIGINT, signal.SIG_IGN)
break
except KeyboardInterrupt:
continue
except (ValueError, OSError):
break