← Files LaunchDarklyARCHIVED FILE

skills/built-in-metrics/references/strands-tracking.md

4.9 KB · Oct 3, 2026 · 06:25 UTC

↓ Download file

# Strands Agents Metrics Tracking

**There is no LaunchDarkly provider package for Strands.** Strands is a provider-agnostic agent SDK — the same `Agent` class runs against Anthropic, OpenAI, and Bedrock by swapping the `model` argument — so the tracking pattern plugs in at the agent layer, not the provider layer. Tier 3 (custom extractor + `trackMetricsOf`) is the canonical path.

The Strands `AgentResult` object exposes a `metrics.accumulated_usage` dict (Python) / `metrics.accumulatedUsage` object (Node) that already aggregates token counts across every provider call the agent made in a single `invoke_async` turn — including any tool-calling round trips. That means one extractor call covers the whole turn, unlike the per-response shape from Anthropic or OpenAI direct.

The key names inside `accumulated_usage` are camelCase even in Python: `inputTokens`, `outputTokens`, `totalTokens`.

## Tier 1 is not available

`ManagedModel` does not currently ship a Strands runner. Strands owns its own agent loop and short-term memory (`SlidingWindowConversationManager`), so wrapping it in a LaunchDarkly managed runner would fight against the framework. Stay on Tier 3.

## Tier 3 — Explicit `track_duration_of` + manual `track_tokens` (primary)

This is the shape in the LaunchDarkly Strands integration guide. Use it when the call site is already async and you want token extraction split out from duration tracking.

```python
from ldai.tracker import TokenUsage


def track_strands_metrics(tracker, result):
    """Record token usage from a Strands AgentResult on the LD tracker."""
    usage = getattr(result.metrics, "accumulated_usage", {}) or {}
    input_tokens = usage.get("inputTokens", 0)
    output_tokens = usage.get("outputTokens", 0)
    total = usage.get("totalTokens", 0) or (input_tokens + output_tokens)
    if total > 0:
        tracker.track_tokens(
            TokenUsage(input=input_tokens, output=output_tokens, total=total)
        )


async def run_turn(agent, tracker, user_input):
    try:
        result = await tracker.track_duration_of(lambda: agent.invoke_async(user_input))
        tracker.track_success()
        track_strands_metrics(tracker, result)
        return result.message["content"][0]["text"]
    except Exception:
        tracker.track_error()
        raise
```

**What this tracks:**
- Duration — from the `track_duration_of` wrapper around `invoke_async`.
- Tokens — from `accumulated_usage`, including any tool-calling round trips inside the turn.
- Success / error — explicit, in the try/except.

## Tier 3 — Single-call `track_metrics_of_async` variant

If you prefer the single-call form that matches the rest of the provider-tracking references, fold the extractor into an `LDAIMetrics` return and use `track_metrics_of_async`:

```python
from ldai.providers.types import LDAIMetrics, TokenUsage


def strands_extractor(result) -> LDAIMetrics:
    usage = getattr(result.metrics, "accumulated_usage", {}) or {}
    input_tokens = usage.get("inputTokens", 0)
    output_tokens = usage.get("outputTokens", 0)
    total = usage.get("totalTokens", 0) or (input_tokens + output_tokens)
    return LDAIMetrics(
        success=True,
        tokens=TokenUsage(input=input_tokens, output=output_tokens, total=total),
    )


async def run_turn(agent, tracker, user_input):
    # Exceptions are tracked automatically — track_metrics_of_async catches
    # exceptions, records tracker.track_error(), and re-raises.
    result = await tracker.track_metrics_of_async(
        strands_extractor,
        lambda: agent.invoke_async(user_input),
    )
    return result.message["content"][0]["text"]
```

Pick the style that matches the rest of the codebase — the two variants record the same metrics.

## Provider dispatch stays in your code

Strands model classes are provider-specific (`AnthropicModel`, `OpenAIModel`, `BedrockModel`). To serve more than one provider from a single config key, dispatch on `agent_config.provider.name` before constructing the `Agent`. See [agent-mode-frameworks.md § Strands Agent](../../migrate/references/agent-mode-frameworks.md) for the `create_strands_model` dispatcher, including the rule that `parameters.tools` must be dropped before being passed into the Strands model class (tools flow through the `Agent` constructor, not through model params).

## Always flush before exit

Strands examples are commonly short-lived scripts (`python run_agent.py ...`). Trailing analytics events can be lost if the client closes before flushing. Always call `ldclient.get().flush()` (and `ldclient.get().close()` on exit) after the last turn.

## Node / TypeScript caveat

The Strands TypeScript SDK ships `BedrockModel` and `OpenAIModel` only — no `AnthropicModel`. The same Tier-3 pattern applies (custom extractor over `result.metrics.accumulatedUsage`, then `tracker.trackMetricsOf` or explicit `trackDurationOf` + `trackTokens`), but multi-provider variations that include Anthropic require the Python SDK today.

SHA-256: 3d26092ba3d3ce8603f8d8d178895b388b7c6c32bdba0a13d1e86f8f3495687a