Spaces:
Runtime error
Runtime error
| import asyncio | |
| import threading | |
| import traceback | |
| from typing import Any | |
| from config import GENERAL_LLM_MODEL | |
| class HuggingFaceChatModel: | |
| """Lazy local Hugging Face chat model for non-topic reasoning tasks.""" | |
| def __init__(self, model_name: str = GENERAL_LLM_MODEL): | |
| self.model_name = model_name | |
| self._model: Any | None = None | |
| self._tokenizer: Any | None = None | |
| self._load_lock = threading.Lock() | |
| self.last_error = "" | |
| def is_loaded(self) -> bool: | |
| return self._model is not None and self._tokenizer is not None | |
| async def warmup(self) -> None: | |
| await asyncio.to_thread(self._ensure_model_loaded_sync) | |
| async def generate( | |
| self, | |
| system_prompt: str, | |
| user_prompt: str, | |
| max_new_tokens: int = 512, | |
| ) -> str: | |
| try: | |
| return await asyncio.to_thread( | |
| self._generate_sync, | |
| system_prompt, | |
| user_prompt, | |
| max_new_tokens, | |
| ) | |
| except Exception as exc: | |
| self.last_error = str(exc) | |
| return "" | |
| def _generate_sync( | |
| self, | |
| system_prompt: str, | |
| user_prompt: str, | |
| max_new_tokens: int, | |
| ) -> str: | |
| self.last_error = "" | |
| self._ensure_model_loaded_sync() | |
| messages = [ | |
| {"role": "system", "content": system_prompt}, | |
| {"role": "user", "content": user_prompt}, | |
| ] | |
| try: | |
| encoded = self._tokenizer.apply_chat_template( | |
| messages, | |
| add_generation_prompt=True, | |
| return_tensors="pt", | |
| return_dict=True, | |
| ) | |
| encoded = {key: value.to(self._model.device) for key, value in encoded.items()} | |
| except Exception as exc: | |
| raise RuntimeError(f"chat template failed for {self.model_name}: {exc}") from exc | |
| import torch | |
| try: | |
| with torch.no_grad(): | |
| outputs = self._model.generate( | |
| **encoded, | |
| max_new_tokens=max_new_tokens, | |
| do_sample=False, | |
| pad_token_id=self._tokenizer.eos_token_id, | |
| ) | |
| except Exception as exc: | |
| detail = "".join(traceback.format_exception_only(type(exc), exc)).strip() | |
| raise RuntimeError( | |
| f"generation failed for {self.model_name}: {detail}. " | |
| "This can happen if the model exceeds available memory or the Transformers input format changed." | |
| ) from exc | |
| input_length = encoded["input_ids"].shape[-1] | |
| generated = outputs[0][input_length:] | |
| return self._tokenizer.decode(generated, skip_special_tokens=True).strip() | |
| def _ensure_model_loaded_sync(self) -> None: | |
| if self._model is not None and self._tokenizer is not None: | |
| return | |
| with self._load_lock: | |
| if self._model is not None and self._tokenizer is not None: | |
| return | |
| import torch | |
| from transformers import AutoModelForCausalLM, AutoTokenizer | |
| try: | |
| tokenizer = AutoTokenizer.from_pretrained(self.model_name, trust_remote_code=False) | |
| except Exception as exc: | |
| detail = "".join(traceback.format_exception_only(type(exc), exc)).strip() | |
| raise RuntimeError(f"tokenizer load failed for {self.model_name}: {detail}") from exc | |
| if tokenizer.pad_token_id is None: | |
| tokenizer.pad_token = tokenizer.eos_token | |
| model_kwargs = {"trust_remote_code": False, "low_cpu_mem_usage": True} | |
| if torch.backends.mps.is_available(): | |
| model_kwargs["torch_dtype"] = torch.float16 | |
| try: | |
| model = AutoModelForCausalLM.from_pretrained(self.model_name, **model_kwargs) | |
| except Exception as exc: | |
| detail = "".join(traceback.format_exception_only(type(exc), exc)).strip() | |
| raise RuntimeError(f"model load failed for {self.model_name}: {detail}") from exc | |
| if torch.backends.mps.is_available(): | |
| model = model.to("mps") | |
| model.eval() | |
| self._tokenizer = tokenizer | |
| self._model = model | |