Hi everyone,
I want to add inline citations to model responses which are grounded to Google Search.
I rely on the explanation here:
However, the resulting indices in the grounding meta data are off and add the citation often in the next line:
Here are the results from the German 2nd Division's second matchday last weekend.
**Saturday, August 9, 2025**
* **Fortuna Düsseldorf 0 - 2 Hannover 96**
[1](https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHQ_C0W-iRph_DVmOe_qRRZyx9BlJLCPiKjLTzDNriQ-zmbzQVzm0j5bNu86-wgPXkvsPqg67o-Q7k-yEdPGZeukbs3u1QdeqsFKS0Tzau-K3k8MkCi6fC1cZeGkninOP6h3Yeve9yfZe_UBgkpIPKQEJlRkTEUjXtXaiadGFhBg0E_jOv36g==), [2](https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGUwILTvRkTDxhQlrWKdfcasXNmzqgAOSKhoOBJ7B_Pfp473emZPUO2gzRYnKeMIKc7Pmu3oQPV13fda2PIJMFs99hzAxSIhQIDSN9aW5ID7o6QpOgE7IQ-REt-k4ShYuFfGjFrbD4NdpTkNy5Z)* **Eintracht Braunschweig 3 - 2 SpVgg Greuther Fürth**
*[1](https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHQ_C0W-iRph_DVmOe_qRRZyx9BlJLCPiKjLTzDNriQ-zmbzQVzm0j5bNu86-wgPXkvsPqg67o-Q7k-yEdPGZeukbs3u1QdeqsFKS0Tzau-K3k8MkCi6fC1cZeGkninOP6h3Yeve9yfZe_UBgkpIPKQEJlRkTEUjXtXaiadGFhBg0E_jOv36g==) **Dynamo Dresden 1 - 2 1. FC Magdeburg**
*[1](https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHQ_C0W-iRph_DVmOe_qRRZyx9BlJLCPiKjLTzDNriQ-zmbzQVzm0j5bNu86-wgPXkvsPqg67o-Q7k-yEdPGZeukbs3u1QdeqsFKS0Tzau-K3k8MkCi6fC1cZeGkninOP6h3Yeve9yfZe_UBgkpIPKQEJlRkTEUjXtXaiadGFhBg0E_jOv36g==) **1. FC Kaiserslautern 1 - 0 FC Schalke 04**
[1](https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHQ_C0W-iRph_DVmOe_qRRZyx9BlJLCPiKjLTzDNriQ-zmbzQVzm0j5bNu86-wgPXkvsPqg67o-Q7k-yEdPGZeukbs3u1QdeqsFKS0Tzau-K3k8MkCi6fC1cZeGkninOP6h3Yeve9yfZe_UBgkpIPKQEJlRkTEUjXtXaiadGFhBg0E_jOv36g==)**Sunday, August 10, 2025**
[truncated]
As you see, this interrupts Markdown rendering. In some edge cases, they are placed mid-sentence.
Code example:
from google import genai
from google.genai import types
import base64
def add_citations(response):
text = response.text
supports = response.candidates[0].grounding_metadata.grounding_supports
chunks = response.candidates[0].grounding_metadata.grounding_chunks
# Sort supports by end_index in descending order to avoid shifting issues when inserting.
sorted_supports = sorted(supports, key=lambda s: s.segment.end_index, reverse=True)
for support in sorted_supports:
end_index = support.segment.end_index
if support.grounding_chunk_indices:
# Create citation string like [1](link1)[2](link2)
citation_links = []
for i in support.grounding_chunk_indices:
if i < len(chunks):
uri = chunks[i].web.uri
citation_links.append(f"[{i + 1}]({uri})")
citation_string = ", ".join(citation_links)
text = text[:end_index] + citation_string + text[end_index:]
return text
def generate():
client = genai.Client(
vertexai=True,
project="bah-da-llm-prod",
location="global",
)
model = "gemini-2.5-pro"
contents = [
types.Content(
role="user",
parts=[
types.Part.from_text(text="""What are the results from the German 2nd Division last weekend?""")
]
),
]
tools = [
types.Tool(google_search=types.GoogleSearch()),
]
generate_content_config = types.GenerateContentConfig(
temperature = 1,
top_p = 0.95,
seed = 0,
max_output_tokens = 65535,
safety_settings = [types.SafetySetting(
category="HARM_CATEGORY_HATE_SPEECH",
threshold="OFF"
),types.SafetySetting(
category="HARM_CATEGORY_DANGEROUS_CONTENT",
threshold="OFF"
),types.SafetySetting(
category="HARM_CATEGORY_SEXUALLY_EXPLICIT",
threshold="OFF"
),types.SafetySetting(
category="HARM_CATEGORY_HARASSMENT",
threshold="OFF"
)],
tools = tools,
thinking_config=types.ThinkingConfig(
thinking_budget=-1,
),
)
response = client.models.generate_content(
model = model,
contents = contents,
config = generate_content_config,
)
return response
response = generate()
# Assuming response with grounding metadata
text_with_citations = add_citations(response)
print(text_with_citations)