Responses PDF Input URL

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.

pdf_input_url.py
"""
Openai Pdf Input Url
====================

Cookbook example for `openai/responses/pdf_input_url.py`.
"""

from agno.agent import Agent
from agno.db.postgres import PostgresDb
from agno.media import File
from agno.models.openai.responses import OpenAIResponses

# ---------------------------------------------------------------------------
# Create Agent
# ---------------------------------------------------------------------------

# Setup the database for the Agent Session to be stored
db_url = "postgresql+psycopg://ai:ai@localhost:5532/ai"
db = PostgresDb(db_url=db_url)

agent = Agent(
    model=OpenAIResponses(id="gpt-5.2"),
    db=db,
    tools=[{"type": "file_search"}, {"type": "web_search_preview"}],
    markdown=True,
)

agent.print_response(
    "Summarize the contents of the attached file and search the web for more information.",
    files=[File(url="https://agno-public.s3.amazonaws.com/recipes/ThaiRecipes.pdf")],
)

# Get the stored Agent session, to check the response citations
session = agent.get_session()
if session and session.runs and session.runs[-1].citations:
    print("Citations:")
    print(session.runs[-1].citations)

# ---------------------------------------------------------------------------
# Run Agent
# ---------------------------------------------------------------------------

if __name__ == "__main__":
    pass

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.

openai_file_search.py
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.

pdf_search_current.py
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
import httpx

download = httpx.get(
    "https://agno-public.s3.amazonaws.com/recipes/ThaiRecipes.pdf",
    timeout=30.0,
    follow_redirects=True,
)
download.raise_for_status()
pdf_bytes = download.content

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.

The current example focuses on PDF search and uses in-memory history. The original source also enables web search and stores a session in Postgres. File-only annotations can be absent from Agno's result.citations on this revision; the original conditional print is not a completeness check for file citations.

Run the Example

Set up your virtual environment

uv venv --python 3.12
source .venv/bin/activate

Install dependencies

uv pip install -U agno openai httpx

Export 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.py

Full source: cookbook/90_models/openai/responses/pdf_input_url.py