Files
wehub-resource-sync 97e91a83f3
Ruff / Ruff (push) Has been cancelled
Test / Core Tests (push) Has been cancelled
Test / Offline Coverage Tests (Python 3.10) (push) Has been cancelled
Test / Offline Coverage Tests (Python 3.11) (push) Has been cancelled
Test / Offline Coverage Tests (Python 3.12) (push) Has been cancelled
Test / Offline Coverage Tests (Python 3.13) (push) Has been cancelled
Test / Offline Coverage Tests (Python 3.9) (push) Has been cancelled
Test / Full Coverage (Python 3.11) (push) Has been cancelled
Test / Core Provider Tests (OpenAI) (push) Has been cancelled
Test / Core Provider Tests (Anthropic) (push) Has been cancelled
Test / Core Provider Tests (Google) (push) Has been cancelled
Test / Core Provider Tests (Other) (push) Has been cancelled
Test / Anthropic Tests (push) Has been cancelled
Test / Gemini Tests (push) Has been cancelled
Test / Google GenAI Tests (push) Has been cancelled
Test / Vertex AI Tests (push) Has been cancelled
Test / OpenAI Tests (push) Has been cancelled
Test / Writer Tests (push) Has been cancelled
Test / Auto Client Tests (push) Has been cancelled
ty / type-check (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:36:38 +08:00

214 lines
6.9 KiB
Python

import pytest
from instructor.processing.multimodal import Image, Audio
import instructor
from pydantic import Field, BaseModel
from itertools import product
import requests
from pathlib import Path
import base64
import os
audio_url = "https://raw.githubusercontent.com/instructor-ai/instructor/main/tests/assets/gettysburg.wav"
image_url = "https://raw.githubusercontent.com/instructor-ai/instructor/main/tests/assets/image.jpg"
pdf_url = "https://raw.githubusercontent.com/instructor-ai/instructor/main/tests/assets/invoice.pdf"
curr_file = os.path.dirname(__file__)
pdf_path = os.path.join(curr_file, "../../assets/invoice.pdf")
pdf_base64 = base64.b64encode(open(pdf_path, "rb").read()).decode("utf-8")
pdf_base64_string = f"data:application/pdf;base64,{pdf_base64}"
models = ["gpt-4.1-nano"]
modes = [
instructor.Mode.TOOLS,
]
class LineItem(BaseModel):
name: str
price: int
quantity: int
class Receipt(BaseModel):
total: int
items: list[str]
def gettysburg_audio():
audio_file = Path("gettysburg.wav")
if not audio_file.exists():
response = requests.get(audio_url)
response.raise_for_status()
with open(audio_file, "wb") as f:
f.write(response.content)
return audio_file
@pytest.mark.parametrize(
"audio_file, mode",
[(Audio.from_url(audio_url), mode) for mode in modes],
)
def test_multimodal_audio_description(audio_file, mode, client):
client = instructor.from_openai(client, mode=mode)
if client.mode in {
instructor.Mode.RESPONSES_TOOLS,
instructor.Mode.RESPONSES_TOOLS_WITH_INBUILT_TOOLS,
}:
pytest.skip("Audio isn't supported in responses for now")
class AudioDescription(BaseModel):
source: str
response = client.chat.completions.create(
model="gpt-audio-1.5",
response_model=AudioDescription,
modalities=["text"],
messages=[
{
"role": "user",
"content": [
"Where's this excerpt from?",
audio_file,
], # type: ignore
},
],
audio={"voice": "alloy", "format": "wav"}, # type: ignore
)
class ImageDescription(BaseModel):
objects: list[str] = Field(..., description="The objects in the image")
scene: str = Field(..., description="The scene of the image")
colors: list[str] = Field(..., description="The colors in the image")
@pytest.mark.parametrize("model, mode", product(models, modes))
def test_multimodal_image_description(model, mode, client):
client = instructor.from_openai(client, mode=mode)
response = client.chat.completions.create(
model=model, # Ensure this is a vision-capable model
response_model=ImageDescription,
messages=[
{
"role": "system",
"content": "You are a helpful assistant that can describe images",
},
{
"role": "user",
"content": [
"What is this?",
Image.from_url(image_url),
], # type: ignore
},
],
)
# Assertions to validate the response
assert isinstance(response, ImageDescription)
assert len(response.objects) > 0
assert response.scene != ""
assert len(response.colors) > 0
# Additional assertions can be added based on expected content of the sample image
@pytest.mark.parametrize("model, mode", product(models, modes))
def test_multimodal_image_description_autodetect(model, mode, client):
client = instructor.from_openai(client, mode=mode)
response = client.chat.completions.create(
model=model, # Ensure this is a vision-capable model
response_model=ImageDescription,
messages=[
{
"role": "system",
"content": "You are a helpful assistant that can describe images",
},
{
"role": "user",
"content": [
"What is this?",
image_url,
], # type: ignore
},
],
autodetect_images=True, # type: ignore
)
# Assertions to validate the response
assert isinstance(response, ImageDescription)
assert len(response.objects) > 0
assert response.scene != ""
assert len(response.colors) > 0
# Additional assertions can be added based on expected content of the sample image
@pytest.mark.parametrize("model, mode", product(models, modes))
def test_multimodal_image_description_autodetect_no_response_model(model, mode, client):
client = instructor.from_openai(client, mode=mode)
response = client.chat.completions.create(
response_model=None,
model=model, # Ensure this is a vision-capable model
messages=[
{
"role": "system",
"content": "You are a helpful assistant that can describe images. "
"If looking at an image, reply with 'This is an image' and nothing else.",
},
{
"role": "user",
"content": image_url,
},
],
max_tokens=1000,
temperature=1,
autodetect_images=True,
)
if mode not in {
instructor.Mode.RESPONSES_TOOLS,
instructor.Mode.RESPONSES_TOOLS_WITH_INBUILT_TOOLS,
}:
assert response.choices[0].message.content.startswith("This is an image")
else:
assert response.output[0].content[0].text
@pytest.mark.parametrize("pdf_source", [pdf_path, pdf_url, pdf_base64_string])
@pytest.mark.parametrize("model, mode", product(models, modes))
def test_multimodal_pdf_file(model, mode, client, pdf_source):
client = instructor.from_openai(client, mode=mode)
# Retry logic for flaky LLM responses
max_retries = 3
for attempt in range(max_retries):
response = client.chat.completions.create(
model=model, # Ensure this is a vision-capable model
messages=[
{
"role": "system",
"content": "Extract the total and items from the invoice. Be precise and only extract the final total amount and list of item names. The total should be exactly 220.",
},
{
"role": "user",
"content": instructor.processing.multimodal.PDF.autodetect(
pdf_source
),
},
],
autodetect_images=False,
response_model=Receipt,
temperature=0, # Keep for consistent responses
)
if response.total == 220 and len(response.items) == 2:
break
elif attempt == max_retries - 1:
pytest.fail(
f"After {max_retries} attempts, got total={response.total}, items={response.items}, expected total=220, items=2"
)
assert response.total == 220
assert len(response.items) == 2