feat: AgentControl data model, parsing & interpolation (AIC-2662)#171
feat: AgentControl data model, parsing & interpolation (AIC-2662)#171ctawiah wants to merge 5 commits into
Conversation
…ation (AIC-2662) Implements Step 2 of the Java AI SDK: the AICONF data model plus the JSON-protocol parsing and interpolation layers. No client methods, tracker, or evaluation are included (those are later steps). Public data model (com.launchdarkly.sdk.server.ai.datamodel): - LDMessage (role enum + content), ModelConfig, ProviderConfig, ToolConfig, JudgeConfiguration (+ nested Judge), AIConfigMode. - Immutable, builder-based, documented public types. Internal parsing/interpolation (com.launchdarkly.sdk.server.ai.internal): - LDValueConverter: depth-capped LDValue -> plain Java tree. Integral numbers within +/-2^53 decode to Long, otherwise Double (precision beyond 2^53 is documented). - AIConfigParser + AIConfigFlagValue: defensive LDValue -> typed parse. Malformed/wrong-typed fields never throw; tools fall back to model.parameters.tools[]; evaluationMetricKey resolves to the first non-blank of evaluationMetricKey / evaluationMetricKeys[]. - Interpolator: jmustache engine matching the JS/Python escaping policy (no HTML escaping, missing/null -> ""), with ldctx merged last so it overrides any caller-supplied ldctx, and a thread-safe compiled-template cache. Build: - Re-add the (now audited) com.samskivert:jmustache:1.16 dependency as implementation scope; flip javadoc failOnError back on now that public types exist. Tests: 32 unit tests covering defensive parsing fallbacks, number/tool/ metric-key resolution, tri-state enabled, and interpolation parity (escaping, missing-variable, ldctx-wins). Co-authored-by: Cursor <cursoragent@cursor.com>
jmustache 1.16 is compiled for Java 9 (class file version 53.0) and throws UnsupportedClassVersionError on the Java 8 runtimes this SDK supports, which surfaced as JDK 8 test failures in CI. 1.15 is the last release compiled for Java (bytecode major 51) and exposes the same compiler API we use (escapeHTML / defaultValue), with no transitive dependencies. Co-authored-by: Cursor <cursoragent@cursor.com>
…C-2695 (AIC-2662) Per the SDK team's supply-chain guidance we will not link the external com.samskivert:jmustache artifact. Drop the dependency and the Interpolator (plus its tests) from this PR; the Mustache engine will be vendored (source copied into an internal, relocated package) together with the Interpolator in AIC-2695, which gates the v1.0 release. This PR now ships only the data model and the defensive LDValue parsing layer, neither of which depends on a templating engine. Co-authored-by: Cursor <cursoragent@cursor.com>
3ed2dc5 to
47ceb0f
Compare
…ties (AIC-2662) These empty sonatypeUsername/sonatypePassword placeholders aren't needed; the core SDK module (lib/sdk/server) carries none and publishes fine. The nexus publish plugin resolves credentials from the environment at release time. Co-authored-by: Cursor <cursoragent@cursor.com>
jsonbailey
left a comment
There was a problem hiding this comment.
Approved from me but would be good to also get Todds review.
mattrmc1
left a comment
There was a problem hiding this comment.
nit: For the .NET SDK, we opted to organize shared shapes like Message, Model, Provider, Tools, etc into a static class LdAiConfigTypes. This reduces the number of files and frees up generic class names (ie Message) within the namespace. Could do the same thing here for consistency
There was a problem hiding this comment.
Is it the case that none of the enums added in this PR are for public consumers to switch over? The reason I bring that up is adding another value to an enum is breaking in java.
There was a problem hiding this comment.
There is a high probably that we can introduce a new enum for the mode in the future. The mode is mainly for internal use and theres no plans to expose that to customers.
Group the shared AI Config shapes (Mode, Message, Model, Provider, Tool, JudgeConfiguration) as nested types under a single non-instantiable LDAIConfigTypes container instead of separate top-level classes. This reduces the file count and frees up generic names like Message and Model within the datamodel package, matching the organization used by the .NET SDK (LdAiConfigTypes). Per Matt's review on #171. Internal references (AIConfigParser, AIConfigFlagValue) and tests are updated to the nested names; behavior is unchanged. Co-authored-by: Cursor <cursoragent@cursor.com>
Requirements
Related issues
main.Describe the solution you've provided
Step 2 of the Java AI SDK: the AICONF data model and the defensive JSON-protocol parsing layer. No
LDAIClientmethods, tracker, or evaluation (those are AIC-2663+).Public data model —
com.launchdarkly.sdk.server.ai.datamodel(immutable, builder-based, documented):LDMessage(Roleenum + content),ModelConfig(name/parameters/custom),ProviderConfig,ToolConfig(root-level tool),JudgeConfiguration(+ nestedJudge), and theAIConfigModeenum.Internal parsing —
com.launchdarkly.sdk.server.ai.internal(excluded from published Javadoc/sources):LDValueConverter— depth-capped (MAX_DEPTH = 100)LDValue→ plain Java tree. Integral numbers within ±2^53 decode toLong, otherwiseDouble.AIConfigParser+AIConfigFlagValue—LDValue→ strongly-typed parse. Malformed/missing/wrong-typed fields never throw; tools fall back tomodel.parameters.tools[];evaluationMetricKeyresolves to the first non-blank ofevaluationMetricKey/evaluationMetricKeys[];_ldMetascalars are boxed to preserve tri-stateenabled(absent ≠ false).Design decisions (documented in code)
...server.ai.datamodelrather than extracting a shared module now, to avoid premature abstraction; can relocate if a client-side AI SDK ever needs it._ldMetascalars (enabled/version) are boxed in the parsed value so "absent" is distinguishable from a concrete value; resolved configs (later step) will use primitives with documented defaults.Double.Additional context
Field shapes and behaviors mirror the Python (
python-server-sdk-ai) and JS (js-core/packages/sdk/server-ai) reference SDKs for parity. 22 unit tests cover defensive parsing fallbacks and number/tool/metric-key resolution; full module build (compile + checkstyle main/test + javadoc + tests) is green.Note
Low Risk
New library surface and internal parsers only; no client evaluation or runtime wiring yet, with broad unit test coverage for malformed payloads.
Overview
Adds the Java server AI SDK data model and defensive flag-variation parsing for AI Configs (AIC-2662). Mustache interpolation is not in this PR;
build.gradledocuments vendoring Mustache in AIC-2695 instead of linkingjmustache.Public API — New
LDAIConfigTypesindatamodelgroups immutable nested types:Mode,Message(+Role),Model,Provider,Tool, andJudgeConfiguration(+Judge), with builders where needed andMessage.withContentreserved for future interpolation.Internal parsing —
AIConfigParsermapsLDValue→AIConfigFlagValuewithout throwing on bad input;LDValueConverterturns nested JSON into plain Java maps/lists with depth cap (MAX_DEPTH = 100) and integral-number handling within ±2^53. Parsing covers_ldMeta(boxedenabledfor absent vs false), tools (roottoolsovermodel.parameters.tools[]), and evaluation metric key (scalar then first non-blank inevaluationMetricKeys[]).Build — Javadoc no longer sets
failOnError = falsenow that public types exist; emptysonatype*entries removed fromgradle.properties. Unit tests cover parser and converter edge cases.Reviewed by Cursor Bugbot for commit 69416f0. Bugbot is set up for automated code reviews on this repo. Configure here.