"""GPU generation function — called from app.py's @spaces.GPU wrapper."""
import re
import logging
import config
logger = logging.getLogger(__name__)
_model_cache = {}
def _load_and_generate(prompt: str, max_tokens: int = None) -> str:
"""Load model and generate text. Runs inside GPU context."""
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
max_tokens = max_tokens or config.TEXT_MODEL_MAX_TOKENS
if "model" not in _model_cache:
repos = [config.TEXT_MODEL_REPO, config.LIGHT_MODEL_REPO]
for repo in repos:
try:
logger.info(f"Loading: {repo}")
tokenizer = AutoTokenizer.from_pretrained(repo, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(
repo,
torch_dtype=torch.float16,
trust_remote_code=True,
low_cpu_mem_usage=True,
device_map="auto",
)
_model_cache["model"] = model
_model_cache["tokenizer"] = tokenizer
_model_cache["repo"] = repo
logger.info(f"Loaded: {repo}")
break
except Exception as e:
logger.warning(f"Failed {repo}: {e}")
continue
else:
return ""
model = _model_cache["model"]
tokenizer = _model_cache["tokenizer"]
system = "Output directly. No thinking. No tags. Chinese only."
messages = [
{"role": "system", "content": system},
{"role": "user", "content": prompt},
]
try:
text = tokenizer.apply_chat_template(
messages, tokenize=False, add_generation_prompt=True
)
except Exception:
text = f"{system}\n\n{prompt}\n\nAnswer:"
inputs = tokenizer(text, return_tensors="pt")
inputs.pop("token_type_ids", None)
if torch.cuda.is_available():
inputs = {k: v.to("cuda") for k, v in inputs.items()}
with torch.no_grad():
outputs = model.generate(
**inputs,
max_new_tokens=max_tokens,
do_sample=True,
temperature=0.7,
top_p=0.9,
repetition_penalty=1.1,
)
raw = tokenizer.decode(
outputs[0][inputs["input_ids"].shape[1]:],
skip_special_tokens=True,
)
thinking = re.findall(r'(.*?)', raw, re.DOTALL)
raw = re.sub(r'.*?\s*', '', raw, flags=re.DOTALL)
result = raw.strip()
if not result and thinking:
last = thinking[-1].strip()
lines = [l.strip() for l in last.split('\n') if l.strip()]
skip = ['嗯', '想到', '考虑', '需要', '首先', '然后', '用户']
good = [l for l in lines if not any(l.startswith(w) for w in skip)]
result = '\n'.join(good[-3:]) if good else '\n'.join(lines[-2:])
logger.info(f"Generated {len(result)} chars")
return result
# Fallback: direct call (used when app.py hasn't registered the GPU version)
def gpu_generate(prompt: str, max_tokens: int = None) -> str:
return _load_and_generate(prompt, max_tokens)