feat(chat): file upload artifacts#2077
Conversation
b4fe245 to
90158e2
Compare
There was a problem hiding this comment.
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 surfacingtask.artifacts. - Go ADK runtime: wire in-memory
ArtifactService, enableSaveInputBlobsAsArtifacts, emit saved artifacts viaArtifactDelta, and addsave_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.
90158e2 to
8ff8380
Compare
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>
8ff8380 to
5acac27
Compare
| return nil, fmt.Errorf("failed to save artifact %q: %w", name, err) | ||
| } | ||
|
|
||
| return map[string]any{ |
There was a problem hiding this comment.
couldn't we return the resp from artifacts.Save here?
|
|
||
| // File extensions allowed as a fallback when the browser reports an empty MIME. | ||
| export const ALLOWED_EXTENSIONS = [ | ||
| ".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 |
There was a problem hiding this comment.
is this still correct that python is out of scope?
|
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? |

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. Seedesign/EP-89405-file-upload-artifacts.md.ArtifactServiceinto the runner; enableSaveInputBlobsAsArtifacts; emit agent-saved artifacts as A2AFilePartevents driven by theArtifactDeltasignal; registerload_artifacts+ a newsave_artifacttool; add afileextractpackage (PDF + others) so non-multimodal models still receive document text._file_extract.py) and inject into the OpenAI path.client_max_body_size(default50m) 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.pyinjection (+ 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}—clientMaxBodySizedesign/EP-89405-file-upload-artifacts.md— design EPTest plan
make -C go test(adapter, agent, executor, artifacts, save_artifact, fileextract, openai_adk)make -C go e2e—TestE2EFileUploadGoADKAgentround trip on kinduv run pytest(test_file_extract.py,test_openai.py)fileUpload,messageHandlers,FileAttachment,ChatMessage)ui-nginx-configmap_test.yaml