novoyaz-20b / handler.py
ZennyKenny's picture
Update handler.py
38c558b verified
Raw
History Blame
2.99 kB
# handler.py
from __future__ import annotations
import os
from typing import Any, Dict, List, Union
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM
GEN_KW = {
"temperature": float(os.getenv("GEN_TEMPERATURE", "0.2")),
"do_sample": os.getenv("GEN_DO_SAMPLE", "false").lower() == "true",
"max_new_tokens": int(os.getenv("GEN_MAX_NEW_TOKENS", "512")),
"repetition_penalty": float(os.getenv("GEN_REP_PENALTY", "1.0")),
}
PROMPT_PREFIX = (
"Преобразуй дореформенный русский текст в современную орфографию, "
"сохранив смысл и пунктуацию. Верни только преобразованный текст.\n\nТекст:\n"
)
PROMPT_SUFFIX = "\n\nСовременный вариант:"
def _to_list(x: Union[str, List[str]]) -> List[str]:
if isinstance(x, list):
return [str(t) for t in x]
return [str(x)]
class EndpointHandler:
def __init__(self, model_dir: str):
# model_dir is the local path downloaded by the endpoint
self.device = "cuda" if torch.cuda.is_available() else "cpu"
# IMPORTANT: trust_remote_code to load custom arch "gpt_oss"
self.tokenizer = AutoTokenizer.from_pretrained(
model_dir, use_fast=True, trust_remote_code=True
)
self.model = AutoModelForCausalLM.from_pretrained(
model_dir,
torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32,
device_map="auto" if torch.cuda.is_available() else None,
trust_remote_code=True,
)
if not torch.cuda.is_available():
# For safety on CPU, disable KV cache to reduce RAM spikes
self.model.config.use_cache = False
self.model.eval()
def _prepare_inputs(self, texts: List[str]) -> Dict[str, Any]:
prompts = [f"{PROMPT_PREFIX}{t}{PROMPT_SUFFIX}" for t in texts]
toks = self.tokenizer(
prompts, return_tensors="pt", padding=True, truncation=True
)
return {k: v.to(self.model.device) for k, v in toks.items()}
@torch.inference_mode()
def __call__(self, data: Dict[str, Any]) -> List[Dict[str, str]]:
"""
Accepts { "inputs": "text" } or { "inputs": ["t1","t2",...] }
Returns [{ "generated_text": "..." }, ...]
"""
if "inputs" not in data:
return [{"error": "missing 'inputs'"}]
texts = _to_list(data["inputs"])
inputs = self._prepare_inputs(texts)
out = self.model.generate(**inputs, **GEN_KW)
results: List[Dict[str, str]] = []
# decode only the newly generated part
for i, seq in enumerate(out):
inp_len = inputs["input_ids"][i].shape[-1]
gen_part = seq[inp_len:]
text = self.tokenizer.decode(gen_part, skip_special_tokens=True).strip()
results.append({"generated_text": text})
return results