Skip to content

Commit 4c96dca

Browse files
committed
fix: record operation duration when trackMetricsOf extractor throws
1 parent c21fdd7 commit 4c96dca

2 files changed

Lines changed: 22 additions & 3 deletions

File tree

lib/sdk/server-ai/src/main/java/com/launchdarkly/sdk/server/ai/internal/LDAIConfigTrackerImpl.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -318,9 +318,16 @@ public <T> T trackMetricsOf(
318318
// Capture operation duration immediately so a slow extractor does not inflate the metric.
319319
long operationElapsedMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
320320

321-
// Extractor exceptions propagate to the caller — do NOT catch them here.
322-
// Do NOT call trackError() on extractor failure; the AI operation itself succeeded.
323-
AIMetrics metrics = Objects.requireNonNull(metricsExtractor.apply(result), "metricsExtractor returned null");
321+
// Extractor exceptions propagate to the caller, but the operation's duration must still be
322+
// recorded — the AI operation itself succeeded, only the user-supplied extractor failed.
323+
// Do NOT call trackError(); that signals the operation failed, which is not what happened.
324+
AIMetrics metrics;
325+
try {
326+
metrics = Objects.requireNonNull(metricsExtractor.apply(result), "metricsExtractor returned null");
327+
} catch (RuntimeException e) {
328+
trackDuration(Duration.ofMillis(operationElapsedMs));
329+
throw e;
330+
}
324331

325332
// Duration: prefer runner-reported value (§1.1.13.2), fall back to wall-clock.
326333
if (metrics.getDurationMs() != null) {

lib/sdk/server-ai/src/test/java/com/launchdarkly/sdk/server/ai/internal/LDAIConfigTrackerImplTest.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,18 @@ public void trackMetricsOfExtractorExceptionPropagatesAndDoesNotCallTrackError()
545545
verify(client, never()).trackMetric(eq("$ld:ai:generation:success"), any(), any(), anyDouble());
546546
}
547547

548+
@Test
549+
public void trackMetricsOfRecordsDurationWhenExtractorThrows() {
550+
try {
551+
tracker.trackMetricsOf(
552+
r -> { throw new RuntimeException("extractor failed"); },
553+
() -> "ok");
554+
} catch (Exception e) {
555+
// expected; we care that duration was recorded before the throw
556+
}
557+
verify(client).trackMetric(eq("$ld:ai:duration:total"), any(), any(), anyDouble());
558+
}
559+
548560
@Test
549561
public void trackMetricsOfTracksToolCalls() throws Exception {
550562
AIMetrics metrics = AIMetrics.builder()

0 commit comments

Comments
 (0)