@launchdarkly/server-sdk-ai 0.10.1 → 0.11.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.
Files changed (54) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/__tests__/LDAIClientImpl.test.ts +328 -1
  3. package/__tests__/LDAIConfigTrackerImpl.test.ts +94 -136
  4. package/dist/LDAIClientImpl.d.ts +7 -0
  5. package/dist/LDAIClientImpl.d.ts.map +1 -1
  6. package/dist/LDAIClientImpl.js +60 -11
  7. package/dist/LDAIClientImpl.js.map +1 -1
  8. package/dist/LDAIConfigTrackerImpl.d.ts +3 -1
  9. package/dist/LDAIConfigTrackerImpl.d.ts.map +1 -1
  10. package/dist/LDAIConfigTrackerImpl.js +5 -3
  11. package/dist/LDAIConfigTrackerImpl.js.map +1 -1
  12. package/dist/api/LDAIClient.d.ts +70 -0
  13. package/dist/api/LDAIClient.d.ts.map +1 -1
  14. package/dist/api/agents/LDAIAgent.d.ts +32 -0
  15. package/dist/api/agents/LDAIAgent.d.ts.map +1 -0
  16. package/dist/api/agents/LDAIAgent.js +3 -0
  17. package/dist/api/agents/LDAIAgent.js.map +1 -0
  18. package/dist/api/agents/index.d.ts +2 -0
  19. package/dist/api/agents/index.d.ts.map +1 -0
  20. package/dist/api/agents/index.js +18 -0
  21. package/dist/api/agents/index.js.map +1 -0
  22. package/dist/api/index.d.ts +1 -0
  23. package/dist/api/index.d.ts.map +1 -1
  24. package/dist/api/index.js +1 -0
  25. package/dist/api/index.js.map +1 -1
  26. package/docs/assets/highlight.css +15 -8
  27. package/docs/assets/search.js +1 -1
  28. package/docs/enums/LDFeedbackKind.html +11 -8
  29. package/docs/functions/createBedrockTokenUsage.html +9 -6
  30. package/docs/functions/createOpenAiUsage.html +9 -6
  31. package/docs/functions/createVercelAISDKTokenUsage.html +9 -6
  32. package/docs/functions/initAi.html +9 -6
  33. package/docs/index.html +12 -6
  34. package/docs/interfaces/LDAIAgent.html +177 -0
  35. package/docs/interfaces/LDAIAgentConfig.html +111 -0
  36. package/docs/interfaces/LDAIClient.html +96 -10
  37. package/docs/interfaces/LDAIConfig.html +15 -12
  38. package/docs/interfaces/LDAIConfigTracker.html +21 -18
  39. package/docs/interfaces/LDMessage.html +11 -8
  40. package/docs/interfaces/LDModelConfig.html +12 -9
  41. package/docs/interfaces/LDProviderConfig.html +10 -7
  42. package/docs/interfaces/LDTokenUsage.html +12 -9
  43. package/docs/interfaces/VercelAISDKConfig.html +19 -16
  44. package/docs/interfaces/VercelAISDKMapOptions.html +10 -7
  45. package/docs/types/LDAIAgentDefaults.html +63 -0
  46. package/docs/types/LDAIDefaults.html +9 -6
  47. package/docs/types/VercelAISDKProvider.html +9 -6
  48. package/package.json +1 -1
  49. package/src/LDAIClientImpl.ts +146 -12
  50. package/src/LDAIConfigTrackerImpl.ts +11 -3
  51. package/src/api/LDAIClient.ts +80 -0
  52. package/src/api/agents/LDAIAgent.ts +36 -0
  53. package/src/api/agents/index.ts +1 -0
  54. package/src/api/index.ts +1 -0
@@ -2,8 +2,10 @@ import * as Mustache from 'mustache';
2
2
 
3
3
  import { LDContext } from '@launchdarkly/js-server-sdk-common';
4
4
 
5
+ import { LDAIAgent, LDAIAgentConfig, LDAIAgentDefaults } from './api/agents';
5
6
  import {
6
7
  LDAIConfig,
8
+ LDAIConfigTracker,
7
9
  LDAIDefaults,
8
10
  LDMessage,
9
11
  LDModelConfig,
@@ -17,13 +19,16 @@ import { LDAIConfigMapper } from './LDAIConfigMapper';
17
19
  import { LDAIConfigTrackerImpl } from './LDAIConfigTrackerImpl';
18
20
  import { LDClientMin } from './LDClientMin';
19
21
 
22
+ type Mode = 'completion' | 'agent';
23
+
20
24
  /**
21
- * Metadata assorted with a model configuration variation.
25
+ * Metadata associated with a model configuration variation.
22
26
  */
23
27
  interface LDMeta {
24
28
  variationKey: string;
25
29
  enabled: boolean;
26
30
  version?: number;
31
+ mode?: Mode;
27
32
  }
28
33
 
29
34
  /**
@@ -33,10 +38,24 @@ interface LDMeta {
33
38
  interface VariationContent {
34
39
  model?: LDModelConfig;
35
40
  messages?: LDMessage[];
41
+ instructions?: string;
36
42
  provider?: LDProviderConfig;
37
43
  _ldMeta?: LDMeta;
38
44
  }
39
45
 
46
+ /**
47
+ * The result of evaluating a configuration.
48
+ */
49
+ interface EvaluationResult {
50
+ tracker: LDAIConfigTracker;
51
+ enabled: boolean;
52
+ model?: LDModelConfig;
53
+ provider?: LDProviderConfig;
54
+ messages?: LDMessage[];
55
+ instructions?: string;
56
+ mode?: string;
57
+ }
58
+
40
59
  export class LDAIClientImpl implements LDAIClient {
41
60
  constructor(private _ldClient: LDClientMin) {}
42
61
 
@@ -44,13 +63,13 @@ export class LDAIClientImpl implements LDAIClient {
44
63
  return Mustache.render(template, variables, undefined, { escape: (item: any) => item });
45
64
  }
46
65
 
47
- async config(
66
+ private async _evaluate(
48
67
  key: string,
49
68
  context: LDContext,
50
69
  defaultValue: LDAIDefaults,
51
- variables?: Record<string, unknown>,
52
- ): Promise<LDAIConfig> {
70
+ ): Promise<EvaluationResult> {
53
71
  const value: VariationContent = await this._ldClient.variation(key, context, defaultValue);
72
+
54
73
  const tracker = new LDAIConfigTrackerImpl(
55
74
  this._ldClient,
56
75
  key,
@@ -58,26 +77,100 @@ export class LDAIClientImpl implements LDAIClient {
58
77
  value._ldMeta?.variationKey ?? '',
59
78
  // eslint-disable-next-line no-underscore-dangle
60
79
  value._ldMeta?.version ?? 1,
80
+ value.model?.name ?? '',
81
+ value.provider?.name ?? '',
61
82
  context,
62
83
  );
84
+
63
85
  // eslint-disable-next-line no-underscore-dangle
64
86
  const enabled = !!value._ldMeta?.enabled;
87
+
88
+ return {
89
+ tracker,
90
+ enabled,
91
+ model: value.model,
92
+ provider: value.provider,
93
+ messages: value.messages,
94
+ instructions: value.instructions,
95
+ // eslint-disable-next-line no-underscore-dangle
96
+ mode: value._ldMeta?.mode ?? 'completion',
97
+ };
98
+ }
99
+
100
+ private async _evaluateAgent(
101
+ key: string,
102
+ context: LDContext,
103
+ defaultValue: LDAIAgentDefaults,
104
+ variables?: Record<string, unknown>,
105
+ ): Promise<LDAIAgent> {
106
+ const { tracker, enabled, model, provider, instructions } = await this._evaluate(
107
+ key,
108
+ context,
109
+ defaultValue,
110
+ );
111
+
112
+ const mapper = new LDAIConfigMapper(model, provider, undefined);
113
+ const agent: Omit<LDAIAgent, 'toVercelAISDK'> = {
114
+ tracker,
115
+ enabled,
116
+ };
117
+
118
+ // We are going to modify the contents before returning them, so we make a copy.
119
+ // This isn't a deep copy and the application developer should not modify the returned content.
120
+ if (model) {
121
+ agent.model = { ...model };
122
+ }
123
+
124
+ if (provider) {
125
+ agent.provider = { ...provider };
126
+ }
127
+
128
+ const allVariables = { ...variables, ldctx: context };
129
+
130
+ if (instructions) {
131
+ agent.instructions = this._interpolateTemplate(instructions, allVariables);
132
+ }
133
+
134
+ return {
135
+ ...agent,
136
+ toVercelAISDK: <TMod>(
137
+ sdkProvider: VercelAISDKProvider<TMod> | Record<string, VercelAISDKProvider<TMod>>,
138
+ options?: VercelAISDKMapOptions | undefined,
139
+ ): VercelAISDKConfig<TMod> => mapper.toVercelAISDK(sdkProvider, options),
140
+ };
141
+ }
142
+
143
+ async config(
144
+ key: string,
145
+ context: LDContext,
146
+ defaultValue: LDAIDefaults,
147
+ variables?: Record<string, unknown>,
148
+ ): Promise<LDAIConfig> {
149
+ const {
150
+ tracker,
151
+ enabled,
152
+ model,
153
+ provider: configProvider,
154
+ messages,
155
+ } = await this._evaluate(key, context, defaultValue);
156
+
65
157
  const config: Omit<LDAIConfig, 'toVercelAISDK'> = {
66
158
  tracker,
67
159
  enabled,
68
160
  };
161
+
69
162
  // We are going to modify the contents before returning them, so we make a copy.
70
163
  // This isn't a deep copy and the application developer should not modify the returned content.
71
- if (value.model) {
72
- config.model = { ...value.model };
164
+ if (model) {
165
+ config.model = { ...model };
73
166
  }
74
- if (value.provider) {
75
- config.provider = { ...value.provider };
167
+ if (configProvider) {
168
+ config.provider = { ...configProvider };
76
169
  }
77
170
  const allVariables = { ...variables, ldctx: context };
78
171
 
79
- if (value.messages) {
80
- config.messages = value.messages.map((entry: any) => ({
172
+ if (messages) {
173
+ config.messages = messages.map((entry: any) => ({
81
174
  ...entry,
82
175
  content: this._interpolateTemplate(entry.content, allVariables),
83
176
  }));
@@ -88,9 +181,50 @@ export class LDAIClientImpl implements LDAIClient {
88
181
  return {
89
182
  ...config,
90
183
  toVercelAISDK: <TMod>(
91
- provider: VercelAISDKProvider<TMod> | Record<string, VercelAISDKProvider<TMod>>,
184
+ sdkProvider: VercelAISDKProvider<TMod> | Record<string, VercelAISDKProvider<TMod>>,
92
185
  options?: VercelAISDKMapOptions | undefined,
93
- ): VercelAISDKConfig<TMod> => mapper.toVercelAISDK(provider, options),
186
+ ): VercelAISDKConfig<TMod> => mapper.toVercelAISDK(sdkProvider, options),
94
187
  };
95
188
  }
189
+
190
+ async agent(
191
+ key: string,
192
+ context: LDContext,
193
+ defaultValue: LDAIAgentDefaults,
194
+ variables?: Record<string, unknown>,
195
+ ): Promise<LDAIAgent> {
196
+ // Track agent usage
197
+ this._ldClient.track('$ld:ai:agent:function:single', context, key, 1);
198
+
199
+ return this._evaluateAgent(key, context, defaultValue, variables);
200
+ }
201
+
202
+ async agents<const T extends readonly (LDAIAgentConfig & { defaultValue: LDAIAgentDefaults })[]>(
203
+ agentConfigs: T,
204
+ context: LDContext,
205
+ ): Promise<Record<T[number]['key'], LDAIAgent>> {
206
+ // Track multiple agents usage
207
+ this._ldClient.track(
208
+ '$ld:ai:agent:function:multiple',
209
+ context,
210
+ agentConfigs.length,
211
+ agentConfigs.length,
212
+ );
213
+
214
+ const agents = {} as Record<T[number]['key'], LDAIAgent>;
215
+
216
+ await Promise.all(
217
+ agentConfigs.map(async (config) => {
218
+ const agent = await this._evaluateAgent(
219
+ config.key,
220
+ context,
221
+ config.defaultValue,
222
+ config.variables,
223
+ );
224
+ agents[config.key as T[number]['key']] = agent;
225
+ }),
226
+ );
227
+
228
+ return agents;
229
+ }
96
230
  }
@@ -19,14 +19,24 @@ export class LDAIConfigTrackerImpl implements LDAIConfigTracker {
19
19
  private _configKey: string,
20
20
  private _variationKey: string,
21
21
  private _version: number,
22
+ private _modelName: string,
23
+ private _providerName: string,
22
24
  private _context: LDContext,
23
25
  ) {}
24
26
 
25
- private _getTrackData(): { variationKey: string; configKey: string; version: number } {
27
+ private _getTrackData(): {
28
+ variationKey: string;
29
+ configKey: string;
30
+ version: number;
31
+ modelName: string;
32
+ providerName: string;
33
+ } {
26
34
  return {
27
35
  variationKey: this._variationKey,
28
36
  configKey: this._configKey,
29
37
  version: this._version,
38
+ modelName: this._modelName,
39
+ providerName: this._providerName,
30
40
  };
31
41
  }
32
42
 
@@ -69,13 +79,11 @@ export class LDAIConfigTrackerImpl implements LDAIConfigTracker {
69
79
 
70
80
  trackSuccess(): void {
71
81
  this._trackedMetrics.success = true;
72
- this._ldClient.track('$ld:ai:generation', this._context, this._getTrackData(), 1);
73
82
  this._ldClient.track('$ld:ai:generation:success', this._context, this._getTrackData(), 1);
74
83
  }
75
84
 
76
85
  trackError(): void {
77
86
  this._trackedMetrics.success = false;
78
- this._ldClient.track('$ld:ai:generation', this._context, this._getTrackData(), 1);
79
87
  this._ldClient.track('$ld:ai:generation:error', this._context, this._getTrackData(), 1);
80
88
  }
81
89
 
@@ -1,5 +1,6 @@
1
1
  import { LDContext } from '@launchdarkly/js-server-sdk-common';
2
2
 
3
+ import { LDAIAgent, LDAIAgentConfig, LDAIAgentDefaults } from './agents';
3
4
  import { LDAIConfig, LDAIDefaults } from './config/LDAIConfig';
4
5
 
5
6
  /**
@@ -63,4 +64,83 @@ export interface LDAIClient {
63
64
  defaultValue: LDAIDefaults,
64
65
  variables?: Record<string, unknown>,
65
66
  ): Promise<LDAIConfig>;
67
+
68
+ /**
69
+ * Retrieves and processes a single AI Config agent based on the provided key, LaunchDarkly context,
70
+ * and variables. This includes the model configuration and the customized instructions.
71
+ *
72
+ * @param key The key of the AI Config agent.
73
+ * @param context The LaunchDarkly context object that contains relevant information about the
74
+ * current environment, user, or session. This context may influence how the configuration is
75
+ * processed or personalized.
76
+ * @param defaultValue A fallback value containing model configuration and instructions.
77
+ * @param variables A map of key-value pairs representing dynamic variables to be injected into
78
+ * the instructions. The keys correspond to placeholders within the template, and the values
79
+ * are the corresponding replacements.
80
+ *
81
+ * @returns An AI agent with customized `instructions` and a `tracker`. If the configuration
82
+ * cannot be accessed from LaunchDarkly, then the return value will include information from the
83
+ * `defaultValue`. The returned `tracker` can be used to track AI operation metrics (latency, token usage, etc.).
84
+ *
85
+ * @example
86
+ * ```
87
+ * const key = "research_agent";
88
+ * const context = {...};
89
+ * const variables = { topic: 'climate change' };
90
+ * const agent = await client.agent(key, context, {
91
+ * enabled: true,
92
+ * instructions: 'You are a research assistant.',
93
+ * }, variables);
94
+ *
95
+ * const researchResult = agent.instructions; // Interpolated instructions
96
+ * agent.tracker.trackSuccess();
97
+ * ```
98
+ */
99
+ agent(
100
+ key: string,
101
+ context: LDContext,
102
+ defaultValue: LDAIAgentDefaults,
103
+ variables?: Record<string, unknown>,
104
+ ): Promise<LDAIAgent>;
105
+
106
+ /**
107
+ * Retrieves and processes multiple AI Config agents based on the provided agent configurations
108
+ * and LaunchDarkly context. This includes the model configuration and the customized instructions.
109
+ *
110
+ * @param agentConfigs An array of agent configurations, each containing the agent key, default configuration,
111
+ * and variables for instructions interpolation.
112
+ * @param context The LaunchDarkly context object that contains relevant information about the
113
+ * current environment, user, or session. This context may influence how the configuration is
114
+ * processed or personalized.
115
+ *
116
+ * @returns A map of agent keys to their respective AI agents with customized `instructions` and `tracker`.
117
+ * If a configuration cannot be accessed from LaunchDarkly, then the return value will include information
118
+ * from the respective `defaultValue`. The returned `tracker` can be used to track AI operation metrics
119
+ * (latency, token usage, etc.).
120
+ *
121
+ * @example
122
+ * ```
123
+ * const agentConfigs = [
124
+ * {
125
+ * key: 'research_agent',
126
+ * defaultValue: { enabled: true, instructions: 'You are a research assistant.' },
127
+ * variables: { topic: 'climate change' }
128
+ * },
129
+ * {
130
+ * key: 'writing_agent',
131
+ * defaultValue: { enabled: true, instructions: 'You are a writing assistant.' },
132
+ * variables: { style: 'academic' }
133
+ * }
134
+ * ] as const;
135
+ * const context = {...};
136
+ *
137
+ * const agents = await client.agents(agentConfigs, context);
138
+ * const researchResult = agents["research_agent"].instructions; // Interpolated instructions
139
+ * agents["research_agent"].tracker.trackSuccess();
140
+ * ```
141
+ */
142
+ agents<const T extends readonly LDAIAgentConfig[]>(
143
+ agentConfigs: T,
144
+ context: LDContext,
145
+ ): Promise<Record<T[number]['key'], LDAIAgent>>;
66
146
  }
@@ -0,0 +1,36 @@
1
+ import { LDAIConfig } from '../config';
2
+
3
+ /**
4
+ * AI Config agent and tracker.
5
+ */
6
+ export interface LDAIAgent extends Omit<LDAIConfig, 'messages'> {
7
+ /**
8
+ * Instructions for the agent.
9
+ */
10
+ instructions?: string;
11
+ }
12
+
13
+ /**
14
+ * Configuration for a single agent request.
15
+ */
16
+ export interface LDAIAgentConfig {
17
+ /**
18
+ * The agent key to retrieve.
19
+ */
20
+ key: string;
21
+
22
+ /**
23
+ * Default configuration for the agent.
24
+ */
25
+ defaultValue: LDAIAgentDefaults;
26
+
27
+ /**
28
+ * Variables for instructions interpolation.
29
+ */
30
+ variables?: Record<string, unknown>;
31
+ }
32
+
33
+ /**
34
+ * Default values for an agent.
35
+ */
36
+ export type LDAIAgentDefaults = Omit<LDAIAgent, 'tracker'>;
@@ -0,0 +1 @@
1
+ export * from './LDAIAgent';
package/src/api/index.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export * from './config';
2
+ export * from './agents';
2
3
  export * from './metrics';
3
4
  export * from './LDAIClient';