File size: 5,129 Bytes
386458c 8602cb1 386458c aa51703 76b569b 386458c aa51703 386458c aa51703 386458c 8602cb1 386458c aa51703 386458c 54a489d aa51703 8602cb1 386458c 8602cb1 386458c 14c8b5d 54a489d 8602cb1 14c8b5d 8602cb1 386458c 8602cb1 386458c 8602cb1 386458c 76b569b aa51703 8602cb1 76b569b aa51703 76b569b 54a489d 76b569b 8602cb1 76b569b 386458c 76b569b aa51703 76b569b 8602cb1 386458c 8602cb1 386458c aa51703 14c8b5d 386458c 76b569b 386458c 76b569b 8602cb1 54a489d 8602cb1 76b569b aa51703 76b569b aa51703 76b569b aa51703 8602cb1 aa51703 54a489d 76b569b 386458c 76b569b | 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 | import os
import re
import threading
from typing import List, Dict, Optional
import gradio as gr
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM, TextIteratorStreamer
MODEL_ID = "Amey9766/qwen-0.6b-hospitality-housekeeping"
tokenizer = None
model = None
device = None
def load_model(hf_access_token: Optional[str] = None):
global tokenizer, model, device
if tokenizer is not None and model is not None:
return
device = "cuda" if torch.cuda.is_available() else "cpu"
dtype = torch.float16 if device == "cuda" else torch.float32
use_token = hf_access_token or os.getenv("HF_TOKEN")
tokenizer = AutoTokenizer.from_pretrained(
MODEL_ID,
token=use_token,
trust_remote_code=True,
use_fast=True,
)
model = AutoModelForCausalLM.from_pretrained(
MODEL_ID,
token=use_token,
torch_dtype=dtype,
device_map="auto" if device == "cuda" else None,
trust_remote_code=True,
)
if device == "cpu":
model.to(device)
print("✅ Loaded model:", getattr(model.config, "_name_or_path", "unknown"))
def build_plain_prompt(system_message: str, history: List[Dict[str, str]], user_message: str) -> str:
"""
Universal prompt builder that does NOT require tokenizer.chat_template.
This works with any CausalLM.
"""
hard_rules = (
"You are a professional hotel housekeeping assistant.\n"
"STRICT RULES:\n"
"1) Respond in English only.\n"
"2) Answer ONLY the user's last question.\n"
"3) Do NOT generate follow-up questions.\n"
"4) Do NOT mention rules, instructions, or your reasoning.\n"
"5) Provide only the final answer.\n"
)
sys = (system_message or "").strip()
prompt = f"{hard_rules}\nSYSTEM NOTE: {sys}\n\n"
# Convert Gradio "messages" history into a readable transcript
for m in history:
role = (m.get("role") or "user").lower()
content = (m.get("content") or "").strip()
if not content:
continue
if role == "user":
prompt += f"User: {content}\n"
else:
prompt += f"Assistant: {content}\n"
prompt += f"User: {user_message.strip()}\nAssistant:"
return prompt
def clean_output(text: str) -> str:
"""
Removes common fine-tune artifacts without being too aggressive.
"""
# Remove leading parenthetical meta like: "(Answering in English...)"
text = re.sub(r"^\s*\(.*?\)\s*", "", text, flags=re.DOTALL)
# If the model starts adding "Question:" sections, cut everything after it
cut_markers = [
"\nQuestion:",
"\nNow, let's",
"\nNow let's",
"\nBased on the rules",
"\nAccording to the rules",
]
for marker in cut_markers:
idx = text.lower().find(marker.lower())
if idx != -1:
text = text[:idx].strip()
break
return text.strip()
def respond(
message: str,
history: List[Dict[str, str]],
system_message: str,
max_tokens: int,
temperature: float,
top_p: float,
hf_token: gr.OAuthToken,
):
load_model(hf_token.token if hf_token else None)
prompt = build_plain_prompt(system_message, history, message)
inputs = tokenizer(prompt, return_tensors="pt")
inputs = {k: v.to(device) for k, v in inputs.items()}
streamer = TextIteratorStreamer(
tokenizer,
skip_prompt=True,
skip_special_tokens=True,
)
# Lower randomness to reduce “roleplay / training artifact” behavior
gen_kwargs = dict(
**inputs,
max_new_tokens=int(max_tokens),
do_sample=True,
temperature=float(temperature),
top_p=float(top_p),
repetition_penalty=1.15,
streamer=streamer,
eos_token_id=tokenizer.eos_token_id,
pad_token_id=tokenizer.eos_token_id,
)
thread = threading.Thread(target=model.generate, kwargs=gen_kwargs)
thread.start()
out = ""
for chunk in streamer:
out += chunk
# Stream the cleaned output live
cleaned = clean_output(out)
# Hard stop if it starts self-questioning
if any(x in out.lower() for x in ["\nquestion:", "now, let's generate", "based on the rules"]):
yield cleaned
break
yield cleaned
chatbot = gr.ChatInterface(
respond,
type="messages",
title="Hospitality Housekeeping Assistant",
description=f"Running model: `{MODEL_ID}`",
additional_inputs=[
gr.Textbox(
value="Give SOP-style housekeeping answers. Use bullet points when helpful.",
label="System message",
),
gr.Slider(1, 1024, value=256, step=1, label="Max new tokens"),
gr.Slider(0.1, 1.0, value=0.3, step=0.05, label="Temperature"),
gr.Slider(0.5, 1.0, value=0.85, step=0.05, label="Top-p"),
],
)
with gr.Blocks() as demo:
with gr.Sidebar():
gr.LoginButton()
gr.Markdown(f"**Model:** `{MODEL_ID}`")
chatbot.render()
if __name__ == "__main__":
demo.launch()
|