Skip to content

Commit 4da59c6

Browse files
author
Danilo Poccia
committed
feat(citations): Add support for web and search result citations
Add support for web-based and search result citations in the Bedrock Converse API, enabling the SDK to handle all citation location types. Changes: - Add WebLocation and WebLocationInner types for web citations - Add SearchResultLocation and SearchResultLocationInner types - Update _format_citation_location to handle web and searchResultLocation - Add tests for web and search result citation types This completes support for all CitationLocation types as per the Bedrock Converse API specification.
1 parent 02dbe8a commit 4da59c6

File tree

3 files changed

+122
-2
lines changed

3 files changed

+122
-2
lines changed

src/strands/models/bedrock.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,8 @@ def _format_citation_location(self, location: Mapping[str, Any]) -> dict[str, An
543543
"""Format a citation location preserving the tagged union structure.
544544
545545
The Bedrock API requires CitationLocation to be a tagged union with exactly one
546-
of the following keys: documentChar, documentPage, or documentChunk.
546+
of the following keys: web, documentChar, documentPage, documentChunk, or
547+
searchResultLocation.
547548
548549
Args:
549550
location: Citation location to format.
@@ -556,9 +557,11 @@ def _format_citation_location(self, location: Mapping[str, Any]) -> dict[str, An
556557
"""
557558
# Allowed fields for each tagged union type
558559
allowed_fields = {
560+
"web": ("url", "domain"),
559561
"documentChar": ("documentIndex", "start", "end"),
560562
"documentPage": ("documentIndex", "start", "end"),
561563
"documentChunk": ("documentIndex", "start", "end"),
564+
"searchResultLocation": ("searchResultIndex", "start", "end"),
562565
}
563566

564567
for location_type, fields in allowed_fields.items():

src/strands/types/citations.py

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,35 @@ class DocumentPageLocationInner(TypedDict, total=False):
7070
end: int
7171

7272

73+
class WebLocationInner(TypedDict, total=False):
74+
"""Inner content for web-based location.
75+
76+
Attributes:
77+
url: The URL of the web page containing the cited content.
78+
domain: The domain of the web page containing the cited content.
79+
"""
80+
81+
url: str
82+
domain: str
83+
84+
85+
class SearchResultLocationInner(TypedDict, total=False):
86+
"""Inner content for search result location.
87+
88+
Attributes:
89+
searchResultIndex: The index of the search result content block where
90+
the cited content is found. Minimum value of 0.
91+
start: The starting position in the content array where the cited
92+
content begins. Minimum value of 0.
93+
end: The ending position in the content array where the cited
94+
content ends. Minimum value of 0.
95+
"""
96+
97+
searchResultIndex: int
98+
start: int
99+
end: int
100+
101+
73102
class DocumentCharLocation(TypedDict, total=False):
74103
"""Tagged union wrapper for character-level document location.
75104
@@ -100,8 +129,30 @@ class DocumentPageLocation(TypedDict, total=False):
100129
documentPage: DocumentPageLocationInner
101130

102131

132+
class WebLocation(TypedDict, total=False):
133+
"""Tagged union wrapper for web-based location.
134+
135+
Attributes:
136+
web: The web location data.
137+
"""
138+
139+
web: WebLocationInner
140+
141+
142+
class SearchResultLocation(TypedDict, total=False):
143+
"""Tagged union wrapper for search result location.
144+
145+
Attributes:
146+
searchResultLocation: The search result location data.
147+
"""
148+
149+
searchResultLocation: SearchResultLocationInner
150+
151+
103152
# Union type for citation locations - tagged union where exactly one key is present
104-
CitationLocation = Union[DocumentCharLocation, DocumentChunkLocation, DocumentPageLocation]
153+
CitationLocation = Union[
154+
DocumentCharLocation, DocumentChunkLocation, DocumentPageLocation, WebLocation, SearchResultLocation
155+
]
105156

106157

107158
class CitationSourceContent(TypedDict, total=False):

tests/strands/models/test_bedrock.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2173,3 +2173,69 @@ def test_format_request_message_content_citation_unknown_location_type(model):
21732173
result = model._format_request_message_content(content)
21742174

21752175
assert "location" not in result["citationsContent"]["citations"][0]
2176+
2177+
2178+
def test_format_request_message_content_web_citation(model):
2179+
"""Test that web citations preserve tagged union structure."""
2180+
content = {
2181+
"citationsContent": {
2182+
"citations": [
2183+
{
2184+
"title": "Web Citation",
2185+
"location": {"web": {"url": "https://example.com", "domain": "example.com"}},
2186+
"sourceContent": [{"text": "Web content"}],
2187+
}
2188+
],
2189+
"content": [{"text": "Generated text"}],
2190+
}
2191+
}
2192+
2193+
result = model._format_request_message_content(content)
2194+
2195+
assert result["citationsContent"]["citations"][0]["location"] == {
2196+
"web": {"url": "https://example.com", "domain": "example.com"}
2197+
}
2198+
2199+
2200+
def test_format_request_message_content_web_citation_filters_extra_fields(model):
2201+
"""Test that extra fields in web citation location are filtered out."""
2202+
content = {
2203+
"citationsContent": {
2204+
"citations": [
2205+
{
2206+
"title": "Web Citation",
2207+
"location": {"web": {"url": "https://example.com", "domain": "example.com", "extra": "ignored"}},
2208+
"sourceContent": [{"text": "Content"}],
2209+
}
2210+
],
2211+
"content": [{"text": "Text"}],
2212+
}
2213+
}
2214+
2215+
result = model._format_request_message_content(content)
2216+
2217+
assert result["citationsContent"]["citations"][0]["location"] == {
2218+
"web": {"url": "https://example.com", "domain": "example.com"}
2219+
}
2220+
2221+
2222+
def test_format_request_message_content_search_result_citation(model):
2223+
"""Test that searchResultLocation citations preserve tagged union structure."""
2224+
content = {
2225+
"citationsContent": {
2226+
"citations": [
2227+
{
2228+
"title": "Search Citation",
2229+
"location": {"searchResultLocation": {"searchResultIndex": 1, "start": 0, "end": 50}},
2230+
"sourceContent": [{"text": "Search result"}],
2231+
}
2232+
],
2233+
"content": [{"text": "Generated text"}],
2234+
}
2235+
}
2236+
2237+
result = model._format_request_message_content(content)
2238+
2239+
assert result["citationsContent"]["citations"][0]["location"] == {
2240+
"searchResultLocation": {"searchResultIndex": 1, "start": 0, "end": 50}
2241+
}

0 commit comments

Comments
 (0)