Responses PDF Input Local
Query a PDF with an explicit File Search index, bounded indexing checks, and cleanup of owned resources.
With file_search enabled, Agno automatically uploads the attached PDF, creates a vector store, and adds its ID to the tool. The source does not delete those resources, its indexing poll has no deadline, and a failed indexing status does not prevent the model request. Use the controlled example below for bounded indexing and cleanup.
"""
Openai Pdf Input Local
======================
Cookbook example for `openai/responses/pdf_input_local.py`.
"""
from pathlib import Path
from agno.agent import Agent
from agno.media import File
from agno.models.openai.responses import OpenAIResponses
from agno.utils.media import download_file
# ---------------------------------------------------------------------------
# Create Agent
# ---------------------------------------------------------------------------
pdf_path = Path(__file__).parent.joinpath("ThaiRecipes.pdf")
# Download the file using the download_file function
download_file(
"https://agno-public.s3.amazonaws.com/recipes/ThaiRecipes.pdf", str(pdf_path)
)
agent = Agent(
model=OpenAIResponses(id="gpt-5.2"),
tools=[{"type": "file_search"}],
markdown=True,
add_history_to_context=True,
)
agent.print_response(
"Summarize the contents of the attached file.",
files=[File(filepath=pdf_path)],
)
agent.print_response("Suggest me a recipe from the attached file.")
# ---------------------------------------------------------------------------
# Run Agent
# ---------------------------------------------------------------------------
if __name__ == "__main__":
passControlled File Search
Save this helper as openai_file_search.py. It creates one store for one PDF, waits up to 120 seconds for indexing, and yields the store ID only after indexing completes. SDK requests have a 30-second timeout and automatic retries are disabled; this polling budget is not a deadline for the entire agent run.
from contextlib import contextmanager
from time import monotonic, sleep
import sys
from openai import OpenAI
@contextmanager
def indexed_pdf(pdf_bytes: bytes, filename: str = "document.pdf"):
file_id = store_id = None
with OpenAI(timeout=30.0, max_retries=0) as client:
try:
uploaded = client.files.create(
file=(filename, pdf_bytes, "application/pdf"), purpose="assistants"
)
file_id = uploaded.id
store_id = client.vector_stores.create(name="Agno PDF example").id
indexed = client.vector_stores.files.create(
vector_store_id=store_id, file_id=file_id
)
deadline = monotonic() + 120.0
while indexed.status != "completed":
if indexed.status in {"failed", "cancelled"}:
raise RuntimeError(f"PDF indexing {indexed.status}: {indexed.last_error}")
remaining = deadline - monotonic()
if remaining <= 0:
raise TimeoutError("PDF indexing did not complete within 120 seconds")
sleep(min(1.0, remaining))
remaining = deadline - monotonic()
if remaining <= 0:
raise TimeoutError("PDF indexing did not complete within 120 seconds")
indexed = client.vector_stores.files.retrieve(
file_id, vector_store_id=store_id, timeout=min(30.0, remaining)
)
if monotonic() > deadline:
raise TimeoutError("PDF indexing did not complete within 120 seconds")
yield store_id
finally:
# Attempt both deletions, including after indexing or agent failures.
for resource_id, delete in (
(store_id, client.vector_stores.delete),
(file_id, client.files.delete),
):
if resource_id is not None:
try:
delete(resource_id)
except Exception as exc:
print(f"Cleanup failed for {resource_id}: {exc}", file=sys.stderr)The helper deletes only resources it creates. Cleanup failures report the IDs so you can retry deletion. A lost creation response or process termination can leave resources behind; inspect your OpenAI project if the script is interrupted. Deleting a vector store alone does not delete the uploaded File object, so both are handled separately. See OpenAI File Search.
Save the following as pdf_search_current.py beside the helper. It supplies an explicit store ID and does not pass files= to the agent, avoiding another automatic upload. Both turns share an in-memory session; the store remains available until the with block exits.
from agno.agent import Agent
from agno.db.in_memory import InMemoryDb
from agno.models.openai import OpenAIResponses
from openai_file_search import indexed_pdf
from pathlib import Path
pdf_bytes = Path(__file__).with_name("ThaiRecipes.pdf").read_bytes()
with indexed_pdf(pdf_bytes, "ThaiRecipes.pdf") as store_id:
agent = Agent(
model=OpenAIResponses(id="gpt-5.6-luna"),
db=InMemoryDb(),
add_history_to_context=True,
tools=[{"type": "file_search", "vector_store_ids": [store_id]}],
markdown=True,
)
agent.print_response("Search the indexed recipe PDF and summarize its contents.")
agent.print_response("Suggest a recipe from the same PDF.")For small documents that do not need an index, see direct file input.
Place ThaiRecipes.pdf beside the script first. You can download the sample PDF used by the original source.
Run the Example
Set up your virtual environment
uv venv --python 3.12
source .venv/bin/activateInstall dependencies
uv pip install -U agno openai httpxExport your OpenAI API key
export OPENAI_API_KEY="your_openai_api_key_here"Run the example
Save the helper as openai_file_search.py and the controlled example as pdf_search_current.py in the same directory, then run:
python pdf_search_current.pyFull source: cookbook/90_models/openai/responses/pdf_input_local.py