From b210c6bf4cc6e5695257503085a37bb98dcb64e3 Mon Sep 17 00:00:00 2001 From: hx <1367557521@qq.com> Date: Sun, 27 Sep 2026 18:16:11 +0800 Subject: [PATCH] fix(xai): clear type-discipline violations in imagine transforms Four `# mutable-ok` comments had drifted off the lines they were meant to cover and suppressed nothing (LIT013 is frozen at 0), and the new locals were missing `Final` (LIT010). Build the optional image params with one filtered comprehension instead of chained `x or {}` dicts Co-authored-by: Cursor --- litellm/llms/xai/image_edit/transformation.py | 38 +++++++++---------- .../xai/image_generation/transformation.py | 15 +++----- litellm/llms/xai/videos/transformation.py | 38 +++++++++---------- 3 files changed, 41 insertions(+), 50 deletions(-) diff --git a/litellm/llms/xai/image_edit/transformation.py b/litellm/llms/xai/image_edit/transformation.py index 496a0c7e65d..b20382a7acf 100644 --- a/litellm/llms/xai/image_edit/transformation.py +++ b/litellm/llms/xai/image_edit/transformation.py @@ -54,7 +54,7 @@ class XAIImageEditConfig(BaseImageEditConfig): ) -> dict: # mutable-ok: provider JSON body and base-class dict signature supported: Final = frozenset(self.get_supported_openai_params(model)) allowed: Final = supported | _XAI_NATIVE_PARAMS - raw = image_edit_optional_params + raw: Final = image_edit_optional_params incoming: Final = dict(raw) # mutable-ok: provider JSON body and base-class dict signature unknown: Final = tuple(key for key in incoming if key not in allowed) if unknown and not drop_params: @@ -64,7 +64,7 @@ class XAIImageEditConfig(BaseImageEditConfig): "Set drop_params=True to drop unsupported parameters." ) - pairs = ((key, value) for key, value in incoming.items() if key in allowed) + pairs: Final = ((key, value) for key, value in incoming.items() if key in allowed) mapped: Final = dict(pairs) # mutable-ok: provider JSON body and base-class dict signature size: Final = mapped.get("size") aspect_ratio: Final = mapped.get("aspect_ratio") or ( @@ -72,17 +72,12 @@ class XAIImageEditConfig(BaseImageEditConfig): ) n: Final = mapped.get("n") resolution: Final = mapped.get("resolution") - aspect = {"aspect_ratio": aspect_ratio} if aspect_ratio is not None else None # mutable-ok: provider JSON body - count = {"n": int(n)} if n is not None else None # mutable-ok: provider JSON body - quality = {"resolution": resolution} if resolution is not None else None # mutable-ok: provider JSON body - aspect_ratio_field: Final = aspect or {} # mutable-ok: provider JSON body and base-class dict signature - n_field: Final = count or {} # mutable-ok: provider JSON body and base-class dict signature - resolution_field: Final = quality or {} # mutable-ok: provider JSON body and base-class dict signature - return { # mutable-ok: provider JSON body and base-class dict signature - **aspect_ratio_field, - **n_field, - **resolution_field, - } # mutable-ok: provider JSON body and base-class dict signature + fields: Final = ( + ("aspect_ratio", aspect_ratio), + ("n", int(n) if n is not None else None), + ("resolution", resolution), + ) + return {key: value for key, value in fields if value is not None} # mutable-ok: base class returns a dict def use_multipart_form_data(self) -> bool: return False @@ -163,12 +158,12 @@ class XAIImageEditConfig(BaseImageEditConfig): raise ValueError("xAI image edit requires at least one reference image.") n: Final = image_edit_optional_request_params.get("n") - prompt_body = {"prompt": prompt} if prompt is not None else None # mutable-ok: provider JSON body - many = {"images": list(image_payloads)} # mutable-ok: provider JSON body - one = image_payloads[0] - image_body = {"image": one} if len(image_payloads) == 1 else many # mutable-ok: provider JSON body + prompt_body: Final = {"prompt": prompt} if prompt is not None else None # mutable-ok: provider JSON body + many: Final = {"images": list(image_payloads)} # mutable-ok: provider JSON body + one: Final = image_payloads[0] + image_body: Final = {"image": one} if len(image_payloads) == 1 else many # mutable-ok: provider JSON body prompt_field: Final = prompt_body or {} # mutable-ok: provider JSON body and base-class dict signature - image_field: Final = image_body # mutable-ok: provider JSON body and base-class dict signature + image_field: Final = image_body request: Final[dict[str, object]] = { # mutable-ok: provider JSON body and base-class dict signature "model": XAIModelInfo.get_base_model(model) or model, **prompt_field, @@ -214,8 +209,9 @@ class XAIImageEditConfig(BaseImageEditConfig): return ImageResponse(data=list(images)) # mutable-ok: provider JSON body and base-class dict signature def _as_image_list( - self, image: FileTypes | list[FileTypes] - ) -> tuple[FileTypes, ...]: # mutable-ok: provider JSON body and base-class dict signature + self, + image: FileTypes | list[FileTypes], # mutable-ok: the proxy passes multi-image edits as a list of uploads + ) -> tuple[FileTypes, ...]: if isinstance(image, list): return tuple(item for item in image if item is not None) return (image,) @@ -229,7 +225,7 @@ class XAIImageEditConfig(BaseImageEditConfig): if image.get("url"): return {"url": str(image["url"])} # mutable-ok: provider JSON body and base-class dict signature if image.get("file_id"): - file_id = str(image["file_id"]) + file_id: Final = str(image["file_id"]) return {"file_id": file_id} # mutable-ok: provider JSON body and base-class dict signature mime: Final = ImageEditRequestUtils.get_image_content_type(image) diff --git a/litellm/llms/xai/image_generation/transformation.py b/litellm/llms/xai/image_generation/transformation.py index e0290748218..5db907fe809 100644 --- a/litellm/llms/xai/image_generation/transformation.py +++ b/litellm/llms/xai/image_generation/transformation.py @@ -57,7 +57,7 @@ class XAIImageGenerationConfig(BaseImageGenerationConfig): "Set drop_params=True to drop unsupported parameters." ) - pairs = ((k, v) for k, v in non_default_params.items() if k in allowed) + pairs: Final = ((k, v) for k, v in non_default_params.items() if k in allowed) native: Final = dict(pairs) # mutable-ok: provider JSON body and base-class dict signature merged: Final = {**optional_params, **native} # mutable-ok: provider JSON body and base-class dict signature size: Final = merged.get("size") @@ -65,11 +65,8 @@ class XAIImageGenerationConfig(BaseImageGenerationConfig): _SIZE_TO_ASPECT_RATIO.get(str(size), "1:1") if size else None ) n: Final = merged.get("n") - aspect = {"aspect_ratio": aspect_ratio} if aspect_ratio is not None else None # mutable-ok: provider JSON body - count = {"n": int(n)} if n is not None else None # mutable-ok: provider JSON body - aspect_ratio_field: Final = aspect or {} # mutable-ok: provider JSON body and base-class dict signature - n_field: Final = count or {} # mutable-ok: provider JSON body and base-class dict signature - return {**aspect_ratio_field, **n_field} # mutable-ok: provider JSON body and base-class dict signature + fields: Final = (("aspect_ratio", aspect_ratio), ("n", int(n) if n is not None else None)) + return {key: value for key, value in fields if value is not None} # mutable-ok: base class returns a dict def get_complete_url( self, @@ -143,9 +140,9 @@ class XAIImageGenerationConfig(BaseImageGenerationConfig): headers: dict, # mutable-ok: provider JSON body and base-class dict signature ) -> dict: # mutable-ok: provider JSON body and base-class dict signature n: Final = optional_params.get("n") - requested_ratio = optional_params.get("aspect_ratio") - aspect = {"aspect_ratio": requested_ratio} if requested_ratio is not None else None # fmt: skip # mutable-ok: provider JSON body - count = {"n": int(n)} if n is not None else None # mutable-ok: provider JSON body + requested_ratio: Final = optional_params.get("aspect_ratio") + aspect: Final = {"aspect_ratio": requested_ratio} if requested_ratio is not None else None # fmt: skip # mutable-ok: provider JSON body + count: Final = {"n": int(n)} if n is not None else None # mutable-ok: provider JSON body return { # mutable-ok: provider JSON body and base-class dict signature "model": XAIModelInfo.get_base_model(model) or model, "prompt": prompt, diff --git a/litellm/llms/xai/videos/transformation.py b/litellm/llms/xai/videos/transformation.py index ef3786947f5..5b4cfb94fb4 100644 --- a/litellm/llms/xai/videos/transformation.py +++ b/litellm/llms/xai/videos/transformation.py @@ -27,7 +27,7 @@ from litellm.types.videos.utils import ( if TYPE_CHECKING: from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj -_DROPPED = frozenset(("seconds", "size", "input_reference", "user", "extra_headers", "model")) +_DROPPED: Final = frozenset(("seconds", "size", "input_reference", "user", "extra_headers", "model")) _SIZE_TO_ASPECT_RATIO: Final = { # mutable-ok: provider JSON body and base-class dict signature "1024x1024": "1:1", "1792x1024": "16:9", @@ -78,19 +78,19 @@ class XAIVideoConfig(BaseVideoConfig): model: str, drop_params: bool, ) -> dict: # mutable-ok: provider JSON body and base-class dict signature - raw = video_create_optional_params + raw: Final = video_create_optional_params incoming: Final = dict(raw) # mutable-ok: provider JSON body and base-class dict signature size: Final = incoming.get("size") - seconds = incoming.get("seconds") - use_duration = "seconds" in incoming and "duration" not in incoming - duration_value = _duration_from_seconds(seconds) - duration = {"duration": duration_value} if use_duration else None # mutable-ok: provider JSON body - mapped_ratio = incoming.get("aspect_ratio") or _SIZE_TO_ASPECT_RATIO.get(str(size), "16:9") - use_ratio = bool(size) and "aspect_ratio" not in incoming - ratio = {"aspect_ratio": mapped_ratio} if use_ratio else None # mutable-ok: provider JSON body - image_ref = incoming.get("image") or incoming.get("input_reference") - use_image = bool(incoming.get("input_reference")) and "image" not in incoming - image = {"image": image_ref} if use_image else None # mutable-ok: provider JSON body + seconds: Final = incoming.get("seconds") + use_duration: Final = "seconds" in incoming and "duration" not in incoming + duration_value: Final = _duration_from_seconds(seconds) + duration: Final = {"duration": duration_value} if use_duration else None # mutable-ok: provider JSON body + mapped_ratio: Final = incoming.get("aspect_ratio") or _SIZE_TO_ASPECT_RATIO.get(str(size), "16:9") + use_ratio: Final = bool(size) and "aspect_ratio" not in incoming + ratio: Final = {"aspect_ratio": mapped_ratio} if use_ratio else None # mutable-ok: provider JSON body + image_ref: Final = incoming.get("image") or incoming.get("input_reference") + use_image: Final = bool(incoming.get("input_reference")) and "image" not in incoming + image: Final = {"image": image_ref} if use_image else None # mutable-ok: provider JSON body return { # mutable-ok: provider JSON body and base-class dict signature **{ # mutable-ok: provider JSON body and base-class dict signature key: value for key, value in incoming.items() if key not in _DROPPED @@ -104,9 +104,7 @@ class XAIVideoConfig(BaseVideoConfig): self, api_base: str | None, api_key: str | None, - litellm_params: GenericLiteLLMParams - | dict - | None, # mutable-ok: provider JSON body and base-class dict signature + litellm_params: GenericLiteLLMParams | dict | None, # mutable-ok: get_complete_url receives the base-class dict ) -> str: from litellm.llms.xai.oauth import XAIOAuthAuthenticator, should_use_xai_oauth @@ -146,7 +144,7 @@ class XAIVideoConfig(BaseVideoConfig): should_use_xai_oauth, ) - dumped = litellm_params.model_dump() if litellm_params is not None else None + dumped: Final = litellm_params.model_dump() if litellm_params is not None else None params: Final = dumped or {} # mutable-ok: provider JSON body and base-class dict signature resolved_api_key: Final = api_key or (litellm_params.api_key if litellm_params else None) dynamic_api_key: Final = XAIModelInfo.get_api_key(resolved_api_key) @@ -211,8 +209,8 @@ class XAIVideoConfig(BaseVideoConfig): ) if video_create_optional_request_params.get(key) is not None } - prompt_body = {"prompt": prompt} if prompt else None # mutable-ok: provider JSON body - duration_body = {"duration": 6} if "duration" not in copied else None # mutable-ok: provider JSON body + prompt_body: Final = {"prompt": prompt} if prompt else None # mutable-ok: provider JSON body + duration_body: Final = {"duration": 6} if "duration" not in copied else None # mutable-ok: provider JSON body return ( { # mutable-ok: provider JSON body and base-class dict signature "model": XAIModelInfo.get_base_model(model) or model, @@ -248,7 +246,7 @@ class XAIVideoConfig(BaseVideoConfig): ) if custom_llm_provider: video_obj.id = encode_video_id_with_provider(video_obj.id, custom_llm_provider, model) - usage_body = usage if isinstance(usage, dict) else None + usage_body: Final = usage if isinstance(usage, dict) else None video_obj.usage = usage_body or {} # mutable-ok: provider JSON body and base-class dict signature video_obj._hidden_params["video_url"] = None return video_obj @@ -280,7 +278,7 @@ class XAIVideoConfig(BaseVideoConfig): response_data: Final = raw_response.json() status_raw: Final = str(response_data.get("status") or "processing").lower() status: Final = _STATUS_MAP.get(status_raw, status_raw) - video_body = response_data.get("video") + video_body: Final = response_data.get("video") video_meta: Final = video_body or {} # mutable-ok: provider JSON body and base-class dict signature video_url: Final = video_meta.get("url") if isinstance(video_meta, dict) else None seconds: Final = (