Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

fix(showcase): bundle multimodal sample assets for agno and mastra#6157

Draft
jpr5 wants to merge 1 commit into
mainCopilotKit/CopilotKit:mainfrom
fix/multimodal-missing-demo-filesCopilotKit/CopilotKit:fix/multimodal-missing-demo-filesCopy head branch name to clipboard
Draft

fix(showcase): bundle multimodal sample assets for agno and mastra#6157
jpr5 wants to merge 1 commit into
mainCopilotKit/CopilotKit:mainfrom
fix/multimodal-missing-demo-filesCopilotKit/CopilotKit:fix/multimodal-missing-demo-filesCopy head branch name to clipboard

Conversation

@jpr5

@jpr5 jpr5 commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

⚠️ DEPENDENCY — this PR does not fix agno/mastra on its own

The assets here are committed as plain Git LFS pointers, governed by the repo-root *.png / *.pdf LFS rules, with no per-integration .gitattributes override. LFS tracking stays uniform across the fleet — demo assets are not carved out of LFS per integration.

Integration image builds currently do not fetch LFS: every entry in ALL_SERVICES in .github/workflows/showcase_build.yml is "lfs": false. Until that is fixed, the built agno/mastra images will ship 130-byte pointer stubs, the component's magic-byte guard will reject them, and the D5 multimodal cells stay red.

This PR requires the build-side LFS-fetch fix to land first, or to be merged together with it. Merging this alone changes nothing for agno/mastra.

The earlier RED → GREEN proof in this PR was obtained with real binaries in the image (the .gitattributes override, since removed) and no longer describes what this branch produces. It is retained below, clearly marked SUPERSEDED. No new green proof is claimed, and none can be produced until the build-side fix exists. Re-verification happens once both changes are combined.


What

agno and mastra were the only 2 of 20 showcase integrations with no public/demo-files/ directory ever committed. This adds the two bundled sample assets (plus the provenance README) to both, LFS-tracked under the repo-root rules.

The failure

The Attachments demo fetches its bundled sample and validates the magic bytes before building the message (sample-attachment-buttons.tsxfetchUrl: "/demo-files/sample.png"). With the asset absent, it never gets that far. Verbatim console output from the D5 probe, identical on both slugs:

[error] Failed to load resource: the server responded with a status of 404 (Not Found)
[error] [multimodal-demo] sample-attachment send failed Error: Could not fetch sample "sample.png" — HTTP 404. Is the file bundled under public/demo-files/sample.png?

Page body at failure:

Bundled samples:
Try with sample image
Try with sample PDF
Could not fetch sample "sample.png" — HTTP 404. Is the file bundled under public/demo-files/sample.png?
How can I help you today?

userMsgCount: 0 — nothing was ever sent, so waitForTurnComplete timed out on turn 1 and the cell surfaced the generic conversation-error bucket. The backends are fine: both integrations have real vision-capable multimodal agents (agno/src/agents/multimodal_agent.py, mastra/src/mastra/agents/index.ts) and correct aimock fixtures whose userMessage keys match the component's autoPrompt strings verbatim. This is purely a missing-asset / packaging bug — a different bug from the LFS-pointer problem, and the reason this PR is still needed.

Why only these 2 of 20

public/demo-files/ is not part of the shared/ symlink mechanism — unlike _shared/, tools/, and shared-tools/, it is a per-integration duplicated directory. That is exactly how two integrations came to be missing it. Confirmed against origin/main: the directory has never existed in either integration's history, while all 18 others resolve to a real commit.

What was added, and from where

sample.png (10083 B), sample.pdf (2486 B), README.md (1174 B) copied byte-for-byte from showcase/integrations/langgraph-python/public/demo-files/, the canonical source. Real files, not symlinks — matching the other 18 integrations. sha256 verified identical.

Committed blob form

Both binaries are committed as LFS pointers. No .gitattributes is added by this PR:

$ git show --stat HEAD
 showcase/integrations/agno/public/demo-files/README.md    | 22 ++++++++++++++++++++++
 showcase/integrations/agno/public/demo-files/sample.pdf   |  3 +++
 showcase/integrations/agno/public/demo-files/sample.png   |  3 +++
 showcase/integrations/mastra/public/demo-files/README.md  | 22 ++++++++++++++++++++++
 showcase/integrations/mastra/public/demo-files/sample.pdf |  3 +++
 showcase/integrations/mastra/public/demo-files/sample.png |  3 +++
 6 files changed, 56 insertions(+)

$ git cat-file -p HEAD:showcase/integrations/agno/public/demo-files/sample.png
version https://git-lfs.github.com/spec/v1
oid sha256:01aa5681de99461247543e9215c1e4da3242e26b2bee11593fcdbe209672d973
size 10083

$ git cat-file -p HEAD:showcase/integrations/agno/public/demo-files/sample.pdf
version https://git-lfs.github.com/spec/v1
oid sha256:3da2afae36a1a81fd2c02f15e54bfc38b6c22e41655c31a5b54ff1e0e3daab41
size 2486

mastra is byte-identical to agno for both files. Git blob sizes are 130 / 129 bytes (pointer text), matching the pointer-mode integrations already on main (llamaindex, ag2, pydantic-ai, …), which reference these same two OIDs.

LFS objects are real, not dangling

A pointer with no backing object is worse than either alternative, so this was verified against the server rather than the local cache. The OIDs are the ones already stored for the identical assets in ten other integrations, so no new upload was required (git lfs push --dry-run origin HEAD reports nothing to transfer). Server-side existence, and then the actual bytes, were confirmed through the LFS batch API:

POST https://github.com/copilotkit/copilotkit.git/info/lfs/objects/batch  (operation=download)
  01aa5681de994612  size=10083  download action present
  3da2afae36a1a81f  size=2486   download action present

$ curl "<download href for 01aa5681…>" -o /tmp/lfs-dl.png
$ shasum -a 256 /tmp/lfs-dl.png
01aa5681de99461247543e9215c1e4da3242e26b2bee11593fcdbe209672d973
$ file /tmp/lfs-dl.png
PNG image data, 393 x 90, 8-bit/color RGBA, non-interlaced

The object downloaded from GitHub's LFS store is a real PNG whose hash matches the pointer.

Scope

Static assets only. No source, no fixtures, no manifests, no runtime code, no lockfile churn, no workflow changes.


SUPERSEDED — verification below no longer applies

Everything from here down was produced when this branch carried per-integration .gitattributes overrides that committed the assets as real binaries. Those overrides have been removed. The image-level and RED → GREEN evidence therefore describes a state this branch no longer produces. It is kept for the record of what was tested and why the missing-asset diagnosis is sound — not as a claim that this branch is green.

Specifically, still valid from the run below: the RED evidence (both slugs failed with a 404 on the missing asset, and the langgraph-python CONTROL was green), and the finding that the attachment pipeline past the fetch works once the asset is genuinely present in the image. No longer valid: the GREEN result, because it depended on real binaries reaching the runtime layer, which now requires the build-side LFS fetch.

Superseded image-level and RED → GREEN evidence (click to expand)

Image-level verification (SUPERSEDED)

A file in git that Docker never copies fixes nothing, so this was checked in the built images, not just the source tree. Both Dockerfiles do COPY --from=frontend /app/public ./public, and neither .dockerignore excludes the path.

Before (showcase build agno mastra from this branch's base):

=== showcase-agno:local ===
ls: cannot access '/app/public/demo-files': No such file or directory
=== showcase-mastra:local ===
ls: cannot access '/app/public/demo-files': No such file or directory

After (same command, post-change — with the since-removed .gitattributes overrides):

=== showcase-agno:local (post-change) ===
-rw-r--r-- 1 app app  1174 Jul 24 22:43 README.md
-rw-r--r-- 1 app app  2486 Jul 24 22:43 sample.pdf
-rw-r--r-- 1 app app 10083 Jul 24 22:43 sample.png
png magic: 0000000 211   P   N   G
pdf magic: 0000000   %   P   D   F

=== showcase-mastra:local (post-change) ===
-rw-r--r-- 1 app app  1174 Jul 24 22:43 README.md
-rw-r--r-- 1 app app  2486 Jul 24 22:43 sample.pdf
-rw-r--r-- 1 app app 10083 Jul 24 22:43 sample.png
png magic: 0000000 211   P   N   G
pdf magic: 0000000   %   P   D   F

Served over HTTP from the recreated containers:

agno   /demos/multimodal            200
agno   /demo-files/sample.png       200
agno   /demo-files/sample.pdf       200
mastra /demos/multimodal            200
mastra /demo-files/sample.png       200
mastra /demo-files/sample.pdf       200

(pre-change, same endpoints: page 200, both assets 404)

RED → GREEN → CONTROL (GREEN half SUPERSEDED)

All three exercised the real failure surface — the actual Playwright D5 probe driving the live demo page against the built image, not tests against fakes.

RED — before the change (still valid)

bin/showcase test mastra:multimodal --d5 --direct --verbose

[conversation-runner] turn 1/2 — FAILED { errorCategory: 'assertion-failed', ... }
probe.e2e-full.runFeature.conversation-failed {"featureType":"multimodal","slug":"mastra","failureTurn":1,"turnsCompleted":0,"totalTurns":2,"error":"waitForTurnComplete: turn 1 did not complete within 60000ms (reason=dom-missing, runsFinished=0, count=0, attrPresent=true, runningNow=false, runStartCount=0)"}
...
"bodyTextSnippet":"Bundled samples:\nTry with sample image\nTry with sample PDF\nCould not fetch sample \"sample.png\" — HTTP 404. Is the file bundled under public/demo-files/sample.png?\nHow can I help you today?..."
"consoleLogs":["[error] Failed to load resource: the server responded with a status of 404 (Not Found)","[error] [multimodal-demo] sample-attachment send failed Error: Could not fetch sample \"sample.png\" — HTTP 404. Is the file bundled under public/demo-files/sample.png?"]
probe.e2e-full.service-complete {"slug":"mastra","passed":0,"failed":1,"skipped":0,"incapable":0,"total":1,"state":"red","durationMs":121319}
  ✗ d5-single-pill-e2e:mastra red (121.5s)
  0 passed, 1 failed (121.5s)
⚠ Tests failed for mastra:multimodal (exit 1)

bin/showcase test agno:multimodal --d5 --direct --verbose

probe.e2e-full.runFeature.flap-diagnostics {"slug":"agno","featureType":"multimodal","error":"waitForTurnComplete: turn 1 did not complete within 60000ms (reason=dom-missing, runsFinished=0, count=0, attrPresent=true, runningNow=false, runStartCount=0)"}
"bodyTextSnippet":"Bundled samples:\nTry with sample image\nTry with sample PDF\nCould not fetch sample \"sample.png\" — HTTP 404. Is the file bundled under public/demo-files/sample.png?\nHow can I help you today?..."
"consoleLogs":["[error] Failed to load resource: the server responded with a status of 404 (Not Found)","[error] [multimodal-demo] sample-attachment send failed Error: Could not fetch sample \"sample.png\" — HTTP 404. Is the file bundled under public/demo-files/sample.png?"]
probe.e2e-full.service-complete {"slug":"agno","passed":0,"failed":1,"skipped":0,"incapable":0,"total":1,"state":"red","durationMs":120976}
⚠ Tests failed for agno:multimodal (exit 1)

GREEN — SUPERSEDED, does not describe this branch

Obtained with the since-removed .gitattributes overrides, i.e. with real binaries baked into the image. Same commands, after showcase build agno mastra + showcase recreate agno mastra.

agno:

[conversation-runner] turn 1/2 — assertions passed
[conversation-runner] turn 2/2 — assertions passed
probe.e2e-full.service-complete {"slug":"agno","passed":1,"failed":0,"skipped":0,"incapable":0,"total":1,"state":"green","durationMs":8492}
✓ Tests passed for agno:multimodal

mastra:

[conversation-runner] turn 1/2 — assertions passed
[conversation-runner] turn 2/2 — assertions passed
probe.e2e-full.feature-complete {"slug":"mastra","featureType":"multimodal","pass":true,"durationMs":7462}
probe.e2e-full.service-complete {"slug":"mastra","passed":1,"failed":0,"skipped":0,"incapable":0,"total":1,"state":"green","durationMs":7828}
✓ Tests passed for mastra:multimodal

Both turns passed on both slugs — the image turn and the PDF turn. What this settled and still stands: the attachment pipeline past the fetch had never once executed on these two services, and it works when the real asset is present. What it does not establish: that this branch as it now stands is green.

CONTROL — an already-green integration, unchanged (still valid)

bin/showcase test langgraph-python:multimodal --d5 --direct --verbose (no code touched):

[conversation-runner] turn 1/2 — assertions passed
[conversation-runner] turn 2/2 — assistant settled { bubbleIndex: 1, textLength: 233, hasAssertions: true }
[conversation-runner] turn 2/2 — assertions passed
probe.e2e-full.feature-complete {"slug":"langgraph-python","featureType":"multimodal","pass":true,"durationMs":8783}
probe.e2e-full.service-complete {"slug":"langgraph-python","passed":1,"failed":0,"skipped":0,"incapable":0,"total":1,"state":"green","durationMs":9547}

Confirms the local setup, the shared probe, the shared frontend and the shared fixtures were all healthy — the two REDs above were real per-integration failures, not a setup artifact.


Fleet state, for whoever lands the build fix

Measured on origin/main, demo-files/sample.png splits three ways across the 20 integrations:

  • 10 pointer-mode (130-byte LFS pointers, no override): ag2, built-in-agent, claude-sdk-python, claude-sdk-typescript, crewai-crews, langroid, llamaindex, ms-agent-harness-dotnet, pydantic-ai, spring-ai
  • 8 raw-blob mode (10083-byte real binaries, carved out of LFS by a per-integration .gitattributes): google-adk, langgraph-fastapi, langgraph-python, langgraph-typescript, ms-agent-dotnet, ms-agent-python, strands-typescript, strands
  • 2 missing entirely: agno, mastra — what this PR adds, in pointer mode

So main is currently inconsistent in both directions. This PR puts its two integrations on the uniform-LFS side. The remaining 8 raw-blob overrides should be removed by the build-side fix so the whole fleet is pointer-mode plus an LFS-fetching build.

Noted, not fixed (out of scope)

  • showcase/integrations/mastra/shared-tools is a real directory on origin/main (mode 040000), not the symlink the iron rules require. Pre-existing erosion, unrelated to this change, worth a separate PR.
  • The mastra multimodal page's <title> renders as "LangChain - Python" — a frontend-parity oddity visible in the probe diagnostics. Unrelated.

@github-actions

github-actions Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Production Digest-Pinning Audit

All 41 services digest-pinned.

Run 2026-07-24 16:16:50 PDT — 0 finding(s).

agno and mastra were the only 2 of 20 integrations with no
`public/demo-files/` directory ever committed. Their Attachments demo
fetches `/demo-files/sample.png` before building the message, so the
demo failed at the fetch with:

    [multimodal-demo] sample-attachment send failed Error: Could not
    fetch sample "sample.png" — HTTP 404. Is the file bundled under
    public/demo-files/sample.png?

Nothing was ever sent to the agent — `userMsgCount: 0` — so the D5
probe timed out on turn 1. Both integrations already have working
vision-capable multimodal agents and correct aimock fixtures; this was
purely a missing-asset/packaging bug.

Copies sample.png / sample.pdf / README.md byte-for-byte from
langgraph-python (the canonical source) into each integration's own
`public/demo-files/` — a per-integration duplicated directory, not part
of the `shared/` symlink mechanism, which is exactly how two
integrations came to be missing it.

The assets are committed as plain Git LFS pointers, governed by the
repo-root `*.png` / `*.pdf` LFS rules, with no per-integration
`.gitattributes` override. LFS tracking stays uniform across the
fleet; the deploy-time problem (integration image builds do not fetch
LFS) is fixed in the build, not by carving these files out of LFS.

The pointer OIDs are the ones already stored for the identical assets
in the other integrations, so the backing LFS objects exist server-side
and are not dangling:

    sample.png  sha256:01aa5681de99461247543e9215c1e4da3242e26b2bee11593fcdbe209672d973  10083
    sample.pdf  sha256:3da2afae36a1a81fd2c02f15e54bfc38b6c22e41655c31a5b54ff1e0e3daab41   2486

NOTE: this change does not make agno/mastra pass on its own. It
requires the build-side LFS fetch fix to land first or together.
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.

1 participant

Morty Proxy This is a proxified and sanitized view of the page, visit original site.