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.

python
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.

Diagram

type accepted

capability limit

addressing error

πŸ“Ž Attachment

πŸ”€ Router
extension + capability

βœ… Model-visible part

⚠️ Marker part + warn

🚫 raise AttachmentError

Quick Start

  1. 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"])
  2. 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 file part; a non-PDF model gets locally-extracted text, labelled as extracted.

  3. 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.

Diagram

dict content part

http/https/data URI

local path / Path

no / directory / unreadable

yes

image

pdf

audio

text-like

video / unknown

yes

no

yes

no

image URL / data:image

non-image

πŸ“Ž attachment

What is it?

Pass through unchanged

Image?

Exists & is a file?

🚫 AttachmentError
names path + cwd

Extension

βœ… image_url part

Model supports PDF?

Model supports audio?

βœ… inline text part

⚠️ warn + marker part

βœ… file part

⚠️ extract text locally,
label ‘extracted’

βœ… input_audio part

⚠️ warn + marker part

βœ… image_url part

⚠️ warn + marker part

What each file type does

The outcome depends on the file type and the target model’s capability.

File typeVision modelNon-vision modelAudio-capableNon-audio-capablePDF-capableNon-PDF-capable
Image (jpg/jpeg/png/gif/webp)passed as image_urlpassed as image_url (unchanged)β€”β€”β€”β€”
PDFβ€”β€”β€”β€”passed as file parttext extracted locally, part labelled “extracted”
Audio (mp3/wav/m4a/ogg/opus/flac/aac)β€”β€”passed as input_audiowarn + 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 throughpassed throughβ€”β€”β€”β€”
data: URI (non-image)β€”β€”β€”β€”β€”warn + marker part
pathlib.Pathresolved 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.

raises β€” addressing errors
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')
warns β€” capability limits
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.

python
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")                     # True

Error handling

AttachmentError subclasses ValueError, so any existing except ValueError around chat() keeps catching it.

python
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.

Related