client.py 2.93 KB
"""HTTP client for the translation service."""

from __future__ import annotations

import logging
from typing import List, Optional, Sequence, Union

import requests

from config.loader import get_app_config
from translation.settings import normalize_translation_model, normalize_translation_scene

logger = logging.getLogger(__name__)


class TranslationServiceClient:
    """Business-side translation client that talks to the translator service."""

    def __init__(
        self,
        *,
        base_url: Optional[str] = None,
        default_model: Optional[str] = None,
        default_scene: Optional[str] = None,
        timeout_sec: Optional[float] = None,
    ) -> None:
        cfg = get_app_config().services.translation.as_dict()
        self.base_url = str(base_url or cfg["service_url"]).rstrip("/")
        self.default_model = normalize_translation_model(cfg, default_model or cfg["default_model"])
        self.default_scene = normalize_translation_scene(cfg, default_scene or cfg["default_scene"])
        self.timeout_sec = float(cfg["timeout_sec"] if timeout_sec is None else timeout_sec)

    @property
    def model(self) -> str:
        return self.default_model

    @property
    def supports_batch(self) -> bool:
        return True

    def translate(
        self,
        text: Union[str, Sequence[str]],
        target_lang: str,
        source_lang: Optional[str] = None,
        scene: Optional[str] = None,
        model: Optional[str] = None,
    ) -> Union[Optional[str], List[Optional[str]]]:
        if isinstance(text, tuple):
            text = list(text)
        payload = {
            "text": text,
            "target_lang": target_lang,
            "source_lang": source_lang,
            "model": (model or self.default_model),
            "scene": self.default_scene if scene is None else scene,
        }
        try:
            response = requests.post(
                f"{self.base_url}/translate",
                json=payload,
                timeout=self.timeout_sec,
            )
            if response.status_code != 200:
                logger.warning(
                    "Translation service request failed: status=%s body=%s",
                    response.status_code,
                    (response.text or "")[:300],
                )
                return self._empty_result_for(text)
            data = response.json()
            return data.get("translated_text")
        except Exception as exc:
            logger.warning("Translation service request error: %s", exc, exc_info=True)
            return self._empty_result_for(text)

    @staticmethod
    def _empty_result_for(
        text: Union[str, Sequence[str]],
    ) -> Union[Optional[str], List[Optional[str]]]:
        if isinstance(text, (list, tuple)):
            return [None for _ in text]
        return None


def create_translation_client() -> TranslationServiceClient:
    """Create the business-side translation client."""
    return TranslationServiceClient()