PDF Input File Upload
Upload a PDF to the Gemini Files API, then ask an agent to summarize it and suggest a recipe from it.
In this example, we upload a PDF file to Google GenAI directly and then use it as an input to an agent.
The source checks file presence rather than readiness and enables history without a database. Its large-file auto-upload comment is also inaccurate: the current local-file formatter does not upload PDFs at or above 20 MiB. Use the current adaptation below instead of running the source snapshot unchanged.
"""
In this example, we upload a PDF file to Google GenAI directly and then use it as an input to an agent.
Note: If the size of the file is greater than 20MB, and a file path is provided, the file automatically gets uploaded to Google GenAI.
"""
from pathlib import Path
from time import sleep
from agno.agent import Agent
from agno.media import File
from agno.models.google import Gemini
from google import genai
# ---------------------------------------------------------------------------
# Create Agent
# ---------------------------------------------------------------------------
pdf_path = Path(__file__).parent.joinpath("ThaiRecipes.pdf")
client = genai.Client()
# Upload the file to Google GenAI
upload_result = client.files.upload(file=pdf_path)
# Get the file from Google GenAI
if upload_result and upload_result.name:
retrieved_file = client.files.get(name=upload_result.name)
else:
retrieved_file = None
# Retry up to 3 times if file is not ready
retries = 0
wait_time = 5
while retrieved_file is None and retries < 3:
retries += 1
sleep(wait_time)
if upload_result and upload_result.name:
retrieved_file = client.files.get(name=upload_result.name)
else:
retrieved_file = None
if retrieved_file is not None:
agent = Agent(
model=Gemini(id="gemini-3.7-flash"),
markdown=True,
add_history_to_context=True,
)
agent.print_response(
"Summarize the contents of the attached file.",
files=[File(external=retrieved_file)],
)
agent.print_response(
"Suggest me a recipe from the attached file.",
)
else:
print("Error: File was not ready after multiple attempts.")
# ---------------------------------------------------------------------------
# Run Agent
# ---------------------------------------------------------------------------
if __name__ == "__main__":
passCurrent adaptation
Place ThaiRecipes.pdf beside this script. You can set GOOGLE_FILE_NAME to a previously uploaded file's exact files/... name to reuse it; otherwise this example uploads and removes its own file. Both queries complete before cleanup.
Save this helper as google_files.py beside the runnable example. It accepts a local file or an existing Files API name, waits up to five minutes for ACTIVE, and rejects failed or incomplete uploads. It deletes only files it uploaded itself; an existing file remains owned by its caller. Developer API uploads otherwise expire after 48 hours.
from contextlib import contextmanager
from pathlib import Path
from time import monotonic, sleep
@contextmanager
def ready_file(client, path: Path, existing_name: str | None = None):
existing_name = existing_name or None
if existing_name is None and not path.is_file():
raise FileNotFoundError(path)
uploaded = (
client.files.get(name=existing_name)
if existing_name
else client.files.upload(file=path)
)
owned_name = uploaded.name if existing_name is None else None
try:
deadline = monotonic() + 300
while True:
state = uploaded.state.name if uploaded.state else None
if state == "ACTIVE":
if not uploaded.uri or not uploaded.mime_type:
raise RuntimeError("Active file has no URI or MIME type")
yield uploaded
return
if state == "FAILED":
raise RuntimeError(f"File processing failed: {uploaded.name}")
if state != "PROCESSING" or not uploaded.name:
raise RuntimeError(f"Unexpected file state: {state}")
if monotonic() >= deadline:
raise TimeoutError("File processing exceeded five minutes")
sleep(2)
uploaded = client.files.get(name=uploaded.name)
finally:
if owned_name:
client.files.delete(name=owned_name)from os import environ
from pathlib import Path
from agno.agent import Agent
from agno.db.in_memory import InMemoryDb
from agno.media import File
from agno.models.google import Gemini
from google_files import ready_file
model = Gemini(id="gemini-3.7-flash")
agent = Agent(
model=model,
db=InMemoryDb(),
add_history_to_context=True,
markdown=True,
)
path = Path(__file__).parent / "ThaiRecipes.pdf"
with ready_file(model.get_client(), path, environ.get("GOOGLE_FILE_NAME")) as uploaded:
agent.print_response(
"Summarize this PDF.",
files=[File(external=uploaded)],
stream=True,
)
agent.print_response("Summarize the main point from our conversation.")File(external=...) passes the uploaded file URI and MIME type. InMemoryDb retains this conversation only within the process. File access must remain valid while history containing its URI is reused.
Run the Example
Set up your virtual environment
uv venv --python 3.12
source .venv/bin/activateInstall dependencies
uv pip install -U agno google-genaiExport your Google API key
export GOOGLE_API_KEY="your_google_api_key_here"Run the example
Save the current adaptation as query_uploaded_pdf.py and any helper beside it, then run:
python query_uploaded_pdf.pyFull source: cookbook/90_models/google/gemini/pdf_input_file_upload.py