11package com .launchdarkly .sdk .server .ai .internal ;
22
33import com .launchdarkly .sdk .LDContext ;
4+ import com .launchdarkly .sdk .LDContextEncoder ;
45import com .launchdarkly .sdk .server .ai .internal .mustache .Mustache ;
56import com .launchdarkly .sdk .server .ai .internal .mustache .Template ;
67
@@ -61,7 +62,7 @@ public String interpolate(String template, Map<String, Object> variables, LDCont
6162 merged .putAll (variables );
6263 }
6364 // ldctx is added last so it always wins over any caller-supplied "ldctx" entry.
64- merged .put ("ldctx" , contextToMap (context ));
65+ merged .put ("ldctx" , LDContextEncoder . encode (context ));
6566 return render (template , merged );
6667 }
6768
@@ -83,51 +84,4 @@ private String render(String template, Map<String, Object> variables) {
8384 Template compiled = templateCache .computeIfAbsent (template , compiler ::compile );
8485 return compiled .execute (variables );
8586 }
86-
87- /**
88- * Encodes the evaluation context directly into the nested map structure exposed to templates as
89- * {@code ldctx}, without round-tripping through JSON serialization. A single-kind context becomes
90- * a map of its attributes; a multi-kind context becomes
91- * {@code {"kind":"multi", "key":<fully-qualified key>, <kind>: {...}}} with one nested map per
92- * individual context.
93- */
94- private static Map <String , Object > contextToMap (LDContext context ) {
95- if (context == null || !context .isValid ()) {
96- return new HashMap <>();
97- }
98- if (context .isMultiple ()) {
99- Map <String , Object > map = new HashMap <>();
100- map .put ("kind" , "multi" );
101- map .put ("key" , context .getFullyQualifiedKey ());
102- int count = context .getIndividualContextCount ();
103- for (int i = 0 ; i < count ; i ++) {
104- LDContext individual = context .getIndividualContext (i );
105- if (individual != null ) {
106- // Mirror LaunchDarkly's standard context JSON: the per-kind objects nested under a
107- // multi-kind context omit "kind" because it is already implied by the property key.
108- map .put (individual .getKind ().toString (), singleContextToMap (individual , false ));
109- }
110- }
111- return map ;
112- }
113- return singleContextToMap (context , true );
114- }
115-
116- private static Map <String , Object > singleContextToMap (LDContext context , boolean includeKind ) {
117- Map <String , Object > map = new HashMap <>();
118- if (includeKind ) {
119- map .put ("kind" , context .getKind ().toString ());
120- }
121- map .put ("key" , context .getKey ());
122- if (context .getName () != null ) {
123- map .put ("name" , context .getName ());
124- }
125- map .put ("anonymous" , context .isAnonymous ());
126- // Custom attribute values can be arbitrary JSON; convert each LDValue to a plain Java value
127- // (depth-capped) so nested objects/arrays remain addressable from templates.
128- for (String attribute : context .getCustomAttributeNames ()) {
129- map .put (attribute , LDValueConverter .toJavaObject (context .getValue (attribute )));
130- }
131- return map ;
132- }
13387}
0 commit comments