Skip to content

Commit 676f740

Browse files
committed
test(v2): cover provider-owned client architecture
1 parent a2259f7 commit 676f740

25 files changed

Lines changed: 1203 additions & 1013 deletions

.github/workflows/ty.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ jobs:
2727
run: uv run ty check --error-on-warning --output-format github instructor/
2828
- name: Run type check with ty (tests)
2929
run: uv run ty check --config-file ty-tests.toml --error-on-warning --output-format github tests
30+
- name: Run public surface inference check with Pyright
31+
run: uv run --with 'pyright==1.1.408' pyright tests/typing/test_public_surface.py
3032
- name: Check installed-package public typing
3133
run: tests/typing/check_installed_package.sh
3234
- name: Check supported Python versions and platforms

tests/llm/test_openai/test_multimodal.py

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import base64
99
import os
1010

11+
from instructor.core.exceptions import InstructorRetryException
12+
1113
audio_url = "https://raw.githubusercontent.com/instructor-ai/instructor/main/tests/assets/gettysburg.wav"
1214
image_url = "https://raw.githubusercontent.com/instructor-ai/instructor/main/tests/assets/image.jpg"
1315

@@ -60,21 +62,32 @@ def test_multimodal_audio_description(audio_file, mode, client):
6062
class AudioDescription(BaseModel):
6163
source: str
6264

63-
response = client.chat.completions.create(
64-
model="gpt-audio-1.5",
65-
response_model=AudioDescription,
66-
modalities=["text"],
67-
messages=[
68-
{
69-
"role": "user",
70-
"content": [
71-
"Where's this excerpt from?",
72-
audio_file,
73-
], # type: ignore
74-
},
75-
],
76-
audio={"voice": "alloy", "format": "wav"}, # type: ignore
77-
)
65+
try:
66+
response = client.chat.completions.create(
67+
model="gpt-audio-1.5",
68+
response_model=AudioDescription,
69+
modalities=["text"],
70+
messages=[
71+
{
72+
"role": "user",
73+
"content": [
74+
"Where's this excerpt from?",
75+
audio_file,
76+
], # type: ignore
77+
},
78+
],
79+
audio={"voice": "alloy", "format": "wav"}, # type: ignore
80+
)
81+
except InstructorRetryException as exc:
82+
message = str(exc).lower()
83+
if (
84+
"model_not_found" in message
85+
or "does not exist or you do not have access" in message
86+
):
87+
pytest.skip(f"Audio model unavailable in this environment: {exc}")
88+
raise
89+
90+
assert isinstance(response, AudioDescription)
7891

7992

8093
class ImageDescription(BaseModel):

tests/test_lazy_imports.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
import importlib
22
import json
3+
import os
34
import subprocess
45
import sys
6+
from pathlib import Path
57
from typing import TypedDict
68

9+
_PROJECT_ROOT = Path(__file__).resolve().parents[1]
10+
711

812
class ColdImportState(TypedDict):
913
modules: list[str]
@@ -21,11 +25,17 @@ def _cold_import_state(code: str) -> ColdImportState:
2125
)
2226
print(json.dumps(mods))
2327
"""
28+
env = dict(os.environ)
29+
env["PYTHONPATH"] = os.pathsep.join(
30+
path for path in (str(_PROJECT_ROOT), env.get("PYTHONPATH")) if path
31+
)
2432
result = subprocess.run(
2533
[sys.executable, "-c", probe, code],
2634
check=True,
2735
capture_output=True,
2836
text=True,
37+
cwd=_PROJECT_ROOT,
38+
env=env,
2939
)
3040
modules = json.loads(result.stdout)
3141
return {"modules": modules, "count": len(modules)}

tests/v2/provider_matrix.py

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
import importlib.util
66
from pathlib import Path
7-
from typing import Any
87

98
import pytest
109

@@ -23,25 +22,31 @@
2322
PROVIDER_HANDLER_MODES = {
2423
provider: spec.supported_modes for provider, spec in PROVIDER_SPECS.items()
2524
}
26-
27-
28-
def legacy_config_dicts() -> dict[Provider, dict[str, Any]]:
29-
"""Expose the old dict shape while the baseline tests migrate."""
30-
return {
31-
provider: {
32-
"provider_string": spec.provider_string,
33-
"supported_modes": list(spec.supported_modes),
34-
"unsupported_modes": list(spec.unsupported_modes),
35-
"legacy_modes": spec.legacy_modes,
36-
"from_function": spec.from_function,
37-
"sdk_module": spec.sdk_module,
38-
"basic_modes": list(spec.basic_modes),
39-
"async_modes": list(spec.async_modes),
40-
"missing_sdk_message": spec.missing_sdk_message,
41-
}
42-
for provider, spec in TEST_PROVIDER_SPECS.items()
43-
}
44-
25+
PARTIAL_STREAM_CASES = tuple(
26+
(provider, mode)
27+
for provider, spec in TEST_PROVIDER_SPECS.items()
28+
for mode in spec.capabilities.partial_stream_modes
29+
)
30+
ITERABLE_STREAM_CASES = tuple(
31+
(provider, mode)
32+
for provider, spec in TEST_PROVIDER_SPECS.items()
33+
for mode in spec.capabilities.iterable_stream_modes
34+
)
35+
TYPED_MULTIMODAL_PROVIDERS = tuple(
36+
provider
37+
for provider, spec in TEST_PROVIDER_SPECS.items()
38+
if spec.capabilities.multimodal_inputs
39+
)
40+
TYPED_MULTIMODAL_CASES = tuple(
41+
(provider, media_type)
42+
for provider, spec in TEST_PROVIDER_SPECS.items()
43+
for media_type in spec.capabilities.multimodal_inputs
44+
)
45+
EXPLICIT_PARALLEL_PROVIDERS = tuple(
46+
provider
47+
for provider, spec in TEST_PROVIDER_SPECS.items()
48+
if spec.capabilities.explicit_parallel_tools
49+
)
4550

4651
_PROJECT_ROOT = Path(__file__).resolve().parents[2]
4752
_HANDLERS_LOADED: set[Provider] = set()
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
from __future__ import annotations
2+
3+
from collections.abc import Iterable
4+
5+
import pytest
6+
from pydantic import BaseModel
7+
8+
from instructor.v2.providers.anthropic import parallel
9+
10+
11+
class Weather(BaseModel):
12+
city: str
13+
14+
15+
class Score(BaseModel):
16+
value: int
17+
18+
19+
def test_parallel_schema_generation_is_owned_by_anthropic(
20+
monkeypatch: pytest.MonkeyPatch,
21+
) -> None:
22+
calls: list[type[BaseModel]] = []
23+
24+
def fake_schema(model: type[BaseModel]) -> dict[str, str]:
25+
calls.append(model)
26+
return {"name": model.__name__}
27+
28+
monkeypatch.setattr(parallel, "generate_anthropic_schema", fake_schema)
29+
30+
schemas = parallel.handle_parallel_model(Iterable[Weather | Score])
31+
32+
assert schemas == [{"name": "Weather"}, {"name": "Score"}]
33+
assert calls == [Weather, Score]

tests/v2/test_bedrock_client.py

Lines changed: 0 additions & 67 deletions
This file was deleted.

tests/v2/test_cerebras_client.py

Lines changed: 0 additions & 72 deletions
This file was deleted.

0 commit comments

Comments
 (0)