Skip to content

Commit dc83ff6

Browse files
authored
Expand test suite (#19)
- Add 58 tests across three focused modules with 98% coverage - Fix several CI issues that were silently failing - Align local pre-commit with CI so they catch the same problems - Clean up dev dependencies now that the example code has moved
1 parent 176d521 commit dc83ff6

8 files changed

Lines changed: 480 additions & 1446 deletions

File tree

.github/workflows/checks.yml

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ jobs:
2626
with:
2727
enable-cache: true
2828

29-
- run: uv sync --all-extras --all-packages --group lint
29+
- run: uv sync --all-packages --group lint
3030

3131
- uses: pre-commit/action@v3.0.0
3232
with:
@@ -40,6 +40,11 @@ jobs:
4040
test:
4141
runs-on: ubuntu-latest
4242
timeout-minutes: 10
43+
strategy:
44+
fail-fast: false
45+
matrix:
46+
pydantic-ai-version: [min, latest]
47+
name: test (pydantic-ai ${{ matrix.pydantic-ai-version }})
4348
steps:
4449
- uses: actions/checkout@v4
4550

@@ -48,5 +53,10 @@ jobs:
4853
enable-cache: true
4954

5055
- run: mkdir .coverage
51-
- run: uv sync --all-extras --group dev
52-
- run: uv run coverage run -m pytest --durations=100 -n auto --dist=loadgroup
56+
- if: matrix.pydantic-ai-version == 'min'
57+
run: uv sync --group dev --resolution lowest-direct
58+
- if: matrix.pydantic-ai-version == 'latest'
59+
run: uv sync --group dev --upgrade-package pydantic-ai
60+
env:
61+
UV_FROZEN: "0"
62+
- run: make test

.pre-commit-config.yaml

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,6 @@ repos:
2626

2727
- repo: local
2828
hooks:
29-
- id: format
30-
name: Format
31-
entry: make
32-
args: [format]
33-
language: system
34-
types: [python]
35-
pass_filenames: false
3629
- id: lint
3730
name: Lint
3831
entry: make

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
.PHONY: install
2+
install: ## Install git hooks for local development
3+
uv run pre-commit install
4+
15
.PHONY: format
26
format: ## Format the code
37
uv run ruff format

pyproject.toml

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,24 +20,15 @@ version = { attr = "cragents._version.__version__" }
2020

2121
[dependency-groups]
2222
dev = [
23-
"anyio>=4.11.0",
24-
"coverage>=7.12.0",
23+
"anyio>=4.12.1",
24+
"coverage>=7.13.2",
2525
"inline-snapshot>=0.31.1",
26-
"pyright>=1.1.406",
27-
"pytest>=9.0.1",
26+
"pre-commit>=4.5.1",
27+
"pyright>=1.1.408",
28+
"pytest>=9.0.2",
2829
"pytest-xdist>=3.8.0",
2930
"trio>=0.32.0",
3031
]
31-
example = [
32-
"chromadb>=1.3.7",
33-
"datasets>=4.4.2",
34-
"langchain-text-splitters>=1.1.0",
35-
"markdownify>=1.2.2",
36-
"playwright>=1.57.0",
37-
"requests-cache>=1.2.1",
38-
"rich>=14.2.0",
39-
"transformers>=4.57.3",
40-
]
4132
lint = [
4233
"pyright>=1.1.406",
4334
"ruff>=0.14.1",

tests/test_.py renamed to tests/test_agent.py

Lines changed: 104 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import pytest
22
from inline_snapshot import snapshot
33
from pydantic_ai import ToolOutput
4-
from pydantic_ai.models.openai import OpenAIChatModel
4+
from pydantic_ai.models.openai import OpenAIChatModel, OpenAIChatModelSettings
5+
from pydantic_ai.models.test import TestModel
56
from pydantic_ai.providers.openai import OpenAIProvider
67

78
from cragents import Anchor, Constrain, CRAgent, Free, Think, UseTools, vllm_model_profile
@@ -24,6 +25,9 @@
2425
]
2526

2627

28+
# ── end-to-end set_guide output type tests ────────────────────────────────────
29+
30+
2731
async def test_default_agent_output():
2832
agent = CRAgent(model)
2933
await agent.set_guide(generation_sequence)
@@ -134,3 +138,102 @@ async def test_mixed_output_type():
134138
}
135139
}
136140
)
141+
142+
143+
# ── set_guide error handling and model settings ───────────────────────────────
144+
145+
146+
async def test_set_guide_requires_openai_model():
147+
agent = CRAgent(TestModel())
148+
with pytest.raises(RuntimeError, match="OpenAIChatModel required"):
149+
await agent.set_guide([Anchor("hi ")])
150+
151+
152+
async def test_set_guide_creates_model_settings_when_none():
153+
agent = CRAgent(model)
154+
assert agent.model_settings is None
155+
await agent.set_guide([Anchor("hi ")])
156+
assert agent.model_settings is not None
157+
assert "extra_body" in agent.model_settings
158+
159+
160+
async def test_set_guide_preserves_existing_model_settings():
161+
agent = CRAgent(model, model_settings=OpenAIChatModelSettings(temperature=0.5))
162+
await agent.set_guide([Anchor("hi ")])
163+
assert agent.model_settings["temperature"] == 0.5
164+
assert "extra_body" in agent.model_settings
165+
166+
167+
async def test_set_guide_overwrites_on_second_call():
168+
agent = CRAgent(model)
169+
await agent.set_guide([Anchor("first ")])
170+
first_grammar = agent.model_settings["extra_body"]["structured_outputs"]["grammar"]
171+
await agent.set_guide([Anchor("second ")])
172+
second_grammar = agent.model_settings["extra_body"]["structured_outputs"]["grammar"]
173+
assert first_grammar != second_grammar
174+
assert "second" in second_grammar
175+
176+
177+
# ── set_guide UseTools schema handling ────────────────────────────────────────
178+
179+
180+
async def test_set_guide_explicit_use_tools_schema_not_overwritten():
181+
explicit_schema = {"type": "number"}
182+
agent = CRAgent(model)
183+
await agent.set_guide([UseTools(json_schema=explicit_schema)])
184+
grammar = agent.model_settings["extra_body"]["structured_outputs"]["grammar"]
185+
assert '"type": "number"' in grammar
186+
187+
188+
async def test_set_guide_use_tools_with_registered_tool():
189+
agent = CRAgent(model)
190+
191+
@agent.tool_plain
192+
def my_tool(x: int) -> str:
193+
return str(x)
194+
195+
await agent.set_guide([UseTools()])
196+
grammar = agent.model_settings["extra_body"]["structured_outputs"]["grammar"]
197+
# The tool's parameter schema (containing "x") should appear in the grammar
198+
assert '"x"' in grammar
199+
200+
201+
async def test_set_guide_use_tools_tool_names():
202+
agent = CRAgent(model)
203+
await agent.set_guide([UseTools(json_schema={"type": "string"}, tool_names=["alpha", "beta"])])
204+
grammar = agent.model_settings["extra_body"]["structured_outputs"]["grammar"]
205+
assert 'FUNCTION_NAME: ("alpha" | "beta")' in grammar
206+
207+
208+
async def test_set_guide_merges_toolset_with_anyof_output():
209+
# When the output schema already has anyOf (multiple output types) and the agent
210+
# also has registered tools, anyOf = toolset_schemas + return_schema["anyOf"]
211+
agent = CRAgent(model, output_type=[ToolOutput(bool), ToolOutput(int)])
212+
213+
@agent.tool_plain
214+
def helper(x: str) -> str:
215+
return x
216+
217+
await agent.set_guide([UseTools()])
218+
grammar = agent.model_settings["extra_body"]["structured_outputs"]["grammar"]
219+
assert "tool_schema" in grammar
220+
assert "anyOf" in grammar
221+
222+
223+
# ── vllm_model_profile ─────────────────────────────────────────────────────────
224+
225+
226+
def test_vllm_profile_strict_tool_definition():
227+
assert vllm_model_profile.openai_supports_strict_tool_definition is False
228+
229+
230+
def test_vllm_profile_tool_choice_required():
231+
assert vllm_model_profile.openai_supports_tool_choice_required is False
232+
233+
234+
def test_vllm_profile_json_object_output():
235+
assert vllm_model_profile.supports_json_object_output is False
236+
237+
238+
def test_vllm_profile_json_schema_output():
239+
assert vllm_model_profile.supports_json_schema_output is True

0 commit comments

Comments
 (0)