mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-05 02:41:56 +00:00
refactor(soniox): derive handler options and provider params without mutating a local dict
This commit is contained in:
parent
3d9e261c20
commit
70f8bcee9e
1 changed files with 44 additions and 34 deletions
|
|
@ -19,6 +19,7 @@ import asyncio
|
|||
import math
|
||||
import time
|
||||
from collections.abc import Coroutine, Mapping, Sequence
|
||||
from types import MappingProxyType
|
||||
from typing import TYPE_CHECKING, Any, Final
|
||||
|
||||
import httpx
|
||||
|
|
@ -36,6 +37,7 @@ from litellm.llms.custom_httpx.http_handler import (
|
|||
get_async_httpx_client,
|
||||
)
|
||||
from litellm.llms.soniox.audio_transcription.transformation import (
|
||||
SONIOX_HANDLER_ONLY_PARAMS,
|
||||
SonioxAudioTranscriptionConfig,
|
||||
decode_soniox_form_params,
|
||||
)
|
||||
|
|
@ -61,12 +63,32 @@ else:
|
|||
|
||||
|
||||
_CLEANUP_TARGETS: Final = TypeAdapter(tuple[str, ...])
|
||||
# `language` is translated to `language_hints` upstream; `audio_url`/`file_id`/`response_format`
|
||||
# are placed by the handler, everything else here is a LiteLLM-only knob.
|
||||
_NOT_FORWARDED_TO_SONIOX: Final[frozenset[str]] = frozenset(
|
||||
(*SONIOX_HANDLER_ONLY_PARAMS, "audio_url", "file_id", "response_format", "language")
|
||||
)
|
||||
|
||||
|
||||
def _optional_str(value: object) -> str | None:
|
||||
return None if value is None else str(value)
|
||||
|
||||
|
||||
def _float_or_default(value: object, default: float) -> float:
|
||||
try:
|
||||
parsed: Final = float(str(value))
|
||||
except ValueError:
|
||||
return default
|
||||
return parsed if math.isfinite(parsed) else default
|
||||
|
||||
|
||||
def _int_or_default(value: object, default: int) -> int:
|
||||
try:
|
||||
return int(str(value))
|
||||
except (ValueError, OverflowError):
|
||||
return default
|
||||
|
||||
|
||||
class _TranscriptionMeta(TypedDict, total=False):
|
||||
"""Fields the handler reads from a Soniox transcription object."""
|
||||
|
||||
|
|
@ -191,7 +213,7 @@ class SonioxAudioTranscriptionHandler:
|
|||
) -> tuple[
|
||||
dict[str, str], # auth headers
|
||||
str, # api_base (no trailing slash)
|
||||
dict[str, object], # body for POST /v1/transcriptions (without file_id/audio_url)
|
||||
Mapping[str, object], # body for POST /v1/transcriptions (without file_id/audio_url)
|
||||
_HandlerOptions, # handler-only options (poll interval, cleanup, ...)
|
||||
]:
|
||||
# Validate env -> auth headers.
|
||||
|
|
@ -207,18 +229,18 @@ class SonioxAudioTranscriptionHandler:
|
|||
|
||||
base_url: Final = get_soniox_api_base(api_base)
|
||||
|
||||
# Decoded copy so the caller's dict is never mutated
|
||||
# (the caller may reuse `optional_params` for retries or logging).
|
||||
params: Final = dict(decode_soniox_form_params(optional_params))
|
||||
decoded: Final = decode_soniox_form_params(optional_params)
|
||||
|
||||
# Pull handler-only kwargs out of params so they aren't sent
|
||||
# to Soniox.
|
||||
poll_interval = float(str(params.pop("soniox_polling_interval", SONIOX_DEFAULT_POLL_INTERVAL)))
|
||||
try:
|
||||
max_attempts = int(str(params.pop("soniox_max_polling_attempts", SONIOX_DEFAULT_MAX_POLL_ATTEMPTS)))
|
||||
except (ValueError, OverflowError):
|
||||
max_attempts = SONIOX_DEFAULT_MAX_POLL_ATTEMPTS
|
||||
cleanup_raw: Final = params.pop("soniox_cleanup", SONIOX_DEFAULT_CLEANUP)
|
||||
# Server-side clamps. Caller-supplied poll settings (from request kwargs)
|
||||
# are bounded so an authenticated caller cannot force a worker into a
|
||||
# tight poll loop (zero interval) or pin it indefinitely (huge attempt
|
||||
# count). Total polling time is bounded by
|
||||
# SONIOX_MAX_POLL_ATTEMPTS * SONIOX_MAX_POLL_INTERVAL.
|
||||
poll_interval: Final = _float_or_default(decoded.get("soniox_polling_interval"), SONIOX_DEFAULT_POLL_INTERVAL)
|
||||
max_attempts: Final = _int_or_default(
|
||||
decoded.get("soniox_max_polling_attempts"), SONIOX_DEFAULT_MAX_POLL_ATTEMPTS
|
||||
)
|
||||
cleanup_raw: Final = decoded.get("soniox_cleanup", SONIOX_DEFAULT_CLEANUP)
|
||||
cleanup: Final[tuple[str, ...]] = (
|
||||
()
|
||||
if cleanup_raw is None
|
||||
|
|
@ -226,34 +248,22 @@ class SonioxAudioTranscriptionHandler:
|
|||
if isinstance(cleanup_raw, str)
|
||||
else _CLEANUP_TARGETS.validate_python(cleanup_raw)
|
||||
)
|
||||
filename_override: Final = _optional_str(params.pop("filename", None))
|
||||
|
||||
# Server-side clamps. Caller-supplied poll settings (from request kwargs)
|
||||
# are bounded so an authenticated caller cannot force a worker into a
|
||||
# tight poll loop (zero interval) or pin it indefinitely (huge attempt
|
||||
# count). Total polling time is bounded by
|
||||
# SONIOX_MAX_POLL_ATTEMPTS * SONIOX_MAX_POLL_INTERVAL.
|
||||
if not math.isfinite(poll_interval):
|
||||
poll_interval = SONIOX_DEFAULT_POLL_INTERVAL
|
||||
clamped_poll_interval: Final = max(SONIOX_MIN_POLL_INTERVAL, min(poll_interval, SONIOX_MAX_POLL_INTERVAL))
|
||||
clamped_max_attempts: Final = max(1, min(max_attempts, SONIOX_MAX_POLL_ATTEMPTS))
|
||||
|
||||
# response_format is handled by LiteLLM post-processing, not Soniox.
|
||||
handler_opts: Final[_HandlerOptions] = {
|
||||
"poll_interval": clamped_poll_interval,
|
||||
"max_attempts": clamped_max_attempts,
|
||||
"poll_interval": max(SONIOX_MIN_POLL_INTERVAL, min(poll_interval, SONIOX_MAX_POLL_INTERVAL)),
|
||||
"max_attempts": max(1, min(max_attempts, SONIOX_MAX_POLL_ATTEMPTS)),
|
||||
"cleanup": cleanup,
|
||||
"filename_override": filename_override,
|
||||
"audio_url": _optional_str(params.pop("audio_url", None)),
|
||||
"file_id": _optional_str(params.pop("file_id", None)),
|
||||
"response_format": _optional_str(params.pop("response_format", None)),
|
||||
"filename_override": _optional_str(decoded.get("filename")),
|
||||
"audio_url": _optional_str(decoded.get("audio_url")),
|
||||
"file_id": _optional_str(decoded.get("file_id")),
|
||||
"response_format": _optional_str(decoded.get("response_format")),
|
||||
}
|
||||
|
||||
# Soniox does not accept `language` directly; map_openai_params should
|
||||
# already have translated it, but drop any leftover to be safe.
|
||||
params.pop("language", None)
|
||||
|
||||
return auth_headers, base_url, params, handler_opts
|
||||
provider_params: Final = MappingProxyType(
|
||||
{key: value for key, value in decoded.items() if key not in _NOT_FORWARDED_TO_SONIOX}
|
||||
)
|
||||
return auth_headers, base_url, provider_params, handler_opts
|
||||
|
||||
def _build_create_body(
|
||||
self,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue