Skip to content

Commit 8286cd7

Browse files
committed
add integration tests for FileSearchTool
Added integration tests for both OpenAI and Google that create resources using SDK clients, run the agent, and clean up properly. Tests cover both streaming and non-streaming for each provider.
1 parent c2765ac commit 8286cd7

File tree

2 files changed

+175
-2
lines changed

2 files changed

+175
-2
lines changed

tests/models/test_google.py

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3203,8 +3203,95 @@ def _generate_response_with_texts(response_id: str, texts: list[str]) -> Generat
32033203
)
32043204

32053205

3206+
async def test_google_model_file_search_tool(allow_model_requests: None, google_provider: GoogleProvider):
3207+
"""Integration test for FileSearchTool with Google."""
3208+
from pydantic_ai.builtin_tools import FileSearchTool
3209+
3210+
client = google_provider.client
3211+
3212+
import tempfile
3213+
with tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False) as f:
3214+
f.write('Paris is the capital of France. The Eiffel Tower is a famous landmark in Paris.')
3215+
test_file_path = f.name
3216+
3217+
try:
3218+
store = await client.aio.file_search_stores.create(
3219+
display_name='test-file-search-store'
3220+
)
3221+
3222+
with open(test_file_path, 'rb') as f:
3223+
await client.aio.file_search_stores.upload_to_file_search_store(
3224+
file_search_store=store.name,
3225+
file=f,
3226+
config={'mime_type': 'text/plain'}
3227+
)
3228+
3229+
import asyncio
3230+
await asyncio.sleep(3)
3231+
3232+
model = GoogleModel('gemini-2.5-pro', provider=google_provider)
3233+
agent = Agent(model=model, builtin_tools=[FileSearchTool(vector_store_ids=[store.name])])
3234+
3235+
result = await agent.run('What is the capital of France according to my files?')
3236+
3237+
assert 'Paris' in result.output or 'paris' in result.output.lower()
3238+
3239+
finally:
3240+
import os
3241+
os.unlink(test_file_path)
3242+
if 'store' in locals():
3243+
await client.aio.file_search_stores.delete(name=store.name)
3244+
3245+
3246+
async def test_google_model_file_search_tool_stream(allow_model_requests: None, google_provider: GoogleProvider):
3247+
"""Integration test for FileSearchTool streaming with Google."""
3248+
from pydantic_ai.builtin_tools import FileSearchTool
3249+
3250+
client = google_provider.client
3251+
3252+
import tempfile
3253+
with tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False) as f:
3254+
f.write('The Louvre Museum is located in Paris, France. It houses the Mona Lisa.')
3255+
test_file_path = f.name
3256+
3257+
try:
3258+
store = await client.aio.file_search_stores.create(
3259+
display_name='test-file-search-stream'
3260+
)
3261+
3262+
with open(test_file_path, 'rb') as f:
3263+
await client.aio.file_search_stores.upload_to_file_search_store(
3264+
file_search_store=store.name,
3265+
file=f,
3266+
config={'mime_type': 'text/plain'}
3267+
)
3268+
3269+
import asyncio
3270+
await asyncio.sleep(3)
3271+
3272+
model = GoogleModel('gemini-2.5-pro', provider=google_provider)
3273+
agent = Agent(model=model, builtin_tools=[FileSearchTool(vector_store_ids=[store.name])])
3274+
3275+
events = []
3276+
async with agent.iter(user_prompt='Where is the Louvre Museum according to my files?') as agent_run:
3277+
async for node in agent_run:
3278+
if Agent.is_model_request_node(node):
3279+
async with node.stream(agent_run.ctx) as stream:
3280+
async for event in stream:
3281+
events.append(event)
3282+
3283+
assert len(events) > 0
3284+
output = ''.join(str(e.part.content) if hasattr(e, 'part') and hasattr(e.part, 'content') else '' for e in events)
3285+
assert 'Paris' in output or 'France' in output or 'Louvre' in output or 'paris' in output.lower()
3286+
3287+
finally:
3288+
import os
3289+
os.unlink(test_file_path)
3290+
if 'store' in locals():
3291+
await client.aio.file_search_stores.delete(name=store.name)
3292+
3293+
32063294
# Unit tests below validate the FileSearchTool parsing logic.
3207-
# Integration tests require setting up file search stores via the Google API.
32083295

32093296

32103297
def test_map_file_search_grounding_metadata():

tests/models/test_openai_responses.py

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7436,7 +7436,93 @@ def get_meaning_of_life() -> int:
74367436
)
74377437

74387438

7439-
# Integration tests for FileSearchTool require vector store setup and cassette recording.
7439+
async def test_openai_responses_model_file_search_tool(allow_model_requests: None, openai_api_key: str):
7440+
"""Integration test for FileSearchTool with OpenAI."""
7441+
from openai import AsyncOpenAI
7442+
7443+
from pydantic_ai.builtin_tools import FileSearchTool
7444+
from pydantic_ai.providers.openai import OpenAIProvider
7445+
7446+
async_client = AsyncOpenAI(api_key=openai_api_key)
7447+
7448+
import tempfile
7449+
with tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False) as f:
7450+
f.write('Paris is the capital of France. It is known for the Eiffel Tower.')
7451+
test_file_path = f.name
7452+
7453+
try:
7454+
with open(test_file_path, 'rb') as f:
7455+
file = await async_client.files.create(file=f, purpose='assistants')
7456+
7457+
vector_store = await async_client.vector_stores.create(name='test-file-search')
7458+
await async_client.vector_stores.files.create(vector_store_id=vector_store.id, file_id=file.id)
7459+
7460+
import asyncio
7461+
await asyncio.sleep(2)
7462+
7463+
model = OpenAIResponsesModel('gpt-4o', provider=OpenAIProvider(openai_client=async_client))
7464+
agent = Agent(model=model, builtin_tools=[FileSearchTool(vector_store_ids=[vector_store.id])])
7465+
7466+
result = await agent.run('What is the capital of France according to my files?')
7467+
7468+
assert 'Paris' in result.output or 'paris' in result.output.lower()
7469+
7470+
finally:
7471+
import os
7472+
os.unlink(test_file_path)
7473+
if 'file' in locals():
7474+
await async_client.files.delete(file.id)
7475+
if 'vector_store' in locals():
7476+
await async_client.vector_stores.delete(vector_store.id)
7477+
await async_client.close()
7478+
7479+
7480+
async def test_openai_responses_model_file_search_tool_stream(allow_model_requests: None, openai_api_key: str):
7481+
"""Integration test for FileSearchTool streaming with OpenAI."""
7482+
from openai import AsyncOpenAI
7483+
7484+
from pydantic_ai.builtin_tools import FileSearchTool
7485+
from pydantic_ai.providers.openai import OpenAIProvider
7486+
7487+
async_client = AsyncOpenAI(api_key=openai_api_key)
7488+
7489+
import tempfile
7490+
with tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False) as f:
7491+
f.write('The Eiffel Tower is located in Paris, France.')
7492+
test_file_path = f.name
7493+
7494+
try:
7495+
with open(test_file_path, 'rb') as f:
7496+
file = await async_client.files.create(file=f, purpose='assistants')
7497+
7498+
vector_store = await async_client.vector_stores.create(name='test-file-search-stream')
7499+
await async_client.vector_stores.files.create(vector_store_id=vector_store.id, file_id=file.id)
7500+
7501+
import asyncio
7502+
await asyncio.sleep(2)
7503+
7504+
model = OpenAIResponsesModel('gpt-4o', provider=OpenAIProvider(openai_client=async_client))
7505+
agent = Agent(model=model, builtin_tools=[FileSearchTool(vector_store_ids=[vector_store.id])])
7506+
7507+
parts = []
7508+
async with agent.run_stream('Where is the Eiffel Tower according to my files?') as result:
7509+
async for part in result.stream_responses(debounce_by=None):
7510+
parts.append(part)
7511+
output = await result.get_output()
7512+
7513+
assert len(parts) > 0
7514+
assert 'Paris' in output or 'France' in output or 'paris' in output.lower()
7515+
7516+
finally:
7517+
import os
7518+
os.unlink(test_file_path)
7519+
if 'file' in locals():
7520+
await async_client.files.delete(file.id)
7521+
if 'vector_store' in locals():
7522+
await async_client.vector_stores.delete(vector_store.id)
7523+
await async_client.close()
7524+
7525+
74407526
# Unit tests below validate the parsing logic.
74417527

74427528

0 commit comments

Comments
 (0)