@launchdarkly/server-sdk-ai 0.19.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,129 +1,110 @@
1
1
  // src/LDAIClientImpl.ts
2
- import Mustache2 from "mustache";
2
+ import Mustache from "mustache";
3
3
  import { randomUUID } from "crypto";
4
4
 
5
- // src/api/chat/TrackedChat.ts
6
- var TrackedChat = class {
7
- constructor(aiConfig, provider, judges = {}, _logger) {
8
- this.aiConfig = aiConfig;
9
- this.provider = provider;
10
- this.judges = judges;
5
+ // src/api/ManagedAgent.ts
6
+ var ManagedAgent = class {
7
+ constructor(aiAgentConfig, runner, _logger) {
8
+ this.aiAgentConfig = aiAgentConfig;
9
+ this.runner = runner;
11
10
  this._logger = _logger;
12
- this.messages = [];
13
11
  }
14
12
  /**
15
- * Invoke the chat model with a prompt string.
16
- * This method handles conversation management and tracking, delegating to the provider's invokeModel method.
13
+ * Invoke the agent with a prompt string and return a ManagedResult.
14
+ *
15
+ * `run()` resolves before `ManagedResult.evaluations` resolves. Awaiting
16
+ * `evaluations` guarantees both judge evaluation and tracker.trackJudgeResult()
17
+ * are complete.
18
+ *
19
+ * @param prompt The user input to send to the agent.
20
+ * @returns Promise resolving to ManagedResult (before evaluations settle).
17
21
  */
18
- async invoke(prompt) {
19
- const tracker = this.aiConfig.createTracker();
20
- const userMessage = {
21
- role: "user",
22
- content: prompt
23
- };
24
- this.messages.push(userMessage);
25
- const configMessages = this.aiConfig.messages || [];
26
- const allMessages = [...configMessages, ...this.messages];
27
- const response = await tracker.trackMetricsOf(
28
- (result) => result.metrics,
29
- () => this.provider.invokeModel(allMessages)
22
+ async run(prompt) {
23
+ const tracker = this.aiAgentConfig.createTracker();
24
+ const result = await tracker.trackMetricsOf(
25
+ (r) => r.metrics,
26
+ () => this.runner.run(prompt)
30
27
  );
31
- if (this.aiConfig.judgeConfiguration?.judges && this.aiConfig.judgeConfiguration.judges.length > 0) {
32
- response.evaluations = this._evaluateWithJudges(this.messages, response).then(
33
- (evaluations) => {
34
- evaluations.forEach((judgeResult) => {
35
- tracker.trackJudgeResult(judgeResult);
36
- });
37
- return evaluations;
28
+ const metrics = tracker.getSummary();
29
+ const output = result.content;
30
+ const evaluations = this.aiAgentConfig.evaluator.evaluate(prompt, output).then((results) => {
31
+ results.forEach((judgeResult) => {
32
+ if (!judgeResult.sampled) {
33
+ return;
38
34
  }
39
- );
40
- }
41
- this.messages.push(response.message);
42
- return response;
43
- }
44
- /**
45
- * Evaluates the response with all configured judges.
46
- * Returns a promise that resolves to an array of evaluation results.
47
- *
48
- * @param messages Array of messages representing the conversation history
49
- * @param response The AI response to be evaluated
50
- * @returns Promise resolving to array of judge evaluation results
51
- */
52
- async _evaluateWithJudges(messages, response) {
53
- const judgeConfigs = this.aiConfig.judgeConfiguration.judges;
54
- const evaluationPromises = judgeConfigs.map(async (judgeConfig) => {
55
- const judge = this.judges[judgeConfig.key];
56
- if (!judge) {
57
- this._logger?.warn(
58
- `Judge configuration is not enabled for ${judgeConfig.key} in ${this.aiConfig.key}`
59
- );
60
- const result = {
61
- success: false,
62
- sampled: true,
63
- errorMessage: `Judge configuration is not enabled for ${judgeConfig.key}`
64
- };
65
- return result;
66
- }
67
- return judge.evaluateMessages(messages, response, judgeConfig.samplingRate);
68
- });
69
- const results = await Promise.allSettled(evaluationPromises);
70
- return results.map((settled) => {
71
- if (settled.status === "fulfilled") {
72
- return settled.value;
73
- }
74
- const result = {
75
- success: false,
76
- sampled: true,
77
- errorMessage: "Judge evaluation failed"
78
- };
79
- return result;
35
+ tracker.trackJudgeResult(judgeResult);
36
+ });
37
+ return results;
38
+ }).catch((err) => {
39
+ this._logger?.warn("Judge evaluation failed unexpectedly:", err);
40
+ return [];
80
41
  });
42
+ return {
43
+ content: output,
44
+ metrics,
45
+ raw: result.raw,
46
+ parsed: result.parsed,
47
+ evaluations
48
+ };
81
49
  }
82
50
  /**
83
- * Get the underlying AI configuration used to initialize this TrackedChat.
51
+ * Get the underlying AI agent configuration used to initialize this ManagedAgent.
84
52
  */
85
53
  getConfig() {
86
- return this.aiConfig;
54
+ return this.aiAgentConfig;
87
55
  }
88
- /**
89
- * Get the underlying AI provider instance.
90
- * This provides direct access to the provider for advanced use cases.
91
- */
92
- getProvider() {
93
- return this.provider;
94
- }
95
- /**
96
- * Get the judges associated with this TrackedChat.
97
- * Returns a record of judge instances keyed by their configuration keys.
98
- */
99
- getJudges() {
100
- return this.judges;
56
+ };
57
+
58
+ // src/api/ManagedModel.ts
59
+ var ManagedModel = class {
60
+ constructor(aiConfig, runner, _logger) {
61
+ this.aiConfig = aiConfig;
62
+ this.runner = runner;
63
+ this._logger = _logger;
101
64
  }
102
65
  /**
103
- * Append messages to the conversation history.
104
- * Adds messages to the conversation history without invoking the model,
105
- * which is useful for managing multi-turn conversations or injecting context.
66
+ * Invoke the model with a prompt string and return a ManagedResult.
67
+ *
68
+ * `run()` resolves before `ManagedResult.evaluations` resolves. Awaiting
69
+ * `evaluations` guarantees both judge evaluation and tracker.trackJudgeResult()
70
+ * are complete.
106
71
  *
107
- * @param messages Array of messages to append to the conversation history
72
+ * @param prompt The user input to send to the model.
73
+ * @returns Promise resolving to ManagedResult (before evaluations settle).
108
74
  */
109
- appendMessages(messages) {
110
- this.messages.push(...messages);
75
+ async run(prompt) {
76
+ const tracker = this.aiConfig.createTracker();
77
+ const result = await tracker.trackMetricsOf(
78
+ (r) => r.metrics,
79
+ () => this.runner.run(prompt)
80
+ );
81
+ const metrics = tracker.getSummary();
82
+ const output = result.content;
83
+ const evaluations = this.aiConfig.evaluator.evaluate(prompt, output).then((results) => {
84
+ results.forEach((judgeResult) => {
85
+ if (!judgeResult.sampled) {
86
+ return;
87
+ }
88
+ tracker.trackJudgeResult(judgeResult);
89
+ });
90
+ return results;
91
+ }).catch((err) => {
92
+ this._logger?.warn("Judge evaluation failed unexpectedly:", err);
93
+ return [];
94
+ });
95
+ return {
96
+ content: output,
97
+ metrics,
98
+ raw: result.raw,
99
+ parsed: result.parsed,
100
+ evaluations
101
+ };
111
102
  }
112
103
  /**
113
- * Get all messages in the conversation history.
114
- *
115
- * @param includeConfigMessages Whether to include the config messages from the AIConfig.
116
- * Defaults to false.
117
- * @returns Array of messages. When includeConfigMessages is true, returns both config
118
- * messages and conversation history with config messages prepended. When false,
119
- * returns only the conversation history messages.
104
+ * Get the underlying AI configuration used to initialize this ManagedModel.
120
105
  */
121
- getMessages(includeConfigMessages = false) {
122
- if (includeConfigMessages) {
123
- const configMessages = this.aiConfig.messages || [];
124
- return [...configMessages, ...this.messages];
125
- }
126
- return [...this.messages];
106
+ getConfig() {
107
+ return this.aiConfig;
127
108
  }
128
109
  };
129
110
 
@@ -158,9 +139,6 @@ var LDAIConfigUtils = class {
158
139
  if ("evaluationMetricKey" in config && config.evaluationMetricKey !== void 0) {
159
140
  flagValue.evaluationMetricKey = config.evaluationMetricKey;
160
141
  }
161
- if ("evaluationMetricKeys" in config && config.evaluationMetricKeys !== void 0) {
162
- flagValue.evaluationMetricKeys = config.evaluationMetricKeys;
163
- }
164
142
  if ("judgeConfiguration" in config && config.judgeConfiguration !== void 0) {
165
143
  flagValue.judgeConfiguration = config.judgeConfiguration;
166
144
  }
@@ -174,47 +152,53 @@ var LDAIConfigUtils = class {
174
152
  *
175
153
  * @param key The configuration key
176
154
  * @param flagValue The flag value from LaunchDarkly
177
- * @param trackerFactory A factory function that creates a new tracker for each execution
155
+ * @param trackerFactory A factory function that creates a new tracker for each AI run
156
+ * @param evaluator The evaluator to attach to completion and agent configs
178
157
  * @returns The appropriate AI configuration type
179
158
  */
180
- static fromFlagValue(key, flagValue, trackerFactory) {
159
+ static fromFlagValue(key, flagValue, trackerFactory, evaluator) {
181
160
  const flagValueMode = flagValue._ldMeta?.mode;
182
161
  switch (flagValueMode) {
183
162
  case "agent":
184
- return this.toAgentConfig(key, flagValue, trackerFactory);
163
+ return this.toAgentConfig(key, flagValue, trackerFactory, evaluator);
185
164
  case "judge":
186
165
  return this.toJudgeConfig(key, flagValue, trackerFactory);
187
166
  case "completion":
188
167
  default:
189
- return this.toCompletionConfig(key, flagValue, trackerFactory);
168
+ return this.toCompletionConfig(key, flagValue, trackerFactory, evaluator);
190
169
  }
191
170
  }
192
171
  /**
193
172
  * Creates a disabled configuration of the specified mode.
194
173
  *
174
+ * @param key The configuration key
195
175
  * @param mode The mode for the disabled config
176
+ * @param createTracker A factory function that creates a new tracker for each AI run
177
+ * @param evaluator The evaluator to attach to completion and agent configs
196
178
  * @returns A disabled config of the appropriate type
197
179
  */
198
- static createDisabledConfig(key, mode) {
180
+ static createDisabledConfig(key, mode, createTracker, evaluator) {
199
181
  switch (mode) {
200
182
  case "agent":
201
183
  return {
202
184
  key,
203
185
  enabled: false,
204
- createTracker: void 0
186
+ createTracker,
187
+ evaluator
205
188
  };
206
189
  case "judge":
207
190
  return {
208
191
  key,
209
192
  enabled: false,
210
- createTracker: void 0
193
+ createTracker
211
194
  };
212
195
  case "completion":
213
196
  default:
214
197
  return {
215
198
  key,
216
199
  enabled: false,
217
- createTracker: void 0
200
+ createTracker,
201
+ evaluator
218
202
  };
219
203
  }
220
204
  }
@@ -255,13 +239,15 @@ var LDAIConfigUtils = class {
255
239
  *
256
240
  * @param key The configuration key
257
241
  * @param flagValue The flag value from LaunchDarkly
258
- * @param trackerFactory A factory function that creates a new tracker for each execution
242
+ * @param trackerFactory A factory function that creates a new tracker for each AI run
243
+ * @param evaluator The evaluator for this completion config
259
244
  * @returns A completion configuration
260
245
  */
261
- static toCompletionConfig(key, flagValue, trackerFactory) {
246
+ static toCompletionConfig(key, flagValue, trackerFactory, evaluator) {
262
247
  return {
263
248
  ...this._toBaseConfig(key, flagValue),
264
249
  createTracker: trackerFactory,
250
+ evaluator,
265
251
  messages: flagValue.messages,
266
252
  judgeConfiguration: flagValue.judgeConfiguration,
267
253
  tools: this._resolveTools(flagValue)
@@ -272,13 +258,15 @@ var LDAIConfigUtils = class {
272
258
  *
273
259
  * @param key The configuration key
274
260
  * @param flagValue The flag value from LaunchDarkly
275
- * @param trackerFactory A factory function that creates a new tracker for each execution
261
+ * @param trackerFactory A factory function that creates a new tracker for each AI run
262
+ * @param evaluator The evaluator for this agent config
276
263
  * @returns An agent configuration
277
264
  */
278
- static toAgentConfig(key, flagValue, trackerFactory) {
265
+ static toAgentConfig(key, flagValue, trackerFactory, evaluator) {
279
266
  return {
280
267
  ...this._toBaseConfig(key, flagValue),
281
268
  createTracker: trackerFactory,
269
+ evaluator,
282
270
  instructions: flagValue.instructions,
283
271
  judgeConfiguration: flagValue.judgeConfiguration,
284
272
  tools: this._resolveTools(flagValue)
@@ -289,7 +277,7 @@ var LDAIConfigUtils = class {
289
277
  *
290
278
  * @param key The configuration key
291
279
  * @param flagValue The flag value from LaunchDarkly
292
- * @param trackerFactory A factory function that creates a new tracker for each execution
280
+ * @param trackerFactory A factory function that creates a new tracker for each AI run
293
281
  * @returns A judge configuration
294
282
  */
295
283
  static toJudgeConfig(key, flagValue, trackerFactory) {
@@ -422,10 +410,10 @@ var AgentGraphDefinition = class _AgentGraphDefinition {
422
410
  return this._agentGraph;
423
411
  }
424
412
  /**
425
- * Returns a new {@link LDGraphTracker} for this graph invocation.
413
+ * Returns a new {@link LDGraphTracker} for a fresh graph run.
426
414
  *
427
- * Call this once per invocation. Each call produces a tracker with a fresh `runId`
428
- * that groups all events for that invocation.
415
+ * Call this once per graph run. Each call produces a tracker with a fresh `runId`
416
+ * that groups all events for that run.
429
417
  */
430
418
  createTracker() {
431
419
  return this._createTracker();
@@ -543,8 +531,23 @@ var AgentGraphDefinition = class _AgentGraphDefinition {
543
531
  }
544
532
  };
545
533
 
534
+ // src/api/judge/Evaluator.ts
535
+ var Evaluator = class _Evaluator {
536
+ constructor(_judges) {
537
+ this._judges = _judges;
538
+ }
539
+ static noop() {
540
+ return new _Evaluator([]);
541
+ }
542
+ async evaluate(input, output) {
543
+ if (this._judges.length === 0) {
544
+ return [];
545
+ }
546
+ return Promise.all(this._judges.map((judge) => judge.evaluate(input, output)));
547
+ }
548
+ };
549
+
546
550
  // src/api/judge/Judge.ts
547
- import Mustache from "mustache";
548
551
  var EVALUATION_SCHEMA = {
549
552
  type: "object",
550
553
  properties: {
@@ -562,15 +565,27 @@ var EVALUATION_SCHEMA = {
562
565
  required: ["score", "reasoning"],
563
566
  additionalProperties: false
564
567
  };
568
+ function stripLegacyJudgeMessages(messages) {
569
+ return messages.filter(
570
+ (msg) => msg.role === "system" || !msg.content.includes("{{message_history}}") && !msg.content.includes("{{response_to_evaluate}}")
571
+ );
572
+ }
565
573
  var Judge = class {
566
- constructor(_aiConfig, _aiProvider, logger) {
574
+ constructor(_aiConfig, _runner, _sampleRate = 1, logger) {
567
575
  this._aiConfig = _aiConfig;
568
- this._aiProvider = _aiProvider;
576
+ this._runner = _runner;
577
+ this._sampleRate = _sampleRate;
569
578
  this._logger = logger;
570
579
  }
571
580
  /**
572
- * Gets the evaluation metric key, prioritizing evaluationMetricKey over evaluationMetricKeys.
573
- * Falls back to the first valid (non-empty, non-whitespace) value in evaluationMetricKeys if evaluationMetricKey is not provided.
581
+ * The default sampling rate baked in at construction. Used by `evaluate` /
582
+ * `evaluateMessages` when no per-call rate is supplied.
583
+ */
584
+ get sampleRate() {
585
+ return this._sampleRate;
586
+ }
587
+ /**
588
+ * Gets the evaluation metric key from the judge AI config.
574
589
  * Treats empty strings and whitespace-only strings as invalid.
575
590
  * @returns The evaluation metric key, or undefined if not available
576
591
  */
@@ -578,12 +593,6 @@ var Judge = class {
578
593
  if (this._aiConfig.evaluationMetricKey && this._aiConfig.evaluationMetricKey.trim().length > 0) {
579
594
  return this._aiConfig.evaluationMetricKey.trim();
580
595
  }
581
- if (this._aiConfig.evaluationMetricKeys && this._aiConfig.evaluationMetricKeys.length > 0) {
582
- const validKey = this._aiConfig.evaluationMetricKeys.find(
583
- (key) => key && key.trim().length > 0
584
- );
585
- return validKey ? validKey.trim() : void 0;
586
- }
587
596
  return void 0;
588
597
  }
589
598
  /**
@@ -591,10 +600,13 @@ var Judge = class {
591
600
  *
592
601
  * @param input The input prompt or question that was provided to the AI
593
602
  * @param output The AI-generated response to be evaluated
594
- * @param samplingRate Sampling rate (0-1) to determine if evaluation should be processed (defaults to 1)
603
+ * @param samplingRate Sampling rate (0-1) to determine if evaluation should be processed.
604
+ * When omitted, the Judge's constructor-default rate is used. An explicit `0` overrides
605
+ * the default — only `undefined` falls through.
595
606
  * @returns Promise that resolves to evaluation results
596
607
  */
597
- async evaluate(input, output, samplingRate = 1) {
608
+ async evaluate(input, output, samplingRate) {
609
+ const effectiveRate = samplingRate ?? this._sampleRate;
598
610
  const result = {
599
611
  success: false,
600
612
  sampled: false,
@@ -612,26 +624,20 @@ var Judge = class {
612
624
  result.errorMessage = "Judge configuration is missing required evaluation metric key";
613
625
  return result;
614
626
  }
615
- if (!this._aiConfig.messages) {
616
- this._logger?.warn("Judge configuration must include messages", tracker.getTrackData());
617
- result.sampled = true;
618
- result.errorMessage = "Judge configuration must include messages";
619
- return result;
620
- }
621
- if (Math.random() > samplingRate) {
622
- this._logger?.debug(`Judge evaluation skipped due to sampling rate: ${samplingRate}`);
627
+ if (Math.random() > effectiveRate) {
628
+ this._logger?.debug(`Judge evaluation skipped due to sampling rate: ${effectiveRate}`);
623
629
  return result;
624
630
  }
625
631
  result.sampled = true;
626
- const messages = this._constructEvaluationMessages(input, output);
632
+ const evaluationInput = this._buildEvaluationInput(input, output);
627
633
  const response = await tracker.trackMetricsOf(
628
634
  (r) => r.metrics,
629
- () => this._aiProvider.invokeStructuredModel(messages, EVALUATION_SCHEMA)
635
+ () => this._runner.run(evaluationInput, EVALUATION_SCHEMA)
630
636
  );
631
- const evalResult = this._parseEvaluationResponse(response.data);
637
+ const evalResult = this._parseEvaluationResponse(response.parsed);
632
638
  if (!evalResult) {
633
639
  this._logger?.warn(
634
- `Could not parse evaluation response: ${JSON.stringify(response.data)}`,
640
+ `Could not parse evaluation response: ${JSON.stringify(response.parsed)}`,
635
641
  tracker.getTrackData()
636
642
  );
637
643
  return result;
@@ -651,16 +657,21 @@ var Judge = class {
651
657
  }
652
658
  }
653
659
  /**
654
- * Evaluates an AI response from chat messages and response.
660
+ * Evaluates an AI response from chat messages and a runner result.
661
+ *
662
+ * Each message is rendered as `<role>: <content>` so the judge model can
663
+ * distinguish speakers in the message history. Messages are joined with a
664
+ * single newline.
655
665
  *
656
666
  * @param messages Array of messages representing the conversation history
657
- * @param response The AI response to be evaluated
658
- * @param samplingRatio Sampling ratio (0-1) to determine if evaluation should be processed (defaults to 1)
667
+ * @param response The runner result containing the AI-generated content to evaluate
668
+ * @param samplingRatio Sampling ratio (0-1). When omitted, the Judge's
669
+ * constructor-default rate is used.
659
670
  * @returns Promise that resolves to evaluation results
660
671
  */
661
- async evaluateMessages(messages, response, samplingRatio = 1) {
662
- const input = messages.length === 0 ? "" : messages.map((msg) => msg.content).join("\r\n");
663
- const output = response.message.content;
672
+ async evaluateMessages(messages, response, samplingRatio) {
673
+ const input = messages.length === 0 ? "" : messages.map((msg) => `${msg.role}: ${msg.content}`).join("\n");
674
+ const output = response.content;
664
675
  return this.evaluate(input, output, samplingRatio);
665
676
  }
666
677
  /**
@@ -670,29 +681,23 @@ var Judge = class {
670
681
  return this._aiConfig;
671
682
  }
672
683
  /**
673
- * Returns the AI provider used by this judge.
684
+ * Returns the runner used by this judge.
674
685
  */
675
- getProvider() {
676
- return this._aiProvider;
677
- }
678
- /**
679
- * Constructs evaluation messages by combining judge's config messages with input/output.
680
- */
681
- _constructEvaluationMessages(input, output) {
682
- const messages = this._aiConfig.messages.map((msg) => ({
683
- ...msg,
684
- content: this._interpolateMessage(msg.content, {
685
- message_history: input,
686
- response_to_evaluate: output
687
- })
688
- }));
689
- return messages;
686
+ getRunner() {
687
+ return this._runner;
690
688
  }
691
689
  /**
692
- * Interpolates message content with variables using Mustache templating.
690
+ * Builds the evaluation input string passed to the runner.
691
+ *
692
+ * Combines the original prompt and the response into a single, well-known
693
+ * format the judge model is expected to evaluate.
693
694
  */
694
- _interpolateMessage(content, variables) {
695
- return Mustache.render(content, variables, void 0, { escape: (item) => item });
695
+ _buildEvaluationInput(input, output) {
696
+ return `MESSAGE HISTORY:
697
+ ${input}
698
+
699
+ RESPONSE TO EVALUATE:
700
+ ${output}`;
696
701
  }
697
702
  /**
698
703
  * Parses the structured evaluation response. Expects top-level {score, reasoning}.
@@ -718,115 +723,105 @@ var Judge = class {
718
723
  // src/api/providers/AIProvider.ts
719
724
  var AIProvider = class {
720
725
  constructor(logger) {
721
- this.logger = logger;
726
+ this._logger = logger;
722
727
  }
723
728
  /**
724
- * Invoke the chat model with an array of messages.
725
- * This method should convert messages to provider format, invoke the model,
726
- * and return a ChatResponse with the result and metrics.
729
+ * Create a Runner for a completion or judge AI Config.
727
730
  *
728
- * Default implementation takes no action and returns a placeholder response.
729
- * Provider implementations should override this method.
731
+ * Override in provider subclasses to return a configured {@link Runner}.
732
+ * Default implementation returns `undefined`.
730
733
  *
731
- * @param messages Array of LDMessage objects representing the conversation
732
- * @returns Promise that resolves to a ChatResponse containing the model's response
734
+ * @param config The completion or judge AI configuration.
735
+ * @param multiTurn Whether the runner should accumulate conversation history
736
+ * across successive `run()` calls. Defaults to `true` (chat semantics).
737
+ * Pass `false` for stateless runners such as judges where each call must
738
+ * start from the initial config messages.
739
+ * @returns Promise resolving to a {@link Runner}, or `undefined` if this
740
+ * provider does not support model creation.
733
741
  */
734
- async invokeModel(_messages) {
735
- this.logger?.warn("invokeModel not implemented by this provider");
736
- return {
737
- message: {
738
- role: "assistant",
739
- content: ""
740
- },
741
- metrics: {
742
- success: false,
743
- usage: {
744
- total: 0,
745
- input: 0,
746
- output: 0
747
- }
748
- }
749
- };
742
+ async createModel(_config, _multiTurn = true) {
743
+ return void 0;
750
744
  }
751
745
  /**
752
- * Invoke the chat model with structured output support.
753
- * This method should convert messages to provider format, invoke the model with
754
- * structured output configuration, and return a structured response.
746
+ * Create a Runner for an agent AI Config.
755
747
  *
756
- * Default implementation takes no action and returns a placeholder response.
757
- * Provider implementations should override this method.
748
+ * Override in provider subclasses to return a configured {@link Runner}.
749
+ * Default implementation returns `undefined`.
758
750
  *
759
- * @param messages Array of LDMessage objects representing the conversation
760
- * @param responseStructure Dictionary of output configurations keyed by output name
761
- * @returns Promise that resolves to a structured response
751
+ * @param config The agent AI configuration.
752
+ * @param tools Optional registry of callable tools.
753
+ * @returns Promise resolving to a {@link Runner}, or `undefined` if this
754
+ * provider does not support agent creation.
762
755
  */
763
- async invokeStructuredModel(_messages, _responseStructure) {
764
- this.logger?.warn("invokeStructuredModel not implemented by this provider");
765
- return {
766
- data: {},
767
- rawResponse: "",
768
- metrics: {
769
- success: false,
770
- usage: {
771
- total: 0,
772
- input: 0,
773
- output: 0
774
- }
775
- }
776
- };
756
+ async createAgent(_config, _tools) {
757
+ return void 0;
777
758
  }
778
759
  /**
779
- * Static method that constructs an instance of the provider.
780
- * Each provider implementation must provide their own static create method
781
- * that accepts an AIConfig and returns a configured instance.
760
+ * Create an AgentGraphRunner for an agent graph definition.
761
+ *
762
+ * Override in provider subclasses to return a configured {@link AgentGraphRunner}.
763
+ * Default implementation returns `undefined`.
782
764
  *
783
- * @param aiConfig The LaunchDarkly AI configuration
784
- * @param logger Optional logger for the provider
785
- * @returns Promise that resolves to a configured provider instance
765
+ * @param graphDef The agent graph definition.
766
+ * @param tools Optional registry of callable tools.
767
+ * @returns Promise resolving to an {@link AgentGraphRunner}, or `undefined` if
768
+ * this provider does not support graph execution.
786
769
  */
787
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
788
- static async create(aiConfig, logger) {
789
- throw new Error("Provider implementations must override the static create method");
770
+ async createAgentGraph(_graphDef, _tools) {
771
+ return void 0;
790
772
  }
791
773
  };
792
774
 
793
- // src/api/providers/AIProviderFactory.ts
775
+ // src/api/providers/RunnerFactory.ts
794
776
  var SUPPORTED_AI_PROVIDERS = [
795
777
  "openai",
796
778
  // Multi-provider packages should be last in the list
797
779
  "langchain",
798
780
  "vercel"
799
781
  ];
800
- var AIProviderFactory = class {
782
+ var RunnerFactory = class _RunnerFactory {
801
783
  /**
802
- * Create an AIProvider instance based on the AI configuration.
803
- * This method attempts to load provider-specific implementations dynamically.
804
- * Returns undefined if the provider is not supported.
784
+ * Load and return the AIProvider factory for the given provider type.
805
785
  *
806
- * @param aiConfig The AI configuration
807
- * @param logger Optional logger for logging provider initialization
808
- * @param defaultAiProvider Optional default AI provider to use
786
+ * This is the single place in the codebase that knows provider package names.
787
+ * Each supported provider package exports a `*RunnerFactory` class that
788
+ * extends {@link AIProvider}; this method instantiates it directly.
789
+ *
790
+ * @param providerType One of the {@link SUPPORTED_AI_PROVIDERS} values.
791
+ * @param logger Optional logger forwarded to the provider factory.
792
+ * @returns A configured {@link AIProvider} instance, or `undefined` if the
793
+ * package cannot be loaded.
809
794
  */
810
- static async create(aiConfig, logger, defaultAiProvider) {
811
- const providerName = aiConfig.provider?.name?.toLowerCase();
812
- const providersToTry = this._getProvidersToTry(defaultAiProvider, providerName);
813
- for (const providerType of providersToTry) {
814
- logger?.debug(
815
- `Attempting to create AIProvider for: ${aiConfig.provider?.name} with provider type: ${providerType}`
816
- );
817
- const provider = await this._tryCreateProvider(providerType, aiConfig, logger);
818
- if (provider) {
819
- logger?.debug(`Successfully created AIProvider for: ${aiConfig.provider?.name}`);
820
- return provider;
795
+ static async _getProviderFactory(providerType, logger) {
796
+ try {
797
+ let module;
798
+ switch (providerType) {
799
+ case "openai": {
800
+ module = await import("@launchdarkly/server-sdk-ai-openai");
801
+ return new module.OpenAIRunnerFactory(logger);
802
+ }
803
+ case "langchain": {
804
+ module = await import("@launchdarkly/server-sdk-ai-langchain");
805
+ return new module.LangChainRunnerFactory(logger);
806
+ }
807
+ case "vercel": {
808
+ module = await import("@launchdarkly/server-sdk-ai-vercel");
809
+ return new module.VercelRunnerFactory(logger);
810
+ }
811
+ default:
812
+ return void 0;
821
813
  }
814
+ } catch (error) {
815
+ logger?.warn(
816
+ `Unable to load provider package. Check that you have installed the correct package. ${error.message}`
817
+ );
818
+ return void 0;
822
819
  }
823
- logger?.warn(
824
- `Provider is not supported or failed to initialize: ${aiConfig.provider?.name ?? "unknown"}`
825
- );
826
- return void 0;
827
820
  }
828
821
  /**
829
822
  * Determine which providers to try based on defaultAiProvider and providerName.
823
+ *
824
+ * Mirrors Python's `_get_providers_to_try` helper.
830
825
  */
831
826
  static _getProvidersToTry(defaultAiProvider, providerName) {
832
827
  if (defaultAiProvider) {
@@ -843,57 +838,118 @@ var AIProviderFactory = class {
843
838
  return Array.from(providerSet);
844
839
  }
845
840
  /**
846
- * Try to create a provider of the specified type.
841
+ * Try each provider in order and return the first non-undefined result.
842
+ *
843
+ * Mirrors Python's `_with_fallback` helper. Loads each provider factory via
844
+ * {@link _getProviderFactory} and calls `fn` with it. Returns the first
845
+ * truthy result, or `undefined` if no provider succeeds.
846
+ *
847
+ * @param providers Ordered list of provider types to try.
848
+ * @param fn Callback that calls the appropriate factory method on the provider.
849
+ * @param logger Optional logger forwarded to each provider factory.
847
850
  */
848
- static async _tryCreateProvider(providerType, aiConfig, logger) {
849
- try {
850
- let module;
851
- switch (providerType) {
852
- case "openai": {
853
- module = await import("@launchdarkly/server-sdk-ai-openai");
854
- const provider = await module.OpenAIProvider.create(aiConfig, logger);
855
- return provider;
856
- }
857
- case "langchain": {
858
- module = await import("@launchdarkly/server-sdk-ai-langchain");
859
- const provider = await module.LangChainProvider.create(aiConfig, logger);
860
- return provider;
851
+ static async _withFallback(providers, fn, logger) {
852
+ for (const providerType of providers) {
853
+ logger?.debug(`Attempting to create runner with provider: ${providerType}`);
854
+ const factory = await _RunnerFactory._getProviderFactory(providerType, logger);
855
+ if (factory) {
856
+ const result = await fn(factory);
857
+ if (result) {
858
+ logger?.debug(`Successfully created runner with provider: ${providerType}`);
859
+ return result;
861
860
  }
862
- case "vercel": {
863
- module = await import("@launchdarkly/server-sdk-ai-vercel");
864
- const provider = await module.VercelProvider.create(aiConfig, logger);
865
- return provider;
866
- }
867
- default:
868
- return void 0;
869
861
  }
870
- } catch (error) {
862
+ }
863
+ return void 0;
864
+ }
865
+ /**
866
+ * Create a Runner for the given AI configuration.
867
+ *
868
+ * Suitable for completion, judge, and agent config modes. Dynamically
869
+ * loads the matching provider package via {@link _getProviderFactory} and
870
+ * delegates to its {@link AIProvider.createModel} method.
871
+ *
872
+ * @param config The AI configuration (completion, agent, or judge).
873
+ * @param logger Optional logger forwarded to the underlying provider.
874
+ * @param defaultAiProvider Optional provider override
875
+ * ('openai', 'langchain', 'vercel', …). When set, only that provider is
876
+ * tried. When omitted, providers are tried in priority order based on the
877
+ * provider name in the config.
878
+ * @param multiTurn Whether the runner should accumulate conversation history
879
+ * across successive `run()` calls. Defaults to `true` (chat semantics).
880
+ * Judges pass `false` so each evaluation starts from the initial config
881
+ * messages.
882
+ * @returns A configured {@link Runner} ready to invoke the model, or
883
+ * `undefined` if no suitable provider could be loaded.
884
+ */
885
+ static async createModel(config, logger, defaultAiProvider, multiTurn = true) {
886
+ const providerName = config.provider?.name?.toLowerCase();
887
+ const providers = _RunnerFactory._getProvidersToTry(defaultAiProvider, providerName);
888
+ const runner = await _RunnerFactory._withFallback(
889
+ providers,
890
+ (factory) => factory.createModel(config, multiTurn),
891
+ logger
892
+ );
893
+ if (!runner) {
871
894
  logger?.warn(
872
- `Unable to create AIProvider. Check that you have installed the correct package. ${error.message}`
895
+ `Provider is not supported or failed to initialize: ${config.provider?.name ?? "unknown"}`
873
896
  );
874
- return void 0;
875
897
  }
898
+ return runner;
899
+ }
900
+ /**
901
+ * Create a Runner for an agent AI Config.
902
+ *
903
+ * Delegates to the provider factory's {@link AIProvider.createAgent} method.
904
+ *
905
+ * @param config The agent AI configuration.
906
+ * @param tools Optional registry of callable tools.
907
+ * @param logger Optional logger forwarded to the underlying provider.
908
+ * @param defaultAiProvider Optional provider override.
909
+ * @returns A configured {@link Runner}, or `undefined` if no suitable
910
+ * provider could be loaded.
911
+ */
912
+ static async createAgent(config, tools, logger, defaultAiProvider) {
913
+ const providerName = config.provider?.name?.toLowerCase();
914
+ const providers = _RunnerFactory._getProvidersToTry(defaultAiProvider, providerName);
915
+ const runner = await _RunnerFactory._withFallback(
916
+ providers,
917
+ (factory) => factory.createAgent(config, tools),
918
+ logger
919
+ );
920
+ if (!runner) {
921
+ logger?.warn(
922
+ `Provider is not supported or failed to initialize: ${config.provider?.name ?? "unknown"}`
923
+ );
924
+ }
925
+ return runner;
926
+ }
927
+ /**
928
+ * Create an AgentGraphRunner for the given agent graph definition.
929
+ *
930
+ * Delegates to the provider factory's {@link AIProvider.createAgentGraph} method.
931
+ *
932
+ * @param graphDef The agent graph definition.
933
+ * @param tools Optional registry of callable tools.
934
+ * @param logger Optional logger forwarded to the underlying provider.
935
+ * @param defaultAiProvider Optional provider override.
936
+ * @returns A configured {@link AgentGraphRunner}, or `undefined` if no
937
+ * suitable provider could be loaded.
938
+ */
939
+ static async createAgentGraph(graphDef, tools, logger, defaultAiProvider) {
940
+ const providers = _RunnerFactory._getProvidersToTry(defaultAiProvider);
941
+ const runner = await _RunnerFactory._withFallback(
942
+ providers,
943
+ (factory) => factory.createAgentGraph(graphDef, tools),
944
+ logger
945
+ );
946
+ if (!runner) {
947
+ logger?.warn(`No provider could create an AgentGraphRunner for the given graph definition.`);
948
+ }
949
+ return runner;
876
950
  }
877
951
  };
878
952
 
879
- // src/api/metrics/BedrockTokenUsage.ts
880
- function createBedrockTokenUsage(data) {
881
- return {
882
- total: data.totalTokens || 0,
883
- input: data.inputTokens || 0,
884
- output: data.outputTokens || 0
885
- };
886
- }
887
-
888
- // src/api/metrics/OpenAiUsage.ts
889
- function createOpenAiUsage(data) {
890
- return {
891
- total: data.total_tokens ?? 0,
892
- input: data.prompt_tokens ?? 0,
893
- output: data.completion_tokens ?? 0
894
- };
895
- }
896
-
897
953
  // src/api/metrics/LDFeedbackKind.ts
898
954
  var LDFeedbackKind = /* @__PURE__ */ ((LDFeedbackKind2) => {
899
955
  LDFeedbackKind2["Positive"] = "positive";
@@ -901,15 +957,6 @@ var LDFeedbackKind = /* @__PURE__ */ ((LDFeedbackKind2) => {
901
957
  return LDFeedbackKind2;
902
958
  })(LDFeedbackKind || {});
903
959
 
904
- // src/api/metrics/VercelAISDKTokenUsage.ts
905
- function createVercelAISDKTokenUsage(data) {
906
- return {
907
- total: data.totalTokens ?? 0,
908
- input: data.inputTokens ?? data.promptTokens ?? 0,
909
- output: data.outputTokens ?? data.completionTokens ?? 0
910
- };
911
- }
912
-
913
960
  // src/LDAIConfigTrackerImpl.ts
914
961
  var LDAIConfigTrackerImpl = class _LDAIConfigTrackerImpl {
915
962
  constructor(_ldClient, _runId, _configKey, _variationKey, _version, _modelName, _providerName, _context, _graphKey) {
@@ -923,6 +970,7 @@ var LDAIConfigTrackerImpl = class _LDAIConfigTrackerImpl {
923
970
  this._context = _context;
924
971
  this._graphKey = _graphKey;
925
972
  this._trackedMetrics = {};
973
+ this._trackedMetrics.resumptionToken = this.resumptionToken;
926
974
  }
927
975
  getTrackData() {
928
976
  return {
@@ -963,7 +1011,7 @@ var LDAIConfigTrackerImpl = class _LDAIConfigTrackerImpl {
963
1011
  trackDuration(duration) {
964
1012
  if (this._trackedMetrics.durationMs !== void 0) {
965
1013
  this._ldClient.logger?.warn(
966
- "Duration has already been tracked for this execution. Use createTracker() for a new execution."
1014
+ "Skipping trackDuration: duration already recorded on this tracker. Call createTracker on the AI Config for a new run."
967
1015
  );
968
1016
  return;
969
1017
  }
@@ -984,7 +1032,7 @@ var LDAIConfigTrackerImpl = class _LDAIConfigTrackerImpl {
984
1032
  trackTimeToFirstToken(timeToFirstTokenMs) {
985
1033
  if (this._trackedMetrics.timeToFirstTokenMs !== void 0) {
986
1034
  this._ldClient.logger?.warn(
987
- "Time to first token has already been tracked for this execution. Use createTracker() for a new execution."
1035
+ "Skipping trackTimeToFirstToken: time-to-first-token already recorded on this tracker. Call createTracker on the AI Config for a new run."
988
1036
  );
989
1037
  return;
990
1038
  }
@@ -1006,6 +1054,10 @@ var LDAIConfigTrackerImpl = class _LDAIConfigTrackerImpl {
1006
1054
  }
1007
1055
  }
1008
1056
  trackToolCall(toolKey) {
1057
+ if (!this._trackedMetrics.toolCalls) {
1058
+ this._trackedMetrics.toolCalls = [];
1059
+ }
1060
+ this._trackedMetrics.toolCalls.push(toolKey);
1009
1061
  this._ldClient.track("$ld:ai:tool_call", this._context, { ...this.getTrackData(), toolKey }, 1);
1010
1062
  }
1011
1063
  trackToolCalls(toolKeys) {
@@ -1016,7 +1068,7 @@ var LDAIConfigTrackerImpl = class _LDAIConfigTrackerImpl {
1016
1068
  trackFeedback(feedback) {
1017
1069
  if (this._trackedMetrics.feedback !== void 0) {
1018
1070
  this._ldClient.logger?.warn(
1019
- "Feedback has already been tracked for this execution. Use createTracker() for a new execution."
1071
+ "Skipping trackFeedback: feedback already recorded on this tracker. Call createTracker on the AI Config for a new run."
1020
1072
  );
1021
1073
  return;
1022
1074
  }
@@ -1030,7 +1082,7 @@ var LDAIConfigTrackerImpl = class _LDAIConfigTrackerImpl {
1030
1082
  trackSuccess() {
1031
1083
  if (this._trackedMetrics.success !== void 0) {
1032
1084
  this._ldClient.logger?.warn(
1033
- "Generation result has already been tracked for this execution. Use createTracker() for a new execution."
1085
+ "Skipping trackSuccess: success/error already recorded on this tracker. Call createTracker on the AI Config for a new run."
1034
1086
  );
1035
1087
  return;
1036
1088
  }
@@ -1040,7 +1092,7 @@ var LDAIConfigTrackerImpl = class _LDAIConfigTrackerImpl {
1040
1092
  trackError() {
1041
1093
  if (this._trackedMetrics.success !== void 0) {
1042
1094
  this._ldClient.logger?.warn(
1043
- "Generation result has already been tracked for this execution. Use createTracker() for a new execution."
1095
+ "Skipping trackError: success/error already recorded on this tracker. Call createTracker on the AI Config for a new run."
1044
1096
  );
1045
1097
  return;
1046
1098
  }
@@ -1061,8 +1113,11 @@ var LDAIConfigTrackerImpl = class _LDAIConfigTrackerImpl {
1061
1113
  } else {
1062
1114
  this.trackError();
1063
1115
  }
1064
- if (metrics.usage) {
1065
- this.trackTokens(metrics.usage);
1116
+ if (metrics.tokens) {
1117
+ this.trackTokens(metrics.tokens);
1118
+ }
1119
+ if (metrics.toolCalls?.length) {
1120
+ this.trackToolCalls(metrics.toolCalls);
1066
1121
  }
1067
1122
  return result;
1068
1123
  }
@@ -1086,8 +1141,11 @@ var LDAIConfigTrackerImpl = class _LDAIConfigTrackerImpl {
1086
1141
  } else {
1087
1142
  this.trackError();
1088
1143
  }
1089
- if (metrics.usage) {
1090
- this.trackTokens(metrics.usage);
1144
+ if (metrics.tokens) {
1145
+ this.trackTokens(metrics.tokens);
1146
+ }
1147
+ if (metrics.toolCalls?.length) {
1148
+ this.trackToolCalls(metrics.toolCalls);
1091
1149
  }
1092
1150
  } catch (error) {
1093
1151
  this.trackError();
@@ -1095,50 +1153,10 @@ var LDAIConfigTrackerImpl = class _LDAIConfigTrackerImpl {
1095
1153
  this.trackDuration(Date.now() - startTime);
1096
1154
  }
1097
1155
  }
1098
- async trackOpenAIMetrics(func) {
1099
- try {
1100
- const result = await this.trackDurationOf(func);
1101
- this.trackSuccess();
1102
- if (result.usage) {
1103
- this.trackTokens(createOpenAiUsage(result.usage));
1104
- }
1105
- return result;
1106
- } catch (err) {
1107
- this.trackError();
1108
- throw err;
1109
- }
1110
- }
1111
- trackBedrockConverseMetrics(res) {
1112
- if (res.$metadata?.httpStatusCode === 200) {
1113
- this.trackSuccess();
1114
- } else if (res.$metadata?.httpStatusCode && res.$metadata.httpStatusCode >= 400) {
1115
- this.trackError();
1116
- }
1117
- if (res.metrics && res.metrics.latencyMs) {
1118
- this.trackDuration(res.metrics.latencyMs);
1119
- }
1120
- if (res.usage) {
1121
- this.trackTokens(createBedrockTokenUsage(res.usage));
1122
- }
1123
- return res;
1124
- }
1125
- async trackVercelAISDKGenerateTextMetrics(func) {
1126
- try {
1127
- const result = await this.trackDurationOf(func);
1128
- this.trackSuccess();
1129
- if (result.usage) {
1130
- this.trackTokens(createVercelAISDKTokenUsage(result.usage));
1131
- }
1132
- return result;
1133
- } catch (err) {
1134
- this.trackError();
1135
- throw err;
1136
- }
1137
- }
1138
1156
  trackTokens(tokens) {
1139
1157
  if (this._trackedMetrics.tokens !== void 0) {
1140
1158
  this._ldClient.logger?.warn(
1141
- "Token usage has already been tracked for this execution. Use createTracker() for a new execution."
1159
+ "Skipping trackTokens: token usage already recorded on this tracker. Call createTracker on the AI Config for a new run."
1142
1160
  );
1143
1161
  return;
1144
1162
  }
@@ -1172,6 +1190,7 @@ var LDGraphTrackerImpl = class _LDGraphTrackerImpl {
1172
1190
  this._version = _version;
1173
1191
  this._context = _context;
1174
1192
  this._summary = {};
1193
+ this._summary.resumptionToken = this.resumptionToken;
1175
1194
  }
1176
1195
  /**
1177
1196
  * Reconstructs an {@link LDGraphTrackerImpl} from a resumption token, preserving
@@ -1197,15 +1216,12 @@ var LDGraphTrackerImpl = class _LDGraphTrackerImpl {
1197
1216
  );
1198
1217
  }
1199
1218
  getTrackData() {
1200
- const data = {
1219
+ return {
1201
1220
  runId: this._runId,
1202
1221
  graphKey: this._graphKey,
1203
- version: this._version
1222
+ version: this._version,
1223
+ ...this._variationKey !== void 0 ? { variationKey: this._variationKey } : {}
1204
1224
  };
1205
- if (this._variationKey !== void 0) {
1206
- data.variationKey = this._variationKey;
1207
- }
1208
- return data;
1209
1225
  }
1210
1226
  getSummary() {
1211
1227
  return { ...this._summary };
@@ -1225,7 +1241,7 @@ var LDGraphTrackerImpl = class _LDGraphTrackerImpl {
1225
1241
  trackInvocationSuccess() {
1226
1242
  if (this._summary.success !== void 0) {
1227
1243
  this._ldClient.logger?.warn(
1228
- "LDGraphTracker: invocation success/failure already recorded for this run \u2014 dropping duplicate call."
1244
+ "Skipping trackInvocationSuccess: invocation result already recorded on this graph tracker. Call createTracker on the agent graph for a new run."
1229
1245
  );
1230
1246
  return;
1231
1247
  }
@@ -1235,7 +1251,7 @@ var LDGraphTrackerImpl = class _LDGraphTrackerImpl {
1235
1251
  trackInvocationFailure() {
1236
1252
  if (this._summary.success !== void 0) {
1237
1253
  this._ldClient.logger?.warn(
1238
- "LDGraphTracker: invocation success/failure already recorded for this run \u2014 dropping duplicate call."
1254
+ "Skipping trackInvocationFailure: invocation result already recorded on this graph tracker. Call createTracker on the agent graph for a new run."
1239
1255
  );
1240
1256
  return;
1241
1257
  }
@@ -1245,7 +1261,7 @@ var LDGraphTrackerImpl = class _LDGraphTrackerImpl {
1245
1261
  trackDuration(durationMs) {
1246
1262
  if (this._summary.durationMs !== void 0) {
1247
1263
  this._ldClient.logger?.warn(
1248
- "LDGraphTracker: trackDuration already called for this run \u2014 dropping duplicate call."
1264
+ "Skipping trackDuration: duration already recorded on this graph tracker. Call createTracker on the agent graph for a new run."
1249
1265
  );
1250
1266
  return;
1251
1267
  }
@@ -1260,7 +1276,7 @@ var LDGraphTrackerImpl = class _LDGraphTrackerImpl {
1260
1276
  trackTotalTokens(tokens) {
1261
1277
  if (this._summary.tokens !== void 0) {
1262
1278
  this._ldClient.logger?.warn(
1263
- "LDGraphTracker: trackTotalTokens already called for this run \u2014 dropping duplicate call."
1279
+ "Skipping trackTotalTokens: tokens already recorded on this graph tracker. Call createTracker on the agent graph for a new run."
1264
1280
  );
1265
1281
  return;
1266
1282
  }
@@ -1275,7 +1291,7 @@ var LDGraphTrackerImpl = class _LDGraphTrackerImpl {
1275
1291
  trackPath(path) {
1276
1292
  if (this._summary.path !== void 0) {
1277
1293
  this._ldClient.logger?.warn(
1278
- "LDGraphTracker: trackPath already called for this run \u2014 dropping duplicate call."
1294
+ "Skipping trackPath: path already recorded on this graph tracker. Call createTracker on the agent graph for a new run."
1279
1295
  );
1280
1296
  return;
1281
1297
  }
@@ -1310,13 +1326,14 @@ var LDGraphTrackerImpl = class _LDGraphTrackerImpl {
1310
1326
 
1311
1327
  // src/sdkInfo.ts
1312
1328
  var aiSdkName = "@launchdarkly/server-sdk-ai";
1313
- var aiSdkVersion = "0.19.1";
1329
+ var aiSdkVersion = "1.0.0";
1314
1330
  var aiSdkLanguage = "javascript";
1315
1331
 
1316
1332
  // src/LDAIClientImpl.ts
1317
1333
  var TRACK_SDK_INFO = "$ld:ai:sdk:info";
1318
1334
  var TRACK_USAGE_COMPLETION_CONFIG = "$ld:ai:usage:completion-config";
1319
1335
  var TRACK_USAGE_CREATE_CHAT = "$ld:ai:usage:create-chat";
1336
+ var TRACK_USAGE_CREATE_AGENT = "$ld:ai:usage:create-agent";
1320
1337
  var TRACK_USAGE_JUDGE_CONFIG = "$ld:ai:usage:judge-config";
1321
1338
  var TRACK_USAGE_CREATE_JUDGE = "$ld:ai:usage:create-judge";
1322
1339
  var TRACK_USAGE_AGENT_CONFIG = "$ld:ai:usage:agent-config";
@@ -1344,18 +1361,11 @@ var LDAIClientImpl = class {
1344
1361
  );
1345
1362
  }
1346
1363
  _interpolateTemplate(template, variables) {
1347
- return Mustache2.render(template, variables, void 0, { escape: (item) => item });
1364
+ return Mustache.render(template, variables, void 0, { escape: (item) => item });
1348
1365
  }
1349
- async _evaluate(key, context, defaultValue, mode, variables, graphKey) {
1366
+ async _evaluate(key, context, defaultValue, mode, variables, graphKey, defaultAiProvider) {
1350
1367
  const ldFlagValue = LDAIConfigUtils.toFlagValue(defaultValue, mode);
1351
1368
  const value = await this._ldClient.variation(key, context, ldFlagValue);
1352
- const flagMode = value._ldMeta?.mode ?? "completion";
1353
- if (flagMode !== mode) {
1354
- this._logger?.warn(
1355
- `AI Config mode mismatch for ${key}: expected ${mode}, got ${flagMode}. Returning disabled config.`
1356
- );
1357
- return LDAIConfigUtils.createDisabledConfig(key, mode);
1358
- }
1359
1369
  const trackerFactory = () => new LDAIConfigTrackerImpl(
1360
1370
  this._ldClient,
1361
1371
  randomUUID(),
@@ -1369,7 +1379,23 @@ var LDAIClientImpl = class {
1369
1379
  context,
1370
1380
  graphKey
1371
1381
  );
1372
- const config = LDAIConfigUtils.fromFlagValue(key, value, trackerFactory);
1382
+ const flagMode = value._ldMeta?.mode ?? "completion";
1383
+ let evaluator = Evaluator.noop();
1384
+ if (flagMode !== mode) {
1385
+ this._logger?.warn(
1386
+ `AI Config mode mismatch for ${key}: expected ${mode}, got ${flagMode}. Returning disabled config.`
1387
+ );
1388
+ return LDAIConfigUtils.createDisabledConfig(key, mode, trackerFactory, evaluator);
1389
+ }
1390
+ if (flagMode !== "judge") {
1391
+ evaluator = await this._buildEvaluator(
1392
+ value.judgeConfiguration?.judges ?? [],
1393
+ context,
1394
+ variables,
1395
+ defaultAiProvider
1396
+ );
1397
+ }
1398
+ const config = LDAIConfigUtils.fromFlagValue(key, value, trackerFactory, evaluator);
1373
1399
  return this._applyInterpolation(config, context, variables);
1374
1400
  }
1375
1401
  _applyInterpolation(config, context, variables) {
@@ -1391,61 +1417,98 @@ var LDAIClientImpl = class {
1391
1417
  }
1392
1418
  return config;
1393
1419
  }
1394
- async _initializeJudges(judgeConfigs, context, variables, defaultAiProvider) {
1395
- const judges = {};
1396
- const judgePromises = judgeConfigs.map(async (judgeConfig) => {
1397
- const judge = await this.createJudge(
1398
- judgeConfig.key,
1399
- context,
1400
- void 0,
1401
- variables,
1402
- defaultAiProvider
1403
- );
1404
- return judge ? { key: judgeConfig.key, judge } : null;
1405
- });
1406
- const results = await Promise.all(judgePromises);
1407
- results.forEach((result) => {
1408
- if (result) {
1409
- judges[result.key] = result.judge;
1410
- }
1411
- });
1412
- return judges;
1413
- }
1414
- async _completionConfig(key, context, defaultValue, variables) {
1415
- const config = await this._evaluate(key, context, defaultValue, "completion", variables);
1416
- return config;
1420
+ async _buildEvaluator(judgeConfigs, context, variables, defaultAiProvider) {
1421
+ if (judgeConfigs.length === 0) {
1422
+ return Evaluator.noop();
1423
+ }
1424
+ const judgeInstances = (await Promise.all(
1425
+ judgeConfigs.map(
1426
+ (jc) => this._createJudgeInstance(
1427
+ jc.key,
1428
+ context,
1429
+ void 0,
1430
+ variables,
1431
+ defaultAiProvider,
1432
+ jc.samplingRate
1433
+ )
1434
+ )
1435
+ )).filter((j) => j !== void 0);
1436
+ return new Evaluator(judgeInstances);
1437
+ }
1438
+ async _completionConfig(key, context, defaultValue, variables, defaultAiProvider) {
1439
+ return await this._evaluate(
1440
+ key,
1441
+ context,
1442
+ defaultValue,
1443
+ "completion",
1444
+ variables,
1445
+ void 0,
1446
+ defaultAiProvider
1447
+ );
1417
1448
  }
1418
- async completionConfig(key, context, defaultValue, variables) {
1449
+ async completionConfig(key, context, defaultValue, variables, defaultAiProvider) {
1419
1450
  this._ldClient.track(TRACK_USAGE_COMPLETION_CONFIG, context, key, 1);
1420
- return this._completionConfig(key, context, defaultValue ?? disabledAIConfig, variables);
1421
- }
1422
- /**
1423
- * @deprecated Use `completionConfig` instead. This method will be removed in a future version.
1424
- */
1425
- async config(key, context, defaultValue, variables) {
1426
- return this.completionConfig(key, context, defaultValue, variables);
1451
+ return this._completionConfig(
1452
+ key,
1453
+ context,
1454
+ defaultValue ?? disabledAIConfig,
1455
+ variables,
1456
+ defaultAiProvider
1457
+ );
1427
1458
  }
1428
1459
  async _judgeConfig(key, context, defaultValue, variables) {
1429
- const config = await this._evaluate(key, context, defaultValue, "judge", variables);
1460
+ if (variables?.message_history !== void 0) {
1461
+ this._logger?.warn(
1462
+ "The variable 'message_history' is reserved by the judge and will be ignored."
1463
+ );
1464
+ }
1465
+ if (variables?.response_to_evaluate !== void 0) {
1466
+ this._logger?.warn(
1467
+ "The variable 'response_to_evaluate' is reserved by the judge and will be ignored."
1468
+ );
1469
+ }
1470
+ const extendedVariables = {
1471
+ ...variables,
1472
+ message_history: "{{message_history}}",
1473
+ response_to_evaluate: "{{response_to_evaluate}}"
1474
+ };
1475
+ const config = await this._evaluate(
1476
+ key,
1477
+ context,
1478
+ defaultValue,
1479
+ "judge",
1480
+ extendedVariables
1481
+ );
1482
+ if (config.messages) {
1483
+ return { ...config, messages: stripLegacyJudgeMessages(config.messages) };
1484
+ }
1430
1485
  return config;
1431
1486
  }
1432
1487
  async judgeConfig(key, context, defaultValue, variables) {
1433
1488
  this._ldClient.track(TRACK_USAGE_JUDGE_CONFIG, context, key, 1);
1434
1489
  return this._judgeConfig(key, context, defaultValue ?? disabledAIConfig, variables);
1435
1490
  }
1436
- async _agentConfig(key, context, defaultValue, variables, graphKey) {
1437
- const config = await this._evaluate(key, context, defaultValue, "agent", variables, graphKey);
1438
- return config;
1491
+ async _agentConfig(key, context, defaultValue, variables, graphKey, defaultAiProvider) {
1492
+ return await this._evaluate(
1493
+ key,
1494
+ context,
1495
+ defaultValue,
1496
+ "agent",
1497
+ variables,
1498
+ graphKey,
1499
+ defaultAiProvider
1500
+ );
1439
1501
  }
1440
- async agentConfig(key, context, defaultValue, variables) {
1502
+ async agentConfig(key, context, defaultValue, variables, defaultAiProvider) {
1441
1503
  this._ldClient.track(TRACK_USAGE_AGENT_CONFIG, context, key, 1);
1442
- return this._agentConfig(key, context, defaultValue ?? disabledAIConfig, variables);
1443
- }
1444
- /**
1445
- * @deprecated Use `agentConfig` instead. This method will be removed in a future version.
1446
- */
1447
- async agent(key, context, defaultValue, variables) {
1448
- return this.agentConfig(key, context, defaultValue, variables);
1504
+ return this._agentConfig(
1505
+ key,
1506
+ context,
1507
+ defaultValue ?? disabledAIConfig,
1508
+ variables,
1509
+ void 0,
1510
+ defaultAiProvider
1511
+ );
1449
1512
  }
1450
1513
  async agentConfigs(agentConfigs, context) {
1451
1514
  this._ldClient.track(
@@ -1468,79 +1531,82 @@ var LDAIClientImpl = class {
1468
1531
  );
1469
1532
  return agents;
1470
1533
  }
1471
- /**
1472
- * @deprecated Use `agentConfigs` instead. This method will be removed in a future version.
1473
- */
1474
- async agents(agentConfigs, context) {
1475
- return this.agentConfigs(agentConfigs, context);
1476
- }
1477
- async createChat(key, context, defaultValue, variables, defaultAiProvider) {
1478
- this._ldClient.track(TRACK_USAGE_CREATE_CHAT, context, key, 1);
1479
- const config = await this._completionConfig(
1534
+ async createJudge(key, context, defaultValue, variables, defaultAiProvider, sampleRate = 1) {
1535
+ this._ldClient.track(TRACK_USAGE_CREATE_JUDGE, context, key, 1);
1536
+ return this._createJudgeInstance(
1480
1537
  key,
1481
1538
  context,
1482
- defaultValue ?? disabledAIConfig,
1483
- variables
1484
- );
1485
- if (!config.enabled) {
1486
- this._logger?.info(`Chat configuration is disabled: ${key}`);
1487
- return void 0;
1488
- }
1489
- const provider = await AIProviderFactory.create(config, this._logger, defaultAiProvider);
1490
- if (!provider) {
1491
- return void 0;
1492
- }
1493
- const judges = await this._initializeJudges(
1494
- config.judgeConfiguration?.judges ?? [],
1495
- context,
1539
+ defaultValue,
1496
1540
  variables,
1497
- defaultAiProvider
1541
+ defaultAiProvider,
1542
+ sampleRate
1498
1543
  );
1499
- return new TrackedChat(config, provider, judges, this._logger);
1500
1544
  }
1501
- async createJudge(key, context, defaultValue, variables, defaultAiProvider) {
1502
- this._ldClient.track(TRACK_USAGE_CREATE_JUDGE, context, key, 1);
1545
+ async _createJudgeInstance(key, context, defaultValue, variables, defaultAiProvider, sampleRate = 1) {
1503
1546
  try {
1504
- if (variables?.message_history !== void 0) {
1505
- this._logger?.warn(
1506
- "The variable 'message_history' is reserved by the judge and will be ignored."
1507
- );
1508
- }
1509
- if (variables?.response_to_evaluate !== void 0) {
1510
- this._logger?.warn(
1511
- "The variable 'response_to_evaluate' is reserved by the judge and will be ignored."
1512
- );
1513
- }
1514
- const extendedVariables = {
1515
- ...variables,
1516
- message_history: "{{message_history}}",
1517
- response_to_evaluate: "{{response_to_evaluate}}"
1518
- };
1519
1547
  const judgeConfig = await this._judgeConfig(
1520
1548
  key,
1521
1549
  context,
1522
1550
  defaultValue ?? disabledAIConfig,
1523
- extendedVariables
1551
+ variables
1524
1552
  );
1525
1553
  if (!judgeConfig.enabled) {
1526
1554
  this._logger?.info(`Judge configuration is disabled: ${key}`);
1527
1555
  return void 0;
1528
1556
  }
1529
- const provider = await AIProviderFactory.create(judgeConfig, this._logger, defaultAiProvider);
1530
- if (!provider) {
1557
+ const runner = await RunnerFactory.createModel(
1558
+ judgeConfig,
1559
+ this._logger,
1560
+ defaultAiProvider,
1561
+ false
1562
+ );
1563
+ if (!runner) {
1531
1564
  return void 0;
1532
1565
  }
1533
- return new Judge(judgeConfig, provider, this._logger);
1566
+ return new Judge(judgeConfig, runner, sampleRate, this._logger);
1534
1567
  } catch (error) {
1535
1568
  this._logger?.error(`Failed to initialize judge ${key}:`, error);
1536
1569
  return void 0;
1537
1570
  }
1538
1571
  }
1539
- /**
1540
- * @deprecated Use `createChat` instead. This method will be removed in a future version.
1541
- */
1542
- async initChat(key, context, defaultValue, variables, defaultAiProvider) {
1543
- return this.createChat(key, context, defaultValue, variables, defaultAiProvider);
1572
+ async createModel(key, context, defaultValue, variables, defaultAiProvider) {
1573
+ this._ldClient.track(TRACK_USAGE_CREATE_CHAT, context, key, 1);
1574
+ const config = await this._completionConfig(
1575
+ key,
1576
+ context,
1577
+ defaultValue ?? disabledAIConfig,
1578
+ variables,
1579
+ defaultAiProvider
1580
+ );
1581
+ if (!config.enabled) {
1582
+ this._logger?.info(`Completion configuration is disabled: ${key}`);
1583
+ return void 0;
1584
+ }
1585
+ const runner = await RunnerFactory.createModel(config, this._logger, defaultAiProvider);
1586
+ if (!runner) {
1587
+ return void 0;
1588
+ }
1589
+ return new ManagedModel(config, runner, this._logger);
1590
+ }
1591
+ async createAgent(key, context, defaultValue, variables, defaultAiProvider) {
1592
+ this._ldClient.track(TRACK_USAGE_CREATE_AGENT, context, key, 1);
1593
+ const config = await this._agentConfig(
1594
+ key,
1595
+ context,
1596
+ defaultValue ?? disabledAIConfig,
1597
+ variables,
1598
+ void 0,
1599
+ defaultAiProvider
1600
+ );
1601
+ if (!config.enabled) {
1602
+ this._logger?.info(`Agent configuration is disabled: ${key}`);
1603
+ return void 0;
1604
+ }
1605
+ const runner = await RunnerFactory.createAgent(config, void 0, this._logger, defaultAiProvider);
1606
+ if (!runner) {
1607
+ return void 0;
1608
+ }
1609
+ return new ManagedAgent(config, runner, this._logger);
1544
1610
  }
1545
1611
  createTracker(token, context) {
1546
1612
  return LDAIConfigTrackerImpl.fromResumptionToken(token, this._ldClient, context);
@@ -1619,23 +1685,97 @@ var LDAIClientImpl = class {
1619
1685
  }
1620
1686
  };
1621
1687
 
1688
+ // src/api/ManagedAgentGraph.ts
1689
+ var ManagedAgentGraph = class {
1690
+ constructor(_graphDefinition, _logger) {
1691
+ this._graphDefinition = _graphDefinition;
1692
+ this._logger = _logger;
1693
+ }
1694
+ /**
1695
+ * Runs the agent graph using the provided runner function and returns a ManagedGraphResult.
1696
+ *
1697
+ * The runner function receives the graph tracker and AgentGraphDefinition,
1698
+ * executes the graph, and returns an AgentGraphRunnerResult.
1699
+ *
1700
+ * run() returns before ManagedGraphResult.evaluations resolves.
1701
+ *
1702
+ * @param runner Async function that executes the graph and returns AgentGraphRunnerResult.
1703
+ * @returns ManagedGraphResult with LDAIGraphMetricSummary and evaluations promise.
1704
+ */
1705
+ async run(runner) {
1706
+ const graphTracker = this._graphDefinition.createTracker();
1707
+ const runnerResult = await runner(this._graphDefinition, graphTracker);
1708
+ const metrics = {
1709
+ success: runnerResult.metrics.success,
1710
+ path: runnerResult.metrics.path,
1711
+ durationMs: runnerResult.metrics.durationMs,
1712
+ tokens: runnerResult.metrics.tokens,
1713
+ nodeMetrics: this._trackNodeMetrics(runnerResult.metrics.nodeMetrics),
1714
+ resumptionToken: graphTracker.resumptionToken
1715
+ };
1716
+ const evaluations = Promise.resolve([]);
1717
+ return {
1718
+ content: runnerResult.content,
1719
+ metrics,
1720
+ raw: runnerResult.raw,
1721
+ evaluations
1722
+ };
1723
+ }
1724
+ /**
1725
+ * Converts per-node LDAIMetrics from the runner into LDAIMetricSummary by
1726
+ * creating a per-node tracker, firing tracking events, and calling getSummary().
1727
+ */
1728
+ _trackNodeMetrics(nodeMetrics) {
1729
+ const summaries = {};
1730
+ for (const [nodeKey, metrics] of Object.entries(nodeMetrics)) {
1731
+ const node = this._graphDefinition.getNode(nodeKey);
1732
+ if (!node) {
1733
+ this._logger?.warn(`ManagedAgentGraph: no node found for key "${nodeKey}", skipping metrics`);
1734
+ continue;
1735
+ }
1736
+ const tracker = node.getConfig().createTracker();
1737
+ if (metrics.tokens) {
1738
+ tracker.trackTokens(metrics.tokens);
1739
+ }
1740
+ if (metrics.durationMs !== void 0) {
1741
+ tracker.trackDuration(metrics.durationMs);
1742
+ }
1743
+ if (metrics.toolCalls?.length) {
1744
+ tracker.trackToolCalls(metrics.toolCalls);
1745
+ }
1746
+ if (metrics.success) {
1747
+ tracker.trackSuccess();
1748
+ } else {
1749
+ tracker.trackError();
1750
+ }
1751
+ summaries[nodeKey] = tracker.getSummary();
1752
+ }
1753
+ return summaries;
1754
+ }
1755
+ /**
1756
+ * Returns the underlying AgentGraphDefinition.
1757
+ */
1758
+ getGraphDefinition() {
1759
+ return this._graphDefinition;
1760
+ }
1761
+ };
1762
+
1622
1763
  // src/index.ts
1623
1764
  function initAi(ldClient) {
1624
1765
  return new LDAIClientImpl(ldClient);
1625
1766
  }
1626
1767
  export {
1627
1768
  AIProvider,
1628
- AIProviderFactory,
1629
1769
  AgentGraphDefinition,
1630
1770
  AgentGraphNode,
1631
1771
  Judge,
1632
1772
  LDFeedbackKind,
1633
1773
  LDGraphTrackerImpl,
1774
+ ManagedAgent,
1775
+ ManagedAgentGraph,
1776
+ ManagedModel,
1777
+ RunnerFactory,
1634
1778
  SUPPORTED_AI_PROVIDERS,
1635
- TrackedChat,
1636
- createBedrockTokenUsage,
1637
- createOpenAiUsage,
1638
- createVercelAISDKTokenUsage,
1639
1779
  initAi
1640
1780
  };
1641
1781
  //# sourceMappingURL=index.js.map