Commit d954ba6
feat: AgentControl data model, parsing & interpolation (AIC-2662) (#171)
**Requirements**
- [x] I have added test coverage for new or changed functionality
- [x] I have followed the repository's [pull request submission
guidelines](../blob/main/CONTRIBUTING.md#submitting-pull-requests)
- [x] I have validated my changes against all supported platform
versions
**Related issues**
- Implements
[AIC-2662](https://launchdarkly.atlassian.net/browse/AIC-2662) — Step 2
of the Java AI SDK (epic
[AIC-2629](https://launchdarkly.atlassian.net/browse/AIC-2629)).
- Foundation (AIC-2661, #170) has merged; this PR is now based on
`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 `LDAIClient` methods, tracker, or
evaluation (those are AIC-2663+).
> **Scope note:** Mustache interpolation has been **pulled out of this
PR**. Per the SDK team's supply-chain guidance we will not link the
external `com.samskivert:jmustache` artifact. The Mustache engine will
be **vendored** (source copied into an internal, relocated package)
together with the `Interpolator` in
[AIC-2695](https://launchdarkly.atlassian.net/browse/AIC-2695), which
gates the v1.0 release (AIC-2666). This PR therefore has **no
third-party templating dependency**.
_Public data model_ — `com.launchdarkly.sdk.server.ai.datamodel`
(immutable, builder-based, documented):
- `LDMessage` (`Role` enum + content), `ModelConfig`
(name/parameters/custom), `ProviderConfig`, `ToolConfig` (root-level
tool), `JudgeConfiguration` (+ nested `Judge`), and the `AIConfigMode`
enum.
_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 to `Long`,
otherwise `Double`.
- `AIConfigParser` + `AIConfigFlagValue` — `LDValue` → strongly-typed
parse. Malformed/missing/wrong-typed fields **never throw**; tools fall
back to `model.parameters.tools[]`; `evaluationMetricKey` resolves to
the first non-blank of `evaluationMetricKey` / `evaluationMetricKeys[]`;
`_ldMeta` scalars are boxed to preserve tri-state `enabled` (absent ≠
false).
**Design decisions (documented in code)**
- **Data-model placement:** kept in `...server.ai.datamodel` rather than
extracting a shared module now, to avoid premature abstraction; can
relocate if a client-side AI SDK ever needs it.
- **Boxed vs primitive:** `_ldMeta` scalars (`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.
- **Number precision:** whole numbers outside ±2^53 cannot be
represented exactly and decode to the nearest `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.
[AIC-2662]:
https://launchdarkly.atlassian.net/browse/AIC-2662?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
[AIC-2629]:
https://launchdarkly.atlassian.net/browse/AIC-2629?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
[AIC-2695]:
https://launchdarkly.atlassian.net/browse/AIC-2695?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
<!-- CURSOR_SUMMARY -->
---
> [!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.gradle` documents vendoring Mustache in AIC-2695
instead of linking `jmustache`.
>
> **Public API** — New `LDAIConfigTypes` in `datamodel` groups immutable
nested types: `Mode`, `Message` (+ `Role`), `Model`, `Provider`, `Tool`,
and `JudgeConfiguration` (+ `Judge`), with builders where needed and
`Message.withContent` reserved for future interpolation.
>
> **Internal parsing** — `AIConfigParser` maps `LDValue` →
`AIConfigFlagValue` without throwing on bad input; `LDValueConverter`
turns nested JSON into plain Java maps/lists with depth cap (`MAX_DEPTH
= 100`) and integral-number handling within ±2^53. Parsing covers
`_ldMeta` (boxed `enabled` for absent vs false), tools (root `tools`
over `model.parameters.tools[]`), and evaluation metric key (scalar then
first non-blank in `evaluationMetricKeys[]`).
>
> **Build** — Javadoc no longer sets `failOnError = false` now that
public types exist; empty `sonatype*` entries removed from
`gradle.properties`. Unit tests cover parser and converter edge cases.
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
69416f0. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
---------
Co-authored-by: Cursor <cursoragent@cursor.com>1 parent f40c86e commit d954ba6
8 files changed
Lines changed: 1647 additions & 13 deletions
File tree
- lib/sdk/server-ai
- src
- main/java/com/launchdarkly/sdk/server/ai
- datamodel
- internal
- test/java/com/launchdarkly/sdk/server/ai/internal
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
45 | 45 | | |
46 | 46 | | |
47 | 47 | | |
48 | | - | |
49 | | - | |
50 | | - | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
51 | 53 | | |
52 | 54 | | |
53 | 55 | | |
| |||
74 | 76 | | |
75 | 77 | | |
76 | 78 | | |
77 | | - | |
78 | | - | |
79 | | - | |
80 | | - | |
81 | | - | |
82 | 79 | | |
83 | 80 | | |
84 | 81 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
4 | | - | |
5 | | - | |
6 | | - | |
7 | | - | |
8 | | - | |
0 commit comments