Skip to content

Google Gemini

Gemini is captured through the google-genai client (@google/genai on npm). Wrap the client where you construct it.

app/llm.py
import metergraph
from google import genai
client = metergraph.wrap(genai.Client())
response = client.models.generate_content(
model="gemini-2.5-flash",
contents="Summarize this invoice.",
)
MethodRecorded as
models.generate_contentmodels.generate_content
models.generate_content_streammodels.generate_content.stream
aio.models.generate_contentmodels.generate_content
aio.models.generate_content_streammodels.generate_content.stream

A method outside this table produces no row. That includes embeddings, caches, files, tunings and the Live API.

app/llm.py
client = metergraph.wrap(genai.Client())
async def summarize(text: str) -> str:
response = await client.aio.models.generate_content(
model="gemini-2.5-flash",
contents=text,
)
return response.text

One wrap() covers both namespaces. The sync and async seams are patched on the same client object.

app/llm.py
for chunk in client.models.generate_content_stream(
model="gemini-2.5-flash",
contents="Write a release note.",
):
print(chunk.text or "", end="")

The row is finalized when the stream ends, is abandoned, or raises. Time to first token is measured from the first chunk whose candidates carry content, including a chunk whose only content is a function call.

Function calling needs no configuration. Each generate_content call is one row, so an automatic function-calling loop produces one row per model turn. Wrap the loop in a trace() to keep it together.

app/agent.py
with metergraph.trace("support-agent"):
response = client.models.generate_content(
model="gemini-2.5-flash",
contents=question,
config={"tools": [tool_declarations]},
)

Function calls and their responses are normalized into the same tool-event shape used for the other providers. See Captured fields.

client.chats is not in the seam table, but chat sessions are still captured. The chat session holds a reference to the same models object that wrap() patched, and it resolves the method at call time, so send_message and send_message_stream land on the instrumented path and produce ordinary models.generate_content rows.

app/chat.py
client = metergraph.wrap(genai.Client())
chat = client.chats.create(model="gemini-2.5-flash")
chat.send_message("Hello") # captured

Give the conversation a session ID so the turns group together. See Identity model.

wrap() identifies a Gemini client by the presence of models.generate_content (models.generateContent in TypeScript). A proxy, a mock or a subclass that hides that attribute falls through to the Anthropic branch, and no seams get patched. The log line tells you: it names the provider it decided on and how many seams it found.

Name the provider explicitly to settle it.

client = metergraph.wrap(unusual_client, provider="google")