Skip to content

feat!: extend AiMetrics, MetricSummary, and TrackMetricsOf#300

Merged
mattrmc1 merged 9 commits into
mainfrom
mmccarthy/AIC-2727/extend-metrics-ai-metrics
Jul 7, 2026
Merged

feat!: extend AiMetrics, MetricSummary, and TrackMetricsOf#300
mattrmc1 merged 9 commits into
mainfrom
mmccarthy/AIC-2727/extend-metrics-ai-metrics

Conversation

@mattrmc1

@mattrmc1 mattrmc1 commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

BEGIN_COMMIT_OVERRIDE
feat!: ILdAiConfigTracker.TrackDuration now takes a double instead of a float — custom ILdAiConfigTracker implementations must update their TrackDuration override signature
feat: Extend AiMetrics with optional ToolCalls and DurationMs (duration override)
feat: Extend MetricSummary with accumulated ToolCalls and ResumptionToken
feat: TrackMetricsOf auto-tracks tool calls, honors DurationMs override, and aligns operation-vs-extractor failure semantics with the other SDKs
END_COMMIT_OVERRIDE

Summary

Extends AiMetrics, MetricSummary, and TrackMetricsOf to bring the .NET AI SDK to parity with the JS, Python, and Java SDKs for metric tracking.

AiMetrics — new optional fields

public AiMetrics(bool success, Usage? tokens = null,
    IReadOnlyList<string> toolCalls = null, double? durationMs = null)

ToolCalls carries tool keys invoked during the operation. DurationMs provides an explicit duration override — when set, TrackMetricsOf uses it instead of the stopwatch measurement.

MetricSummary — new optional fields

public record struct MetricSummary(
    double? DurationMs,
    Feedback? Feedback,
    Usage? Tokens,
    bool? Success,
    double? TimeToFirstTokenMs,
    IReadOnlyList<string> ToolCalls = null,      // NEW
    string ResumptionToken = null                 // NEW
);

ToolCalls accumulates keys from TrackToolCall invocations. ResumptionToken surfaces the tracker's resumption token on the summary.

TrackMetricsOf — tool call auto-tracking & duration override

TrackMetricsOf now extracts ToolCalls from AiMetrics and calls TrackToolCalls automatically. It also honors DurationMs when present, falling back to the stopwatch value when null.

The error-handling semantics now match the other SDKs: if the operation throws, duration + error are tracked and the exception propagates. If the metrics extractor throws, duration is tracked but error is NOT — the AI operation itself succeeded.

TrackToolCall — accumulates keys on summary

TrackToolCall now appends to an internal list (lock-guarded) so that Summary.ToolCalls reflects all tool invocations. The existing per-call $ld:ai:tool_call event emission is unchanged.

Breaking change

ILdAiConfigTracker.TrackDuration now takes a double instead of a float:

- public void TrackDuration(float durationMs);
+ public void TrackDuration(double durationMs);

ILdAiConfigTracker is a public interface. Callers that invoke TrackDuration(...) are unaffected — a float widens implicitly to double. Anyone who implements ILdAiConfigTracker (custom or test trackers) must update their override signature. This aligns the tracker duration type with MetricSummary.DurationMs/TimeToFirstTokenMs (already double?) and avoids the precision loss of the previous (float)sw.Elapsed.TotalMilliseconds cast.

Migration

Update any custom ILdAiConfigTracker implementation's TrackDuration signature:

Before:

public void TrackDuration(float durationMs) { /* ... */ }

After:

public void TrackDuration(double durationMs) { /* ... */ }

No changes are required for code that only calls TrackDuration, TrackMetricsOf, or uses the SDK-provided tracker.

How to test

dotnet test pkgs/sdk/server-ai/test/LaunchDarkly.ServerSdk.Ai.Tests.csproj --framework net8.0

9 new test cases covering backward compat, new field population, auto tool call tracking, duration override vs. stopwatch fallback, extractor failure semantics, and summary accumulation.

Known limitations

The only breaking change is the ILdAiConfigTracker.TrackDuration signature (floatdouble), which affects custom implementations of the interface — see Migration above. All other changes are additive: existing constructors and call sites compile and behave identically.


Note

Medium Risk
Public API break on ILdAiConfigTracker.TrackDuration affects custom implementations; TrackMetricsOf error/duration semantics changed in ways that alter emitted metrics for extractor failures.

Overview
Brings the .NET server AI SDK’s metric tracking in line with the JS, Python, and Java SDKs.

Breaking: ILdAiConfigTracker.TrackDuration now takes double instead of float (call sites widen implicitly; custom interface implementations must update). Duration is recorded without casting stopwatch results to float.

Additive: AiMetrics gains optional ToolCalls and DurationMs. MetricSummary adds accumulated ToolCalls (from TrackToolCall) and ResumptionToken. TrackMetricsOf auto-emits tool-call events from AiMetrics.ToolCalls, uses DurationMs when set, stops the timer before the extractor so slow extractors don’t inflate duration, and treats operation failures (duration + error) separately from extractor failures (duration only, no generation error). TrackToolCall appends keys under a lock for summary snapshots.

Reviewed by Cursor Bugbot for commit 0a17bde. Bugbot is set up for automated code reviews on this repo. Configure here.

…alls, duration override, and resumption token
@mattrmc1 mattrmc1 marked this pull request as ready for review June 29, 2026 22:57
@mattrmc1 mattrmc1 requested a review from a team as a code owner June 29, 2026 22:57

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit b2c7bc2. Configure here.

Comment thread pkgs/sdk/server-ai/src/LdAiConfigTracker.cs Outdated
Comment thread pkgs/sdk/server-ai/src/Tracking/AiMetrics.cs
@tanderson-ld

Copy link
Copy Markdown
Contributor

Jason should be able to review before you merge.

@jsonbailey jsonbailey left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Take a look at the tool call list comment, other than that this seems okay.

Comment thread pkgs/sdk/server-ai/src/Tracking/AiMetrics.cs
<!-- CURSOR_SUMMARY -->
> [!NOTE]
> **Medium Risk**
> Public interface signature change on ILdAiConfigTracker is a
binary-breaking change for external implementers, though call sites
passing float literals still compile via implicit widening.
> 
> **Overview**
> **`ILdAiConfigTracker.TrackDuration`** and
**`LdAiConfigTracker.TrackDuration`** now take **`double durationMs`**
instead of **`float`**, matching how durations are already stored
internally and how **`Stopwatch.Elapsed.TotalMilliseconds`** /
**`AiMetrics.DurationMs`** are produced.
> 
> Internal call sites in **`TrackDurationOf`** and **`TrackMetricsOf`**
no longer cast millisecond values to **`float`** before recording. Unit
tests were updated to pass **`double`** literals where they exercise
**`TrackDuration`** directly.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
f6cb49d. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
@mattrmc1 mattrmc1 changed the title feat: extend AiMetrics, MetricSummary, and TrackMetricsOf feat!: extend AiMetrics, MetricSummary, and TrackMetricsOf Jul 7, 2026
@mattrmc1 mattrmc1 requested a review from jsonbailey July 7, 2026 16:27
Comment thread pkgs/sdk/server-ai/src/LdAiConfigTracker.cs Outdated
@mattrmc1 mattrmc1 requested a review from jsonbailey July 7, 2026 19:13
@mattrmc1 mattrmc1 merged commit 2e307c9 into main Jul 7, 2026
17 of 18 checks passed
@mattrmc1 mattrmc1 deleted the mmccarthy/AIC-2727/extend-metrics-ai-metrics branch July 7, 2026 19:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants