Skip to content

Commit 4060f48

Browse files
authored
Make openai optional (#91)
1 parent d29d5e0 commit 4060f48

File tree

8 files changed

+80
-57
lines changed

8 files changed

+80
-57
lines changed

.github/workflows/ci.yml

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,15 @@ jobs:
8080
with:
8181
enable-cache: true
8282

83-
- run: uv run --python 3.12 --frozen --extra vertexai --extra groq pytest tests/test_live.py -v --durations=100
83+
- run: >
84+
uv run
85+
--python 3.12
86+
--frozen
87+
--extra openai
88+
--extra vertexai
89+
--extra groq
90+
pytest tests/test_live.py -v
91+
--durations=100
8492
env:
8593
PYDANTIC_AI_LIVE_TEST_DANGEROUS: 'CHARGE-ME!'
8694
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
@@ -171,8 +179,6 @@ jobs:
171179
environment: release
172180

173181
permissions:
174-
# necessary while private, see https://github.com/actions/checkout/issues/254#issuecomment-1961296625
175-
contents: read
176182
id-token: write
177183

178184
steps:

docs/install.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ To use Logfire with PydanticAI, install PydanticAI with the `logfire` optional g
1919
pip/uv-add 'pydantic-ai[logfire]'
2020
```
2121

22-
From there, follow the [Logfire setup cods](logfire.md#integrating-logfire) to configure Logfire.
22+
From there, follow the [Logfire setup docs](logfire.md#integrating-logfire) to configure Logfire.
2323

2424
## Running Examples
2525

@@ -31,4 +31,4 @@ To install extra dependencies required to run examples, install the `examples` o
3131
pip/uv-add 'pydantic-ai[examples]'
3232
```
3333

34-
For next steps, follow the instructions [in the examples](examples/index.md).
34+
To run the examples, follow instructions [in the examples](examples/index.md).

pydantic_ai/models/groq.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,6 @@
66
from datetime import datetime, timezone
77
from typing import Literal, overload
88

9-
from groq import NOT_GIVEN, AsyncGroq, AsyncStream
10-
from groq.types import chat
11-
from groq.types.chat import ChatCompletion, ChatCompletionChunk
12-
from groq.types.chat.chat_completion_chunk import ChoiceDeltaToolCall
139
from httpx import AsyncClient as AsyncHTTPClient
1410
from typing_extensions import assert_never
1511

@@ -36,6 +32,17 @@
3632
check_allow_model_requests,
3733
)
3834

35+
try:
36+
from groq import NOT_GIVEN, AsyncGroq, AsyncStream
37+
from groq.types import chat
38+
from groq.types.chat import ChatCompletion, ChatCompletionChunk
39+
from groq.types.chat.chat_completion_chunk import ChoiceDeltaToolCall
40+
except ImportError as e:
41+
raise ImportError(
42+
'Please install `groq` to use the Groq model, '
43+
"you can use the `groq` optional group — `pip install 'pydantic-ai[groq]'`"
44+
) from e
45+
3946
GroqModelName = Literal[
4047
'llama-3.1-70b-versatile',
4148
'llama3-groq-70b-8192-tool-use-preview',

pydantic_ai/models/openai.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,6 @@
77
from typing import Literal, overload
88

99
from httpx import AsyncClient as AsyncHTTPClient
10-
from openai import NOT_GIVEN, AsyncOpenAI, AsyncStream
11-
from openai.types import ChatModel, chat
12-
from openai.types.chat import ChatCompletionChunk
13-
from openai.types.chat.chat_completion_chunk import ChoiceDeltaToolCall
1410
from typing_extensions import assert_never
1511

1612
from .. import UnexpectedModelBehavior, _utils, result
@@ -36,6 +32,17 @@
3632
check_allow_model_requests,
3733
)
3834

35+
try:
36+
from openai import NOT_GIVEN, AsyncOpenAI, AsyncStream
37+
from openai.types import ChatModel, chat
38+
from openai.types.chat import ChatCompletionChunk
39+
from openai.types.chat.chat_completion_chunk import ChoiceDeltaToolCall
40+
except ImportError as e:
41+
raise ImportError(
42+
'Please install `openai` to use the OpenAI model, '
43+
"you can use the `openai` optional group — `pip install 'pydantic-ai[openai]'`"
44+
) from e
45+
3946

4047
@dataclass(init=False)
4148
class OpenAIModel(Model):

pyproject.toml

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,28 +38,22 @@ dependencies = [
3838
"griffe>=1.3.2",
3939
"httpx>=0.27.2",
4040
"logfire-api>=1.2.0",
41-
"openai>=1.54.3",
4241
"pydantic>=2.10",
4342
]
4443

4544
[project.optional-dependencies]
46-
vertexai = [
47-
"google-auth>=2.36.0",
48-
"requests>=2.32.3",
49-
]
50-
logfire = [
51-
"logfire>=2.3",
52-
]
45+
openai = ["openai>=1.54.3"]
46+
vertexai = ["google-auth>=2.36.0", "requests>=2.32.3"]
47+
groq = ["groq>=0.12.0"]
48+
logfire = ["logfire>=2.3"]
5349
examples = [
5450
"asyncpg>=0.30.0",
5551
"fastapi>=0.115.4",
5652
"logfire[asyncpg,fastapi]>=2.3",
5753
"python-multipart>=0.0.17",
5854
"rich>=13.9.2",
5955
"uvicorn>=0.32.0",
60-
]
61-
groq = [
62-
"groq>=0.12.0",
56+
"pydantic-ai[openai,vertexai,groq]",
6357
]
6458

6559
[dependency-groups]

tests/models/test_model.py

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,18 @@
1-
from typing import TYPE_CHECKING
2-
31
import pytest
42

53
from pydantic_ai import UserError
64
from pydantic_ai.models import infer_model
75
from pydantic_ai.models.gemini import GeminiModel
8-
from pydantic_ai.models.openai import OpenAIModel
9-
from tests.conftest import TestEnv
10-
11-
if TYPE_CHECKING:
12-
from pydantic_ai.models.vertexai import VertexAIModel
6+
from tests.conftest import TestEnv, try_import
137

14-
google_auth_installed = True
8+
with try_import() as openai_imports_successful:
9+
from pydantic_ai.models.openai import OpenAIModel
1510

16-
else:
17-
try:
18-
from pydantic_ai.models.vertexai import VertexAIModel
19-
except ImportError:
20-
google_auth_installed = False
21-
else:
22-
google_auth_installed = True
11+
with try_import() as vertexai_imports_successful:
12+
from pydantic_ai.models.vertexai import VertexAIModel
2313

2414

15+
@pytest.mark.skipif(not openai_imports_successful(), reason='openai not installed')
2516
def test_infer_str_openai(env: TestEnv):
2617
env.set('OPENAI_API_KEY', 'via-env-var')
2718
m = infer_model('openai:gpt-3.5-turbo')
@@ -39,7 +30,7 @@ def test_infer_str_gemini(env: TestEnv):
3930
assert m.name() == 'gemini-1.5-flash'
4031

4132

42-
@pytest.mark.skipif(not google_auth_installed, reason='google-auth not installed')
33+
@pytest.mark.skipif(not vertexai_imports_successful(), reason='google-auth not installed')
4334
def test_infer_vertexai(env: TestEnv):
4435
m = infer_model('vertexai:gemini-1.5-flash')
4536
assert isinstance(m, VertexAIModel)

tests/models/test_openai.py

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,6 @@
99

1010
import pytest
1111
from inline_snapshot import snapshot
12-
from openai import AsyncOpenAI
13-
from openai.types import chat
14-
from openai.types.chat.chat_completion import Choice
15-
from openai.types.chat.chat_completion_chunk import (
16-
Choice as ChunkChoice,
17-
ChoiceDelta,
18-
ChoiceDeltaToolCall,
19-
ChoiceDeltaToolCallFunction,
20-
)
21-
from openai.types.chat.chat_completion_message import ChatCompletionMessage
22-
from openai.types.chat.chat_completion_message_tool_call import Function
23-
from openai.types.completion_usage import CompletionUsage, PromptTokensDetails
2412
from typing_extensions import TypedDict
2513

2614
from pydantic_ai import Agent, ModelRetry, UnexpectedModelBehavior, _utils
@@ -34,11 +22,29 @@
3422
ToolReturn,
3523
UserPrompt,
3624
)
37-
from pydantic_ai.models.openai import OpenAIModel
3825
from pydantic_ai.result import Cost
39-
from tests.conftest import IsNow
26+
from tests.conftest import IsNow, try_import
27+
28+
with try_import() as imports_successful:
29+
from openai import AsyncOpenAI
30+
from openai.types import chat
31+
from openai.types.chat.chat_completion import Choice
32+
from openai.types.chat.chat_completion_chunk import (
33+
Choice as ChunkChoice,
34+
ChoiceDelta,
35+
ChoiceDeltaToolCall,
36+
ChoiceDeltaToolCallFunction,
37+
)
38+
from openai.types.chat.chat_completion_message import ChatCompletionMessage
39+
from openai.types.chat.chat_completion_message_tool_call import Function
40+
from openai.types.completion_usage import CompletionUsage, PromptTokensDetails
41+
42+
from pydantic_ai.models.openai import OpenAIModel
4043

41-
pytestmark = pytest.mark.anyio
44+
pytestmark = [
45+
pytest.mark.skipif(not imports_successful(), reason='openai not installed'),
46+
pytest.mark.anyio,
47+
]
4248

4349

4450
def test_init():

uv.lock

Lines changed: 14 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)