import json import mimetypes import ssl import uuid from pathlib import Path from typing import Any, Callable from urllib import request from ...scanner.modelos import SegmentoDeTranscricao class ProviderDeTranscricaoGroq: """Provider Groq compatível com o contrato de transcrição do scanner.""" endpoint = "https://api.groq.com/openai/v1/audio/transcriptions" def __init__(self, api_key: str, modelo: str = "whisper-large-v3-turbo", idioma: str = "pt", tempo_limite: float = 120.0, requisicao: Callable[..., Any] | None = None) -> None: if not api_key.strip(): raise ValueError("A chave da API Groq é obrigatória.") self.api_key = api_key.strip() self.modelo = modelo self.idioma = idioma self.tempo_limite = tempo_limite self._requisicao = requisicao or request.urlopen try: import certifi self._contexto_ssl = ssl.create_default_context(cafile=certifi.where()) except ImportError: self._contexto_ssl = ssl.create_default_context() def transcrever(self, clipe: Any) -> list[SegmentoDeTranscricao]: arquivo = getattr(clipe, "arquivo", None) if not arquivo: raise ValueError("O clipe não possui arquivo de origem para transcrição.") caminho = Path(arquivo) if not caminho.is_file(): raise FileNotFoundError(f"Arquivo do clipe não encontrado: {caminho}") corpo, tipo = self._multipart(caminho) req = request.Request(self.endpoint, data=corpo, method="POST", headers={ "Authorization": f"Bearer {self.api_key}", "Content-Type": tipo, }) argumentos = {"timeout": self.tempo_limite} if self._requisicao is request.urlopen: argumentos["context"] = self._contexto_ssl with self._requisicao(req, **argumentos) as resposta: dados = json.loads(resposta.read().decode("utf-8")) return [SegmentoDeTranscricao(float(item["start"]), float(item["end"]), str(item.get("text", "")).strip(), None) for item in dados.get("segments", [])] def _multipart(self, caminho: Path) -> tuple[bytes, str]: limite = "----engine-groq-" + uuid.uuid4().hex mime = mimetypes.guess_type(caminho.name)[0] or "application/octet-stream" partes: list[bytes] = [] campos = {"model": self.modelo, "language": self.idioma, "response_format": "verbose_json", "timestamp_granularities[]": "segment"} for nome, valor in campos.items(): partes.append(f"--{limite}\r\nContent-Disposition: form-data; name=\"{nome}\"\r\n\r\n{valor}\r\n".encode()) partes.append(f"--{limite}\r\nContent-Disposition: form-data; name=\"file\"; filename=\"{caminho.name}\"\r\nContent-Type: {mime}\r\n\r\n".encode()) partes.append(caminho.read_bytes()) partes.append(f"\r\n--{limite}--\r\n".encode()) return b"".join(partes), f"multipart/form-data; boundary={limite}"