Spaces:
Running
Running
| """Cliente Hugging Face Transformers (modelo público, opcional). | |
| Carrega um modelo público de geração de texto via ``transformers`` e produz | |
| a interpretação do relatório localmente, sem chamar nenhuma API comercial. | |
| Cuidados: | |
| - O modelo NÃO é fixado no código: vem de ``HF_MODEL_REPO_ID``. | |
| - ``transformers``/``torch`` são dependências opcionais (import tardio). | |
| - Erros de memória, modelo ausente ou dependência faltando levantam | |
| :class:`TransformersError` para acionar o fallback para template. | |
| Princípio: este cliente NUNCA calcula indicadores. Apenas interpreta o prompt. | |
| """ | |
| from __future__ import annotations | |
| from typing import Any | |
| from ..utils.logger import get_logger | |
| logger = get_logger("models.transformers") | |
| # Cache de pipelines por (repo_id, device). Evita recarregar o modelo a cada | |
| # chamada — o que era o principal custo quando há várias gerações por execução. | |
| # Compartilhado por todas as instâncias no mesmo processo. | |
| _PIPELINE_CACHE: dict[tuple[str, str], Any] = {} | |
| class TransformersError(RuntimeError): | |
| """Erro controlado ao usar Hugging Face Transformers.""" | |
| class TransformersClient: | |
| """Encapsula um pipeline de ``text-generation`` da Hugging Face.""" | |
| def __init__( | |
| self, | |
| repo_id: str, | |
| device: str = "cpu", | |
| max_new_tokens: int = 512, | |
| ) -> None: | |
| """Inicializa o cliente (sem carregar o modelo ainda). | |
| Args: | |
| repo_id: Identificador do modelo público (``HF_MODEL_REPO_ID``). | |
| device: ``cpu`` ou ``cuda`` (``HF_DEVICE``). | |
| max_new_tokens: Limite de tokens gerados na resposta. | |
| Raises: | |
| TransformersError: Se ``repo_id`` não for informado. | |
| """ | |
| if not repo_id: | |
| raise TransformersError( | |
| "HF_MODEL_REPO_ID não configurado. Defina um modelo público válido." | |
| ) | |
| self._repo_id = repo_id | |
| self._device = device | |
| self._max_new_tokens = max_new_tokens | |
| self._pipeline = None # carregamento preguiçoso (lazy) | |
| def _carregar_pipeline(self): | |
| """Carrega (uma única vez) o pipeline de geração de texto. | |
| Reaproveita um pipeline em cache para o mesmo (modelo, device), evitando | |
| recarregar o modelo a cada chamada. | |
| """ | |
| if self._pipeline is not None: | |
| return self._pipeline | |
| chave = (self._repo_id, self._device) | |
| em_cache = _PIPELINE_CACHE.get(chave) | |
| if em_cache is not None: | |
| self._pipeline = em_cache | |
| return self._pipeline | |
| try: | |
| from transformers import pipeline # import tardio | |
| except ImportError as exc: # pragma: no cover - depende do ambiente | |
| raise TransformersError( | |
| "Dependência ausente. Instale: pip install transformers torch" | |
| ) from exc | |
| try: | |
| # device=-1 força CPU; device=0 usa a primeira GPU. | |
| device_idx = 0 if self._device.lower().startswith("cuda") else -1 | |
| self._pipeline = pipeline( | |
| "text-generation", | |
| model=self._repo_id, | |
| device=device_idx, | |
| ) | |
| except Exception as exc: # modelo ausente, sem memória, etc. | |
| raise TransformersError( | |
| f"Falha ao carregar o modelo '{self._repo_id}': {type(exc).__name__}." | |
| ) from exc | |
| _PIPELINE_CACHE[chave] = self._pipeline | |
| logger.info("Modelo Transformers carregado (repo=%s).", self._repo_id) | |
| return self._pipeline | |
| def gerar(self, prompt: str, system: str | None = None) -> str: | |
| """Gera a interpretação textual a partir do prompt. | |
| Args: | |
| prompt: Prompt completo (com o JSON de métricas). | |
| system: Instrução de sistema opcional (prefixada ao prompt). | |
| Returns: | |
| Texto interpretativo gerado pelo modelo. | |
| Raises: | |
| TransformersError: Em qualquer falha de carga/geração. | |
| """ | |
| pipe = self._carregar_pipeline() | |
| entrada = f"{system}\n\n{prompt}" if system else prompt | |
| try: | |
| saida = pipe( | |
| entrada, | |
| max_new_tokens=self._max_new_tokens, | |
| do_sample=False, | |
| return_full_text=False, | |
| ) | |
| except Exception as exc: | |
| raise TransformersError( | |
| f"Falha na geração com Transformers: {type(exc).__name__}." | |
| ) from exc | |
| texto = "" | |
| if isinstance(saida, list) and saida: | |
| texto = str(saida[0].get("generated_text", "")).strip() | |
| if not texto: | |
| raise TransformersError("Transformers retornou resposta vazia.") | |
| logger.info("Interpretação gerada via Transformers (repo=%s).", self._repo_id) | |
| return texto | |