diff --git a/backend/src/plugins/translate/_llm_http.py b/backend/src/plugins/translate/_llm_http.py index 869215da..d3127edd 100644 --- a/backend/src/plugins/translate/_llm_http.py +++ b/backend/src/plugins/translate/_llm_http.py @@ -11,12 +11,19 @@ # @RATIONALE Extracted from LLMTranslationService (793 lines) to keep module under INV_7 limit. # @REJECTED Single HTTP client class — kept as module-level functions for stateless reusability. +import os import time from typing import Any from ...core.logger import logger +def _get_verify() -> bool: + """Resolve SSL verify flag from LLM_SSL_VERIFY env var.""" + raw = os.getenv("LLM_SSL_VERIFY", "true").strip().lower() + return raw not in ("false", "0", "no", "off") + + # #region call_openai_compatible [C:3] [TYPE Function] [SEMANTICS translate, llm, http, openai] # @BRIEF Call OpenAI-compatible API with rate-limit handling and structured output fallback. # @PRE Valid API endpoint, key, model, and prompt. @@ -141,7 +148,7 @@ def _do_http_request(url: str, headers: dict, payload: dict) -> tuple[Any, str]: _max_retry_429 = 3 _retry_count_429 = 0 while _retry_count_429 < _max_retry_429: - response = http_requests.post(url, headers=headers, json=payload, timeout=180) + response = http_requests.post(url, headers=headers, json=payload, timeout=180, verify=_get_verify()) response_text = response.text if response.status_code == 429: _retry_count_429 += 1 @@ -179,7 +186,7 @@ def _handle_response_format_fallback( logger.explore("Structured outputs not supported, retrying without response_format", extra={"src": "executor"}) payload.pop("response_format", None) - new_response = http_requests.post(url, headers=headers, json=payload, timeout=180) + new_response = http_requests.post(url, headers=headers, json=payload, timeout=180, verify=_get_verify()) response.status_code = new_response.status_code response._content = new_response.content response.encoding = new_response.encoding diff --git a/backend/src/plugins/translate/preview_llm_client.py b/backend/src/plugins/translate/preview_llm_client.py index cad17984..e3fd58a8 100644 --- a/backend/src/plugins/translate/preview_llm_client.py +++ b/backend/src/plugins/translate/preview_llm_client.py @@ -3,12 +3,19 @@ # @SIDE_EFFECT Makes HTTP POST calls to external LLM API. # @RELATION DEPENDS_ON -> [EXT:requests] +import os import time as _time from typing import Any from ...core.logger import logger +def _get_verify() -> bool: + """Resolve SSL verify flag from LLM_SSL_VERIFY env var.""" + raw = os.getenv("LLM_SSL_VERIFY", "true").strip().lower() + return raw not in ("false", "0", "no", "off") + + class LLMClient: """Call OpenAI-compatible LLM APIs with retry and structured output handling.""" @@ -63,7 +70,7 @@ class LLMClient: _max_retry_429 = 3 _retry_count_429 = 0 while _retry_count_429 < _max_retry_429: - response = http_requests.post(url, headers=headers, json=payload, timeout=600) + response = http_requests.post(url, headers=headers, json=payload, timeout=600, verify=_get_verify()) if response.status_code == 429: _retry_count_429 += 1 retry_after = response.headers.get("Retry-After") @@ -80,7 +87,7 @@ class LLMClient: and any(p in (response.text or "").lower() for p in _response_format_error_patterns)): logger.explore("Structured outputs not supported, retrying without response_format") payload.pop("response_format", None) - response = http_requests.post(url, headers=headers, json=payload, timeout=600) + response = http_requests.post(url, headers=headers, json=payload, timeout=600, verify=_get_verify()) if not response.ok: logger.explore(f"LLM API error status={response.status_code} model={payload.get('model')} body={response.text[:2000]}")