Observed since 20 Oct, 16:26 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
This package helps you track your llm costs
Track LLM usage and costs across providers (OpenAI, Gemini, Anthropic, etc.) from a single place.
pip install a2a-llm-tracker
For applications making multiple LLM calls, use a singleton pattern to initialize once and reuse everywhere.
Create tracking.py in your project:
# tracking.py
from dotenv import load_dotenv
import os
import asyncio
import concurrent.futures
load_dotenv()
_meter = None
def get_meter():
"""Get or initialize the global meter singleton."""
global _meter
if _meter is None:
try:
from a2a_llm_tracker import init
client_id = os.getenv("CLIENT_ID", "")
client_secret = os.getenv("CLIENT_SECRET", "")
client_server = os.getenv("CLIENT_SERVER", "https://a2aorchestra.com")
with concurrent.futures.ThreadPoolExecutor() as executor:
future = executor.submit(
asyncio.run,
init(client_id, client_secret, "my-app", client_server)
)
_meter = future.result(timeout=5)
except Exception as e:
print(f"LLM tracking initialization failed: {e}")
return None
return _meter
import os
from openai import OpenAI
from tracking import get_meter
def call_openai(prompt: str):
client = OpenAI()
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}],
)
# Track usage
try:
from a2a_llm_tracker import analyze_response, ResponseType
meter = get_meter()
agent_id = os.getenv("AGENT_ID") # Add AGENT_ID to your .env file
if meter:
analyze_response(response, ResponseType.OPENAI, meter, agent_id=int(agent_id))
except Exception as e:
print("LLM tracking skipped")
return response
Set your credentials in .env file or export them:
CLIENT_ID=your_client_id
CLIENT_SECRET=your_client_secret
CLIENT_SERVER=https://a2aorchestra.com # optional, this is the default
AGENT_ID=my-agent # optional, for tracking which agent made the call
OPENAI_API_KEY=sk-xxxxx
Retrieve your accumulated costs and token usage from CCS:
import os
import asyncio
from a2a_llm_tracker import init
from a2a_llm_tracker.sources import CCSSource
async def get_total_usage():
client_id = os.getenv("CLIENT_ID")
client_secret = os.getenv("CLIENT_SECRET")
await init(
client_id=client_id,
client_secret=client_secret,
application_name="my-app",
)
source = CCSSource(int(client_id))
total_cost = await source.count_cost()
total_tokens = await source.count_total_tokens()
print(f"Total cost: ${total_cost:.4f}")
print(f"Total tokens: {total_tokens}")
asyncio.run(get_total_usage())
Track multiple LLM calls as a single request using set_request_id and set_session_id. These work with any framework - no Starlette required.
from a2a_llm_tracker import set_request_id, set_session_id, generate_id
def handle_request():
# Set at the start of each request - all LLM calls get these IDs automatically
set_request_id(generate_id())
set_session_id("user-session-123")
# All LLM calls anywhere in this request share the same IDs
step_one()
step_two()
step_three()
from flask import Flask, request
from a2a_llm_tracker import set_request_id, set_session_id, generate_id
app = Flask(__name__)
@app.before_request
def before_request():
set_request_id(request.headers.get("X-Request-ID") or generate_id())
set_session_id(request.headers.get("X-Session-ID") or generate_id())
# middleware.py
from a2a_llm_tracker import set_request_id, set_session_id, generate_id
class LLMTrackerMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
set_request_id(request.headers.get("X-Request-ID") or generate_id())
set_session_id(request.headers.get("X-Session-ID") or generate_id())
return self.get_response(request)
If you have Starlette installed, you can use the built-in middleware:
from fastapi import FastAPI
from a2a_llm_tracker import TrackerMiddleware
app = FastAPI()
app.add_middleware(TrackerMiddleware)
TrackerMiddleware now also reads and propagates call-lineage headers when present:
X-Call-IDX-Parent-Call-IDX-Sequence-NumberUse call_agent to call your backend agent URL and automatically forward the current tracking IDs:
X-Request-IDX-Session-IDX-Trace-IDfrom a2a_llm_tracker import call_agent, set_request_id, set_session_id, set_context
set_request_id("req-123")
set_session_id("sess-123")
set_context(trace_id="trace-123")
response = call_agent(
"https://my-backend-agent.example.com/run",
payload={"task": "summarize this"},
)
Async usage:
from a2a_llm_tracker import call_agent_async
response = await call_agent_async(
"https://my-backend-agent.example.com/run",
payload={"task": "summarize this"},
)
Use call_orchestra_agent when you just want to pass text (or parts) and let the SDK:
message/send vs message/streamX-Call-ID, X-Parent-Call-ID, X-Sequence-Number)user_token override)from a2a_llm_tracker import call_orchestra_agent
result = call_orchestra_agent(
input_message="Summarize this contract in 3 bullet points.",
agent_url="https://node.a2aorchestra.com/api/v1/agents/sendmessage/123/.well-known/agent-card.json",
stream=False,
debug=True, # include request headers + payload in result (auth redacted)
)
print(result["success"], result["text"])
print(result["trace_id"], result["session_id"], result["request_id"])
print(result["call_id"], result["parent_call_id"], result["sequence_number"])
Streaming mode (SSE parsed automatically):
result = call_orchestra_agent(
agent_id="123", # optional if agent_url already includes agent path
input_message="Write a short poem.",
stream=True,
)
print(result["text"]) # aggregated text
print(len(result["events"])) # parsed SSE events
Override parent linkage manually (optional):
result = call_orchestra_agent(
agent_id="123",
input_message="Next step",
parent_call_id="my-parent-call-id",
)
Multimodal call (text/image/video parts) by passing parts directly:
result = call_orchestra_agent(
agent_id="123",
parts=[
{"kind": "text", "text": "Describe this image"},
{"kind": "image", "url": "https://example.com/image.jpg"},
],
stream=False,
)
Track LLM usage in Google Agent Development Kit (ADK) agents using the built-in callback:
from google.adk.agents import LlmAgent
from a2a_llm_tracker import create_adk_callback
from tracking import get_meter
meter = get_meter()
agent = LlmAgent(
name="my_agent",
model="gemini-2.0-flash",
instruction="You are a helpful assistant.",
after_model_callback=create_adk_callback(
meter=meter,
agent_id=123, # Your agent concept ID (integer)
),
)
The callback automatically extracts token usage from ADK's LlmResponse.usage_metadata and records it to CCS.
| Provider | ResponseType |
|---|---|
| OpenAI | ResponseType.OPENAI |
| Google Gemini | ResponseType.GEMINI |
| Anthropic | ResponseType.ANTHROPIC |
| Cohere | ResponseType.COHERE |
| Mistral | ResponseType.MISTRAL |
| Groq | ResponseType.GROQ |
| Together AI | ResponseType.TOGETHER |
| AWS Bedrock | ResponseType.BEDROCK |
| Google Vertex AI | ResponseType.VERTEX |
| Google ADK | ResponseType.ADK |
Full documentation available on GitHub:
Download counts and popularity will appear here as the collector receives them.
| Version | Published | Direct dependencies | Open version |
|---|---|---|---|
| 0.0.33 | 17 Apr 2026 | 11 |
This describes our collected data; it is not a security assessment.
Observed since 20 Oct, 16:26 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package:097ae2865242203d235fc7bee578469d99b3a661ca0baecb9a920a1f8ad360e8