Attachments
Attach PDFs, audio, text, video, URLs and Path objects to an agent β no silent drops
Pass any file β image, PDF, audio, text, video, a URL, a data: URI, or a pathlib.Path β to agent.start(...) and the router turns it into a model-visible part or tells you why it could not.
from praisonaiagents import Agent
agent = Agent(instructions="Describe what you are given.", llm="gpt-4o")
agent.start("What is in this image?", attachments=["photo.png"])An attachment is never dropped silently: it either becomes a content part the model sees, or it raises, or it warns and leaves a marker part in the prompt so the model can say it answered without the file.
Quick Start
- Attach an image at start()
python from praisonaiagents import Agent agent = Agent(instructions="Describe images.", llm="gpt-4o") agent.start("What landmark is this?", attachments=["photo.png"]) - Attach a PDF at chat()
python from praisonaiagents import Agent agent = Agent(instructions="Summarise documents.", llm="gpt-4o") agent.chat("Summarise this report.", attachments=["report.pdf"])A PDF-capable model receives the document as a
filepart; a non-PDF model gets locally-extracted text, labelled as extracted. - Mix file types in one list
python from pathlib import Path from praisonaiagents import Agent agent = Agent(instructions="Analyse everything I send.", llm="gpt-4o") agent.chat( "Review these together.", attachments=[ Path("report.pdf"), "clip.mp3", "https://example.com/photo.png", ], )Each entry is routed independently by its type β a
Path, a local file, and a URL in the same call.
How the router decides
The router inspects each attachment’s extension, checks the target model’s capability, and picks one of three outcomes.
What each file type does
The outcome depends on the file type and the target model’s capability.
| File type | Vision model | Non-vision model | Audio-capable | Non-audio-capable | PDF-capable | Non-PDF-capable |
|---|---|---|---|---|---|---|
| Image (jpg/jpeg/png/gif/webp) | passed as image_url | passed as image_url (unchanged) | β | β | β | β |
| β | β | β | β | passed as file part | text extracted locally, part labelled “extracted” | |
| Audio (mp3/wav/m4a/ogg/opus/flac/aac) | β | β | passed as input_audio | warn + marker part injected | β | β |
Text (.txt, .md, code, configβ¦) | β | β | β | β | β | inlined as a text part [Attachment: name] <content> |
| Video (mp4/mov/β¦) | β | β | β | warn + marker part injected | β | β |
| URL (image) | image_url (URL passed straight through) | image_url (unchanged) | β | β | β | β |
URL (non-image, e.g. .pdf) | β | β | β | β | β | warn + marker part (not downloaded) |
data: URI (image) | passed through | passed through | β | β | β | β |
data: URI (non-image) | β | β | β | β | β | warn + marker part |
pathlib.Path | resolved to a path and dispatched by its extension, exactly like a string path |
What raises vs what warns
Two failure classes are treated differently on purpose.
from praisonaiagents import Agent
from praisonaiagents.agent.attachments import AttachmentError
agent = Agent(instructions="Describe files.", llm="gpt-4o")
# Missing path, a directory, an unreadable file, or an unsupported
# Python type all raise AttachmentError β a caller mistake with no
# useful degraded answer.
try:
agent.start("Describe it", attachments=["does-not-exist.png"])
except AttachmentError as e:
print(e)
# does-not-exist.png: no such file (attachment paths are resolved
# relative to the current working directory '/home/you/project')from praisonaiagents import Agent
# gpt-4o cannot accept audio. The file is fine; the model can't ingest it.
# The router logs a warning AND injects a marker part so the model can
# answer and say the audio was withheld β the run still succeeds.
agent = Agent(instructions="Transcribe or describe.", llm="gpt-4o")
agent.start("What does the clip say?", attachments=["clip.mp3"])
# WARNING ... model 'gpt-4o' cannot accept audio input (use an
# audio-capable model such as gpt-4o-audio-preview or a Gemini model,
# or transcribe the file first)
# Prompt gains: [Attachment omitted: clip.mp3 - audio was not sent ...]Capability helpers
Check what a model can ingest before you attach β the same probes the router uses.
from praisonaiagents.llm.model_capabilities import (
supports_vision,
supports_pdf_input,
supports_audio_input,
)
supports_pdf_input("gpt-4o") # False
supports_pdf_input("gemini/gemini-1.5-pro") # True
supports_audio_input("gpt-4o-audio-preview") # True
supports_vision("gpt-4o") # TrueError handling
AttachmentError subclasses ValueError, so any existing except ValueError around chat() keeps catching it.
from praisonaiagents import Agent
from praisonaiagents.agent.attachments import AttachmentError
agent = Agent(instructions="Batch describe files.", llm="gpt-4o")
paths = ["a.png", "missing.png", "b.png"]
for path in paths:
try:
print(agent.chat("Describe this.", attachments=[path]))
except AttachmentError as e:
# Message names the path AND the current working directory:
# missing.png: no such file (attachment paths are resolved
# relative to the current working directory '/home/you/project')
print(f"Skipping {path}: {e}")Best Practices
Prefer Path for local files
Pass pathlib.Path("report.pdf") for local files. The router resolves it and dispatches by extension β earlier releases silently dropped Path objects because an isinstance(str) check was False.
Let the router pick the encoding
Pass the raw path or URL β do not pre-encode to a data: URI yourself. The router chooses the right part shape (image_url, file, input_audio, or text) per file type and model capability.
Keep large media out of chat history
Send big images and documents via attachments= rather than embedding them in the prompt string, so their bytes never fill the conversation history.
Catch AttachmentError in batch jobs
Wrap chat() / start() in except AttachmentError as e when processing a list of files, so one bad path does not abort the whole batch.