Skip to content

Commit 3ace063

Browse files
ctawiahcursoragent
andcommitted
refactor: address PR #173 review feedback (AIC-2663)
- Return the caller's default config (not a hard-disabled config) on AI Config mode mismatch, per the recent spec change; drop the now-unused disabledConfig helper and update the LDAIClient docs/test accordingly. - Remove the unnecessary try/catch around the SDK-info trackMetric call in the constructor (the call cannot throw) and the test that only passed via a throwing mock. - Use release-please block markers on the AISdkInfo VERSION line and register AISdkInfo.java in the package's extra-files so the version is actually bumped. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent ac6b827 commit 3ace063

4 files changed

Lines changed: 28 additions & 45 deletions

File tree

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@
1414
* variables (and the evaluation context, exposed to templates as {@code ldctx}), and returns a
1515
* strongly-typed config.
1616
* <p>
17-
* When the flag is absent or cannot be evaluated, the caller-supplied default is returned as the
18-
* corresponding config type. When the variation's mode does not match the requested kind, a
19-
* disabled config of the requested type is returned and a warning is logged; a config is never
20-
* returned in a state that would force the caller into a {@code NullPointerException}.
17+
* When the flag is absent, cannot be evaluated, or its mode does not match the requested kind, the
18+
* caller-supplied default is returned as the corresponding config type (a warning is logged on a
19+
* mode mismatch); a config is never returned in a state that would force the caller into a
20+
* {@code NullPointerException}.
2121
* <p>
2222
* Implementations are thread-safe.
2323
*/

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

Lines changed: 8 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -78,18 +78,12 @@ public LDAIClientImpl(LDClientInterface client, LDLogger logger) {
7878
this.logger = Objects.requireNonNull(logger, "logger");
7979
this.interpolator = new Interpolator();
8080

81-
// Report SDK info once. Guard it: if the base client is not yet fully initialized, a track call
82-
// must never propagate an exception out of this constructor.
83-
try {
84-
LDValue info = LDValue.buildObject()
85-
.put("aiSdkName", AISdkInfo.NAME)
86-
.put("aiSdkVersion", AISdkInfo.VERSION)
87-
.put("aiSdkLanguage", AISdkInfo.LANGUAGE)
88-
.build();
89-
client.trackMetric(TRACK_SDK_INFO, INIT_TRACK_CONTEXT, info, 1);
90-
} catch (Exception e) {
91-
this.logger.warn("Unable to record AI SDK info event: {}", e.toString());
92-
}
81+
LDValue info = LDValue.buildObject()
82+
.put("aiSdkName", AISdkInfo.NAME)
83+
.put("aiSdkVersion", AISdkInfo.VERSION)
84+
.put("aiSdkLanguage", AISdkInfo.LANGUAGE)
85+
.build();
86+
client.trackMetric(TRACK_SDK_INFO, INIT_TRACK_CONTEXT, info, 1);
9387
}
9488

9589
@Override
@@ -178,9 +172,9 @@ private AIConfig evaluate(
178172
Mode flagMode = parsed.getMode() != null ? parsed.getMode() : Mode.COMPLETION;
179173
if (flagMode != mode) {
180174
logger.warn(
181-
"AI Config mode mismatch for {}: expected {}, got {}. Returning disabled config.",
175+
"AI Config mode mismatch for {}: expected {}, got {}. Returning default config.",
182176
key, mode.getWireValue(), flagMode.getWireValue());
183-
return disabledConfig(key, mode);
177+
return buildConfigFromDefault(key, mode, defaultValue, context, variables);
184178
}
185179

186180
return buildConfig(key, mode, parsed, context, variables);
@@ -276,18 +270,6 @@ private AIConfig buildConfigFromDefault(
276270
}
277271
}
278272

279-
private AIConfig disabledConfig(String key, Mode mode) {
280-
switch (mode) {
281-
case AGENT:
282-
return new AIAgentConfig(key, false, null, null, null, null, null, TRACKER_FACTORY);
283-
case JUDGE:
284-
return new AIJudgeConfig(key, false, null, null, null, null, TRACKER_FACTORY);
285-
case COMPLETION:
286-
default:
287-
return new AICompletionConfig(key, false, null, null, null, null, null, TRACKER_FACTORY);
288-
}
289-
}
290-
291273
private List<Message> interpolateMessages(
292274
List<Message> messages, Map<String, Object> variables, LDContext context) {
293275
if (messages == null) {

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@ public final class AISdkInfo {
2525
* lookup so that it resolves correctly in unit tests and when the classes are used outside the
2626
* packaged jar.
2727
*/
28-
// x-release-please-version
28+
// x-release-please-start-version
2929
public static final String VERSION = "0.1.0";
30+
// x-release-please-end
3031

3132
private AISdkInfo() {
3233
}

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

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -71,15 +71,6 @@ public void constructorEmitsSdkInfoEvent() {
7171
verify(client).trackMetric(eq("$ld:ai:sdk:info"), any(LDContext.class), eq(expected), eq(1.0));
7272
}
7373

74-
@Test
75-
public void constructorDoesNotThrowWhenSdkInfoTrackingFails() {
76-
LDClientInterface throwingClient = mock(LDClientInterface.class);
77-
org.mockito.Mockito.doThrow(new RuntimeException("not initialized"))
78-
.when(throwingClient).trackMetric(eq("$ld:ai:sdk:info"), any(), any(), eq(1.0));
79-
// Must not propagate out of the constructor.
80-
new LDAIClientImpl(throwingClient, logger);
81-
}
82-
8374
// ---- Usage events ---------------------------------------------------------
8475

8576
@Test
@@ -174,18 +165,27 @@ public void judgeConfigResolvesFirstNonBlankEvaluationMetricKey() {
174165
// ---- Mode validation ------------------------------------------------------
175166

176167
@Test
177-
public void modeMismatchReturnsDisabledConfigAndWarnsOnce() {
168+
public void modeMismatchReturnsDefaultConfigAndWarnsOnce() {
178169
String agentJson = "{\"_ldMeta\":{\"enabled\":true,\"mode\":\"agent\"},"
179170
+ "\"instructions\":\"hi\"}";
180171
when(client.jsonValueVariation(anyString(), any(), any())).thenReturn(LDValue.parse(agentJson));
181172

182-
// Requesting a completion config against an agent-mode flag.
183-
AICompletionConfig config = ai.completionConfig("key", context, null, null);
173+
// Requesting a completion config against an agent-mode flag returns the caller's default.
174+
AICompletionConfigDefault dflt = AICompletionConfigDefault.builder()
175+
.enabled(true)
176+
.model(Model.builder("default-model").build())
177+
.messages(Arrays.asList(new Message(Message.Role.SYSTEM, "default {{x}}")))
178+
.build();
179+
Map<String, Object> variables = new HashMap<>();
180+
variables.put("x", "value");
181+
182+
AICompletionConfig config = ai.completionConfig("key", context, dflt, variables);
184183

185184
assertThat(config, is(notNullValue()));
186-
assertThat(config.isEnabled(), is(false));
187-
assertThat(config.getMessages(), is(nullValue()));
188185
assertThat(config.getMode(), is(Mode.COMPLETION));
186+
assertThat(config.isEnabled(), is(true));
187+
assertThat(config.getModel().getName(), is("default-model"));
188+
assertThat(config.getMessages().get(0).getContent(), is("default value"));
189189
assertThat(warnings(), hasSize(1));
190190
assertThat(warnings().get(0), containsString("mode mismatch"));
191191
}

0 commit comments

Comments
 (0)