Skip to content

[UI/Artifacts] Artifacts saved via InvocationContext are not detected by ADK Web UI (unlike Tool/Callback contexts) #4148

@NoelTW

Description

@NoelTW

Describe the bug

I have noticed an inconsistency in how the ADK Web UI detects artifacts depending on the execution context.

When using save_artifact within a Tool context or Callback context, the ADK Web UI correctly detects the new artifact and displays the corresponding UI elements (e.g., artifact diffs or previews).

However, when implementing a custom agent (inheriting from BaseAgent) and manually saving an artifact using ctx.artifact_service.save_artifact within _run_async_impl (InvocationContext), the Web UI does not detect or display the artifact. Although the artifact is successfully saved to the backend service and the content is returned in the Event, the UI treats it as a standard message without the specific artifact UI features.

To Reproduce

Steps to reproduce the behavior:

  1. run adk web on file
  2. Type any text to test
  3. You should see the file is stored in artifact
All artifacts: ['test.jpg']
import base64
import uuid
from typing import AsyncGenerator

from google.adk.agents import BaseAgent, InvocationContext
from google.adk.artifacts import InMemoryArtifactService
from google.adk.events.event import Event
from google.adk.apps import App
from google.adk.runners import Runner
from google.adk.sessions import InMemorySessionService
from google.genai import types
from typing_extensions import override


class ImageAgent(BaseAgent):
    name: str

    def __init__(self, name: str):
        super().__init__(name=name)

    @override
    async def _run_async_impl(
        self, ctx: InvocationContext
    ) -> AsyncGenerator[Event, None]:
        # assert ctx.artifact_service is not None
        assert ctx.artifact_service is not None
        # Save artifact if it doesn't exist
        artifact_id = "test.jpg"
        # Use a base64 encoded 1x1 pixel JPEG image for reproducibility without external files
        image_b64 = (
            "/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAP////////////////////////////////////"
            "///////////////////////////////////////////////////wgALCAABAAEBAREA/8Q"
            "AFBABAAAAAAAAAAAAAAAAAAAAAP/aAAgBAQABPxA="
        )
        image_data = base64.b64decode(image_b64)

        part = types.Part(
            inline_data=types.Blob(data=image_data, mime_type="image/jpeg")
        )

        if artifact_id not in await ctx.artifact_service.list_artifact_keys(
            app_name=ctx.app_name, user_id=ctx.user_id, session_id=ctx.session.id
        ):
            await ctx.artifact_service.save_artifact(
                app_name=ctx.app_name,
                user_id=ctx.user_id,
                session_id=ctx.session.id,
                filename=artifact_id,
                artifact=part,
            )

        artifact = await ctx.artifact_service.load_artifact(
            app_name=ctx.app_name,
            user_id=ctx.user_id,
            session_id=ctx.session.id,
            filename=artifact_id,
        )

        processed_parts = [
            types.Part(
                text=f"[Retrieved Artifact] Below is the content of artifact ID : {artifact_id}"
            ),
            artifact,
        ]

        print(
            "All artifacts:",
            await ctx.artifact_service.list_artifact_keys(
                app_name=ctx.app_name, user_id=ctx.user_id, session_id=ctx.session.id
            ),
        )

        yield Event(
            id=str(uuid.uuid4()),
            author=self.name,
            partial=False,
            content=types.Content(parts=processed_parts),
        )


image_agent = ImageAgent(name="image_agent")


app = App(name="image_agent", root_agent=image_agent)

Expected behavior
The ADK Web UI should recognize that an artifact has been generated/saved during the agent's execution and display it (e.g., in the artifact panel or as a recognized attachment), similar to how it behaves when a Tool returns an artifact.

Screenshots

Image

Desktop (please complete the following information):

  • OS: Linux
  • Python version: 3.12.12
  • ADK version: 1.22.1

Model Information:

  • Are you using LiteLLM: No

Additional context
Is there a specific metadata field (e.g., artifact_ids) that should be attached to the Event object to notify the UI about the new artifact? Or is this a feature gap for custom agents?

Metadata

Metadata

Assignees

No one assigned

    Labels

    web[Component] This issue will be transferred to adk-web

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions