feat: vendor Mustache templating engine & add Interpolator (AIC-2695)#172
feat: vendor Mustache templating engine & add Interpolator (AIC-2695)#172ctawiah wants to merge 4 commits into
Conversation
| @@ -0,0 +1,43 @@ | |||
| This product includes vendored third-party source code. The relevant licenses | |||
There was a problem hiding this comment.
This was only added to give attribution to the vendored lib but if theres existing attribution process, I'm happy to switch to that.
|
|
||
| /** | ||
| * Renders AI Config message and instruction templates using Mustache, following the cross-SDK | ||
| * interpolation policy shared with the JS and Python SDKs: |
There was a problem hiding this comment.
| * interpolation policy shared with the JS and Python SDKs: | |
| * interpolation policy shared with other SDKs: |
| if (context == null || !context.isValid()) { | ||
| return new HashMap<>(); | ||
| } | ||
| LDValue asValue = LDValue.parse(JsonSerialization.serialize(context)); |
There was a problem hiding this comment.
We shouldn't need to go through serialization to get to a map. That's not very efficient. This should probably be something that walks the graph. At the leafs you can use your LDValueConverter in the target PR.
There was a problem hiding this comment.
Consider putting this as a utility in common package next to the context implementation.
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 b8778a2. Configure here.
Vendor the com.samskivert:jmustache:1.15 source into the relocated internal package com.launchdarkly.sdk.server.ai.internal.mustache instead of linking the external artifact, per the SDK team's supply-chain guidance. The library is now compiled from source and ships inside this jar with no third-party runtime dependency, and compiles to Java 8 bytecode against our target (avoiding the class-version mismatch the external jar hit on JDK 8). Also (re)adds the internal Interpolator, which renders AI Config message and instruction templates using the cross-SDK policy (no HTML escaping, missing/null render empty, reserved ldctx wins) and caches compiled templates. - Vendored source kept byte-for-byte from upstream aside from a provenance banner and the relocated package declaration. - THIRD-PARTY-NOTICES.txt records the upstream BSD 3-Clause license. - Vendored package excluded from checkstyle; internal package already excluded from the published javadoc/sources jars. Co-authored-by: Cursor <cursoragent@cursor.com>
Build the ldctx template variable by walking the LDContext's attributes
directly instead of serializing it to a JSON string and parsing it back
into an LDValue. This removes the serialize-then-deserialize round trip
flagged in review, following the generic-encoder shape used by the iOS
SDK. Custom attribute values are still converted via LDValueConverter so
nested objects/arrays remain addressable, and multi-kind contexts are
exposed as {"kind":"multi", <kind>: {...}}.
Co-authored-by: Cursor <cursoragent@cursor.com>
b8778a2 to
97d46c1
Compare
Co-authored-by: Cursor <cursoragent@cursor.com>
…ts (AIC-2695)
Match LaunchDarkly's standard context JSON, where per-kind objects under a
multi-kind context omit "kind" (it is implied by the property key). Keeps
{{ldctx.<kind>.kind}} consistent with the JS and Python SDKs.
Co-authored-by: Cursor <cursoragent@cursor.com>

Requirements
Related issues
AIC-2695 — Vendor the Mustache templating library + add the Interpolator. Part of epic AIC-2629.
Describe the solution you've provided
com.launchdarkly.sdk.server.ai.internal.mustache(7 files). Kept byte-for-byte from upstream1.15aside from a provenance banner and the relocatedpackagedeclaration. It is now compiled from source and ships inside this jar with no third-party runtime dependency.targetCompatibility = 1.8, the vendored classes are emitted as class-version 52 (Java 8). This sidesteps theUnsupportedClassVersionErrorthe external1.16jar hit on JDK 8.Interpolator(internal) renders AI Config message/instruction templates using the cross-SDK policy shared with JS/Python: no HTML escaping, missing/null variables render empty, and the reservedldctxvariable (derived from the eval context) always wins. Compiled templates are cached in aConcurrentHashMap; the compiler and compiledTemplates are immutable/thread-safe.THIRD-PARTY-NOTICES.txtrecords the upstream BSD 3-Clause license (Copyright (c) 2010 Michael Bayne).internal/**exclusion for the published javadoc/sources jars. Verified the main jar contains the compiled vendored classes while the sources jar excludes all internal code.Validated with
./gradlew clean build(checkstyle + all unit tests, including the 10InterpolatorTestparity cases, green).Describe alternatives you've considered
Additional context
This must land before the v1.0 release (AIC-2666).
Made with Cursor
Note
Medium Risk
Large in-tree third-party surface and template rendering will affect AI prompt text once wired, though changes stay internal with tests; supply-chain risk is reduced by removing the external artifact.
Overview
Vendors JMustache 1.15 into
com.launchdarkly.sdk.server.ai.internal.mustacheso AI Config templating ships in the jar with nocom.samskivert:jmustacheruntime dependency, and documents the BSD license inTHIRD-PARTY-NOTICES.txt.Adds an internal
Interpolatorthat renders Mustache templates for AI messages/instructions using the cross-SDK rules: no HTML escaping, missing/null → empty string, andldctxbuilt fromLDContext(including multi-kind layout) merged last so it overrides caller-suppliedldctx. Templates are compiled once and cached in aConcurrentHashMap.build.gradleupdates the Mustache supply-chain note and excludes the vendored package fromcheckstyleMain.InterpolatorTestlocks parity behavior (escaping,ldctx, nesting, cache).Reviewed by Cursor Bugbot for commit 04326f0. Bugbot is set up for automated code reviews on this repo. Configure here.