@@ -45,6 +45,9 @@ public final class LDAIClientImpl implements LDAIClient {
4545 private static final String TRACK_USAGE_AGENT_CONFIG = "$ld:ai:usage:agent-config" ;
4646 private static final String TRACK_USAGE_AGENT_CONFIGS = "$ld:ai:usage:agent-configs" ;
4747 private static final String TRACK_USAGE_JUDGE_CONFIG = "$ld:ai:usage:judge-config" ;
48+ private static final String TRACK_USAGE_COMPLETION_CONFIG_TEMPLATE = "$ld:ai:usage:completion-config-template" ;
49+ private static final String TRACK_USAGE_AGENT_CONFIG_TEMPLATE = "$ld:ai:usage:agent-config-template" ;
50+ private static final String TRACK_USAGE_JUDGE_CONFIG_TEMPLATE = "$ld:ai:usage:judge-config-template" ;
4851
4952 private static final LDContext INIT_TRACK_CONTEXT = LDContext
5053 .builder ("ld-internal-tracking" )
@@ -94,7 +97,7 @@ public AICompletionConfig completionConfig(
9497 client .trackMetric (TRACK_USAGE_COMPLETION_CONFIG , context , LDValue .of (key ), 1 );
9598 AICompletionConfigDefault effectiveDefault =
9699 defaultValue != null ? defaultValue : AICompletionConfigDefault .disabled ();
97- return (AICompletionConfig ) evaluate (key , context , effectiveDefault , Mode .COMPLETION , variables );
100+ return (AICompletionConfig ) evaluate (key , context , effectiveDefault , Mode .COMPLETION , variables , true );
98101 }
99102
100103 @ Override
@@ -137,14 +140,47 @@ public AIJudgeConfig judgeConfig(
137140 client .trackMetric (TRACK_USAGE_JUDGE_CONFIG , context , LDValue .of (key ), 1 );
138141 AIJudgeConfigDefault effectiveDefault =
139142 defaultValue != null ? defaultValue : AIJudgeConfigDefault .disabled ();
140- return (AIJudgeConfig ) evaluate (key , context , effectiveDefault , Mode .JUDGE , variables );
143+ return (AIJudgeConfig ) evaluate (key , context , effectiveDefault , Mode .JUDGE , variables , true );
144+ }
145+
146+ @ Override
147+ public AICompletionConfig completionConfigTemplate (
148+ String key ,
149+ LDContext context ,
150+ AICompletionConfigDefault defaultValue ) {
151+ client .trackMetric (TRACK_USAGE_COMPLETION_CONFIG_TEMPLATE , context , LDValue .of (key ), 1 );
152+ AICompletionConfigDefault effectiveDefault =
153+ defaultValue != null ? defaultValue : AICompletionConfigDefault .disabled ();
154+ return (AICompletionConfig ) evaluate (key , context , effectiveDefault , Mode .COMPLETION , null , false );
155+ }
156+
157+ @ Override
158+ public AIAgentConfig agentConfigTemplate (
159+ String key ,
160+ LDContext context ,
161+ AIAgentConfigDefault defaultValue ) {
162+ client .trackMetric (TRACK_USAGE_AGENT_CONFIG_TEMPLATE , context , LDValue .of (key ), 1 );
163+ AIAgentConfigDefault effectiveDefault =
164+ defaultValue != null ? defaultValue : AIAgentConfigDefault .disabled ();
165+ return (AIAgentConfig ) evaluate (key , context , effectiveDefault , Mode .AGENT , null , false );
166+ }
167+
168+ @ Override
169+ public AIJudgeConfig judgeConfigTemplate (
170+ String key ,
171+ LDContext context ,
172+ AIJudgeConfigDefault defaultValue ) {
173+ client .trackMetric (TRACK_USAGE_JUDGE_CONFIG_TEMPLATE , context , LDValue .of (key ), 1 );
174+ AIJudgeConfigDefault effectiveDefault =
175+ defaultValue != null ? defaultValue : AIJudgeConfigDefault .disabled ();
176+ return (AIJudgeConfig ) evaluate (key , context , effectiveDefault , Mode .JUDGE , null , false );
141177 }
142178
143179 private AIAgentConfig evaluateAgent (
144180 String key , LDContext context , AIAgentConfigDefault defaultValue , Map <String , Object > variables ) {
145181 AIAgentConfigDefault effectiveDefault =
146182 defaultValue != null ? defaultValue : AIAgentConfigDefault .disabled ();
147- return (AIAgentConfig ) evaluate (key , context , effectiveDefault , Mode .AGENT , variables );
183+ return (AIAgentConfig ) evaluate (key , context , effectiveDefault , Mode .AGENT , variables , true );
148184 }
149185
150186 /**
@@ -157,14 +193,15 @@ private AIConfig evaluate(
157193 LDContext context ,
158194 AIConfigDefault defaultValue ,
159195 Mode mode ,
160- Map <String , Object > variables ) {
196+ Map <String , Object > variables ,
197+ boolean interpolate ) {
161198 LDValue value = client .jsonValueVariation (key , context , LDValue .ofNull ());
162199
163200 // A valid AI Config variation is always a JSON object (it carries the _ldMeta block). When the
164201 // flag is absent or cannot be evaluated the base SDK hands back our null sentinel; in that case
165202 // we return the caller's typed default directly rather than serializing it and parsing it back.
166203 if (value == null || value .getType () != LDValueType .OBJECT ) {
167- return buildConfigFromDefault (key , mode , defaultValue , context , variables );
204+ return buildConfigFromDefault (key , mode , defaultValue , context , variables , interpolate );
168205 }
169206
170207 AIConfigFlagValue parsed = AIConfigParser .parse (value );
@@ -174,18 +211,19 @@ private AIConfig evaluate(
174211 logger .warn (
175212 "AI Config mode mismatch for {}: expected {}, got {}. Returning default config." ,
176213 key , mode .getWireValue (), flagMode .getWireValue ());
177- return buildConfigFromDefault (key , mode , defaultValue , context , variables );
214+ return buildConfigFromDefault (key , mode , defaultValue , context , variables , interpolate );
178215 }
179216
180- return buildConfig (key , mode , parsed , context , variables );
217+ return buildConfig (key , mode , parsed , context , variables , interpolate );
181218 }
182219
183220 private AIConfig buildConfig (
184221 String key ,
185222 Mode mode ,
186223 AIConfigFlagValue parsed ,
187224 LDContext context ,
188- Map <String , Object > variables ) {
225+ Map <String , Object > variables ,
226+ boolean interpolate ) {
189227 Supplier <LDAIConfigTracker > factory = trackerFactory (
190228 key , parsed .getVariationKey (), parsed .getVersion (),
191229 parsed .getModel (), parsed .getProvider (), context );
@@ -196,7 +234,8 @@ private AIConfig buildConfig(
196234 parsed .isEnabled (),
197235 parsed .getModel (),
198236 parsed .getProvider (),
199- interpolate (parsed .getInstructions (), variables , context ),
237+ interpolate ? interpolate (parsed .getInstructions (), variables , context )
238+ : parsed .getInstructions (),
200239 parsed .getJudgeConfiguration (),
201240 parsed .getTools (),
202241 factory );
@@ -206,7 +245,8 @@ private AIConfig buildConfig(
206245 parsed .isEnabled (),
207246 parsed .getModel (),
208247 parsed .getProvider (),
209- interpolateMessages (parsed .getMessages (), variables , context ),
248+ interpolate ? interpolateMessages (parsed .getMessages (), variables , context )
249+ : parsed .getMessages (),
210250 parsed .getEvaluationMetricKey (),
211251 factory );
212252 case COMPLETION :
@@ -216,7 +256,8 @@ private AIConfig buildConfig(
216256 parsed .isEnabled (),
217257 parsed .getModel (),
218258 parsed .getProvider (),
219- interpolateMessages (parsed .getMessages (), variables , context ),
259+ interpolate ? interpolateMessages (parsed .getMessages (), variables , context )
260+ : parsed .getMessages (),
220261 parsed .getJudgeConfiguration (),
221262 parsed .getTools (),
222263 factory );
@@ -225,14 +266,16 @@ private AIConfig buildConfig(
225266
226267 /**
227268 * Builds the typed config straight from the caller-supplied default, used when the flag is absent
228- * or cannot be evaluated. Prompt content is interpolated exactly as it is for an evaluated flag.
269+ * or cannot be evaluated. Prompt content is interpolated exactly as it is for an evaluated flag,
270+ * unless {@code interpolate} is {@code false} (template mode).
229271 */
230272 private AIConfig buildConfigFromDefault (
231273 String key ,
232274 Mode mode ,
233275 AIConfigDefault defaultValue ,
234276 LDContext context ,
235- Map <String , Object > variables ) {
277+ Map <String , Object > variables ,
278+ boolean interpolate ) {
236279 // Default configs still get real trackers — the configKey was requested even if no flag was found.
237280 // variationKey is null because no flag evaluation occurred.
238281 Supplier <LDAIConfigTracker > factory = trackerFactory (key , null , null , null , null , context );
@@ -244,7 +287,8 @@ private AIConfig buildConfigFromDefault(
244287 agent .isEnabled (),
245288 agent .getModel (),
246289 agent .getProvider (),
247- interpolate (agent .getInstructions (), variables , context ),
290+ interpolate ? interpolate (agent .getInstructions (), variables , context )
291+ : agent .getInstructions (),
248292 agent .getJudgeConfiguration (),
249293 agent .getTools (),
250294 factory );
@@ -256,7 +300,8 @@ private AIConfig buildConfigFromDefault(
256300 judge .isEnabled (),
257301 judge .getModel (),
258302 judge .getProvider (),
259- interpolateMessages (judge .getMessages (), variables , context ),
303+ interpolate ? interpolateMessages (judge .getMessages (), variables , context )
304+ : judge .getMessages (),
260305 judge .getEvaluationMetricKey (),
261306 factory );
262307 }
@@ -268,7 +313,8 @@ private AIConfig buildConfigFromDefault(
268313 completion .isEnabled (),
269314 completion .getModel (),
270315 completion .getProvider (),
271- interpolateMessages (completion .getMessages (), variables , context ),
316+ interpolate ? interpolateMessages (completion .getMessages (), variables , context )
317+ : completion .getMessages (),
272318 completion .getJudgeConfiguration (),
273319 completion .getTools (),
274320 factory );
0 commit comments