fix: include generated media in run output regardless of store_media setting#6793
Open
Br1an67 wants to merge 12 commits intoagno-agi:mainfrom
Open
fix: include generated media in run output regardless of store_media setting#6793Br1an67 wants to merge 12 commits intoagno-agi:mainfrom
Br1an67 wants to merge 12 commits intoagno-agi:mainfrom
Conversation
Mustafa-Esoofally
added a commit
that referenced
this pull request
Mar 2, 2026
Apply the same store_media decoupling to Team that PR #6793 applied to Agent: remove if-guards on store_media_util() in all 6 team run paths and add save/restore of media fields around cleanup_and_store so callers still see generated media when store_media=False. Add unit tests verifying sync and async agent.run() returns images in RunOutput even with store_media=False. Closes #5101
Apply the same store_media decoupling to Team that PR agno-agi#6793 applied to Agent: remove if-guards on store_media_util() in all 6 team run paths and add save/restore of media fields around cleanup_and_store so callers still see generated media when store_media=False. Add unit tests verifying sync and async agent.run() returns images in RunOutput even with store_media=False. Closes agno-agi#5101
If persistence fails between scrub and restore, media would be left scrubbed on the returned RunOutput. Wrapping in try/finally guarantees the caller always sees media regardless of DB errors.
Tests cover both non-streaming and streaming Slack paths with store_media=False, including real Agent end-to-end scenarios. Verifies media reaches upload_response_media_async in both paths.
Cover sync/async streaming with both stream_events=True and yield_run_output=True to verify images survive cleanup_and_store.
b2d325c to
16cefe1
Compare
When cache_session=True and store_media=False, the finally-block media restore in cleanup_and_store rehydrated media on the same RunOutput object stored in session.runs (reference aliasing). On the next run, save_session would re-persist the old run with restored media, leaking it to DB despite store_media=False. Shallow-copy run_response before upsert_run so session.runs holds an independent snapshot of the scrubbed state.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Decouples
store_mediafrom in-run media availability. Previously,store_media=Falseprevented media from appearing in both the DB and the returnedRunOutput, making it impossible to forward generated images/videos/audio to external services (e.g., WhatsApp, Slack) immediately after a run.Now
store_mediacontrols only DB persistence. Generated media is always available on the returnedRunOutputfor the caller to act on.Closes #5101
Problem
When
store_media=False, generated media (images, videos, audio, files) was stripped before reaching the caller:store_media_util()was gated —if agent.store_media:prevented media from being copied fromModelResponse→RunOutputin all 4 agent run paths and 6 team run pathscleanup_and_store()scrubbed without restoring — media was removed for DB persistence but never restored on the returned object_response.pyguarded image collection during chunk processingThis meant WhatsApp/Slack interfaces couldn't forward generated images back to users when
store_media=False, even though the media existed in-memory during the run.Before vs After
Solution
Three-layer fix across Agent, Team, and streaming:
1. Remove
store_mediagates onstore_media_util()callsMedia is always copied from
ModelResponse→RunOutputregardless ofstore_media. Affected paths:_run()/_arun()/_continue_run()/_acontinue_run()(agent)_run()/_arun()/_run_tasks()/_arun_tasks()/_continue_run()/_acontinue_run()(team)2. Save/scrub/persist/restore pattern in
cleanup_and_storetry/finallyguarantees media is restored even if DB write failscopy.copy()breaks reference aliasing between session cache and returned RunOutput — without this,save_session()on subsequent runs would re-persist the old run with restored media, leaking it to DB3. Remove streaming gate in
_response.pyhandle_model_response_chunknow always collects images intorun_responseduring streaming, not just whenstore_media=True.Files Changed
agno/agent/_response.pystore_mediaguard on streaming image collectionagno/agent/_run.pystore_mediaguard onstore_media_util()in 4 paths; save/scrub/restore incleanup_and_store+acleanup_and_storeagno/team/_run.py_cleanup_and_store+_acleanup_and_storetests/unit/agent/test_store_media_run_output.pytests/unit/os/routers/test_slack_store_media.pyType of change
Checklist
./scripts/format.shand./scripts/validate.sh)Design Notes for Reviewers
Why
copy.copy()instead of just restoring fields?session.upsert_run()stores a reference to the RunOutput insession.runs. Without the copy, thefinally-block restore would rehydrate media on the same object held in the session's run list. On the nextsave_session(), the previously-scrubbed run would be re-persisted with media restored — defeatingstore_media=False.Why
try/finally?If
save_session()orupsert_run()raises between scrub and restore, the returned RunOutput would have media permanently stripped.finallyguarantees the caller always sees media.Interaction with
scrub_media_from_run_output():That utility scrubs media from
input,messages, andreasoning_messages(for DB cleanliness). The explicit nulling in this PR handles the output media fields (images,videos,audio,files). These are complementary, not redundant.Related Issues
This fix addresses a pattern reported across multiple issues where
store_mediaconflated DB persistence with in-run availability:store_media=Falsestrips media from RunOutput — can't forward images to WhatsAppstore_media=Falseto avoid bloat can now still act on media in the current runstore_media=False+store_tool_messages=False— this fix ensures media is still usable before being discardedKey takeaway: Users who disable media persistence do so deliberately (privacy, compliance, ephemeral URLs, DB size). They still need in-run access to forward media to external services. This PR serves that use case without changing behavior for the default
store_media=Truepath.