Skip to content

Commit 3bfbaff

Browse files
fix(gemini): wrap video content in types.Part with inline_data for API compatibility
- Fix Pydantic validation error when sending video content to Gemini - Video data now properly wrapped in types.Part with inline_data containing types.Blob - Resolves issue #35 where video input failed with 'Extra inputs are not permitted' error - Maintains backward compatibility with existing video parsing logic - Updated tests to expect types.Part objects instead of raw dictionaries Co-Authored-By: Elijah Arbee <ec.arbee1@gmail.com>
1 parent 0b4267b commit 3bfbaff

2 files changed

Lines changed: 36 additions & 21 deletions

File tree

src/providers/google.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,12 @@ def _parse_content(self, content: Union[str, List[Dict[str, Any]]]) -> List[Any]
173173
video_bytes = base64.b64decode(video_data["data"])
174174
mime_type = video_data.get("mime_type", "video/mp4")
175175

176-
parts.append({"mime_type": mime_type, "data": video_bytes})
176+
parts.append(types.Part(
177+
inline_data=types.Blob(
178+
mime_type=mime_type,
179+
data=video_bytes
180+
)
181+
))
177182
elif "file_path" in video_data:
178183
file_path = video_data["file_path"]
179184
if os.path.exists(file_path):
@@ -182,7 +187,12 @@ def _parse_content(self, content: Union[str, List[Dict[str, Any]]]) -> List[Any]
182187
mime_type, _ = mimetypes.guess_type(file_path)
183188
if not mime_type or not mime_type.startswith("video/"):
184189
mime_type = "video/mp4"
185-
parts.append({"mime_type": mime_type, "data": video_bytes})
190+
parts.append(types.Part(
191+
inlineData=types.Blob(
192+
mimeType=mime_type,
193+
data=video_bytes
194+
)
195+
))
186196
elif t == "document":
187197
doc_data = item.get("document", {})
188198
if "data" in doc_data:

tests/test_content_types.py

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -124,27 +124,29 @@ def test_video_base64_data_with_prefix(self):
124124
content = [
125125
{"type": "video", "video": {"data": f"data:video/mp4;base64,{video_b64}"}}
126126
]
127-
127+
128128
result = self.google_completions._parse_content(content)
129-
129+
130130
assert len(result) == 1
131-
assert isinstance(result[0], dict)
132-
assert result[0]["mime_type"] == "video/mp4"
133-
assert result[0]["data"] == self.sample_video_bytes
131+
from google.genai import types
132+
assert isinstance(result[0], types.Part)
133+
assert result[0].inline_data.mime_type == "video/mp4"
134+
assert result[0].inline_data.data == self.sample_video_bytes
134135

135136
def test_video_base64_data_with_explicit_mime(self):
136137
"""Test video parsing with explicit mime_type"""
137138
video_b64 = base64.b64encode(self.sample_video_bytes).decode('utf-8')
138139
content = [
139140
{"type": "video", "video": {"data": video_b64, "mime_type": "video/webm"}}
140141
]
141-
142+
142143
result = self.google_completions._parse_content(content)
143-
144+
144145
assert len(result) == 1
145-
assert isinstance(result[0], dict)
146-
assert result[0]["mime_type"] == "video/webm"
147-
assert result[0]["data"] == self.sample_video_bytes
146+
from google.genai import types
147+
assert isinstance(result[0], types.Part)
148+
assert result[0].inline_data.mime_type == "video/webm"
149+
assert result[0].inline_data.data == self.sample_video_bytes
148150

149151
def test_video_file_path_mime_detection(self):
150152
"""Test video parsing from file path with MIME type detection"""
@@ -159,9 +161,10 @@ def test_video_file_path_mime_detection(self):
159161
result = self.google_completions._parse_content(content)
160162

161163
assert len(result) == 1
162-
assert isinstance(result[0], dict)
163-
assert result[0]["mime_type"] == "video/mp4"
164-
assert result[0]["data"] == self.sample_video_bytes
164+
from google.genai import types
165+
assert isinstance(result[0], types.Part)
166+
assert result[0].inline_data.mime_type == "video/mp4"
167+
assert result[0].inline_data.data == self.sample_video_bytes
165168

166169
os.unlink(tmp_file.name)
167170

@@ -178,9 +181,10 @@ def test_video_file_path_unknown_extension_fallback(self):
178181
result = self.google_completions._parse_content(content)
179182

180183
assert len(result) == 1
181-
assert isinstance(result[0], dict)
182-
assert result[0]["mime_type"] == "video/mp4"
183-
assert result[0]["data"] == self.sample_video_bytes
184+
from google.genai import types
185+
assert isinstance(result[0], types.Part)
186+
assert result[0].inline_data.mime_type == "video/mp4"
187+
assert result[0].inline_data.data == self.sample_video_bytes
184188

185189
os.unlink(tmp_file.name)
186190

@@ -272,9 +276,10 @@ def test_mixed_content_types(self):
272276
assert result[1]["mime_type"] == "application/pdf"
273277
assert result[1]["data"] == self.sample_pdf_bytes
274278

275-
assert isinstance(result[2], dict)
276-
assert result[2]["mime_type"] == "video/mp4"
277-
assert result[2]["data"] == self.sample_video_bytes
279+
from google.genai import types
280+
assert isinstance(result[2], types.Part)
281+
assert result[2].inline_data.mime_type == "video/mp4"
282+
assert result[2].inline_data.data == self.sample_video_bytes
278283

279284
assert isinstance(result[3], dict)
280285
assert result[3]["mime_type"] == "text/plain"

0 commit comments

Comments
 (0)