The OpenHands SDK provides metrics tracking at two levels: individual LLM metrics and aggregated conversation-level costs:
You can access detailed metrics from each LLM instance using the llm.metrics object to track token usage, costs, and latencies per API call.
For a complete view, use conversation.conversation_stats to get aggregated costs across all LLMs used in a conversation, including the primary agent LLM and any auxiliary LLMs (such as those used by the context condenser).
The LLM Registry allows you to maintain a centralized registry of LLM instances, each identified by a unique usage_id. This is particularly useful for tracking costs across different LLMs used in your application.
examples/01_standalone_sdk/05_use_llm_registry.py
import osfrom pydantic import SecretStrfrom openhands.sdk import ( LLM, Agent, Conversation, Event, LLMConvertibleEvent, LLMRegistry, Message, TextContent, get_logger,)from openhands.sdk.tool import Toolfrom openhands.tools.terminal import TerminalToollogger = get_logger(__name__)# Configure LLM using LLMRegistryapi_key = os.getenv("LLM_API_KEY")assert api_key is not None, "LLM_API_KEY environment variable is not set."model = os.getenv("LLM_MODEL", "anthropic/claude-sonnet-4-5-20250929")base_url = os.getenv("LLM_BASE_URL")# Create LLM instancemain_llm = LLM( usage_id="agent", model=model, base_url=base_url, api_key=SecretStr(api_key),)# Create LLM registry and add the LLMllm_registry = LLMRegistry()llm_registry.add(main_llm)# Get LLM from registryllm = llm_registry.get("agent")# Toolscwd = os.getcwd()tools = [Tool(name=TerminalTool.name)]# Agentagent = Agent(llm=llm, tools=tools)llm_messages = [] # collect raw LLM messagesdef conversation_callback(event: Event): if isinstance(event, LLMConvertibleEvent): llm_messages.append(event.to_llm_message())conversation = Conversation( agent=agent, callbacks=[conversation_callback], workspace=cwd)conversation.send_message("Please echo 'Hello!'")conversation.run()print("=" * 100)print("Conversation finished. Got the following LLM messages:")for i, message in enumerate(llm_messages): print(f"Message {i}: {str(message)[:200]}")print("=" * 100)print(f"LLM Registry usage IDs: {llm_registry.list_usage_ids()}")# Demonstrate getting the same LLM instance from registrysame_llm = llm_registry.get("agent")print(f"Same LLM instance: {llm is same_llm}")# Demonstrate requesting a completion directly from an LLMresp = llm.completion( messages=[ Message(role="user", content=[TextContent(text="Say hello in one word.")]) ])# Access the response content via OpenHands LLMResponsemsg = resp.messagetexts = [c.text for c in msg.content if isinstance(c, TextContent)]print(f"Direct completion response: {texts[0] if texts else str(msg)}")# Report costcost = llm.metrics.accumulated_costprint(f"EXAMPLE_COST: {cost}")
Running the Example
export LLM_API_KEY="your-api-key"cd agent-sdkuv run python examples/01_standalone_sdk/05_use_llm_registry.py
Each LLM is created with a unique usage_id (e.g., “agent”, “condenser”) that serves as its identifier in the registry. The registry maintains references to all LLM instances, allowing you to:
Register LLMs: Add LLM instances to the registry with llm_registry.add(llm)
Retrieve LLMs: Get LLM instances by their usage ID with llm_registry.get("usage_id")
List Usage IDs: View all registered usage IDs with llm_registry.list_usage_ids()
Track Costs Separately: Each LLM’s metrics are tracked independently by its usage ID
This pattern is essential when using multiple LLMs in your application, such as having a primary agent LLM and a separate LLM for context condensing.
Beyond individual LLM metrics, you can access aggregated costs for an entire conversation using conversation.conversation_stats. This is particularly useful when your conversation involves multiple LLMs, such as the main agent LLM and auxiliary LLMs for tasks like context condensing.
The conversation.conversation_stats object provides cost tracking across all LLMs used in a conversation. It is an instance of the ConversationStats class, which provides the following key features:
usage_to_metrics: A dictionary mapping usage IDs to their respective Metrics objects. This allows you to track costs separately for each LLM used in the conversation.
get_combined_metrics(): Returns a single Metrics object that aggregates costs across all LLMs used in the conversation. This gives you the total cost of the entire conversation.
get_metrics_for_usage(usage_id: str): Retrieves the Metrics object for a specific usage ID, allowing you to inspect costs for individual LLMs.
# Get combined metrics for the entire conversationtotal_metrics = conversation.conversation_stats.get_combined_metrics()print(f"Total cost: ${total_metrics.accumulated_cost:.6f}")# Get metrics for a specific LLM by usage IDagent_metrics = conversation.conversation_stats.get_metrics_for_usage("agent")print(f"Agent cost: ${agent_metrics.accumulated_cost:.6f}")# Access all usage IDs and their metricsfor usage_id, metrics in conversation.conversation_stats.usage_to_metrics.items(): print(f"{usage_id}: ${metrics.accumulated_cost:.6f}")