ADK Integration
Route Google ADK agent LLM calls through Candela for instant observability — tokens, cost, latency — 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 (optional) | 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, 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 |
Cost Attribution with Enrichment SDKs
Section titled “Cost Attribution with Enrichment SDKs”For multitenant applications, combine ADK’s proxy routing with the Candela Enrichment SDK to attribute LLM costs to specific tenants and jobs:
"""ADK + Enrichment SDK: per-tenant cost attribution."""from candela import CandelaSessionfrom google.adk.agents import Agentfrom google.adk.models import Gemini
# SDK validates IDs and generates headerssession = CandelaSession(tenant_id="pharma-co", job_id="ctm-trial-NCT01750580")
# Pass session's httpx client so enrichment headers are injected on every callroot_agent = Agent( model=Gemini( model="gemini-2.5-flash", base_url="http://localhost:8080/proxy/google", http_client=session.httpx_client(), ), name="clinical_agent", instruction="You are a clinical trial analyst.",)The sidecar reads the enrichment headers and records tenant_id and job_id on every span — visible in the dashboard’s Tenant Leaderboard and Job Leaderboard views.
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 candela auth login (or 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 |
| Tenant/job not showing in dashboard | Enrichment headers not injected | Use the Enrichment SDK or set X-Candela-Tenant-Id manually |