Skip to content

feat: Send PDF and Office files to Responses API models end-to-end - #3200

Merged
EItanya merged 10 commits into
kagent-dev:mainfrom
danielle-mustillo:danielle-mustillo/pdf-office-files
Oct 19, 2026
Merged

EItanya merged 10 commits into
kagent-dev:mainfrom
danielle-mustillo:danielle-mustillo/pdf-office-files

Conversation

@danielle-mustillo

@danielle-mustillo danielle-mustillo commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

🤖 written by Claude (start)

Problem

Models did not get the contents of PDF and Office files, or of files at a link.

  • A PDF or Office file sent as bytes became a "could not be read as text" note, also on Responses API models that can read it.
  • The chat UI did not accept PDF or Office files.
  • A file link typed in the chat stayed plain text, so a model that can fetch files did not get the file.
  • An A2A url part reached only Gemini and Responses API models. Other models dropped it with no error.

Resolution

  • A Responses API model (apiFormat: responses) reads PDF, .docx, .xlsx and .pptx files sent as bytes.
  • The chat UI accepts these four file types.
  • The chat also sends a typed link to a known file type as a url part, unless the host is an IP address or a LAN name. Responses API and Gemini models fetch the file.
  • Other models get a url part as its bare URL in the text, so the agent's fetch tool can read it.

User Experience

A kagent UI user can now attach report.pdf, or type Summarize https://example.com/q3-report.pdf, and a Responses API agent answers from the file's contents.

A kagent A2A user can now send a url part to any model. Models that cannot fetch files get the URL as text and do not drop it.

Changes

The user sends Responses API model Gemini Other models
PDF, .docx, .xlsx or .pptx as bytes the model reads the file (new 🆕) "could not be read" note "could not be read" note
Text, CSV, JSON or image as bytes unchanged unchanged unchanged
Typed link to a known file type the model fetches the file (new 🆕) the model fetches the file (new 🆕) unchanged: the URL stays in the text
A2A url part the model fetches the file the model fetches the file bare URL in the text (new 🆕)
Link from an older turn bare URL in the text (new 🆕) bare URL in the text (new 🆕) bare URL in the text
Files as bytes over 50 MB in one chat newest files sent, older ones a note (new 🆕) unchanged unchanged

Limits:

  • A bad link fails only the turn that sends it. A follow-up about that link does not see the file again.
  • A damaged file fails each later turn until the 50 MB cap drops it.
  • Gemini can ignore a link to a type it does not document, such as .docx. It gives no error.
  • A GitHub blob/ link sends the web page's HTML, not the file.
  • Azure documents only PDF.

Tested

Kind cluster at a6704386, Azure OpenAI gpt-4.1-mini. A Responses API agent read the four types, .txt and a PDF link. A Chat Completions agent got the note, and with a fetch tool it fetched the typed link.


🤖 written by Claude (end)

A Responses API model now gets PDF, Word, Excel and PowerPoint bytes as an
input_file part, and the chat UI accepts these types. Other models still get
a "could not be read" note. A link that the model cannot send now fails the
turn it was sent on with an error that names the link.

Signed-off-by: Danielle Mustillo <danielle.mustillo@solo.io>
@github-actions github-actions Bot added the enhancement New feature or request label Oct 14, 2026
@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels Oct 14, 2026
@danielle-mustillo danielle-mustillo self-assigned this Oct 14, 2026
@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels Oct 14, 2026
The model does not see input_file's filename, so a text part before the
file names it, as it already did for links.

Signed-off-by: Danielle Mustillo <danielle.mustillo@solo.io>
The chat sends each http(s) link to a known file type in the message text
also as a url part, with no chip. A model that reads links (Responses API,
Gemini) fetches the file. For other models the runtime drops the part,
since its URL is in the text, or replaces it with the bare URL, so the
agent's fetch tool can read it. This replaces the error for a link the
model cannot read.

Signed-off-by: Danielle Mustillo <danielle.mustillo@solo.io>
@danielle-mustillo
danielle-mustillo force-pushed the danielle-mustillo/pdf-office-files branch from a670438 to 90e9d5d Compare October 15, 2026 01:40
The model reads the message as markdown, so links are now found the same
way: bare URLs, <autolinks> and [markdown](links), but none inside code.
A URL that ends in a bracket or a full stop keeps the right end.

Tests cover the composer, the chip rules and the parser cases.

Signed-off-by: Danielle Mustillo <danielle.mustillo@solo.io>
…rn step

filesToText copies only the user turns that change, and turnFilesToText
decides each part. The comments now say which parts are left as they are.

Signed-off-by: Danielle Mustillo <danielle.mustillo@solo.io>
@danielle-mustillo

This comment has been minimized.

@danielle-mustillo

danielle-mustillo commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor Author

Before/after on a kind cluster with an Azure OpenAI gpt-4.1-mini agent (apiFormat: responses). Base b42a6c53, head 3e437e3c.

  • Composer: PDF and .docx now stage as chips. Before, both were "not a supported file type".
  • Attached files: the model now reads both files and gives each secret word. Before, it never got the files and asked for them.
  • Link in the text: the model now reads a linked PDF and quotes its title. Before, the link stayed text and the model asked to download it.
pr-3200-reel.mp4

Before: the composer rejects PDF and .docx

before-composer-rejects-files

After: the composer stages PDF and .docx

after-composer-stages-files

Before: the model asks for the files

before-reply-without-files

After: the model reads each secret word

after-reply-reads-files

Before: the model asks to download the link

before-reply-link-as-text

After: the model quotes the linked PDF

after-reply-reads-link

🤖 written by Claude

@danielle-mustillo
danielle-mustillo marked this pull request as ready for review October 15, 2026 03:47
@danielle-mustillo danielle-mustillo changed the title feat: Send PDF and Office files to Responses API models feat: Send PDF and Office files to Responses API models end-to-end Oct 15, 2026
@danielle-mustillo danielle-mustillo changed the title feat: Send PDF and Office files to Responses API models end-to-end feat: Send PDF and Office files to Responses API models end-to-end Oct 15, 2026
@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels Oct 15, 2026
…PI models

Also assert OpenAIModel implements FileSender, and stop the mock chat client
from showing a chip for a link in the text.

Signed-off-by: Danielle Mustillo <danielle.mustillo@solo.io>
…he link media type helper

Signed-off-by: Danielle Mustillo <danielle.mustillo@solo.io>
Signed-off-by: Danielle Mustillo <danielle.mustillo@solo.io>
@danielle-mustillo
danielle-mustillo force-pushed the danielle-mustillo/pdf-office-files branch from 0c5afed to 133c20a Compare October 15, 2026 14:34

@Charlesthebird Charlesthebird left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This automated review is a first pass. A manual review will follow once the blockers are addressed.


Blocks merge: the UI turns every typed link with a known file extension into a url part, and Responses API models fetch it again on every turn. One unreachable or non-file link then breaks the whole chat.

⚠️ What I checked
  • ✅ Go tests in fileextract, models and a2a
  • ✅ UI tests for attachments, a2aGrpcChatClient, useChat and ChatComposer
  • ✅ Non-Responses adapters (Anthropic, Bedrock, Ollama) get url parts as bare URL text
  • ✅ filesToText copies changed turns instead of mutating session history
  • Links in textLinks: internal hosts, .md/.yaml blob pages
  • Replay of url parts and file bytes on later turns

🤖 written by Claude

Comment thread ui/src/api/chat/attachments.ts Resolved
Comment thread go/adk/pkg/models/openai_responses.go Resolved
Comment thread go/adk/pkg/models/openai_responses.go Resolved
Links in older user turns are now sent as bare URL text, for all models.
A tool result no longer counts as the newest user turn, so a link stays a
file for the whole turn that sends it.

Responses API models get at most 50 MB of files per request, newest first.
Older files over the cap become a note. A PDF must look like a PDF and an
Office file like a zip, else it gets the "could not be read" note.

Signed-off-by: Danielle Mustillo <danielle.mustillo@solo.io>
The provider fetches a link itself and cannot reach these hosts.

Signed-off-by: Danielle Mustillo <danielle.mustillo@solo.io>
@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels Oct 16, 2026
Comment thread go/adk/pkg/models/openai_responses.go Resolved

@Charlesthebird Charlesthebird left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for adding this and including the screenshots and video!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

⌂ 换网址