@launchdarkly/server-sdk-ai 0.10.1 → 0.11.2

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 +27 -0
  2. package/__tests__/LDAIClientImpl.test.ts +235 -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 +137 -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 +141 -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
package/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.11.2](https://github.com/launchdarkly/js-core/compare/server-sdk-ai-v0.11.1...server-sdk-ai-v0.11.2) (2025-08-26)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **ai-sdk:** Remove Vercel mapping method from LD ([#911](https://github.com/launchdarkly/js-core/issues/911)) ([f71a457](https://github.com/launchdarkly/js-core/commit/f71a45774eb3fc0fd0ac6a93b1443843d14712d2))
9
+
10
+ ## [0.11.1](https://github.com/launchdarkly/js-core/compare/server-sdk-ai-v0.11.0...server-sdk-ai-v0.11.1) (2025-08-19)
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * Add usage tracking to config method ([#904](https://github.com/launchdarkly/js-core/issues/904)) ([7f0a54c](https://github.com/launchdarkly/js-core/commit/7f0a54c4d880c8bc784fee0d4a2bc9155e96c1b7))
16
+
17
+ ## [0.11.0](https://github.com/launchdarkly/js-core/compare/server-sdk-ai-v0.10.1...server-sdk-ai-v0.11.0) (2025-08-01)
18
+
19
+
20
+ ### Features
21
+
22
+ * Adding agent support for AI Configs ([#893](https://github.com/launchdarkly/js-core/issues/893)) ([bf95b92](https://github.com/launchdarkly/js-core/commit/bf95b92946e93b54e1eda7ffef96039b2b42b9aa))
23
+ * Update AI tracker to include model & provider name for metrics generation ([#901](https://github.com/launchdarkly/js-core/issues/901)) ([9474862](https://github.com/launchdarkly/js-core/commit/94748621034ed6b1a74060ee0c536bf96a3cd43d))
24
+
25
+
26
+ ### Bug Fixes
27
+
28
+ * Remove deprecated track generation event ([#902](https://github.com/launchdarkly/js-core/issues/902)) ([40f8593](https://github.com/launchdarkly/js-core/commit/40f859386087a443948214e9b535527f125ffa39))
29
+
3
30
  ## [0.10.1](https://github.com/launchdarkly/js-core/compare/server-sdk-ai-v0.10.0...server-sdk-ai-v0.10.1) (2025-07-23)
4
31
 
5
32
 
@@ -1,5 +1,6 @@
1
1
  import { LDContext } from '@launchdarkly/js-server-sdk-common';
2
2
 
3
+ import { LDAIAgentDefaults } from '../src/api/agents';
3
4
  import { LDAIDefaults } from '../src/api/config';
4
5
  import { LDAIClientImpl } from '../src/LDAIClientImpl';
5
6
  import { LDClientMin } from '../src/LDClientMin';
@@ -11,7 +12,7 @@ const mockLdClient: jest.Mocked<LDClientMin> = {
11
12
 
12
13
  const testContext: LDContext = { kind: 'user', key: 'test-user' };
13
14
 
14
- it('returns config with interpolated messagess', async () => {
15
+ it('returns config with interpolated messages', async () => {
15
16
  const client = new LDAIClientImpl(mockLdClient);
16
17
  const key = 'test-flag';
17
18
  const defaultValue: LDAIDefaults = {
@@ -59,6 +60,14 @@ it('returns config with interpolated messagess', async () => {
59
60
  enabled: true,
60
61
  toVercelAISDK: expect.any(Function),
61
62
  });
63
+
64
+ // Verify tracking was called
65
+ expect(mockLdClient.track).toHaveBeenCalledWith(
66
+ '$ld:ai:config:function:single',
67
+ testContext,
68
+ key,
69
+ 1,
70
+ );
62
71
  });
63
72
 
64
73
  it('includes context in variables for messages interpolation', async () => {
@@ -79,6 +88,7 @@ it('includes context in variables for messages interpolation', async () => {
79
88
  const result = await client.config(key, testContext, defaultValue);
80
89
 
81
90
  expect(result.messages?.[0].content).toBe('User key: test-user');
91
+ expect(result.toVercelAISDK).toEqual(expect.any(Function));
82
92
  });
83
93
 
84
94
  it('handles missing metadata in variation', async () => {
@@ -132,3 +142,227 @@ it('passes the default value to the underlying client', async () => {
132
142
 
133
143
  expect(mockLdClient.variation).toHaveBeenCalledWith(key, testContext, defaultValue);
134
144
  });
145
+
146
+ // New agent-related tests
147
+ it('returns single agent config with interpolated instructions', async () => {
148
+ const client = new LDAIClientImpl(mockLdClient);
149
+ const key = 'test-agent';
150
+ const defaultValue: LDAIAgentDefaults = {
151
+ model: { name: 'test', parameters: { name: 'test-model' } },
152
+ instructions: 'You are a helpful assistant.',
153
+ enabled: true,
154
+ };
155
+
156
+ const mockVariation = {
157
+ model: {
158
+ name: 'example-model',
159
+ parameters: { name: 'imagination', temperature: 0.7, maxTokens: 4096 },
160
+ },
161
+ provider: {
162
+ name: 'example-provider',
163
+ },
164
+ instructions: 'You are a helpful assistant. Your name is {{name}} and your score is {{score}}',
165
+ _ldMeta: {
166
+ variationKey: 'v1',
167
+ enabled: true,
168
+ mode: 'agent',
169
+ },
170
+ };
171
+
172
+ mockLdClient.variation.mockResolvedValue(mockVariation);
173
+
174
+ const variables = { name: 'John', score: 42 };
175
+ const result = await client.agent(key, testContext, defaultValue, variables);
176
+
177
+ expect(result).toEqual({
178
+ model: {
179
+ name: 'example-model',
180
+ parameters: { name: 'imagination', temperature: 0.7, maxTokens: 4096 },
181
+ },
182
+ provider: {
183
+ name: 'example-provider',
184
+ },
185
+ instructions: 'You are a helpful assistant. Your name is John and your score is 42',
186
+ tracker: expect.any(Object),
187
+ enabled: true,
188
+ });
189
+
190
+ // Verify tracking was called
191
+ expect(mockLdClient.track).toHaveBeenCalledWith(
192
+ '$ld:ai:agent:function:single',
193
+ testContext,
194
+ key,
195
+ 1,
196
+ );
197
+ });
198
+
199
+ it('includes context in variables for agent instructions interpolation', async () => {
200
+ const client = new LDAIClientImpl(mockLdClient);
201
+ const key = 'test-agent';
202
+ const defaultValue: LDAIAgentDefaults = {
203
+ model: { name: 'test', parameters: { name: 'test-model' } },
204
+ instructions: 'You are a helpful assistant.',
205
+ enabled: true,
206
+ };
207
+
208
+ const mockVariation = {
209
+ instructions: 'You are a helpful assistant. Your user key is {{ldctx.key}}',
210
+ _ldMeta: { variationKey: 'v1', enabled: true, mode: 'agent' },
211
+ };
212
+
213
+ mockLdClient.variation.mockResolvedValue(mockVariation);
214
+
215
+ const result = await client.agent(key, testContext, defaultValue);
216
+
217
+ expect(result.instructions).toBe('You are a helpful assistant. Your user key is test-user');
218
+ });
219
+
220
+ it('handles missing metadata in agent variation', async () => {
221
+ const client = new LDAIClientImpl(mockLdClient);
222
+ const key = 'test-agent';
223
+ const defaultValue: LDAIAgentDefaults = {
224
+ model: { name: 'test', parameters: { name: 'test-model' } },
225
+ instructions: 'You are a helpful assistant.',
226
+ enabled: true,
227
+ };
228
+
229
+ const mockVariation = {
230
+ model: { name: 'example-provider', parameters: { name: 'imagination' } },
231
+ instructions: 'Hello.',
232
+ };
233
+
234
+ mockLdClient.variation.mockResolvedValue(mockVariation);
235
+
236
+ const result = await client.agent(key, testContext, defaultValue);
237
+
238
+ expect(result).toEqual({
239
+ model: { name: 'example-provider', parameters: { name: 'imagination' } },
240
+ instructions: 'Hello.',
241
+ tracker: expect.any(Object),
242
+ enabled: false,
243
+ });
244
+ });
245
+
246
+ it('passes the default value to the underlying client for single agent', async () => {
247
+ const client = new LDAIClientImpl(mockLdClient);
248
+ const key = 'non-existent-agent';
249
+ const defaultValue: LDAIAgentDefaults = {
250
+ model: { name: 'default-model', parameters: { name: 'default' } },
251
+ provider: { name: 'default-provider' },
252
+ instructions: 'Default instructions',
253
+ enabled: true,
254
+ };
255
+
256
+ mockLdClient.variation.mockResolvedValue(defaultValue);
257
+
258
+ const result = await client.agent(key, testContext, defaultValue);
259
+
260
+ expect(result).toEqual({
261
+ model: defaultValue.model,
262
+ instructions: defaultValue.instructions,
263
+ provider: defaultValue.provider,
264
+ tracker: expect.any(Object),
265
+ enabled: false,
266
+ });
267
+
268
+ expect(mockLdClient.variation).toHaveBeenCalledWith(key, testContext, defaultValue);
269
+ });
270
+
271
+ it('returns multiple agents config with interpolated instructions', async () => {
272
+ const client = new LDAIClientImpl(mockLdClient);
273
+
274
+ const agentConfigs = [
275
+ {
276
+ key: 'research-agent',
277
+ defaultValue: {
278
+ model: { name: 'test', parameters: { name: 'test-model' } },
279
+ instructions: 'You are a research assistant.',
280
+ enabled: true,
281
+ },
282
+ variables: { topic: 'climate change' },
283
+ },
284
+ {
285
+ key: 'writing-agent',
286
+ defaultValue: {
287
+ model: { name: 'test', parameters: { name: 'test-model' } },
288
+ instructions: 'You are a writing assistant.',
289
+ enabled: true,
290
+ },
291
+ variables: { style: 'academic' },
292
+ },
293
+ ] as const;
294
+
295
+ const mockVariations = {
296
+ 'research-agent': {
297
+ model: {
298
+ name: 'research-model',
299
+ parameters: { temperature: 0.3, maxTokens: 2048 },
300
+ },
301
+ provider: { name: 'openai' },
302
+ instructions: 'You are a research assistant specializing in {{topic}}.',
303
+ _ldMeta: { variationKey: 'v1', enabled: true, mode: 'agent' },
304
+ },
305
+ 'writing-agent': {
306
+ model: {
307
+ name: 'writing-model',
308
+ parameters: { temperature: 0.7, maxTokens: 1024 },
309
+ },
310
+ provider: { name: 'anthropic' },
311
+ instructions: 'You are a writing assistant with {{style}} style.',
312
+ _ldMeta: { variationKey: 'v2', enabled: true, mode: 'agent' },
313
+ },
314
+ };
315
+
316
+ mockLdClient.variation.mockImplementation((key) =>
317
+ Promise.resolve(mockVariations[key as keyof typeof mockVariations]),
318
+ );
319
+
320
+ const result = await client.agents(agentConfigs, testContext);
321
+
322
+ expect(result).toEqual({
323
+ 'research-agent': {
324
+ model: {
325
+ name: 'research-model',
326
+ parameters: { temperature: 0.3, maxTokens: 2048 },
327
+ },
328
+ provider: { name: 'openai' },
329
+ instructions: 'You are a research assistant specializing in climate change.',
330
+ tracker: expect.any(Object),
331
+ enabled: true,
332
+ },
333
+ 'writing-agent': {
334
+ model: {
335
+ name: 'writing-model',
336
+ parameters: { temperature: 0.7, maxTokens: 1024 },
337
+ },
338
+ provider: { name: 'anthropic' },
339
+ instructions: 'You are a writing assistant with academic style.',
340
+ tracker: expect.any(Object),
341
+ enabled: true,
342
+ },
343
+ });
344
+
345
+ // Verify tracking was called
346
+ expect(mockLdClient.track).toHaveBeenCalledWith(
347
+ '$ld:ai:agent:function:multiple',
348
+ testContext,
349
+ agentConfigs.length,
350
+ agentConfigs.length,
351
+ );
352
+ });
353
+
354
+ it('handles empty agent configs array', async () => {
355
+ const client = new LDAIClientImpl(mockLdClient);
356
+
357
+ const result = await client.agents([], testContext);
358
+
359
+ expect(result).toEqual({});
360
+
361
+ // Verify tracking was called with 0 agents
362
+ expect(mockLdClient.track).toHaveBeenCalledWith(
363
+ '$ld:ai:agent:function:multiple',
364
+ testContext,
365
+ 0,
366
+ 0,
367
+ );
368
+ });