ADK Integration
Route Google ADK agent LLM calls through Candela for instant observability — tokens, cost, latency, and budget enforcement — with unified trace correlation.
Quick Start (Proxy Only)
Section titled “Quick Start (Proxy Only)”Point ADK’s built-in Gemini model at candela-sidecar using base_url:
from google.adk.agents import Agentfrom 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.
What You Get
Section titled “What You Get”| Metric | Source |
|---|---|
| Token usage (input/output/total) | Parsed from Gemini response |
| Cost (USD) | Calculated by Candela’s cost engine |
| Latency & TTFB | Measured at the proxy |
| Budget enforcement | Per-user budget gating |
| Request/response content | Captured for debugging |
Full Observability (Proxy + OTel)
Section titled “Full Observability (Proxy + OTel)”For deep agent visibility — multi-span DAGs, tool calls, orchestration flow — combine the proxy with ADK’s native OpenTelemetry instrumentation.
Trace Correlation
Section titled “Trace Correlation”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: 234msWithout 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 Agentfrom 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 dependency — opentelemetry-instrumentation-httpx enables automatic traceparent injection:
pip install opentelemetry-instrumentation-httpxRunning the Full Stack
Section titled “Running the Full Stack”| Component | Port | Role |
|---|---|---|
candela-sidecar | 8080 | LLM proxy (tokens, cost, budget, trace correlation) |
| Candela Collector | 4317 / 4318 | OTel span ingestion + GenAI cost enrichment |
adk web | 8000 | ADK dev server |
Environment Variables
Section titled “Environment Variables”| Variable | Default | Description |
|---|---|---|
CANDELA_SIDECAR_URL | http://localhost:8080/proxy/google | Sidecar proxy endpoint |
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT | — | Collector OTLP HTTP endpoint |
OTEL_SERVICE_NAME | — | Service name for OTel spans |
OTEL_SEMCONV_STABILITY_OPT_IN | — | Set to gen_ai_latest_experimental |
Troubleshooting
Section titled “Troubleshooting”| Symptom | Cause | Fix |
|---|---|---|
| Agent works but no proxy spans | base_url not set or wrong port | Verify with curl http://localhost:8080/healthz |
| Proxy spans have separate trace ID | traceparent not propagated | Install opentelemetry-instrumentation-httpx |
401 Unauthorized from sidecar | Missing ADC credentials | Run gcloud auth application-default login |
| Cost shows $0.00 | Unknown model in pricing table | Check collector logs for missing pricing |
ADK import error for Gemini | Old ADK version | pip install --upgrade google-adk |