feat: AICONF config types & LDAIClient methods (AIC-2663)#173
Conversation
b8778a2 to
97d46c1
Compare
72fc433 to
ac6b827
Compare
| return (AIJudgeConfig) evaluate(key, context, effectiveDefault, Mode.JUDGE, variables); | ||
| } | ||
|
|
||
| private AIAgentConfig evaluateAgent( |
There was a problem hiding this comment.
We moved all the config building out of the client class to keep it more focused in dotnet. Not a requirement but something to consider.
There was a problem hiding this comment.
I'm going to take that as a follow-up rather than fold it into this PR. These AI SDK PRs are stacked (config types -> tracker -> evals), and the tracker PR already touches the same buildConfig/buildConfigFromDefault paths, so extracting a ConfigFactory here would force a sizable rebase across the whole stack. I'll open a follow-up ticket to pull the building logic into its own factory (mirroring how .NET structured it) once the stacked PRs lands.
- 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>
68a0c99 to
b1b318e
Compare
Implement the public AI Config types and the LDAIClient retrieval methods that
evaluate a flag, validate its mode, interpolate prompt templates, and return a
typed config.
Config types follow the .NET-style two-hierarchy design (AIConfig /
AIConfigDefault bases): AICompletionConfig(+messages), AIAgentConfig
(+instructions), AIJudgeConfig(+evaluationMetricKey) results plus parallel
*Default builder types (a generic base builder keeps the defaults DRY), and
AIAgentConfigRequest for batch agent retrieval.
LDAIClientImpl mirrors the JS reference the spec points to:
- serialize the caller default to a flag value (with the requested mode) and
pass it through jsonValueVariation, so an absent flag yields the default and
the eval event records the correct default;
- validate mode: a mismatch logs a single warning and returns a disabled config
of the requested type (never a config that would NPE the caller);
- interpolate messages/instructions via the vendored-Mustache Interpolator,
exposing the context as {{ldctx}};
- fire the spec'd usage events ($ld:ai:usage:completion-config, :agent-config,
:agent-configs, :judge-config) and emit $ld:ai:sdk:info once at construction,
guarded so an uninitialized client can't throw from the constructor.
Design decisions (documented in the PR):
- Synchronous API (no CompletableFuture): matches the core Java server SDK and
avoids Android-API/threading concerns; variation is in-memory post-init so
agentConfigs fan-out parallelism buys ~nothing.
- No per-field merge of missing fields from the default (matches JS).
- Tracking is deferred to Step 4 (AIC-2664): LDAIConfigTracker is a placeholder
interface with an internal no-op; configs expose createTracker() so Step 4
fills in behavior without reshaping the public types.
Adds LDValueConverter.fromJavaObject (inverse conversion for default
serialization). LDAIClientImplTest covers usage events, typed retrieval,
interpolation/ldctx, mode-mismatch, default semantics, and agentConfigs.
Co-authored-by: Cursor <cursoragent@cursor.com>
…IC-2663) When a flag is absent or unevaluable, build the typed AIConfig straight from the caller's default rather than serializing the default to LDValue and parsing it back. Drops the now-unused LDValueConverter.fromJavaObject helpers. Co-authored-by: Cursor <cursoragent@cursor.com>
Update the AIConfig hierarchy, LDAIClientImpl, and tests to reference the
consolidated LDAIConfigTypes.{Mode,Message,Model,Provider,Tool,JudgeConfiguration}
types introduced on the data-model PR.
Co-authored-by: Cursor <cursoragent@cursor.com>
- 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>
3ace063 to
dfc1386
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ 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 dfc1386. Configure here.
| } | ||
| result.put( | ||
| request.getKey(), | ||
| evaluateAgent(request.getKey(), context, request.getDefaultValue(), request.getVariables())); |
There was a problem hiding this comment.
Agent batch metric overcounts
Low Severity
agentConfigs reports usage with agentConfigs.size() before the loop, but null entries in the list are skipped without evaluation. The $ld:ai:usage:agent-configs metric value and count can exceed the number of configs actually retrieved.
Reviewed by Cursor Bugbot for commit dfc1386. Configure here.


Requirements
Related issues
AIC-2663 — Step 3: AICONF config types &
LDAIClientmethods. Part of epic AIC-2629.Describe the solution you've provided
Implements the public AI Config types and the
LDAIClientretrieval methods that evaluate a flag, validate its mode, interpolate prompt templates, and return a typed config. The behavior mirrors the JS reference the spec points to..NET-style two-hierarchy design (AIConfig/AIConfigDefaultbases):AICompletionConfig(+messages),AIAgentConfig(+instructions),AIJudgeConfig(+evaluationMetricKey) result types plus parallel*Defaultbuilder types, andAIAgentConfigRequestfor batch agent retrieval. A generic base builder keeps the*Defaultbuilders DRY.LDAIClientmethods —completionConfig,agentConfig,agentConfigs,judgeConfig:mode) and pass it throughjsonValueVariation, so an absent flag yields the default and the eval event records the correct default;Interpolator, exposing the context as{{ldctx}};$ld:ai:usage:completion-config,:agent-config,:agent-configs,:judge-config) and emit$ld:ai:sdk:infoonce at construction, guarded so an uninitialized client can't throw from the constructor.evaluationMetricKeyresolves to the first non-blank entry (handled by the feat: AgentControl data model, parsing & interpolation (AIC-2662) #171 parser).Key decisions
CompletableFuture). Matches the core Java server SDK's blocking style and sidesteps Android API-level/threading concerns.variationis in-memory after init, soagentConfigsfan-out parallelism buys ~nothing while adding real complexity. Resolves the ticket's async-surface question.LDAIConfigTrackeris a placeholder interface with an internal no-op; configs exposecreateTracker()so Step 4 fills in behavior without reshaping the public config types.new LDAIClientImpl(ldClient)(interfaceLDAIClientis provided for mocking/DI). A second constructor accepts anLDLogger.Thread-safety —
LDAIClientImplholds only the thread-safe base client, a logger, and one sharedInterpolator(thread-safe template cache); every returned config is immutable.Tests —
LDAIClientImplTest(17 cases) covers usage events, SDK-info emission + constructor guard, typed retrieval, interpolation/ldctx, mode-mismatch (disabled + single warning, no NPE), default semantics (absent → default; no per-field merge), andagentConfigsordering/count.LDValueConverterTestextended for the new inverse conversion../gradlew clean buildgreen (checkstyle + Javadoc + all tests).Describe alternatives you've considered
CompletableFuturesurface — rejected for a server SDK (see decision above).Additional context
AISdkInfo.VERSIONis currently a constant kept in step withgradle.properties; wiring it to a generated/build-time version can be a follow-up.Made with Cursor
Note
Medium Risk
New public SDK API on the flag-evaluation path; incorrect defaults, mode handling, or interpolation would affect how apps load AI configs in production, though scope is additive with guarded telemetry.
Overview
Adds the public AI Config retrieval surface for the Java server AI SDK: immutable
AICompletionConfig,AIAgentConfig, andAIJudgeConfigtypes (plus matching*Defaultbuilders andAIAgentConfigRequestfor batch agents), wired throughLDAIClient/LDAIClientImpl.LDAIClientImplevaluates flags viajsonValueVariationwith a null sentinel, parses variations, validates mode (mismatch → disabled config + warning), and interpolates messages/instructions (including{{ldctx}}). Absent flags return the caller default with interpolation and no per-field merge from defaults when a variation is present. It emits$ld:ai:sdk:infoat construction (failure-safe) and$ld:ai:usage:*metrics per method;LDAIConfigTrackeris a placeholder with a no-op implementation until a later step.The README now documents constructing
LDAIClientImplfromLDClientand callingcompletionConfig.LDAIClientImplTestcovers usage events, interpolation, mode mismatch, default semantics, and orderedagentConfigs.Reviewed by Cursor Bugbot for commit ac6b827. Bugbot is set up for automated code reviews on this repo. Configure here.