Skip to content

Commit 3f28e30

Browse files
google-genai-botcopybara-github
authored andcommitted
feat: add citation_metadata to LlmResponse
PiperOrigin-RevId: 811997009
1 parent 7b707ce commit 3f28e30

File tree

2 files changed

+86
-0
lines changed

2 files changed

+86
-0
lines changed

src/google/adk/models/llm_response.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,12 @@ class LlmResponse(BaseModel):
126126
This field is automatically populated when context caching is enabled.
127127
"""
128128

129+
citation_metadata: Optional[types.CitationMetadata] = None
130+
"""Citation metadata for the response.
131+
132+
This field is automatically populated when citation is enabled.
133+
"""
134+
129135
@staticmethod
130136
def create(
131137
generate_content_response: types.GenerateContentResponse,
@@ -148,13 +154,15 @@ def create(
148154
grounding_metadata=candidate.grounding_metadata,
149155
usage_metadata=usage_metadata,
150156
finish_reason=candidate.finish_reason,
157+
citation_metadata=candidate.citation_metadata,
151158
avg_logprobs=candidate.avg_logprobs,
152159
logprobs_result=candidate.logprobs_result,
153160
)
154161
else:
155162
return LlmResponse(
156163
error_code=candidate.finish_reason,
157164
error_message=candidate.finish_message,
165+
citation_metadata=candidate.citation_metadata,
158166
usage_metadata=usage_metadata,
159167
finish_reason=candidate.finish_reason,
160168
avg_logprobs=candidate.avg_logprobs,

tests/unittests/models/test_llm_response.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,3 +239,81 @@ def test_llm_response_create_with_partial_logprobs_result():
239239
assert len(response.logprobs_result.top_candidates) == 0
240240
assert response.logprobs_result.chosen_candidates[0].token == 'Hello'
241241
assert response.logprobs_result.chosen_candidates[1].token == ' world'
242+
243+
244+
def test_llm_response_create_with_citation_metadata():
245+
"""Test LlmResponse.create() extracts citation_metadata from candidate."""
246+
citation_metadata = types.CitationMetadata(
247+
citations=[
248+
types.Citation(
249+
start_index=0,
250+
end_index=10,
251+
uri='https://example.com',
252+
)
253+
]
254+
)
255+
256+
generate_content_response = types.GenerateContentResponse(
257+
candidates=[
258+
types.Candidate(
259+
content=types.Content(parts=[types.Part(text='Response text')]),
260+
finish_reason=types.FinishReason.STOP,
261+
citation_metadata=citation_metadata,
262+
)
263+
]
264+
)
265+
266+
response = LlmResponse.create(generate_content_response)
267+
268+
assert response.citation_metadata == citation_metadata
269+
assert response.content.parts[0].text == 'Response text'
270+
271+
272+
def test_llm_response_create_without_citation_metadata():
273+
"""Test LlmResponse.create() handles missing citation_metadata gracefully."""
274+
generate_content_response = types.GenerateContentResponse(
275+
candidates=[
276+
types.Candidate(
277+
content=types.Content(parts=[types.Part(text='Response text')]),
278+
finish_reason=types.FinishReason.STOP,
279+
citation_metadata=None,
280+
)
281+
]
282+
)
283+
284+
response = LlmResponse.create(generate_content_response)
285+
286+
assert response.citation_metadata is None
287+
assert response.content.parts[0].text == 'Response text'
288+
289+
290+
def test_llm_response_create_error_case_with_citation_metadata():
291+
"""Test LlmResponse.create() includes citation_metadata in error cases."""
292+
citation_metadata = types.CitationMetadata(
293+
citations=[
294+
types.Citation(
295+
start_index=0,
296+
end_index=10,
297+
uri='https://example.com',
298+
)
299+
]
300+
)
301+
302+
generate_content_response = types.GenerateContentResponse(
303+
candidates=[
304+
types.Candidate(
305+
content=None, # No content - blocked case
306+
finish_reason=types.FinishReason.RECITATION,
307+
finish_message='Response blocked due to recitation triggered',
308+
citation_metadata=citation_metadata,
309+
)
310+
]
311+
)
312+
313+
response = LlmResponse.create(generate_content_response)
314+
315+
assert response.citation_metadata == citation_metadata
316+
assert response.error_code == types.FinishReason.RECITATION
317+
assert (
318+
response.error_message == 'Response blocked due to recitation triggered'
319+
)

0 commit comments

Comments
 (0)