Skip to content

feat(chat): file upload artifacts#2077

Open
dimetron wants to merge 2 commits into
kagent-dev:mainfrom
dimetron:feature/file-upload-artifacts
Open

feat(chat): file upload artifacts#2077
dimetron wants to merge 2 commits into
kagent-dev:mainfrom
dimetron:feature/file-upload-artifacts

Conversation

@dimetron

@dimetron dimetron commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Summary

End-to-end file upload / download in kagent chat for the Go ADK runtime, riding the existing A2A message/SSE channel (no new HTTP API, no CRD field). Files travel inline (base64) as A2A FileParts; agent-produced files are surfaced back as downloadable artifacts. See design/EP-89405-file-upload-artifacts.md.

  • Go ADK runtime: wire the in-memory ArtifactService into the runner; enable SaveInputBlobsAsArtifacts; emit agent-saved artifacts as A2A FilePart events driven by the ArtifactDelta signal; register load_artifacts + a new save_artifact tool; add a fileextract package (PDF + others) so non-multimodal models still receive document text.
  • Python runtime: mirror text extraction (_file_extract.py) and inject into the OpenAI path.
  • UI: attach multiple files with type/size validation (images, PDF, text/md, CSV, JSON; 10 MB cap), render image thumbnails + downloadable file chips in both user and agent bubbles.
  • Edge: add client_max_body_size (default 50m) to the UI nginx config so uploads aren't rejected at the proxy.

Changes

  • go/adk/pkg/{runner,agent,a2a,tools,fileextract,models}/… — artifact wiring, save/load tools, extraction, A2A surfacing (+ unit tests)
  • go/core/test/e2e/file_upload_test.go — e2e upload round trip (a2a-go v2)
  • python/packages/kagent-adk/…_file_extract.py, _openai.py injection (+ tests)
  • ui/src/lib/fileUpload.ts, ui/src/components/chat/{ChatInterface,ChatMessage,FileAttachment}.tsx, messageHandlers.ts (+ tests)
  • helm/kagent/{files/nginx.conf,values.yaml,tests/ui-nginx-configmap_test.yaml}clientMaxBodySize
  • design/EP-89405-file-upload-artifacts.md — design EP

Test plan

  • make -C go test (adapter, agent, executor, artifacts, save_artifact, fileextract, openai_adk)
  • make -C go e2eTestE2EFileUploadGoADKAgent round trip on kind
  • Python: uv run pytest (test_file_extract.py, test_openai.py)
  • UI: unit tests (fileUpload, messageHandlers, FileAttachment, ChatMessage)
  • Helm: ui-nginx-configmap_test.yaml
  • Manual: attach image/PDF/CSV in chat, confirm thumbnails/chips + agent-produced artifact downloads

Copilot AI review requested due to automatic review settings June 23, 2026 15:00
@github-actions github-actions Bot added enhancement New feature or request enhancement-proposal Indicates that this PR is for an enhancement proposal labels Jun 23, 2026
@dimetron dimetron force-pushed the feature/file-upload-artifacts branch from b4fe245 to 90158e2 Compare June 23, 2026 15:06
@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels Jun 23, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds end-to-end file upload/download support in kagent chat by transporting files inline as A2A FileParts (base64), persisting uploads as ADK artifacts, surfacing agent-produced artifacts back to the UI as downloadable attachments, and extracting non-image document text so non-multimodal models can still “read” uploads.

Changes:

  • UI: multi-file attach + validation, preserve/render FileParts (thumbnails + download chips), and ensure artifacts survive reload by surfacing task.artifacts.
  • Go ADK runtime: wire in-memory ArtifactService, enable SaveInputBlobsAsArtifacts, emit saved artifacts via ArtifactDelta, and add save_artifact + extraction pipeline.
  • Python runtime: extract non-image uploads to text and inject into the OpenAI message conversion path.

Reviewed changes

Copilot reviewed 39 out of 40 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
ui/src/lib/messageHandlers.ts Preserves and surfaces file parts from messages, artifact updates, and persisted task artifacts.
ui/src/lib/fileUpload.ts Adds client-side allowlist/size validation and File→A2A FilePart conversion.
ui/src/lib/tests/messageHandlers.test.ts Tests file part helpers + artifact/task reload surfacing behavior.
ui/src/lib/tests/fileUpload.test.ts Tests allowlist, size validation, and base64 FilePart creation.
ui/src/components/chat/FileAttachment.tsx New UI component to render image thumbnails or download chips for file parts.
ui/src/components/chat/ChatMessage.tsx Renders file attachments in user/agent bubbles and allows file-only messages.
ui/src/components/chat/ChatInterface.tsx Adds attach UI, pending file chips, validation, and outgoing message FileParts.
ui/src/components/chat/tests/FileAttachment.test.tsx Tests thumbnail/chip rendering and download link creation.
ui/src/components/chat/tests/ChatMessage.test.tsx Tests file rendering in both bubbles, including file-only messages.
specs/file-upload/summary.md Documents the overall plan and decisions for file upload/artifacts.
specs/file-upload/rough-idea.md Records the initial concept for the feature.
specs/file-upload/research/go-adk-artifacts.md Background/research on ADK artifact subsystem and integration approach.
specs/file-upload/requirements.md Captures requirements/decisions for transport, limits, tools, and testing.
specs/file-upload/PROMPT.md Implementation prompt/checklist for the feature.
specs/file-upload/plan.md Step-by-step plan for wiring, emission, UI support, and tests.
specs/file-upload/design.md Detailed design/architecture, data flow, and acceptance criteria.
python/packages/kagent-adk/src/kagent/adk/models/_openai.py Injects extracted text for non-image inline uploads into OpenAI messages.
python/packages/kagent-adk/src/kagent/adk/models/_file_extract.py New text-extraction helper for uploaded files via markitdown.
python/packages/kagent-adk/tests/unittests/models/test_openai.py Adds tests ensuring non-image uploads become user text.
python/packages/kagent-adk/tests/unittests/models/test_file_extract.py Adds tests for extraction behavior and supported/unsupported types.
python/packages/kagent-adk/pyproject.toml Adds markitdown dependency for document extraction.
helm/kagent/values.yaml Adds UI nginx/body-size settings + oauth2-proxy timeout/cookie refresh knobs.
helm/kagent/files/nginx.conf Sets client_max_body_size from Helm values.
helm/kagent/tests/ui-nginx-configmap_test.yaml Tests default and overridden client_max_body_size rendering.
go/go.mod Adds github.com/tsawler/tabula for document extraction.
go/go.sum Updates sums for new Go dependencies.
go/core/test/e2e/mocks/invoke_file_upload_agent.json Adds mock OpenAI response fixture for e2e upload test.
go/core/test/e2e/file_upload_test.go Adds e2e test for Go ADK inline file upload path.
go/adk/pkg/runner/adapter.go Wires ADK in-memory artifact service into runner config.
go/adk/pkg/runner/adapter_test.go Tests that runner config includes a non-nil artifact service.
go/adk/pkg/agent/agent.go Registers load_artifacts and new save_artifact tool for agents.
go/adk/pkg/agent/agent_test.go Tests tool registration for load_artifacts and save_artifact.
go/adk/pkg/agent/createllm_test.go Ensures agent runner wiring includes artifact service in tests.
go/adk/pkg/a2a/executor.go Adds inbound file size guard, enables SaveInputBlobsAsArtifacts, emits artifacts.
go/adk/pkg/a2a/executor_test.go Tests inbound persistence and oversized-upload rejection behavior.
go/adk/pkg/a2a/artifacts.go Adds size guard helpers and artifact emission logic.
go/adk/pkg/a2a/artifacts_test.go Tests size guard and artifact emission conversion to A2A FilePart.
go/adk/pkg/tools/save_artifact_tool.go Adds save_artifact tool for agent-produced downloadable artifacts.
go/adk/pkg/tools/save_artifact_tool_test.go Unit tests tool validation, base64 handling, size limits, and save behavior.
go/adk/pkg/fileextract/fileextract.go Adds Go-side extraction of non-image uploads into text/markdown.
go/adk/pkg/fileextract/pdf.go Adds PDF extraction with Type3 font handling and fallback path.
go/adk/pkg/fileextract/fixture_test.go Adds Type3 PDF fixture generator for extraction tests.
go/adk/pkg/fileextract/fileextract_test.go Tests MIME/ext routing and extraction behavior (HTML + Type3 PDF).
go/adk/pkg/models/openai_adk.go Injects extracted non-image file text into OpenAI request path.
go/adk/pkg/models/openai_adk_test.go Tests that non-image inline uploads become user text for OpenAI.
DEVELOPMENT.md Documents upload transport, limits, extraction behavior, and tooling.
design/EP-89405-file-upload-artifacts.md EP-level design/implementation writeup and acceptance criteria mapping.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread ui/src/lib/messageHandlers.ts
Comment thread ui/src/lib/messageHandlers.ts Outdated
Comment thread helm/kagent/values.yaml Outdated
Comment thread design/EP-89405-file-upload-artifacts.md Outdated
@dimetron dimetron force-pushed the feature/file-upload-artifacts branch from 90158e2 to 8ff8380 Compare June 23, 2026 15:27
dimetron added 2 commits June 23, 2026 22:53
Enable end-to-end file upload/download for the Go ADK runtime using the ADK
in-memory artifact service over the existing A2A message channel:

- wire ArtifactService into the runner; register the load_artifacts and
  save_artifact tools for agents
- executor persists inbound uploads (SaveInputBlobsAsArtifacts) and emits
  agent-saved artifacts as A2A file parts (driven by ArtifactDelta)
- file extraction for models without native file input (Go: tabula,
  Python: markitdown) covering PDF/Office documents
- nginx/UI client_max_body_size with a per-file 10 MB limit
- chat UI: attach files, render image thumbnails and downloadable chips
- Go + UI unit tests and a Go ADK e2e upload round trip

Extracted from master-097 (771668d2); unrelated changes excluded.

Signed-off-by: Dmytro Rashko <dmitriy.rashko@amdocs.com>
Document the design and required changes for end-to-end file upload/
download in chat: ADK artifact wiring, A2A FilePart surfacing,
save/load artifact tools, Go+Python text extraction, UI attach/render,
and the nginx client_max_body_size limit.

Signed-off-by: Dmytro Rashko <dmitriy.rashko@amdocs.com>
@dimetron dimetron force-pushed the feature/file-upload-artifacts branch from 8ff8380 to 5acac27 Compare June 23, 2026 20:54
@peterj

peterj commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator

is it expected that I can download the file again after I navigate away from the session? I see the spec says that data is lost on pod restart - not sure if that also means on browser refresh

The download icon is missing:
Screenshot 2026-06-23 at 2 21 58 PM

return nil, fmt.Errorf("failed to save artifact %q: %w", name, err)
}

return map[string]any{

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.

couldn't we return the resp from artifacts.Save here?

Comment thread ui/src/lib/fileUpload.ts

// File extensions allowed as a fallback when the browser reports an empty MIME.
export const ALLOWED_EXTENSIONS = [
".md",

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.

yaml? xml?

Comment thread DEVELOPMENT.md
### File uploads (artifacts)

The chat UI supports uploading files to agents running on the **Go ADK runtime**
(Python runtime is out of scope). Files travel inline (base64) over the existing

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.

is this still correct that python is out of scope?

@mesutoezdil

Copy link
Copy Markdown
Contributor

artifacts.go:60 -- checkinboundfilesizes fully decodes every base64 payload just to measure its size. a 9.9 mb upload allocates ~10 mb on the heap for a size check that could be avoided with a len(fb.bytes) * 3 / 4 > limit pre-screen.

and save_artifact_tool.go:60 -- only base64.stdencoding is tried. if the llm produces url-safe (-_) or unpadded base64, the tool call fails with a confusing decode error. should it fall back to base64.urlencoding / base64.rawstdencoding?

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

Labels

enhancement New feature or request enhancement-proposal Indicates that this PR is for an enhancement proposal

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants