Skip to content

Conversation

@Unshure
Copy link
Member

@Unshure Unshure commented Jan 27, 2026

Description

Adds native S3 location support for media content types in Bedrock, allowing users to reference images, documents, and videos stored in S3 directly rather than requiring base64 encoding.

One decision I made in this pr is the choice between a generic Location source, vs the more specific S3Location source. Some model providers support url for a document location (ref), so it could make sense to make this generic for s3 as well. But since S3 also has an optional bucketOwner, I thought it would make more sense to have a separate S3Location, and then later add something like a URLLocation to cover the openai case.

What Changed

Type System Updates

  • Extended ImageSource, DocumentSource, and VideoSource in types/media.py to accept s3Location as an alternative to bytes
  • Added S3Location TypedDict with required uri field and optional bucketOwner for cross-account access

Provider Behavior

  • Bedrock: Passes S3 locations through unchanged to the API
  • Non-Bedrock providers: These will be addressed in a follow-up pr because this one would be huge otherwise

Updated Integ Tests

I added integ tests which create an s3 bucket (post-fixed with the accountId) where the local file resources are uploaded for testing. Then they are referenced in the tests to ensure the model can recognize and identify the content in them.

Ive also added a short 5 second video resource to the integ tests to cover video modalities

Usage Example

from strands import Agent
from strands.models.bedrock import BedrockModel

agent = Agent(model=BedrockModel())

# Reference an S3-hosted document directly
response = agent(
    [
        {
            "role": "user",
            "content": [
                {"text": "Summarize this document:"},
                {
                    "document": {
                        "format": "pdf",
                        "name": "report.pdf",
                        "source": {
                            "s3Location": {
                                "uri": "s3://my-bucket/documents/report.pdf",
                                "bucketOwner": "123456789012"  # Optional
                            }
                        }
                    }
                }
            ]
        }
    ]
)

Notes on backwards compatibility:
This change updates ImageSource, DocumentSource, and VideoSource to use total=False in their typed dict definition, ultimately making the bytes parameter optional now when it was required before. This is considered safe since all existing code should not be impacted by this. However, going forward, since these fields are essentially union types now, they need to be treated as such by customers going forward.

Related Issues

#1482

Documentation PR

TODO

Type of Change

New feature

Testing

How have you tested the change? Verify that the changes do not break functionality or introduce warnings in consuming repositories: agents-docs, agents-tools, agents-cli

  • I ran hatch run prepare

Checklist

  • I have read the CONTRIBUTING document
  • I have added any necessary tests that prove my fix is effective or my feature works
  • I have updated the documentation accordingly
  • I have added an appropriate example to the documentation to outline the feature, or no new docs are needed
  • My changes generate no new warnings
  • Any dependent changes have been merged and published

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@codecov
Copy link

codecov bot commented Jan 27, 2026

Codecov Report

❌ Patch coverage is 92.30769% with 3 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/strands/models/bedrock.py 89.65% 0 Missing and 3 partials ⚠️

📢 Thoughts on this report? Let us know!

@Unshure Unshure changed the title Agent tasks/10 Allow s3Location as Document, Image, and Video Jan 27, 2026
@Unshure Unshure linked an issue Jan 27, 2026 that may be closed by this pull request
@Unshure Unshure marked this pull request as ready for review January 27, 2026 18:39
@Unshure Unshure marked this pull request as draft January 27, 2026 18:40
@Unshure Unshure marked this pull request as ready for review January 27, 2026 18:40
@github-actions github-actions bot added size/xl and removed size/l labels Jan 27, 2026
@Unshure Unshure marked this pull request as draft January 28, 2026 15:30
@github-actions github-actions bot added size/xl and removed size/xl labels Jan 28, 2026
@github-actions github-actions bot added size/l and removed size/xl labels Jan 28, 2026
@github-actions github-actions bot added size/m and removed size/l labels Jan 28, 2026
@github-actions github-actions bot added size/m and removed size/m labels Jan 28, 2026
@Unshure Unshure marked this pull request as ready for review January 28, 2026 16:24
"""

bytes: bytes
s3Location: S3Location
Copy link
Member

Choose a reason for hiding this comment

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

Where did we land on the location vs s3Location discussion @mkmeral ?

Do we think something like the following is a poor abstraction?

    bytes: bytes
    location: Location
class Location(TypedDict):
    uri: Required[str]
class S3Location(Location):
    bucketOwner: str

Copy link
Member Author

Choose a reason for hiding this comment

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

I made a note in the description of this pr:

One decision I made in this pr is the choice between a generic Location source, vs the more specific S3Location source. Some model providers support url for a document location (ref), so it could make sense to make this generic for s3 as well. But since S3 also has an optional bucketOwner, I thought it would make more sense to have a separate S3Location, and then later add something like a URLLocation to cover the openai case.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ahh, I see what you mean. Im not too strongly opinionated about that. I think a separate key is a bit more explicit, but I dont really mind either way.

Copy link
Contributor

Choose a reason for hiding this comment

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

Do we think something like the following is a poor abstraction?

Yeah I was also thinking of that abstraction. My question right now is, will we get non-S3 URIs today?

If so, we should implement that. Otherwise, I'd be in favor of moving to that structure once we have other Locations (so the abstraction actually means sth, and not over-engineered)

# Handle source - supports bytes or s3Location
if "source" in document:
result["source"] = {"bytes": document["source"]["bytes"]}
source = document["source"]
Copy link
Contributor

Choose a reason for hiding this comment

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

We seem to have so much code duplication here. We write this logic 3 times. Can we improve it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE] Allow s3Location as Document and Image Source

3 participants