Skip to content

ADK Integration

Route Google ADK agent LLM calls through Candela for instant observability — tokens, cost, latency, and budget enforcement — with unified trace correlation.

Point ADK’s built-in Gemini model at candela-sidecar using base_url:

from google.adk.agents import Agent
from google.adk.models import Gemini
root_agent = Agent(
model=Gemini(
model="gemini-2.0-flash",
base_url="http://localhost:8080/proxy/google", # → candela-sidecar
),
name="my_agent",
instruction="You are a helpful assistant.",
)

That’s it. The sidecar handles auth (ADC), token counting, cost calculation, and span export. No extra dependencies.

MetricSource
Token usage (input/output/total)Parsed from Gemini response
Cost (USD)Calculated by Candela’s cost engine
Latency & TTFBMeasured at the proxy
Budget enforcementPer-user budget gating
Request/response contentCaptured for debugging

For deep agent visibility — multi-span DAGs, tool calls, orchestration flow — combine the proxy with ADK’s native OpenTelemetry instrumentation.

When ADK’s httpx client sends an LLM request, the OTel instrumentation automatically injects a traceparent header. The sidecar extracts this header and uses the caller’s trace ID — so the proxy span becomes a child of the ADK agent span:

Trace: 4bf92f3577b34da6a3ce929d0e0e4736
├─ invoke_agent (ADK)
│ ├─ call_llm (ADK)
│ │ ├─ generate_content (ADK)
│ │ │ └─ google.chat (candela-sidecar) ← nested via traceparent
│ │ │ tokens: 150 in / 42 out
│ │ │ cost: $0.0023
│ │ │ ttfb: 234ms

Without trace correlation, the proxy spans would have a separate, unrelated trace ID.

"""Full observability: proxy + OTel unified traces."""
import os
os.environ.setdefault("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", "http://localhost:4318/v1/traces")
os.environ.setdefault("OTEL_SERVICE_NAME", "my-adk-agent")
os.environ.setdefault("OTEL_SEMCONV_STABILITY_OPT_IN", "gen_ai_latest_experimental")
os.environ.setdefault("OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT", "EVENT_ONLY")
from google.adk.agents import Agent
from google.adk.models import Gemini
SIDECAR = os.environ.get("CANDELA_SIDECAR_URL", "http://localhost:8080/proxy/google")
root_agent = Agent(
model=Gemini(model="gemini-2.0-flash", base_url=SIDECAR),
name="my_agent",
instruction="You are a helpful assistant.",
)

Extra dependencyopentelemetry-instrumentation-httpx enables automatic traceparent injection:

Terminal window
pip install opentelemetry-instrumentation-httpx

ComponentPortRole
candela-sidecar8080LLM proxy (tokens, cost, budget, trace correlation)
Candela Collector4317 / 4318OTel span ingestion + GenAI cost enrichment
adk web8000ADK dev server
VariableDefaultDescription
CANDELA_SIDECAR_URLhttp://localhost:8080/proxy/googleSidecar proxy endpoint
OTEL_EXPORTER_OTLP_TRACES_ENDPOINTCollector OTLP HTTP endpoint
OTEL_SERVICE_NAMEService name for OTel spans
OTEL_SEMCONV_STABILITY_OPT_INSet to gen_ai_latest_experimental

SymptomCauseFix
Agent works but no proxy spansbase_url not set or wrong portVerify with curl http://localhost:8080/healthz
Proxy spans have separate trace IDtraceparent not propagatedInstall opentelemetry-instrumentation-httpx
401 Unauthorized from sidecarMissing ADC credentialsRun gcloud auth application-default login
Cost shows $0.00Unknown model in pricing tableCheck collector logs for missing pricing
ADK import error for GeminiOld ADK versionpip install --upgrade google-adk