fix(stt): configure OpenAI client timeout and retries

(cherry picked from commit 2158ee474af441821517da37e724821f46ec2ed5)
This commit is contained in:
fangliquanflq
2026-09-16 22:24:44 +08:00
committed by Teknium
parent 35f69a9101
commit 0690fc1965
4 changed files with 39 additions and 2 deletions

View File

@@ -1625,6 +1625,8 @@ stt:
openai:
model: "whisper-1" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe | gpt-transcribe
language: "" # auto-detect; set to "en", "es", "fr", etc. to force
timeout: 60 # request timeout in seconds; increase for self-hosted model cold starts
max_retries: 1 # OpenAI SDK transport retries; set 0 to disable
# mistral:
# model: "voxtral-mini-latest" # voxtral-mini-latest | voxtral-mini-2602
# deepinfra:

View File

@@ -1129,6 +1129,8 @@ DEFAULT_CONFIG = {
# whisper-1, gpt-4o-mini-transcribe, gpt-4o-transcribe, gpt-transcribe
"model": "whisper-1",
"language": "", # auto-detect; set "en", "es", ... to force
"timeout": 60, # seconds; allow self-hosted backends time to cold-start
"max_retries": 1, # OpenAI SDK transport retries
},
"mistral": {
"model": "voxtral-mini-latest", # voxtral-mini-latest, voxtral-mini-2602

View File

@@ -226,6 +226,32 @@ class TestTranscribeGroq:
assert "openai package" in result["error"]
class TestOpenAIClientConfig:
@pytest.mark.parametrize(
("openai_config", "expected_timeout", "expected_retries"),
[({}, 60, 1), ({"timeout": 95, "max_retries": 3}, 95, 3)],
)
def test_stt_openai_config_controls_sdk_client(
self, monkeypatch, tmp_path, sample_wav, openai_config, expected_timeout, expected_retries
):
monkeypatch.setenv("GROQ_API_KEY", "gsk-test")
monkeypatch.setenv("HERMES_HOME", str(tmp_path))
config_lines = ["stt:", " openai:"]
config_lines.extend(f" {key}: {value}" for key, value in openai_config.items())
(tmp_path / "config.yaml").write_text("\n".join(config_lines) + "\n", encoding="utf-8")
mock_client = MagicMock()
mock_client.audio.transcriptions.create.return_value = "hi"
with patch("tools.transcription_tools._HAS_OPENAI", True), \
patch("openai.OpenAI", return_value=mock_client) as openai_client:
from tools.transcription_tools import _transcribe_groq
result = _transcribe_groq(sample_wav, "whisper-large-v3-turbo")
assert result["success"] is True
assert openai_client.call_args.kwargs["timeout"] == expected_timeout
assert openai_client.call_args.kwargs["max_retries"] == expected_retries
def test_null_groq_subsection_is_safe(self, monkeypatch, sample_wav):
"""`stt.groq: null` in YAML yields None; must not raise, auto-detect stays intact."""
monkeypatch.setenv("GROQ_API_KEY", "gsk-test")

View File

@@ -37,12 +37,19 @@ def _has_xai_stt_credentials() -> bool:
def _with_openai_client(api_key: str, base_url: Optional[str], file_path: str, log_label: str, body):
"""Run ``body(client)`` on a fresh OpenAI SDK client (30s timeout, no retries); always closed.
"""Run ``body(client)`` on a fresh, configured OpenAI SDK client; always closed.
Errors map to the shared envelope. APIConnectionError is checked before APITimeoutError (its
subclass) so timeouts report as connection errors, as they always have."""
try:
from openai import OpenAI
client = OpenAI(api_key=api_key, base_url=base_url, timeout=30, max_retries=0)
from tools.transcription_tools import _load_stt_config
openai_config = _get_stt_section(_load_stt_config(), "openai")
client = OpenAI(
api_key=api_key,
base_url=base_url,
timeout=openai_config.get("timeout", 60),
max_retries=openai_config.get("max_retries", 1),
)
try:
return body(client)
finally: