berriai-litellm-16672-16708
OpenAI video content downloads are broken in both synchronous and asynchronous usage.
The content request must be a GET with no extra parameters. `OpenAIVideoConfig.transform_video_content_request` (litellm/llms/openai/videos/transformation.py) currently returns a `video_id` query parameter alongside the URL, which makes the caller issue a POST to `/v1/videos/{video_id}/content`; OpenAI answers with an `invalid_request_error` whose JSON body can then be written out as if it were the `.mp4`. Called as `transform_video_content_request(video_id="video_123", api_base="https://api.openai.com/v1/videos", litellm_params={}, headers={})` it must return the URL `https://api.openai.com/v1/videos/video_123/content` together with an empty mapping, i.e. `params == {}`. With that empty mapping the HTTP layer already takes its GET path, so `BaseLLMHTTPHandler().video_content_handler(video_id="video_abc", video_content_provider_config=OpenAIVideoConfig(), custom_llm_provider="openai", litellm_params={"api_base": "https://api.openai.com/v1"}, logging_obj=...)` must call the httpx client's `get` exactly once with `url="https://api.openai.com/v1/videos/video_abc/content"`, must never call `post`, and must return the response's raw `content` bytes unchanged.
The config must expose async content handling at runtime. An asynchronous OpenAI video content download currently raises `AttributeError` because `OpenAIVideoConfig` has no `async_transform_video_content_response` when the module is actually imported: its base class is only resolved under `TYPE_CHECKING`, so at runtime the class inherits nothing that provides it. `OpenAIVideoConfig` must be a genuine subclass of `BaseVideoConfig` (litellm.llms.base_llm.videos.transformation) at import time, so that for `cfg = OpenAIVideoConfig()`, `callable(getattr(cfg, "async_transform_video_content_response", None))` is true and an async download returns the video content instead of failing.
Hidden tests · 3 fail-to-pass, 29 pass-to-passrun after the agent submits, in a clean verifier
Test patch · 69 lines
diff --git a/tests/test_litellm/test_video_generation.py b/tests/test_litellm/test_video_generation.py
index b11e38b32..4c8e166f1 100644
--- a/tests/test_litellm/test_video_generation.py
+++ b/tests/test_litellm/test_video_generation.py
@@ -14,6 +14,7 @@ import litellm
from litellm.types.videos.main import VideoObject, VideoResponse
from litellm.videos.main import video_generation, avideo_generation, video_status, avideo_status
from litellm.llms.openai.videos.transformation import OpenAIVideoConfig
+from litellm.llms.custom_httpx.llm_http_handler import BaseLLMHTTPHandler
from litellm.cost_calculator import default_video_cost_calculator
from litellm.litellm_core_utils.litellm_logging import Logging as LitellmLogging
from litellm.integrations.custom_logger import CustomLogger
@@ -713,5 +714,56 @@ class TestVideoLogging:
# The important thing is that the logging payload is created and recognized
+def test_openai_transform_video_content_request_empty_params():
+ """OpenAI content transform should return empty params to ensure GET is used."""
+ config = OpenAIVideoConfig()
+ url, params = config.transform_video_content_request(
+ video_id="video_123",
+ api_base="https://api.openai.com/v1/videos",
+ litellm_params={},
+ headers={},
+ )
+
+ assert url == "https://api.openai.com/v1/videos/video_123/content"
+ assert params == {}
+
+
+def test_video_content_handler_uses_get_for_openai():
+ """HTTP handler must use GET (not POST) for OpenAI content download."""
+ handler = BaseLLMHTTPHandler()
+ config = OpenAIVideoConfig()
+
+ mock_client = MagicMock()
+ mock_response = MagicMock()
+ mock_response.content = b"mp4-bytes"
+ mock_client.get.return_value = mock_response
+
+ with patch(
+ "litellm.llms.custom_httpx.llm_http_handler._get_httpx_client",
+ return_value=mock_client,
+ ):
+ result = handler.video_content_handler(
+ video_id="video_abc",
+ video_content_provider_config=config,
+ custom_llm_provider="openai",
+ litellm_params={"api_base": "https://api.openai.com/v1"},
+ logging_obj=MagicMock(),
+ timeout=5.0,
+ api_key="sk-test",
+ _is_async=False,
+ )
+
+ assert result == b"mp4-bytes"
+ mock_client.get.assert_called_once()
+ assert not mock_client.post.called
+ called_url = mock_client.get.call_args.kwargs["url"]
+ assert called_url == "https://api.openai.com/v1/videos/video_abc/content"
+
+
+def test_openai_video_config_has_async_transform():
+ """Ensure OpenAIVideoConfig exposes async_transform_video_content_response at runtime."""
+ cfg = OpenAIVideoConfig()
+ assert callable(getattr(cfg, "async_transform_video_content_response", None))
+
if __name__ == "__main__":
pytest.main([__file__])
Reference fix · 1 file, +6 −8the upstream merge, used only for grading calibration
The agent could not see this: the repository holds one commit and the sandbox has no network. Leak audit.
litellm/llms/openai/videos/transformation.py
diff --git a/litellm/llms/openai/videos/transformation.py b/litellm/llms/openai/videos/transformation.py
index 9848477f32de..8f5d41fe4678 100644
--- a/litellm/llms/openai/videos/transformation.py
+++ b/litellm/llms/openai/videos/transformation.py
@@ -178,10 +178,10 @@ def transform_video_content_request(
# Construct the URL for video content download
url = f"{api_base.rstrip('/')}/{original_video_id}/content"
- # Add video_id as query parameter
- params = {"video_id": original_video_id}
-
- return url, params
+ # No additional data needed for GET content request
+ data: Dict[str, Any] = {}
+
+ return url, data
def transform_video_remix_request(
self,
@@ -404,4 +404,4 @@ def _add_image_to_files(
if isinstance(image, BufferedReader):
files_list.append((field_name, (image.name, image, image_content_type)))
else:
- files_list.append((field_name, ("input_reference.png", image, image_content_type)))
\ No newline at end of file
+ files_list.append((field_name, ("input_reference.png", image, image_content_type)))
diff --git a/litellm/llms/openai/videos/transformation.py b/litellm/llms/openai/videos/transformation.py
index 8f5d41fe4678..d1d3fc2919ee 100644
--- a/litellm/llms/openai/videos/transformation.py
+++ b/litellm/llms/openai/videos/transformation.py
@@ -4,6 +4,7 @@
import httpx
from httpx._types import RequestFiles
+from litellm.llms.base_llm.videos.transformation import BaseVideoConfig
from litellm.types.videos.main import VideoCreateOptionalRequestParams
from litellm.types.llms.openai import CreateVideoRequest
from litellm.types.router import GenericLiteLLMParams
@@ -15,15 +16,12 @@
if TYPE_CHECKING:
from litellm.litellm_core_utils.litellm_logging import Logging as _LiteLLMLoggingObj
- from ...base_llm.videos.transformation import BaseVideoConfig as _BaseVideoConfig
from ...base_llm.chat.transformation import BaseLLMException as _BaseLLMException
LiteLLMLoggingObj = _LiteLLMLoggingObj
- BaseVideoConfig = _BaseVideoConfig
BaseLLMException = _BaseLLMException
else:
LiteLLMLoggingObj = Any
- BaseVideoConfig = Any
BaseLLMException = Any
GLM-5.3 Flash · attempt 2 · solved · 14 steps · $0.003
Submitted diff · 35 lines
diff --git a/litellm/llms/openai/videos/transformation.py b/litellm/llms/openai/videos/transformation.py
index 9848477..bf1b172 100644
--- a/litellm/llms/openai/videos/transformation.py
+++ b/litellm/llms/openai/videos/transformation.py
@@ -11,19 +11,17 @@ from litellm.secret_managers.main import get_secret_str
from litellm.types.videos.main import VideoObject
from litellm.types.videos.utils import encode_video_id_with_provider, extract_original_video_id
import litellm
+from litellm.llms.base_llm.videos.transformation import BaseVideoConfig
from litellm.llms.openai.image_edit.transformation import ImageEditRequestUtils
if TYPE_CHECKING:
from litellm.litellm_core_utils.litellm_logging import Logging as _LiteLLMLoggingObj
- from ...base_llm.videos.transformation import BaseVideoConfig as _BaseVideoConfig
from ...base_llm.chat.transformation import BaseLLMException as _BaseLLMException
LiteLLMLoggingObj = _LiteLLMLoggingObj
- BaseVideoConfig = _BaseVideoConfig
BaseLLMException = _BaseLLMException
else:
LiteLLMLoggingObj = Any
- BaseVideoConfig = Any
BaseLLMException = Any
@@ -178,8 +176,8 @@ class OpenAIVideoConfig(BaseVideoConfig):
# Construct the URL for video content download
url = f"{api_base.rstrip('/')}/{original_video_id}/content"
- # Add video_id as query parameter
- params = {"video_id": original_video_id}
+ # OpenAI expects a plain GET with no query parameters
+ params: Dict = {}
return url, params$ cd /repo && sed -n 1,200p litellm/llms/openai/videos/transformation.py && grep -n "video_content_handler\|transform_video_content_request\|async_transform_video_content_response" -r litellm/llms/base_llm/videos/transformation.py litellm/llms/custom_httpx/http_handler.py litellm/core.py 2>/dev/null | head -30
output · 29 lines
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union from io import BufferedReader from typing import cast import httpx from httpx._types import RequestFiles from litellm.types.videos.main import VideoCreateOptionalRequestParams from litellm.types.llms.openai import CreateVideoRequest from litellm.types.router import GenericLiteLLMParams from litellm.secret_managers.main import get_secret_str from litellm.types.videos.main import VideoObject from litellm.types.videos.utils import encode_video_id_with_provider, extract_original_video_id
17 more lines
import litellm
from litellm.llms.openai.image_edit.transformation import ImageEditRequestUtils
if TYPE_CHECKING:
from litellm.litellm_core_utils.litellm_logging import Logging as _LiteLLMLoggingObj
from ...base_llm.videos.transformation import BaseVideoConfig as _BaseVideoConfig
from ...base_llm.chat.transformation imp
…[5361 chars omitted]…
) -> Tuple[str, Dict]:
"""
Transform the video remix request for OpenAI API.
OpenAI API expects the following request:
- POST /v1/videos/{video_id}/remix
"""
litellm/llms/base_llm/videos/transformation.py:114: def transform_video_content_request(
litellm/llms/base_llm/videos/transformation.py:137: async def async_transform_video_content_response($ cd /repo && sed -n 100,160p litellm/llms/base_llm/videos/transformation.py && grep -rn "video_content_handler" litellm --include=*.py | head
output · 41 lines
pass
@abstractmethod
def transform_video_create_response(
self,
model: str,
raw_response: httpx.Response,
logging_obj: LiteLLMLoggingObj,
custom_llm_provider: Optional[str] = None,
request_data: Optional[Dict] = None,
) -> VideoObject:
pass29 more lines
@abstractmethod
def transform_video_content_request(
self,
video_id: str,
api_base: str,
litellm_params: GenericLiteLLMParams,
headers: dict,
) -> Tuple[str, Dict]:
"""
Transform the video content request into a URL and data/params
Returns:
Tuple[str, Dict]: (url, params) for the video content request
"""
pass
@abstractmethod
def transform_video_content_response(
self,
raw_response: httpx.Response,
logging_obj: LiteLLMLoggingObj
…[855 chars omitted]…
logging_obj=logging_obj,
)
litellm/llms/custom_httpx/llm_http_handler.py:4265: def video_content_handler(
litellm/llms/custom_httpx/llm_http_handler.py:4282: return self.async_video_content_handler(
litellm/llms/custom_httpx/llm_http_handler.py:4353: async def async_video_content_handler(
litellm/videos/main.py:360: return base_llm_http_handler.video_content_handler($ cd /repo && sed -n 4260,4440p litellm/llms/custom_httpx/llm_http_handler.py
output · 39 lines
custom_llm_provider=custom_llm_provider,
request_data=data,
)
###### VIDEO CONTENT HANDLER ######
def video_content_handler(
self,
video_id: str,
video_content_provider_config: BaseVideoConfig,
custom_llm_provider: str,
litellm_params: GenericLiteLLMParams,
logging_obj: LiteLLMLoggingObj,27 more lines
timeout: Union[float, httpx.Timeout],
extra_headers: Optional[Dict[str, Any]] = None,
api_key: Optional[str] = None,
client: Optional[Union[HTTPHandler, AsyncHTTPHandler]] = None,
_is_async: bool = False,
) -> Union[bytes, Coroutine[Any, Any, bytes]]:
"""
Handle video content download requests.
"""
if _is_async:
return self.async_video_content_handler(
video_id=video_id,
video_content_provider_c
…[5070 chars omitted]…
_handler(
self,
video_id: str,
prompt: str,
video_remix_provider_config: BaseVideoConfig,
custom_llm_provider: str,
litellm_params,
logging_obj,
extra_headers: Optional[Dict[str, Any]] = None,
extra_body: Optional[Dict[str, Any]] = None,
timeout: Optional[float] = None,
_is_async: bool = False,
client=None,$ cd /repo && sed -n 1,30p litellm/llms/base_llm/videos/transformation.py && grep -n "transform_video_content_response" litellm/llms/openai/videos/transformation.py
output · 31 lines
import types from abc import ABC, abstractmethod from typing import TYPE_CHECKING, Any, Dict, Optional, Tuple, Union import httpx from httpx._types import RequestFiles from litellm.types.responses.main import * from litellm.types.router import GenericLiteLLMParams from litellm.types.videos.main import VideoCreateOptionalRequestParams if TYPE_CHECKING:
19 more lines
from litellm.litellm_core_utils.litellm_logging import Logging as _LiteLLMLoggingObj
from litellm.types.videos.main import VideoObject as _VideoObject
from ..chat.transformation import BaseLLMException as _BaseLLMException
LiteLLMLoggingObj = _LiteLLMLoggingObj
BaseLLMException = _BaseLLMException
VideoObject = _VideoObject
else:
LiteLLMLoggingObj = Any
BaseLLMException = Any
VideoObject = Any
class BaseVideoConfig(ABC):
def __init__(self):
pass
215: def transform_video_content_response($ cd /repo && python - <<'PY'
output · 2 lines
True True
('https://api.openai.com/v1/videos/video_123/content', {})$ cd /repo && python - <<'PY'
output · 22 lines
Traceback (most recent call last):
File "/usr/local/lib/python3.12/site-packages/httpx/_transports/default.py", line 101, in map_httpcore_exceptions
yield
File "/usr/local/lib/python3.12/site-packages/httpx/_transports/default.py", line 250, in handle_request
resp = self._pool.handle_request(req)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/httpcore/_sync/connection_pool.py", line 256, in handle_request
raise exc from None
File "/usr/local/lib/python3.12/site-packages/httpcore/_sync/connection_pool.py", line 236, in handle_request
response = connection.handle_request(
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/httpcore/_sync/connection.py", line 101, in handle_request10 more lines
raise exc
File "/usr/local/lib/python3.12/site-packages/httpcore/_sync/connection.py", line 78, in h
…[3126 chars omitted]…
^^
File "/repo/litellm/llms/custom_httpx/llm_http_handler.py", line 3569, in _handle_error
raise provider_config.get_error_class(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/repo/litellm/llms/openai/videos/transformation.py", line 387, in get_error_class
raise BaseLLMException(
litellm.llms.base_llm.chat.transformation.BaseLLMException: [Errno -3] Temporary failure in name resolution$ cd /repo && python - <<'PY'
output · 1 lines
b'MP4BYTES' 1 https://api.openai.com/v1/videos/video_abc/content 0
$ cd /repo && ls tests/ | grep -i video; python -m pytest tests/llms/openai -x -q -k video 2>/dev/null | tail -3
output · 1 lines
no tests ran in 0.01s
$ cd /repo && grep -rln "video_content\|OpenAIVideoConfig" tests | head
output · 7 lines
tests/router_unit_tests/test_router_endpoints.py tests/test_litellm/llms/azure/test_azure_common_utils.py tests/test_litellm/llms/azure/videos/test_azure_video_transformation.py tests/test_litellm/llms/gemini/videos/test_gemini_video_transformation.py tests/test_litellm/llms/runwayml/videos/test_runway_video_transformation.py tests/test_litellm/llms/vertex_ai/videos/test_vertex_video_transformation.py tests/test_litellm/test_video_generation.py
$ cd /repo && python -m pytest tests/test_litellm/test_video_generation.py -q 2>&1 | tail -5
output · 5 lines
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html 29 passed, 1 warning in 1.70s /repo/litellm/llms/custom_httpx/async_client_cleanup.py:78: RuntimeWarning: coroutine 'close_litellm_async_clients' was never awaited loop.close() RuntimeWarning: Enable tracemalloc to get the object allocation traceback
$ cd /repo && python - <<'PY'
output · 28 lines
Traceback (most recent call last):
File "/repo/litellm/llms/custom_httpx/llm_http_handler.py", line 4410, in async_video_content_handler
response = await async_httpx_client.get(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: object MagicMock can't be used in 'await' expression
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<stdin>", line 16, in <module>
File "/usr/local/lib/python3.12/asyncio/runners.py", line 195, in run
return runner.run(main)16 more lines
^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/asyncio/runners.py", line 118, in run
return self._loop.run_until_complete(task)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/asyncio/base_events.py", line 691, in run_until_complete
return future.result()
^^^^^^^^^^^^^^^
File "<stdin>", lin
…[288 chars omitted]…
onfig.get_error_class(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/repo/litellm/llms/openai/videos/transformation.py", line 387, in get_error_class
raise BaseLLMException(
litellm.llms.base_llm.chat.transformation.BaseLLMException: object MagicMock can't be used in 'await' expression
litellm/llms/openai/videos/transformation.py | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)$ cd /repo && python - <<'PY'
output · 1 lines
b'MP4BYTES'
$ cd /repo && git diff
output · 26 lines
diff --git a/litellm/llms/openai/videos/transformation.py b/litellm/llms/openai/videos/transformation.py
index 9848477..bf1b172 100644
--- a/litellm/llms/openai/videos/transformation.py
+++ b/litellm/llms/openai/videos/transformation.py
@@ -11,19 +11,17 @@ from litellm.secret_managers.main import get_secret_str
from litellm.types.videos.main import VideoObject
from litellm.types.videos.utils import encode_video_id_with_provider, extract_original_video_id
import litellm
+from litellm.llms.base_llm.videos.transformation import BaseVideoConfig
from litellm.llms.openai.image_edit.transformation import ImageEditRequestUtils
if TYPE_CHECKING:
from litellm.litellm_core_utils.litellm_logging import Logging as _LiteLLMLoggingObj14 more lines
- from ...base_llm.videos.transformation import BaseVideoConfig as _BaseVideoConfig
from ...base_llm.chat.transformation import BaseLLMException as
…[257 chars omitted]…
8 @@ class OpenAIVideoConfig(BaseVideoConfig):
# Construct the URL for video content download
url = f"{api_base.rstrip('/')}/{original_video_id}/content"
- # Add video_id as query parameter
- params = {"video_id": original_video_id}
+ # OpenAI expects a plain GET with no query parameters
+ params: Dict = {}
return url, paramsBoth fixes verified: empty params → GET path, runtime `BaseVideoConfig` subclass with inherited async handler, and existing video tests pass.
$ done: {"notes": "Fixed OpenAIVideoConfig: transform_video_content_request now returns empty params (GET to /v1/videos/{id}/content), and BaseVideoConfig is imported at runtime so async_transform_video_content_response is available. Verified sync/async handlers issue GET and return raw bytes; existing video tests pass."}output · 1 lines
submitted.
GLM-5.3 Flash · attempt 3 · solved · 14 steps · $0.004
Submitted diff · 34 lines
diff --git a/litellm/llms/openai/videos/transformation.py b/litellm/llms/openai/videos/transformation.py
index 9848477..d9b3a3f 100644
--- a/litellm/llms/openai/videos/transformation.py
+++ b/litellm/llms/openai/videos/transformation.py
@@ -12,18 +12,16 @@ from litellm.types.videos.main import VideoObject
from litellm.types.videos.utils import encode_video_id_with_provider, extract_original_video_id
import litellm
from litellm.llms.openai.image_edit.transformation import ImageEditRequestUtils
+from litellm.llms.base_llm.videos.transformation import BaseVideoConfig
if TYPE_CHECKING:
from litellm.litellm_core_utils.litellm_logging import Logging as _LiteLLMLoggingObj
- from ...base_llm.videos.transformation import BaseVideoConfig as _BaseVideoConfig
from ...base_llm.chat.transformation import BaseLLMException as _BaseLLMException
LiteLLMLoggingObj = _LiteLLMLoggingObj
- BaseVideoConfig = _BaseVideoConfig
BaseLLMException = _BaseLLMException
else:
LiteLLMLoggingObj = Any
- BaseVideoConfig = Any
BaseLLMException = Any
@@ -178,8 +176,8 @@ class OpenAIVideoConfig(BaseVideoConfig):
# Construct the URL for video content download
url = f"{api_base.rstrip('/')}/{original_video_id}/content"
- # Add video_id as query parameter
- params = {"video_id": original_video_id}
+ # OpenAI expects a GET with no extra query parameters
+ params: Dict = {}
return url, params$ sed -n 1,200p /repo/litellm/llms/openai/videos/transformation.py
output · 34 lines
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union from io import BufferedReader from typing import cast import httpx from httpx._types import RequestFiles from litellm.types.videos.main import VideoCreateOptionalRequestParams from litellm.types.llms.openai import CreateVideoRequest from litellm.types.router import GenericLiteLLMParams from litellm.secret_managers.main import get_secret_str from litellm.types.videos.main import VideoObject from litellm.types.videos.utils import encode_video_id_with_provider, extract_original_video_id
22 more lines
import litellm
from litellm.llms.openai.image_edit.transformation import ImageEditRequestUtils
if TYPE_CHECKING:
from litellm.litellm_core_utils.litellm_logging import Logging as _LiteLLMLoggingObj
from ...base_llm.videos.transformation import BaseVideoConfig as _BaseVideoConfig
from ...base_llm.chat.transformation imp
…[5164 chars omitted]…
elf,
video_id: str,
prompt: str,
api_base: str,
litellm_params: GenericLiteLLMParams,
headers: dict,
extra_body: Optional[Dict[str, Any]] = None,
) -> Tuple[str, Dict]:
"""
Transform the video remix request for OpenAI API.
OpenAI API expects the following request:
- POST /v1/videos/{video_id}/remix
"""$ grep -n "async_transform_video_content_response\|def transform_video_content_response\|class BaseVideoConfig" /repo/litellm/llms/base_llm/videos/transformation.py; sed -n 200,320p /repo/litellm/llms/openai/videos/transformation.py
output · 44 lines
27:class BaseVideoConfig(ABC):
130: def transform_video_content_response(
137: async def async_transform_video_content_response(
"""
original_video_id = extract_original_video_id(video_id)
# Construct the URL for video remix
url = f"{api_base.rstrip('/')}/{original_video_id}/remix"
# Prepare the request data
data = {"prompt": prompt}
32 more lines
# Add any extra body parameters
if extra_body:
data.update(extra_body)
return url, data
def transform_video_content_response(
self,
raw_response: httpx.Response,
logging_obj: LiteLLMLoggingObj,
) -> bytes:
"""Transform the OpenAI video content download response."""
return raw_response.content
def transform_video_remix_response(
self,
raw_response: httpx.Respo
…[2803 chars omitted]…
st(
self,
video_id: str,
api_base: str,
litellm_params: GenericLiteLLMParams,
headers: dict,
) -> Tuple[str, Dict]:
"""
Transform the video delete request for OpenAI API.
OpenAI API expects the following request:
- DELETE /v1/videos/{video_id}
"""
original_video_id = extract_original_video_id(video_id)$ sed -n 1,160p /repo/litellm/llms/base_llm/videos/transformation.py
output · 47 lines
import types from abc import ABC, abstractmethod from typing import TYPE_CHECKING, Any, Dict, Optional, Tuple, Union import httpx from httpx._types import RequestFiles from litellm.types.responses.main import * from litellm.types.router import GenericLiteLLMParams from litellm.types.videos.main import VideoCreateOptionalRequestParams if TYPE_CHECKING:
35 more lines
from litellm.litellm_core_utils.litellm_logging import Logging as _LiteLLMLoggingObj
from litellm.types.videos.main import VideoObject as _VideoObject
from ..chat.transformation import BaseLLMException as _BaseLLMException
LiteLLMLoggingObj = _LiteLLMLoggingObj
BaseLLMException = _BaseLLMException
VideoObject = _VideoObject
else:
LiteLLMLoggingObj = Any
BaseLLMException = Any
VideoObject = Any
class BaseVideoConfig(ABC):
def __init__(self):
pass
@classmethod
def get_config(c
…[3033 chars omitted]…
form_video_content_response.
Args:
raw_response: Raw HTTP response
logging_obj: Logging object
Returns:
Video content as bytes
"""
# Default implementation: call sync version
return self.transform_video_content_response(
raw_response=raw_response,
logging_obj=logging_obj,
)$ cd /repo && python - <<'PY'
output · 3 lines
True
True
('https://api.openai.com/v1/videos/video_123/content', {})$ cd /repo && ls tests/llm/ 2>/dev/null | head; find tests -path '*video*' -name '*.py' | head
output · 8 lines
tests/test_litellm/llms/azure/videos/test_azure_video_transformation.py tests/test_litellm/llms/gemini/videos/test_gemini_video_transformation.py tests/test_litellm/llms/gemini/videos/__init__.py tests/test_litellm/llms/runwayml/videos/test_runway_video_transformation.py tests/test_litellm/llms/vertex_ai/videos/test_vertex_video_transformation.py tests/test_litellm/llms/vertex_ai/videos/__init__.py tests/test_litellm/test_azure_video_router.py tests/test_litellm/test_video_generation.py
$ cd /repo && python -m pytest tests/test_litellm/test_video_generation.py -x -q 2>&1 | tail -5
output · 5 lines
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html 29 passed, 1 warning in 1.93s /repo/litellm/llms/custom_httpx/async_client_cleanup.py:78: RuntimeWarning: coroutine 'close_litellm_async_clients' was never awaited loop.close() RuntimeWarning: Enable tracemalloc to get the object allocation traceback
$ cd /repo && python - <<'PY'
output · 3 lines
Traceback (most recent call last): File "<stdin>", line 8, in <module> TypeError: BaseLLMHTTPHandler.video_content_handler() missing 1 required positional argument: 'timeout'
$ cd /repo && grep -n "def video_content_handler" -A 60 litellm/llms/custom_httpx/llm_http_handler.py | head -80
output · 31 lines
4265: def video_content_handler( 4266- self, 4267- video_id: str, 4268- video_content_provider_config: BaseVideoConfig, 4269- custom_llm_provider: str, 4270- litellm_params: GenericLiteLLMParams, 4271- logging_obj: LiteLLMLoggingObj, 4272- timeout: Union[float, httpx.Timeout], 4273- extra_headers: Optional[Dict[str, Any]] = None, 4274- api_key: Optional[str] = None, 4275- client: Optional[Union[HTTPHandler, AsyncHTTPHandler]] = None, 4276- _is_async: bool = False,
19 more lines
4277- ) -> Union[bytes, Coroutine[Any, Any, bytes]]: 4278- """ 4279- Handle video content download requests. 4280- """ 4281- if _is_async: 4282- return self.async_video_content_handler( 4283- video_id=video_id, 4284- video_content_provider_config=video_content_provider_config …[1203 chars omitted]… quest using the provider config 4317- url, data = video_content_provider_config.transform_video_content_request( 4318- video_id=video_id, 4319- api_base=api_base, 4320- litellm_params=litellm_params, 4321- headers=headers, 4322- ) 4323- 4324- try: 4325- # Use POST if params contains data (e.g., Vertex AI fetchPredictOperation)
$ cd /repo && python - <<'PY'
output · 22 lines
Traceback (most recent call last):
File "/usr/local/lib/python3.12/site-packages/httpx/_transports/default.py", line 101, in map_httpcore_exceptions
yield
File "/usr/local/lib/python3.12/site-packages/httpx/_transports/default.py", line 250, in handle_request
resp = self._pool.handle_request(req)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/httpcore/_sync/connection_pool.py", line 256, in handle_request
raise exc from None
File "/usr/local/lib/python3.12/site-packages/httpcore/_sync/connection_pool.py", line 236, in handle_request
response = connection.handle_request(
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/httpcore/_sync/connection.py", line 101, in handle_request10 more lines
raise exc
File "/usr/local/lib/python3.12/site-packages/httpcore/_sync/connection.py", line 78, in h
…[3126 chars omitted]…
^^
File "/repo/litellm/llms/custom_httpx/llm_http_handler.py", line 3569, in _handle_error
raise provider_config.get_error_class(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/repo/litellm/llms/openai/videos/transformation.py", line 387, in get_error_class
raise BaseLLMException(
litellm.llms.base_llm.chat.transformation.BaseLLMException: [Errno -3] Temporary failure in name resolution$ cd /repo && python - <<'PY'
output · 2 lines
call(url='https://api.openai.com/v1/videos/video_abc/content', headers={'Authorization': 'Bearer None'}, params={})
post calls: 0 out: b'mp4bytes'$ cd /repo && git diff && python -m pytest tests/test_litellm/test_video_generation.py tests/test_litellm/llms -q 2>&1 | tail -3
output · 27 lines
diff --git a/litellm/llms/openai/videos/transformation.py b/litellm/llms/openai/videos/transformation.py
index 9848477..d9b3a3f 100644
--- a/litellm/llms/openai/videos/transformation.py
+++ b/litellm/llms/openai/videos/transformation.py
@@ -12,18 +12,16 @@ from litellm.types.videos.main import VideoObject
from litellm.types.videos.utils import encode_video_id_with_provider, extract_original_video_id
import litellm
from litellm.llms.openai.image_edit.transformation import ImageEditRequestUtils
+from litellm.llms.base_llm.videos.transformation import BaseVideoConfig
if TYPE_CHECKING:
from litellm.litellm_core_utils.litellm_logging import Logging as _LiteLLMLoggingObj
15 more lines
- from ...base_llm.videos.transformation import BaseVideoConfig as _BaseVideoConfig
from ...base_llm.chat.transformation import BaseLLMException as _BaseLLMException
LiteLLMLoggingObj = _LiteLLMLog
…[395 chars omitted]…
d video_id as query parameter
- params = {"video_id": original_video_id}
+ # OpenAI expects a GET with no extra query parameters
+ params: Dict = {}
return url, params
ERROR tests/test_litellm/llms/volcengine/test_volcengine_embedding.py - reque...
!!!!!!!!!!!!!!!!!!! Interrupted: 15 errors during collection !!!!!!!!!!!!!!!!!!!
19 warnings, 15 errors in 23.66s$ cd /repo && python -m pytest tests/test_litellm/test_video_generation.py tests/test_litellm/llms/azure/videos tests/test_litellm/llms/gemini/videos tests/test_litellm/llms/runwayml/videos tests/test_litellm/llms/vertex_ai/videos -q 2>&1 | tail -3
output · 3 lines
/repo/litellm/llms/custom_httpx/async_client_cleanup.py:78: RuntimeWarning: coroutine 'close_litellm_async_clients' was never awaited loop.close() RuntimeWarning: Enable tracemalloc to get the object allocation traceback
$ cd /repo && python -m pytest tests/test_litellm/test_video_generation.py tests/test_litellm/llms/azure/videos tests/test_litellm/llms/gemini/videos tests/test_litellm/llms/runwayml/videos tests/test_litellm/llms/vertex_ai/videos -q 2>&1 | grep -E "passed|failed"
output · 1 lines
106 passed, 1 warning in 8.49s
All checks pass.
$ done: {"notes": "Fixed OpenAIVideoConfig: imported BaseVideoConfig at runtime (real subclass, so async_transform_video_content_response is available) and transform_video_content_request now returns empty params so the HTTP layer issues a GET to /v1/videos/{id}/content. Verified with reproducers and existing video tests (106 passed)."}output · 1 lines
submitted.
GLM-5.3 Flash · attempt 4 · solved · 14 steps · $0.002
Submitted diff · 34 lines
diff --git a/litellm/llms/openai/videos/transformation.py b/litellm/llms/openai/videos/transformation.py
index 9848477..5030878 100644
--- a/litellm/llms/openai/videos/transformation.py
+++ b/litellm/llms/openai/videos/transformation.py
@@ -15,17 +15,14 @@ from litellm.llms.openai.image_edit.transformation import ImageEditRequestUtils
if TYPE_CHECKING:
from litellm.litellm_core_utils.litellm_logging import Logging as _LiteLLMLoggingObj
- from ...base_llm.videos.transformation import BaseVideoConfig as _BaseVideoConfig
from ...base_llm.chat.transformation import BaseLLMException as _BaseLLMException
- LiteLLMLoggingObj = _LiteLLMLoggingObj
- BaseVideoConfig = _BaseVideoConfig
- BaseLLMException = _BaseLLMException
else:
LiteLLMLoggingObj = Any
- BaseVideoConfig = Any
BaseLLMException = Any
+from ...base_llm.videos.transformation import BaseVideoConfig
+
class OpenAIVideoConfig(BaseVideoConfig):
"""
@@ -178,8 +175,8 @@ class OpenAIVideoConfig(BaseVideoConfig):
# Construct the URL for video content download
url = f"{api_base.rstrip('/')}/{original_video_id}/content"
- # Add video_id as query parameter
- params = {"video_id": original_video_id}
+ # OpenAI expects a plain GET with no query parameters
+ params: Dict = {}
return url, params$ cd /repo && sed -n 1,80p litellm/llms/openai/videos/transformation.py && grep -n "video_content_handler" -n litellm/llms/custom_httpx/http_handler.py litellm/litellm_core_utils/*.py 2>/dev/null | head
output · 38 lines
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union from io import BufferedReader from typing import cast import httpx from httpx._types import RequestFiles from litellm.types.videos.main import VideoCreateOptionalRequestParams from litellm.types.llms.openai import CreateVideoRequest from litellm.types.router import GenericLiteLLMParams from litellm.secret_managers.main import get_secret_str from litellm.types.videos.main import VideoObject from litellm.types.videos.utils import encode_video_id_with_provider, extract_original_video_id
26 more lines
import litellm
from litellm.llms.openai.image_edit.transformation import ImageEditRequestUtils
if TYPE_CHECKING:
from litellm.litellm_core_utils.litellm_logging import Logging as _LiteLLMLoggingObj
from ...base_llm.videos.transformation import BaseVideoConfig as _BaseVideoConfig
from ...base_llm.chat.transformation imp
…[1138 chars omitted]…
,
model: str,
api_key: Optional[str] = None,
) -> dict:
api_key = (
api_key
or litellm.api_key
or litellm.openai_key
or get_secret_str("OPENAI_API_KEY")
)
headers.update(
{
"Authorization": f"Bearer {api_key}",
}
)
return headers
def get_complete_url($ cd /repo && grep -n "transform_video_content_request" -A15 litellm/llms/openai/videos/transformation.py && grep -n "video_content_handler" -rn litellm --include=*.py | head
output · 20 lines
163: def transform_video_content_request(
164- self,
165- video_id: str,
166- api_base: str,
167- litellm_params: GenericLiteLLMParams,
168- headers: dict,
169- ) -> Tuple[str, Dict]:
170- """
171- Transform the video content request for OpenAI API.
172-
173- OpenAI API expects the following request:
174- - GET /v1/videos/{video_id}/content8 more lines
175- """ 176- original_video_id = extract_original_video_id(video_id) 177- 178- # Construct the URL for video content download litellm/llms/custom_httpx/llm_http_handler.py:4265: def video_content_handler( litellm/llms/custom_httpx/llm_http_handler.py:4282: return self.async_video_content_handler( litellm/llms/custom_httpx/llm_http_handler.py:4353: async def async_video_content_handler( litellm/videos/main.py:360: return base_llm_http_handler.video_content_handler(
$ cd /repo && sed -n 178,200p litellm/llms/openai/videos/transformation.py && sed -n 4265,4340p litellm/llms/custom_httpx/llm_http_handler.py
output · 42 lines
# Construct the URL for video content download
url = f"{api_base.rstrip('/')}/{original_video_id}/content"
# Add video_id as query parameter
params = {"video_id": original_video_id}
return url, params
def transform_video_remix_request(
self,
video_id: str,
prompt: str,30 more lines
api_base: str,
litellm_params: GenericLiteLLMParams,
headers: dict,
extra_body: Optional[Dict[str, Any]] = None,
) -> Tuple[str, Dict]:
"""
Transform the video remix request for OpenAI API.
OpenAI API expects the following request:
- POST /v1/videos/{video_id}/remix
"""
def video_content_handler(
self,
video_id: str,
video_content_provider_config: BaseVideoConfig,
custom_llm_provider: str,
litellm_params: Ge
…[2119 chars omitted]…
response = sync_httpx_client.post(
url=url,
headers=headers,
json=data,
)
else:
# Otherwise it's a GET request with query params
response = sync_httpx_client.get(
url=url,
headers=headers,
params=data,
)$ cd /repo && sed -n 1,40p litellm/llms/base_llm/videos/transformation.py
output · 40 lines
import types from abc import ABC, abstractmethod from typing import TYPE_CHECKING, Any, Dict, Optional, Tuple, Union import httpx from httpx._types import RequestFiles from litellm.types.responses.main import * from litellm.types.router import GenericLiteLLMParams from litellm.types.videos.main import VideoCreateOptionalRequestParams if TYPE_CHECKING:
28 more lines
from litellm.litellm_core_utils.litellm_logging import Logging as _LiteLLMLoggingObj
from litellm.types.videos.main import VideoObject as _VideoObject
from ..chat.transformation import BaseLLMException as _BaseLLMException
LiteLLMLoggingObj = _LiteLLMLoggingObj
BaseLLMException = _BaseLLMException
VideoObject = _VideoObject
else:
LiteLLMLoggingObj = Any
BaseLLMException = Any
VideoObject = Any
class BaseVideoConfig(ABC):
def __init__(self):
pass
@classmethod
def get_config(cls):
return {
k: v
for k, v in cls.__dict__.items()
if not k.startswith("__")
and not k.startswith("_abc")
and not isinstance(
v,
($ cd /repo && python - <<'PY'
output · 38 lines
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union from io import BufferedReader from typing import cast import httpx from httpx._types import RequestFiles from litellm.types.videos.main import VideoCreateOptionalRequestParams from litellm.types.llms.openai import CreateVideoRequest from litellm.types.router import GenericLiteLLMParams from litellm.secret_managers.main import get_secret_str from litellm.types.videos.main import VideoObject from litellm.types.videos.utils import encode_video_id_with_provider, extract_original_video_id
26 more lines
import litellm
from litellm.llms.openai.image_edit.transformation import ImageEditRequestUtils
if TYPE_CHECKING:
from litellm.litellm_core_utils.litellm_logging import Logging as _LiteLLMLoggingObj
from ...base_llm.chat.transformation import BaseLLMException as _BaseLLMException
else:
LiteLLMLoggingObj = Any
BaseL
…[861 chars omitted]…
{original_video_id}/content"
# OpenAI expects a plain GET with no query parameters
params: Dict = {}
return url, params
def transform_video_remix_request(
self,
video_id: str,
prompt: str,
api_base: str,
litellm_params: GenericLiteLLMParams,
headers: dict,
extra_body: Optional[Dict[str, Any]] = None,$ cd /repo && python - <<'PY'
output · 23 lines
https://api.openai.com/v1/videos/video_123/content {}
Traceback (most recent call last):
File "/usr/local/lib/python3.12/site-packages/httpx/_transports/default.py", line 101, in map_httpcore_exceptions
yield
File "/usr/local/lib/python3.12/site-packages/httpx/_transports/default.py", line 250, in handle_request
resp = self._pool.handle_request(req)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/httpcore/_sync/connection_pool.py", line 256, in handle_request
raise exc from None
File "/usr/local/lib/python3.12/site-packages/httpcore/_sync/connection_pool.py", line 236, in handle_request
response = connection.handle_request(
^^^^^^^^^^^^^^^^^^^^^^^^^^11 more lines
File "/usr/local/lib/python3.12/site-packages/httpcore/_sync/connection.py", line 101, in handle_request
raise exc
File "/usr/local/lib/python3.12/site
…[3181 chars omitted]…
^^
File "/repo/litellm/llms/custom_httpx/llm_http_handler.py", line 3569, in _handle_error
raise provider_config.get_error_class(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/repo/litellm/llms/openai/videos/transformation.py", line 386, in get_error_class
raise BaseLLMException(
litellm.llms.base_llm.chat.transformation.BaseLLMException: [Errno -3] Temporary failure in name resolution$ cd /repo && python - <<'PY'
output · 3 lines
Traceback (most recent call last): File "<stdin>", line 12, in <module> NameError: name 'litellm' is not defined
$ cd /repo && python - <<'PY'
output · 3 lines
Traceback (most recent call last): File "<stdin>", line 17, in <module> KeyError: 'url'
$ cd /repo && python - <<'PY'
output · 1 lines
b'BYTES' call('https://api.openai.com/v1/videos/video_abc/content', params={}, headers={'Authorization': 'Bearer None'}, follow_redirects=<httpx._client.UseClientDefault object at 0x74396653b7a0>) 0$ cd /repo && ls tests/ | grep -i video; python -m pytest tests/llm_translation/test_video_generation.py -x -q 2>&1 | tail -5
output · 4 lines
ERROR: file or directory not found: tests/llm_translation/test_video_generation.py no tests ran in 0.02s
$ cd /repo && find tests -iname "*video*" | head
output · 10 lines
tests/test_litellm/llms/azure/videos tests/test_litellm/llms/azure/videos/test_azure_video_transformation.py tests/test_litellm/llms/gemini/videos tests/test_litellm/llms/gemini/videos/test_gemini_video_transformation.py tests/test_litellm/llms/runwayml/videos tests/test_litellm/llms/runwayml/videos/test_runway_video_transformation.py tests/test_litellm/llms/vertex_ai/videos tests/test_litellm/llms/vertex_ai/videos/test_vertex_video_transformation.py tests/test_litellm/test_azure_video_router.py tests/test_litellm/test_video_generation.py
$ cd /repo && python -m pytest tests/test_litellm/test_video_generation.py -q 2>&1 | tail -5
output · 5 lines
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html 29 passed, 1 warning in 1.88s /repo/litellm/llms/custom_httpx/async_client_cleanup.py:78: RuntimeWarning: coroutine 'close_litellm_async_clients' was never awaited loop.close() RuntimeWarning: Enable tracemalloc to get the object allocation traceback
$ cd /repo && git diff
output · 31 lines
diff --git a/litellm/llms/openai/videos/transformation.py b/litellm/llms/openai/videos/transformation.py
index 9848477..5030878 100644
--- a/litellm/llms/openai/videos/transformation.py
+++ b/litellm/llms/openai/videos/transformation.py
@@ -15,17 +15,14 @@ from litellm.llms.openai.image_edit.transformation import ImageEditRequestUtils
if TYPE_CHECKING:
from litellm.litellm_core_utils.litellm_logging import Logging as _LiteLLMLoggingObj
- from ...base_llm.videos.transformation import BaseVideoConfig as _BaseVideoConfig
from ...base_llm.chat.transformation import BaseLLMException as _BaseLLMException
- LiteLLMLoggingObj = _LiteLLMLoggingObj19 more lines
- BaseVideoConfig = _BaseVideoConfig
- BaseLLMException = _BaseLLMException
else:
LiteLLMLoggingObj = Any
- BaseVideoConfig = Any
BaseLLMException = Any
+from ...base_llm.videos.transformation import BaseVid
…[80 chars omitted]…
8 @@ class OpenAIVideoConfig(BaseVideoConfig):
# Construct the URL for video content download
url = f"{api_base.rstrip('/')}/{original_video_id}/content"
- # Add video_id as query parameter
- params = {"video_id": original_video_id}
+ # OpenAI expects a plain GET with no query parameters
+ params: Dict = {}
return url, params$ done: {"notes": "Fixed OpenAI video content download: transform_video_content_request now returns empty params (GET path), and BaseVideoConfig is imported at runtime so OpenAIVideoConfig is a real subclass exposing async_transform_video_content_response. Reproducer and tests/test_litellm/test_video_generation.py pass."}output · 1 lines
submitted.
GLM-5.3 Flash · attempt 5 · solved · 16 steps · $0.003
Submitted diff · 37 lines
diff --git a/litellm/llms/openai/videos/transformation.py b/litellm/llms/openai/videos/transformation.py
index 9848477..cfcf3b7 100644
--- a/litellm/llms/openai/videos/transformation.py
+++ b/litellm/llms/openai/videos/transformation.py
@@ -12,18 +12,16 @@ from litellm.types.videos.main import VideoObject
from litellm.types.videos.utils import encode_video_id_with_provider, extract_original_video_id
import litellm
from litellm.llms.openai.image_edit.transformation import ImageEditRequestUtils
+from litellm.llms.base_llm.videos.transformation import BaseVideoConfig
if TYPE_CHECKING:
from litellm.litellm_core_utils.litellm_logging import Logging as _LiteLLMLoggingObj
- from ...base_llm.videos.transformation import BaseVideoConfig as _BaseVideoConfig
from ...base_llm.chat.transformation import BaseLLMException as _BaseLLMException
LiteLLMLoggingObj = _LiteLLMLoggingObj
- BaseVideoConfig = _BaseVideoConfig
BaseLLMException = _BaseLLMException
else:
LiteLLMLoggingObj = Any
- BaseVideoConfig = Any
BaseLLMException = Any
@@ -178,10 +176,8 @@ class OpenAIVideoConfig(BaseVideoConfig):
# Construct the URL for video content download
url = f"{api_base.rstrip('/')}/{original_video_id}/content"
- # Add video_id as query parameter
- params = {"video_id": original_video_id}
-
- return url, params
+ # Content download is a GET with no extra parameters
+ return url, {}
def transform_video_remix_request(
self,$ sed -n 1,120p /repo/litellm/llms/openai/videos/transformation.py; grep -n "video_content_handler" -n /repo/litellm/llms/base_llm/http_handler.py | head
output · 32 lines
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union from io import BufferedReader from typing import cast import httpx from httpx._types import RequestFiles from litellm.types.videos.main import VideoCreateOptionalRequestParams from litellm.types.llms.openai import CreateVideoRequest from litellm.types.router import GenericLiteLLMParams from litellm.secret_managers.main import get_secret_str from litellm.types.videos.main import VideoObject from litellm.types.videos.utils import encode_video_id_with_provider, extract_original_video_id
20 more lines
import litellm
from litellm.llms.openai.image_edit.transformation import ImageEditRequestUtils
if TYPE_CHECKING:
from litellm.litellm_core_utils.litellm_logging import Logging as _LiteLLMLoggingObj
from ...base_llm.videos.transformation import BaseVideoConfig as _BaseVideoConfig
from ...base_llm.chat.transformation imp
…[2516 chars omitted]…
}
# Create the request data
video_create_request = CreateVideoRequest(
model=model,
prompt=prompt,
**video_create_optional_request_params
)
request_dict = cast(Dict, video_create_request)
# Handle input_reference parameter if provided
grep: /repo/litellm/llms/base_llm/http_handler.py: No such file or directory$ grep -n "transform_video_content_request" -A15 /repo/litellm/llms/openai/videos/transformation.py; grep -rn "video_content_handler" /repo/litellm --include=*.py -l
output · 18 lines
163: def transform_video_content_request(
164- self,
165- video_id: str,
166- api_base: str,
167- litellm_params: GenericLiteLLMParams,
168- headers: dict,
169- ) -> Tuple[str, Dict]:
170- """
171- Transform the video content request for OpenAI API.
172-
173- OpenAI API expects the following request:
174- - GET /v1/videos/{video_id}/content6 more lines
175- """ 176- original_video_id = extract_original_video_id(video_id) 177- 178- # Construct the URL for video content download /repo/litellm/llms/custom_httpx/llm_http_handler.py /repo/litellm/videos/main.py
$ sed -n 178,200p /repo/litellm/llms/openai/videos/transformation.py; grep -n "video_content_handler" -A60 /repo/litellm/llms/custom_httpx/llm_http_handler.py | head -90
output · 38 lines
# Construct the URL for video content download
url = f"{api_base.rstrip('/')}/{original_video_id}/content"
# Add video_id as query parameter
params = {"video_id": original_video_id}
return url, params
def transform_video_remix_request(
self,
video_id: str,
prompt: str,26 more lines
api_base: str,
litellm_params: GenericLiteLLMParams,
headers: dict,
extra_body: Optional[Dict[str, Any]] = None,
) -> Tuple[str, Dict]:
"""
Transform the video remix request for OpenAI API.
OpenAI API expects the following request:
- POST /v1/videos/{video_id}/remix
"""
4265: def video_content_handler(
4266- self,
4267- video_id: str,
4268- video_content_provider_config: BaseVideoConfig,
4269- custom_llm_provider: str,
4
…[3181 chars omitted]…
ent_provider_config: BaseVideoConfig,
4357- custom_llm_provider: str,
4358- litellm_params: GenericLiteLLMParams,
4359- logging_obj: LiteLLMLoggingObj,
4360- timeout: Union[float, httpx.Timeout],
4361- extra_headers: Optional[Dict[str, Any]] = None,
4362- api_key: Optional[str] = None,
4363- client: Optional[Union[HTTPHandler, AsyncHTTPHandler]] = None,$ sed -n 1,60p /repo/litellm/llms/base_llm/videos/transformation.py; grep -n "async_transform_video_content_response\|transform_video_content_response" /repo/litellm/llms/base_llm/videos/transformation.py
output · 43 lines
import types from abc import ABC, abstractmethod from typing import TYPE_CHECKING, Any, Dict, Optional, Tuple, Union import httpx from httpx._types import RequestFiles from litellm.types.responses.main import * from litellm.types.router import GenericLiteLLMParams from litellm.types.videos.main import VideoCreateOptionalRequestParams if TYPE_CHECKING:
31 more lines
from litellm.litellm_core_utils.litellm_logging import Logging as _LiteLLMLoggingObj
from litellm.types.videos.main import VideoObject as _VideoObject
from ..chat.transformation import BaseLLMException as _BaseLLMException
LiteLLMLoggingObj = _LiteLLMLoggingObj
BaseLLMException = _BaseLLMException
VideoObject = _VideoObject
else:
LiteLLMLoggingObj = Any
BaseLLMException = Any
VideoObject = Any
class BaseVideoConfig(ABC):
def __init__(self):
pass
@classmethod
def get_config(c
…[597 chars omitted]…
arams(
self,
video_create_optional_params: VideoCreateOptionalRequestParams,
model: str,
drop_params: bool,
) -> Dict:
130: def transform_video_content_response(
137: async def async_transform_video_content_response(
147: Default implementation falls back to sync transform_video_content_response.
157: return self.transform_video_content_response($ python -c "import litellm.llms.base_llm.videos.transformation as m; print('ok')"output · 1 lines
ok
$ cd /repo && python - <<'PY'
output · 31 lines
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union from io import BufferedReader from typing import cast import httpx from httpx._types import RequestFiles from litellm.types.videos.main import VideoCreateOptionalRequestParams from litellm.types.llms.openai import CreateVideoRequest from litellm.types.router import GenericLiteLLMParams from litellm.secret_managers.main import get_secret_str from litellm.types.videos.main import VideoObject from litellm.types.videos.utils import encode_video_id_with_provider, extract_original_video_id
19 more lines
import litellm
from litellm.llms.openai.image_edit.transformation import ImageEditRequestUtils
from litellm.llms.base_llm.videos.transformation import BaseVideoConfig
if TYPE_CHECKING:
from litellm.litellm_core_utils.litellm_logging import Logging as _LiteLLMLoggingObj
from ...base_llm.chat.transformation import BaseLLMException as _BaseLLMException
LiteLLMLoggingObj = _LiteLLMLoggingObj
BaseLLMException = _BaseLLMException
else:
LiteLLMLoggingObj = Any
BaseLLMException = Any
class OpenAIVideoConfig(BaseVideoConfig):
"""
Configuration class for OpenAI video generation.
180: return url, {}$ cd /repo && python - <<'PY'
output · 4 lines
https://api.openai.com/v1/videos/video_123/content {}
Traceback (most recent call last):
File "<stdin>", line 9, in <module>
ModuleNotFoundError: No module named 'litellm.integrations.custom_httpx_request_logger'$ cd /repo && python - <<'PY'
output · 22 lines
Traceback (most recent call last):
File "/usr/local/lib/python3.12/site-packages/httpx/_transports/default.py", line 101, in map_httpcore_exceptions
yield
File "/usr/local/lib/python3.12/site-packages/httpx/_transports/default.py", line 250, in handle_request
resp = self._pool.handle_request(req)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/httpcore/_sync/connection_pool.py", line 256, in handle_request
raise exc from None
File "/usr/local/lib/python3.12/site-packages/httpcore/_sync/connection_pool.py", line 236, in handle_request
response = connection.handle_request(
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/site-packages/httpcore/_sync/connection.py", line 101, in handle_request10 more lines
raise exc
File "/usr/local/lib/python3.12/site-packages/httpcore/_sync/connection.py", line 78, in h
…[3126 chars omitted]…
^^
File "/repo/litellm/llms/custom_httpx/llm_http_handler.py", line 3569, in _handle_error
raise provider_config.get_error_class(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/repo/litellm/llms/openai/videos/transformation.py", line 385, in get_error_class
raise BaseLLMException(
litellm.llms.base_llm.chat.transformation.BaseLLMException: [Errno -3] Temporary failure in name resolution$ cd /repo && python - <<'PY'
output · 18 lines
Traceback (most recent call last):
File "<stdin>", line 17, in <module>
File "/usr/local/lib/python3.12/asyncio/runners.py", line 195, in run
return runner.run(main)
^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/asyncio/runners.py", line 118, in run
return self._loop.run_until_complete(task)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/asyncio/base_events.py", line 691, in run_until_complete
return future.result()
^^^^^^^^^^^^^^^
File "<stdin>", line 15, in go6 more lines
File "/usr/local/lib/python3.12/unittest/mock.py", line 1467, in __enter__
original, local = self.get_original()
^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/unittest/mock.py", line 1437, in get_original
raise AttributeError(
AttributeError: <module 'litellm.llms.custom_httpx.llm_http_handler' from '/repo/litellm/llms/custom_httpx/llm_http_handler.py'> does not have the attribute '_get_async_httpx_client'$ cd /repo && sed -n 4353,4420p litellm/llms/custom_httpx/llm_http_handler.py | grep -n "client\|get\|post"
output · 9 lines
11: client: Optional[Union[HTTPHandler, AsyncHTTPHandler]] = None,
16: if client is None or not isinstance(client, AsyncHTTPHandler):
17: async_httpx_client = get_async_httpx_client(
19: params={"ssl_verify": litellm_params.get("ssl_verify", None)},
22: async_httpx_client = client
33: api_base = video_content_provider_config.get_complete_url(
35: api_base=litellm_params.get("api_base", None),
51: response = await async_httpx_client.post(
58: response = await async_httpx_client.get($ cd /repo && python - <<'PY'
output · 27 lines
Traceback (most recent call last):
File "/repo/litellm/llms/custom_httpx/llm_http_handler.py", line 4410, in async_video_content_handler
response = await async_httpx_client.get(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: object MagicMock can't be used in 'await' expression
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<stdin>", line 17, in <module>
File "/usr/local/lib/python3.12/asyncio/runners.py", line 195, in run
return runner.run(main)15 more lines
^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/asyncio/runners.py", line 118, in run
return self._loop.run_until_complete(task)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/asyncio/base_events.py", line 691, in run_until_complete
return future.result()
^^^^^^^^^^^^^^^
File "<stdin>", lin
…[178 chars omitted]…
File "/repo/litellm/llms/custom_httpx/llm_http_handler.py", line 3569, in _handle_error
raise provider_config.get_error_class(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/repo/litellm/llms/openai/videos/transformation.py", line 385, in get_error_class
raise BaseLLMException(
litellm.llms.base_llm.chat.transformation.BaseLLMException: object MagicMock can't be used in 'await' expression$ cd /repo && python - <<'PY'
output · 1 lines
async ok
$ cd /repo && find tests -iname "*video*" | head;
output · 10 lines
tests/test_litellm/llms/azure/videos tests/test_litellm/llms/azure/videos/test_azure_video_transformation.py tests/test_litellm/llms/gemini/videos tests/test_litellm/llms/gemini/videos/test_gemini_video_transformation.py tests/test_litellm/llms/runwayml/videos tests/test_litellm/llms/runwayml/videos/test_runway_video_transformation.py tests/test_litellm/llms/vertex_ai/videos tests/test_litellm/llms/vertex_ai/videos/test_vertex_video_transformation.py tests/test_litellm/test_azure_video_router.py tests/test_litellm/test_video_generation.py
$ cd /repo && python -m pytest tests/test_litellm/test_video_generation.py tests/test_litellm/llms/vertex_ai/videos -q 2>&1 | tail -5
output · 5 lines
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html 58 passed, 1 warning in 2.97s /repo/litellm/llms/custom_httpx/async_client_cleanup.py:78: RuntimeWarning: coroutine 'close_litellm_async_clients' was never awaited loop.close() RuntimeWarning: Enable tracemalloc to get the object allocation traceback
$ cd /repo && git diff
output · 29 lines
diff --git a/litellm/llms/openai/videos/transformation.py b/litellm/llms/openai/videos/transformation.py
index 9848477..cfcf3b7 100644
--- a/litellm/llms/openai/videos/transformation.py
+++ b/litellm/llms/openai/videos/transformation.py
@@ -12,18 +12,16 @@ from litellm.types.videos.main import VideoObject
from litellm.types.videos.utils import encode_video_id_with_provider, extract_original_video_id
import litellm
from litellm.llms.openai.image_edit.transformation import ImageEditRequestUtils
+from litellm.llms.base_llm.videos.transformation import BaseVideoConfig
if TYPE_CHECKING:
from litellm.litellm_core_utils.litellm_logging import Logging as _LiteLLMLoggingObj
17 more lines
- from ...base_llm.videos.transformation import BaseVideoConfig as _BaseVideoConfig
from ...base_llm.chat.transformation import BaseLLMException as _BaseLLMException
LiteLLMLoggingObj = _LiteLLMLog
…[252 chars omitted]…
# Construct the URL for video content download
url = f"{api_base.rstrip('/')}/{original_video_id}/content"
- # Add video_id as query parameter
- params = {"video_id": original_video_id}
-
- return url, params
+ # Content download is a GET with no extra parameters
+ return url, {}
def transform_video_remix_request(
self,Both fixes verified: `transform_video_content_request` now returns `url, {}` (GET path, no POST), and `OpenAIVideoConfig` genuinely subclasses `BaseVideoConfig` at runtime so `async_transform_video_content_response` is available. Reproducers pass and existing video tests (58) pass.
$ done: {"notes": "Fixed OpenAI video content download: transform_video_content_request now returns the content URL with empty params (GET path), and OpenAIVideoConfig imports BaseVideoConfig at runtime so async_transform_video_content_response exists. Verified with mock-based reproducers and existing video tests."}output · 1 lines
submitted.