Skip to content
telemetry.dev
Esc
↑↓navigate↵open⌘Jpreview
On this page

Google Gen AI

Trace Gemini and Vertex AI generation, embedding, function-call, and stream operations from TypeScript or Python.

The integration records Google Gen AI model calls. Make a project at telemetry.dev. Copy an API key from the project setup page. Keys use the td_live_... format.

What it captures

The integration records:

  • Contents, system instructions, model, sampling parameters, tools, tool configuration, safety configuration, and response format
  • Response IDs, model versions, candidate content, finish reasons, safety data, errors, and time to first chunk
  • Input, output, total, cache-read, reasoning, and tool-use prompt tokens
  • Text, image, and audio token counts from Gemini modality details, as gen_ai.usage.{text,image,audio}.{input_tokens,output_tokens,cache_read.input_tokens}
  • Embedding counts, dimensions, input-token counts, and billable characters
  • Image generation, image edit, image upscale, and video generation calls
  • Gemini Developer API calls with provider gcp.gemini, and Vertex AI calls with provider gcp.vertex_ai.

Embedding vectors are not part of the span output. Image and video bytes are never captured.

Install

npm i @telemetry-dev/sdk @telemetry-dev/google-genai @google/genai
pip install telemetry-dev-google-genai

TypeScript

Requirement Version
@telemetry-dev/sdk peer ^0.1.0
@google/genai peer >=2 <3
Node.js from @telemetry-dev/sdk >=20.19.0

Python

Requirement Version
Python >=3.10
telemetry-dev >=0.2.6
google-genai >=2,<3

Quickstart

import { GoogleGenAI } from "@google/genai";
import { flush, init, shutdown } from "@telemetry-dev/sdk";
import { wrapGoogleGenAI } from "@telemetry-dev/google-genai";

init({ serviceName: "google-genai-app" });
const ai = wrapGoogleGenAI(
  new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY! }),
);

try {
  const response = await ai.models.generateContent({
    model: "gemini-2.5-flash",
    contents: "Tell me a joke about OpenTelemetry",
    config: { systemInstruction: "You are a helpful bot" },
  });
  console.log(response.text ?? "(no content)");
} finally {
  await flush();
  await shutdown();
}
import os

from google import genai
from telemetry_dev import flush, init, shutdown
from telemetry_dev_google_genai import wrap_google_genai

init(service_name="google-genai-app")
client = wrap_google_genai(
    genai.Client(api_key=os.environ["GEMINI_API_KEY"]),
)

try:
    response = client.models.generate_content(
        model=os.environ.get("MODEL", "gemini-2.5-flash"),
        contents="Tell me a joke about OpenTelemetry",
        config={"system_instruction": "You are a helpful bot"},
    )
    print(response.text or "(no content)")
finally:
    flush()
    shutdown()

API

TypeScript

Function Purpose
wrapGoogleGenAI(client) Instruments one client’s models object and its video operation polls.

The TypeScript package has no global instrumentation and no options object. The Google SDK defines model methods as instance fields, not prototype methods.

Python

Function Purpose
wrap_google_genai(client) Instruments client.models and client.aio.models.
instrument_google_genai() Instruments the synchronous and asynchronous model classes.
uninstrument_google_genai() Restores the original class methods.

These functions have no options object. The integration instruments generate_content, generate_content_stream, embed_content, generate_images, edit_image, upscale_image, and generate_videos, and it instruments operations.get for video polls.

It also instruments private generation methods to collect token usage from automatic function calls. Chat send_message() calls use the instrumented model methods.

Calls

Google Gen AI call Span
models.generateContent() chat {model} generation
models.generateContentStream() chat {model} generation
models.embedContent() embeddings {model} embedding
models.generateImages(), editImage(), upscaleImage() generate_content {model} generation
models.generateVideos() generate_content {model} generation

A chat from client.chats.create() uses these model methods. As a result, sendMessage() and sendMessageStream() also have instrumentation.

The integration does not instrument countTokens, computeTokens, live, caches, files, tunings, or batches.

Images and video

Image and video calls use operation generate_content and set gen_ai.output.type to image or video. The spans keep the model, the text prompt, non-binary configuration and input metadata such as URIs and MIME types, the output count, and operation IDs and storage URIs when the response has them. Image, video, and reference-image bytes are never captured.

A generateVideos() call returns a long-running operation. The two languages record it differently.

TypeScript

The submission is one span that ends when the call returns. Each client.operations.get() or getVideosOperation() call starts a separate generate_content video operation span when you pass the operation object that the same wrapped client returned. A rebuilt operation object with the same name has no poll instrumentation. The last poll records the final output metadata when it is available. No span stays open across the polling loop.

Python

If generate_videos() returns an unfinished operation, its span stays open. The span completes when operations.get() on the same wrapped client sees the operation reach a terminal state.

A poll from another client or process cannot complete the span. The span ends with an error if the operation tracker evicts the operation. Closing the client also ends pending spans for that transport: use client.close() for synchronous operations and await client.aio.aclose() for asynchronous operations. Closing one transport does not finish the other transport’s pending spans. uninstrument_google_genai() closes the tracker for instrument_google_genai() only; it does not close trackers created by wrap_google_genai().

Automatic function calls

TypeScript

A callable tool with a callTool method starts one span for the complete automatic function-call loop. The span includes total usage from the internal calls.

Plain functionDeclarations do not start an automatic loop in the TypeScript SDK. Each manual generateContent() call starts one span.

The usage sum uses AsyncLocalStorage. On an edge runtime without AsyncLocalStorage, the call still has instrumentation, but the internal usage sum is not available.

Python

Pass a Python function in config.tools. The Google SDK calls the function and sends its result to the model.

import os

from google import genai
from telemetry_dev_google_genai import wrap_google_genai


def get_weather(location: str) -> str:
    return f"rainy, 57°F in {location}"


client = wrap_google_genai(
    genai.Client(api_key=os.environ["GEMINI_API_KEY"]),
)
response = client.models.generate_content(
    model="gemini-2.5-flash",
    contents="Weather in Paris?",
    config={"tools": [get_weather]},
)
print(response.text or "(no content)")

One generate_content() call starts one span for the complete automatic function-call loop. The integration sums usage from the internal model calls.

Streaming

TypeScript

The stream state combines text parts, thinking parts, candidates, finish reasons, usage, and automatic function-call turns. The wrapper keeps return, throw, and Symbol.asyncDispose behavior.

Consume or close each stream. A stream that has no consumption does not finish its span.

Python

Gemini stream chunks contain cumulative usage data, so the integration does not change the request. The stream wrappers keep synchronous and asynchronous generator behavior.

Call close() when you stop a synchronous stream early. Use the matching asynchronous close operation for an asynchronous stream.

Flush

TypeScript: For a short script, call await flush() and await shutdown() before exit. In serverless code, call await flush() before return or pass the work to waitUntil.

Python: For a short script, call flush() and shutdown() before exit. In serverless code, call flush() before the runtime freezes.

Open the trace explorer to examine the spans. Refer to the quickstart for API-key setup.

Last updated on August 3, 2026

Was this page helpful?