From 95969ef97b7b1da32c04310bf77f455bf2d7085e Mon Sep 17 00:00:00 2001 From: Carson Date: Wed, 29 Jul 2026 09:45:48 -0500 Subject: [PATCH 1/2] feat(content): add video content types and file/YouTube helpers Introduces ContentVideoInline and ContentVideoUrl (plus the shared ContentVideo base and VideoContentTypes literal) to represent Gemini video input: small inline clips, and public YouTube URLs referenced with no upload and no MIME type. content_video_file() and content_video_youtube() are the public constructors, exported from chatlas and chatlas.types alongside the existing image/PDF helpers. --- chatlas/__init__.py | 3 + chatlas/_content.py | 95 ++++++++++++++++++++++++++ chatlas/_content_video.py | 129 ++++++++++++++++++++++++++++++++++++ chatlas/types/__init__.py | 8 +++ tests/test_content_video.py | 127 +++++++++++++++++++++++++++++++++++ 5 files changed, 362 insertions(+) create mode 100644 chatlas/_content_video.py create mode 100644 tests/test_content_video.py diff --git a/chatlas/__init__.py b/chatlas/__init__.py index 22fe3b0e..14688d6c 100644 --- a/chatlas/__init__.py +++ b/chatlas/__init__.py @@ -13,6 +13,7 @@ ) from ._content_image import content_image_file, content_image_plot, content_image_url from ._content_pdf import content_pdf_file, content_pdf_url +from ._content_video import content_video_file, content_video_youtube from ._files import FileManager from ._interpolate import interpolate, interpolate_file from ._parallel import parallel_chat, parallel_chat_structured, parallel_chat_text @@ -84,6 +85,8 @@ "content_image_url", "content_pdf_file", "content_pdf_url", + "content_video_file", + "content_video_youtube", "ContentToolRequest", "ContentToolResult", "FileManager", diff --git a/chatlas/_content.py b/chatlas/_content.py index 9e32c2e4..31699264 100644 --- a/chatlas/_content.py +++ b/chatlas/_content.py @@ -86,6 +86,22 @@ class ToolAnnotations(TypedDict, total=False): """ +VideoContentTypes = Literal[ + "video/mp4", + "video/mpeg", + "video/mov", + "video/avi", + "video/x-flv", + "video/mpg", + "video/webm", + "video/wmv", + "video/3gpp", +] +""" +Allowable content types for inline video. Only Gemini accepts video input. +""" + + class ToolInfo(BaseModel): """ Serializable tool information @@ -134,6 +150,8 @@ def from_tool(cls, tool: "Tool | ToolBuiltIn") -> "ToolInfo": "text", "image_remote", "image_inline", + "video_inline", + "video_url", "tool_request", "tool_result", "tool_result_image", @@ -255,6 +273,77 @@ def __str__(self): return f"![](data:{self.image_content_type};base64,{self.data})" +class ContentVideo(Content): + """ + Base class for video content. + + This class is not meant to be used directly. Instead, use + [](`~chatlas.content_video_file`) or [](`~chatlas.content_video_youtube`). + """ + + pass + + +class ContentVideoInline(ContentVideo): + """ + Inline video content, for small clips. + + This is the return type for [](`~chatlas.content_video_file`). + It's not meant to be used directly. + + Only Gemini accepts video input, and only for requests that stay under + roughly 100 MB once base64-encoded; use `chat.files.upload()` for larger + files instead. + + Parameters + ---------- + video_content_type + The content type of the video. + data + The base64-encoded video data. + filename + The name of the video file, if known. + """ + + video_content_type: VideoContentTypes + data: str + filename: Optional[str] = None + + content_type: ContentTypeEnum = "video_inline" + + def __str__(self): + name = f" file={self.filename}" if self.filename else "" + return f"" + + +class ContentVideoUrl(ContentVideo): + """ + A video referenced by URL, with no upload involved. + + This is the return type for [](`~chatlas.content_video_youtube`). + It's not meant to be used directly. + + Unlike [](`~chatlas.types.ContentUploaded`), this isn't a file a provider + is hosting on your behalf -- there's nothing to list, download, or + delete, and it doesn't expire. It's sent with no MIME type, since Gemini + determines the video format itself. As of this writing, Gemini only + accepts public YouTube URLs this way (up to 10 per request on Gemini + 2.5+), and only Gemini accepts video URLs at all. + + Parameters + ---------- + url + The URL of the video. + """ + + url: str + + content_type: ContentTypeEnum = "video_url" + + def __str__(self): + return f"