Skip to content

Commit a835030

Browse files
fix: update type hints for Python 3.9 compatibility and fix unknown argument type
Co-Authored-By: jason@jxnl.co <jason@jxnl.co>
1 parent 34728a7 commit a835030

File tree

2 files changed

+27
-17
lines changed

2 files changed

+27
-17
lines changed

.ruff.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ ignore = [
4646
# mutable defaults
4747
"B006",
4848
"B018",
49+
# ignore union syntax warnings for Python 3.9 compatibility
50+
"UP007",
4951
]
5052

5153
unfixable = [

instructor/multimodal.py

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from collections.abc import Mapping
88
from functools import lru_cache, cache
99
from pathlib import Path
10-
from typing import Any, Callable, Literal, Optional, TypeVar, TypedDict, ClassVar
10+
from typing import Any, Callable, Literal, Optional, TypeVar, TypedDict, ClassVar, Union
1111
from urllib.parse import urlparse
1212

1313
import requests
@@ -47,14 +47,16 @@ class Image(BaseModel):
4747
"image/gif",
4848
"image/webp",
4949
]
50-
source: str | Path = Field(
50+
source: Union[str, Path] = Field(
5151
description="URL, file path, or base64 data of the image"
5252
)
5353
media_type: str = Field(description="MIME type of the image")
54-
data: str | None = Field(None, description="Base64 encoded image data", repr=False)
54+
data: Union[str, None] = Field(
55+
None, description="Base64 encoded image data", repr=False
56+
)
5557

5658
@classmethod
57-
def autodetect(cls, source: str | Path) -> Image | None:
59+
def autodetect(cls, source: Union[str, Path]) -> Union[Image, None]:
5860
"""Attempt to autodetect an image from a source string or Path.
5961
6062
Args:
@@ -83,7 +85,7 @@ def autodetect(cls, source: str | Path) -> Image | None:
8385
return None
8486

8587
@classmethod
86-
def autodetect_safely(cls, source: str | Path) -> Image | str:
88+
def autodetect_safely(cls, source: Union[str, Path]) -> Union[Image, str]:
8789
"""Safely attempt to autodetect an image from a source string or path.
8890
8991
Args:
@@ -126,7 +128,7 @@ def from_base64(cls, data: str) -> Image:
126128
return cls(source=data, media_type=media_type, data=encoded)
127129

128130
@classmethod # Caching likely unnecessary
129-
def from_raw_base64(cls, data: str) -> Image | None:
131+
def from_raw_base64(cls, data: str) -> Union[Image, None]:
130132
"""Create an Image from raw base64 data.
131133
132134
Args:
@@ -137,7 +139,7 @@ def from_raw_base64(cls, data: str) -> Image | None:
137139
"""
138140
try:
139141
decoded: bytes = base64.b64decode(data)
140-
img_type: str | None = imghdr.what(None, decoded)
142+
img_type: Union[str, None] = imghdr.what(None, decoded)
141143
if img_type:
142144
media_type = mimetypes.guess_type(data)[0]
143145
if media_type in cls.VALID_MIME_TYPES:
@@ -152,7 +154,7 @@ def from_url(cls, url: str) -> Image:
152154
if cls.is_base64(url):
153155
return cls.from_base64(url)
154156
parsed_url = urlparse(url)
155-
media_type: str | None = mimetypes.guess_type(parsed_url.path)[0]
157+
media_type: Union[str, None] = mimetypes.guess_type(parsed_url.path)[0]
156158

157159
if not media_type:
158160
try:
@@ -167,7 +169,7 @@ def from_url(cls, url: str) -> Image:
167169

168170
@classmethod
169171
@lru_cache
170-
def from_path(cls, path: str | Path) -> Image:
172+
def from_path(cls, path: Union[str, Path]) -> Image:
171173
path = Path(path)
172174
if not path.is_file():
173175
raise FileNotFoundError(f"Image file not found: {path}")
@@ -180,7 +182,7 @@ def from_path(cls, path: str | Path) -> Image:
180182
f"Image file size ({path.stat().st_size / 1024 / 1024:.1f}MB) "
181183
f"exceeds Mistral's limit of {MAX_MISTRAL_IMAGE_SIZE / 1024 / 1024:.1f}MB"
182184
)
183-
media_type: str | None = mimetypes.guess_type(str(path))[0]
185+
media_type: Union[str, None] = mimetypes.guess_type(str(path))[0]
184186
if media_type not in VALID_MISTRAL_MIME_TYPES:
185187
raise ValueError(
186188
f"Unsupported image format: {media_type}. "
@@ -277,8 +279,10 @@ def to_mistral(self) -> dict[str, Any]:
277279
class Audio(BaseModel):
278280
"""Represents an audio that can be loaded from a URL or file path."""
279281

280-
source: str | Path = Field(description="URL or file path of the audio")
281-
data: str | None = Field(None, description="Base64 encoded audio data", repr=False)
282+
source: Union[str, Path] = Field(description="URL or file path of the audio")
283+
data: Union[str, None] = Field(
284+
None, description="Base64 encoded audio data", repr=False
285+
)
282286

283287

284288
class ImageWithCacheControl(Image):
@@ -290,8 +294,8 @@ class ImageWithCacheControl(Image):
290294

291295
@classmethod
292296
def from_image_params(
293-
cls, source: str | Path, image_params: dict[str, Any]
294-
) -> ImageWithCacheControl | None:
297+
cls, source: Union[str, Path], image_params: dict[str, Any]
298+
) -> Union[ImageWithCacheControl, None]:
295299
"""Create an ImageWithCacheControl from image parameters.
296300
297301
Args:
@@ -322,11 +326,13 @@ def to_anthropic(self) -> dict[str, Any]:
322326

323327

324328
def convert_contents(
325-
contents: str | Image | dict[str, Any] | list[str | Image | dict[str, Any]],
329+
contents: Union[
330+
str, Image, dict[str, Any], list[Union[str, Image, dict[str, Any]]]
331+
],
326332
mode: Mode,
327333
*, # Make autodetect_images keyword-only since it's unused
328334
_autodetect_images: bool = True, # Prefix with _ to indicate intentionally unused
329-
) -> str | list[dict[str, Any]]:
335+
) -> Union[str, list[dict[str, Any]]]:
330336
"""Convert contents to the appropriate format for the given mode."""
331337
# Handle single string case
332338
if isinstance(contents, str):
@@ -403,7 +409,9 @@ def convert_messages(
403409

404410
# Handle list content
405411
if isinstance(content, list):
406-
converted_message["content"] = convert_contents(content, mode)
412+
# Explicitly type the content as Union[str, Image, dict[str, Any]]
413+
typed_content: list[Union[str, Image, dict[str, Any]]] = content
414+
converted_message["content"] = convert_contents(typed_content, mode)
407415
converted_messages.append(converted_message)
408416
continue
409417

0 commit comments

Comments
 (0)