Skip to content

Add attachment support for agui - #648

Open
malithaYL wants to merge 4 commits into
feature/523-agui-1-streaming-contractfrom
feature/523-agui-2-attachment-source-forms
Open

Add attachment support for agui#648
malithaYL wants to merge 4 commits into
feature/523-agui-1-streaming-contractfrom
feature/523-agui-2-attachment-source-forms

Conversation

@malithaYL

Copy link
Copy Markdown
Contributor

Description

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Refactoring (no functional changes)
  • Performance improvement
  • Test update
  • CI/CD update
  • Other (please describe):

Related Issues

Fixes #
Relates to #

Changes Made

Testing

  • Unit tests pass locally
  • Integration tests pass locally
  • Manual testing completed
  • New tests added for changes

Checklist

  • My code follows the project's style guidelines
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published

Screenshots (if applicable)

Additional Notes

@malithaYL
malithaYL requested a review from amithad as a code owner August 19, 2026 11:06
@malithaYL
malithaYL force-pushed the feature/523-agui-2-attachment-source-forms branch from 0e012c9 to 2aa2e37 Compare August 19, 2026 12:12
@amithad
amithad requested a lite review from Copilot August 20, 2026 03:02

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

This PR updates the core MultimodalPreHook to correctly interpret different attachment “source forms” (bare base64, base64 data: URIs, and remote references like http(s):// / s3://) so that only truly consumable attachments are described + stored, while remote/unsupported forms are preserved and passed through to downstream adapters.

Changes:

  • Added source-form resolution logic to split base64 data: URIs into (payload, mime_type) and to detect remote references that must not be stored/described.
  • Updated request filtering so only attachments actually consumed by the hook are stripped; declined attachments remain in the request list.
  • Added a comprehensive pytest suite covering consumable vs remote/declined attachment behaviors and edge cases (including scheme/header case folding).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
ak-py/src/agentkernel/core/multimodal/hooks.py Adds attachment source parsing/classification and updates filtering to only remove attachments the hook consumes.
ak-py/tests/test_multimodal_source_forms.py Adds regression tests for the supported attachment source forms and ensures declined attachments are retained.
Suppressed comments (1)

ak-py/src/agentkernel/core/multimodal/hooks.py:330

  • _resolve_source() treats data:*;base64, (base64 marker present but empty payload) as non-consumable because not payload is true, which makes the hook retain an attachment that has no bytes. This is inconsistent with the stated behavior for no-data attachments and can bubble invalid inputs into adapters. Consider recognizing an empty base64 payload as a consumable source with empty data so it can be dropped consistently upstream.
            header, _, payload = source.partition(",")
            if not payload or not header.lower().endswith(";base64"):
                return source, declared_mime or default_mime, False
            uri_mime = header[len("data:") :].split(";", 1)[0].lower()
            return payload, uri_mime or declared_mime or default_mime, True

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread ak-py/src/agentkernel/core/multimodal/hooks.py Outdated
Comment thread ak-py/tests/test_multimodal_source_forms.py

@amithad amithad left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Reviewed against spec #523 §8 (docs/specs/523-ag-ui-support/ on the base branch), plan.md iteration 2, and the ak-dev architecture/code-quality/testing skills. Overall: this conforms to the spec and is well built.

Spec conformance (dimension checked requirement by requirement):

  • §8a implemented: data: URIs split into payload + their own mime type (the URI wins over the declared mime and the image/jpeg fallback), bare base64 unchanged, http/https/s3 classified not consumable and neither described nor stored.
  • §8b implemented: _process_attachments returns the consumed set keyed by id(req), the filter loop retains declined attachments, and AgentRequestAttachmentRef is still always stripped. The retention tests (test_*_is_retained_*) cover exactly the half the spec flags as easy to miss.
  • Verified the spec's factual claims: retained URL/data: requests are handled downstream by the OpenAI, ADK, and Pydantic AI adapters; LangGraph/CrewAI/smolagents ignore attachment requests entirely, which matches what those adapters already do with multimodal.enabled: false.
  • File scope matches plan.md iteration 2 exactly (hooks.py + the new test file); docs are correctly deferred to PR 7 per the plan.
  • Dropping the old getattr(req, "name", ...) fallback is safe: name is a required field on both request models.

Findings:

  • 2 inline [question]s on spec-vs-implementation deltas (see comments).
  • [suggestion] (not anchorable to this diff): the thread-enabled path still has the bug this PR fixes. ConversationThreadManager.store_attachments (integration/thread/manager.py:188-191) stores req.image_data/req.file_data verbatim with the image/jpeg/application/octet-stream fallback, so with thread mode a URL or data: URI is stored corrupted before the hook ever sees it, and the hook's ref path then describes those corrupted bytes. design.md's claim that "after this, all five forms do [work]" therefore holds only for the thread-off path. Not a regression from this PR and consistent with spec §8's stated scope, but worth a follow-up issue, and PR 7's advanced/multimodal.md update should state which path each source form works on.
  • [nit] PR hygiene: the PR description is the unfilled template, with no linked issue. Please fill it in and link #523 (per the repo PR guidelines), and consider a conventional-commits title, e.g. feat: support all attachment source forms in MultimodalPreHook (#523).
  • [nit] optional cleanups, non-blocking: the if TYPE_CHECKING: pass block in hooks.py is dead and could be dropped while this PR is touching the imports; the multimodal_enabled fixture and _run_hook helper are now duplicated verbatim with test_thread_multimodal_hook.py and could move to a shared conftest/helper.

No existing review feedback to dedupe against. CI is fully green.

Comment thread ak-py/src/agentkernel/core/multimodal/hooks.py
Comment thread ak-py/src/agentkernel/core/multimodal/hooks.py Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants