@launchdarkly/server-sdk-ai 0.13.0 → 0.14.1

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 (242) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/README.md +1 -1
  3. package/__tests__/Judge.test.ts +521 -0
  4. package/__tests__/LDAIClientImpl.test.ts +535 -323
  5. package/__tests__/LDAIConfigTrackerImpl.test.ts +50 -290
  6. package/__tests__/TrackedChat.test.ts +5 -5
  7. package/dist/package.json +53 -0
  8. package/dist/src/LDAIClientImpl.d.ts +39 -0
  9. package/dist/src/LDAIClientImpl.d.ts.map +1 -0
  10. package/dist/src/LDAIClientImpl.js +164 -0
  11. package/dist/src/LDAIClientImpl.js.map +1 -0
  12. package/dist/{LDAIConfigTrackerImpl.d.ts → src/LDAIConfigTrackerImpl.d.ts} +12 -11
  13. package/dist/src/LDAIConfigTrackerImpl.d.ts.map +1 -0
  14. package/dist/{LDAIConfigTrackerImpl.js → src/LDAIConfigTrackerImpl.js} +21 -44
  15. package/dist/src/LDAIConfigTrackerImpl.js.map +1 -0
  16. package/dist/src/LDClientMin.d.ts.map +1 -0
  17. package/dist/{LDClientMin.js.map → src/LDClientMin.js.map} +1 -1
  18. package/dist/src/api/LDAIClient.d.ts +258 -0
  19. package/dist/src/api/LDAIClient.d.ts.map +1 -0
  20. package/dist/{api → src/api}/LDAIClient.js.map +1 -1
  21. package/dist/{api → src/api}/chat/TrackedChat.d.ts +22 -4
  22. package/dist/src/api/chat/TrackedChat.d.ts.map +1 -0
  23. package/dist/{api → src/api}/chat/TrackedChat.js +43 -2
  24. package/dist/src/api/chat/TrackedChat.js.map +1 -0
  25. package/dist/src/api/chat/index.d.ts.map +1 -0
  26. package/dist/src/api/chat/index.js.map +1 -0
  27. package/dist/src/api/chat/types.d.ts +22 -0
  28. package/dist/src/api/chat/types.d.ts.map +1 -0
  29. package/dist/{api → src/api}/chat/types.js.map +1 -1
  30. package/dist/{api → src/api}/config/LDAIConfigTracker.d.ts +25 -25
  31. package/dist/src/api/config/LDAIConfigTracker.d.ts.map +1 -0
  32. package/dist/src/api/config/LDAIConfigTracker.js.map +1 -0
  33. package/dist/src/api/config/LDAIConfigUtils.d.ts +2 -0
  34. package/dist/src/api/config/LDAIConfigUtils.d.ts.map +1 -0
  35. package/dist/src/api/config/LDAIConfigUtils.js +145 -0
  36. package/dist/src/api/config/LDAIConfigUtils.js.map +1 -0
  37. package/dist/src/api/config/index.d.ts +3 -0
  38. package/dist/src/api/config/index.d.ts.map +1 -0
  39. package/dist/{api/agents → src/api/config}/index.js +1 -1
  40. package/dist/src/api/config/index.js.map +1 -0
  41. package/dist/src/api/config/types.d.ts +206 -0
  42. package/dist/src/api/config/types.d.ts.map +1 -0
  43. package/dist/{api/agents/LDAIAgent.js → src/api/config/types.js} +1 -1
  44. package/dist/src/api/config/types.js.map +1 -0
  45. package/dist/{api → src/api}/index.d.ts +1 -1
  46. package/dist/src/api/index.d.ts.map +1 -0
  47. package/dist/{api → src/api}/index.js +1 -1
  48. package/dist/src/api/index.js.map +1 -0
  49. package/dist/src/api/judge/EvaluationSchemaBuilder.d.ts +11 -0
  50. package/dist/src/api/judge/EvaluationSchemaBuilder.d.ts.map +1 -0
  51. package/dist/src/api/judge/EvaluationSchemaBuilder.js +52 -0
  52. package/dist/src/api/judge/EvaluationSchemaBuilder.js.map +1 -0
  53. package/dist/src/api/judge/Judge.d.ts +63 -0
  54. package/dist/src/api/judge/Judge.d.ts.map +1 -0
  55. package/dist/src/api/judge/Judge.js +151 -0
  56. package/dist/src/api/judge/Judge.js.map +1 -0
  57. package/dist/src/api/judge/index.d.ts +3 -0
  58. package/dist/src/api/judge/index.d.ts.map +1 -0
  59. package/dist/src/api/judge/index.js +6 -0
  60. package/dist/src/api/judge/index.js.map +1 -0
  61. package/dist/src/api/judge/types.d.ts +37 -0
  62. package/dist/src/api/judge/types.d.ts.map +1 -0
  63. package/dist/{api/config/LDAIConfig.js → src/api/judge/types.js} +1 -1
  64. package/dist/src/api/judge/types.js.map +1 -0
  65. package/dist/src/api/metrics/BedrockTokenUsage.d.ts.map +1 -0
  66. package/dist/src/api/metrics/BedrockTokenUsage.js.map +1 -0
  67. package/dist/src/api/metrics/LDAIMetrics.d.ts.map +1 -0
  68. package/dist/src/api/metrics/LDAIMetrics.js.map +1 -0
  69. package/dist/src/api/metrics/LDFeedbackKind.d.ts.map +1 -0
  70. package/dist/src/api/metrics/LDFeedbackKind.js.map +1 -0
  71. package/dist/src/api/metrics/LDTokenUsage.d.ts.map +1 -0
  72. package/dist/src/api/metrics/LDTokenUsage.js.map +1 -0
  73. package/dist/src/api/metrics/OpenAiUsage.d.ts.map +1 -0
  74. package/dist/src/api/metrics/OpenAiUsage.js.map +1 -0
  75. package/dist/src/api/metrics/VercelAISDKTokenUsage.d.ts.map +1 -0
  76. package/dist/src/api/metrics/VercelAISDKTokenUsage.js.map +1 -0
  77. package/dist/src/api/metrics/index.d.ts.map +1 -0
  78. package/dist/src/api/metrics/index.js.map +1 -0
  79. package/dist/{api → src/api}/providers/AIProvider.d.ts +20 -3
  80. package/dist/src/api/providers/AIProvider.d.ts.map +1 -0
  81. package/dist/src/api/providers/AIProvider.js +88 -0
  82. package/dist/src/api/providers/AIProvider.js.map +1 -0
  83. package/dist/{api → src/api}/providers/AIProviderFactory.d.ts +2 -2
  84. package/dist/src/api/providers/AIProviderFactory.d.ts.map +1 -0
  85. package/dist/src/api/providers/AIProviderFactory.js.map +1 -0
  86. package/dist/src/api/providers/index.d.ts.map +1 -0
  87. package/dist/src/api/providers/index.js.map +1 -0
  88. package/dist/src/index.d.ts.map +1 -0
  89. package/dist/src/index.js.map +1 -0
  90. package/docs/assets/search.js +1 -1
  91. package/docs/classes/AIProvider.html +55 -20
  92. package/docs/classes/AIProviderFactory.html +27 -17
  93. package/docs/classes/Judge.html +322 -0
  94. package/docs/classes/TrackedChat.html +97 -29
  95. package/docs/enums/LDFeedbackKind.html +22 -12
  96. package/docs/functions/createBedrockTokenUsage.html +20 -10
  97. package/docs/functions/createOpenAiUsage.html +20 -10
  98. package/docs/functions/createVercelAISDKTokenUsage.html +20 -10
  99. package/docs/functions/initAi.html +20 -10
  100. package/docs/index.html +36 -16
  101. package/docs/interfaces/ChatResponse.html +35 -14
  102. package/docs/interfaces/EvalScore.html +119 -0
  103. package/docs/interfaces/JudgeResponse.html +139 -0
  104. package/docs/interfaces/LDAIAgentConfig.html +90 -31
  105. package/docs/interfaces/{LDAIAgent.html → LDAIAgentConfigDefault.html} +51 -41
  106. package/docs/interfaces/LDAIAgentRequestConfig.html +129 -0
  107. package/docs/interfaces/LDAIClient.html +234 -40
  108. package/docs/interfaces/{VercelAISDKConfig.html → LDAICompletionConfig.html} +96 -90
  109. package/docs/interfaces/LDAICompletionConfigDefault.html +155 -0
  110. package/docs/interfaces/LDAIConfig.html +52 -75
  111. package/docs/interfaces/LDAIConfigDefault.html +133 -0
  112. package/docs/interfaces/LDAIConfigTracker.html +102 -63
  113. package/docs/interfaces/LDAIJudgeConfig.html +178 -0
  114. package/docs/interfaces/LDAIJudgeConfigDefault.html +155 -0
  115. package/docs/interfaces/LDAIMetrics.html +22 -12
  116. package/docs/interfaces/LDJudge.html +119 -0
  117. package/docs/interfaces/{VercelAISDKMapOptions.html → LDJudgeConfiguration.html} +35 -23
  118. package/docs/interfaces/LDLogger.html +19 -9
  119. package/docs/interfaces/LDMessage.html +22 -12
  120. package/docs/interfaces/LDModelConfig.html +23 -13
  121. package/docs/interfaces/LDProviderConfig.html +21 -11
  122. package/docs/interfaces/LDTokenUsage.html +23 -13
  123. package/docs/interfaces/StructuredResponse.html +129 -0
  124. package/docs/types/{VercelAISDKProvider.html → LDAIConfigDefaultKind.html} +26 -35
  125. package/docs/types/{LDAIAgentDefaults.html → LDAIConfigKind.html} +24 -14
  126. package/docs/types/{LDAIDefaults.html → LDAIConfigMode.html} +24 -24
  127. package/docs/types/SupportedAIProvider.html +20 -10
  128. package/docs/variables/SUPPORTED_AI_PROVIDERS.html +20 -10
  129. package/package.json +3 -3
  130. package/src/LDAIClientImpl.ts +222 -176
  131. package/src/LDAIConfigTrackerImpl.ts +31 -54
  132. package/src/api/LDAIClient.ts +166 -33
  133. package/src/api/chat/TrackedChat.ts +68 -5
  134. package/src/api/chat/types.ts +8 -1
  135. package/src/api/config/LDAIConfigTracker.ts +27 -30
  136. package/src/api/config/LDAIConfigUtils.ts +212 -0
  137. package/src/api/config/index.ts +2 -2
  138. package/src/api/config/types.ts +260 -0
  139. package/src/api/index.ts +1 -1
  140. package/src/api/judge/EvaluationSchemaBuilder.ts +54 -0
  141. package/src/api/judge/Judge.ts +218 -0
  142. package/src/api/judge/index.ts +2 -0
  143. package/src/api/judge/types.ts +41 -0
  144. package/src/api/providers/AIProvider.ts +54 -3
  145. package/src/api/providers/AIProviderFactory.ts +4 -4
  146. package/tsconfig.json +3 -3
  147. package/tsconfig.ref.json +1 -1
  148. package/__tests__/LDAIConfigMapper.test.ts +0 -159
  149. package/dist/LDAIClientImpl.d.ts +0 -23
  150. package/dist/LDAIClientImpl.d.ts.map +0 -1
  151. package/dist/LDAIClientImpl.js +0 -128
  152. package/dist/LDAIClientImpl.js.map +0 -1
  153. package/dist/LDAIConfigMapper.d.ts +0 -14
  154. package/dist/LDAIConfigMapper.d.ts.map +0 -1
  155. package/dist/LDAIConfigMapper.js +0 -59
  156. package/dist/LDAIConfigMapper.js.map +0 -1
  157. package/dist/LDAIConfigTrackerImpl.d.ts.map +0 -1
  158. package/dist/LDAIConfigTrackerImpl.js.map +0 -1
  159. package/dist/LDClientMin.d.ts.map +0 -1
  160. package/dist/api/LDAIClient.d.ts +0 -169
  161. package/dist/api/LDAIClient.d.ts.map +0 -1
  162. package/dist/api/agents/LDAIAgent.d.ts +0 -32
  163. package/dist/api/agents/LDAIAgent.d.ts.map +0 -1
  164. package/dist/api/agents/LDAIAgent.js.map +0 -1
  165. package/dist/api/agents/index.d.ts +0 -2
  166. package/dist/api/agents/index.d.ts.map +0 -1
  167. package/dist/api/agents/index.js.map +0 -1
  168. package/dist/api/chat/TrackedChat.d.ts.map +0 -1
  169. package/dist/api/chat/TrackedChat.js.map +0 -1
  170. package/dist/api/chat/index.d.ts.map +0 -1
  171. package/dist/api/chat/index.js.map +0 -1
  172. package/dist/api/chat/types.d.ts +0 -16
  173. package/dist/api/chat/types.d.ts.map +0 -1
  174. package/dist/api/config/LDAIConfig.d.ts +0 -95
  175. package/dist/api/config/LDAIConfig.d.ts.map +0 -1
  176. package/dist/api/config/LDAIConfig.js.map +0 -1
  177. package/dist/api/config/LDAIConfigTracker.d.ts.map +0 -1
  178. package/dist/api/config/LDAIConfigTracker.js.map +0 -1
  179. package/dist/api/config/VercelAISDK.d.ts +0 -31
  180. package/dist/api/config/VercelAISDK.d.ts.map +0 -1
  181. package/dist/api/config/VercelAISDK.js +0 -3
  182. package/dist/api/config/VercelAISDK.js.map +0 -1
  183. package/dist/api/config/index.d.ts +0 -4
  184. package/dist/api/config/index.d.ts.map +0 -1
  185. package/dist/api/config/index.js +0 -19
  186. package/dist/api/config/index.js.map +0 -1
  187. package/dist/api/index.d.ts.map +0 -1
  188. package/dist/api/index.js.map +0 -1
  189. package/dist/api/metrics/BedrockTokenUsage.d.ts.map +0 -1
  190. package/dist/api/metrics/BedrockTokenUsage.js.map +0 -1
  191. package/dist/api/metrics/LDAIMetrics.d.ts.map +0 -1
  192. package/dist/api/metrics/LDAIMetrics.js.map +0 -1
  193. package/dist/api/metrics/LDFeedbackKind.d.ts.map +0 -1
  194. package/dist/api/metrics/LDFeedbackKind.js.map +0 -1
  195. package/dist/api/metrics/LDTokenUsage.d.ts.map +0 -1
  196. package/dist/api/metrics/LDTokenUsage.js.map +0 -1
  197. package/dist/api/metrics/OpenAiUsage.d.ts.map +0 -1
  198. package/dist/api/metrics/OpenAiUsage.js.map +0 -1
  199. package/dist/api/metrics/VercelAISDKTokenUsage.d.ts.map +0 -1
  200. package/dist/api/metrics/VercelAISDKTokenUsage.js.map +0 -1
  201. package/dist/api/metrics/index.d.ts.map +0 -1
  202. package/dist/api/metrics/index.js.map +0 -1
  203. package/dist/api/providers/AIProvider.d.ts.map +0 -1
  204. package/dist/api/providers/AIProvider.js +0 -31
  205. package/dist/api/providers/AIProvider.js.map +0 -1
  206. package/dist/api/providers/AIProviderFactory.d.ts.map +0 -1
  207. package/dist/api/providers/AIProviderFactory.js.map +0 -1
  208. package/dist/api/providers/index.d.ts.map +0 -1
  209. package/dist/api/providers/index.js.map +0 -1
  210. package/dist/index.d.ts.map +0 -1
  211. package/dist/index.js.map +0 -1
  212. package/src/LDAIConfigMapper.ts +0 -69
  213. package/src/api/agents/LDAIAgent.ts +0 -36
  214. package/src/api/agents/index.ts +0 -1
  215. package/src/api/config/LDAIConfig.ts +0 -104
  216. package/src/api/config/VercelAISDK.ts +0 -33
  217. /package/dist/{LDClientMin.d.ts → src/LDClientMin.d.ts} +0 -0
  218. /package/dist/{LDClientMin.js → src/LDClientMin.js} +0 -0
  219. /package/dist/{api → src/api}/LDAIClient.js +0 -0
  220. /package/dist/{api → src/api}/chat/index.d.ts +0 -0
  221. /package/dist/{api → src/api}/chat/index.js +0 -0
  222. /package/dist/{api → src/api}/chat/types.js +0 -0
  223. /package/dist/{api → src/api}/config/LDAIConfigTracker.js +0 -0
  224. /package/dist/{api → src/api}/metrics/BedrockTokenUsage.d.ts +0 -0
  225. /package/dist/{api → src/api}/metrics/BedrockTokenUsage.js +0 -0
  226. /package/dist/{api → src/api}/metrics/LDAIMetrics.d.ts +0 -0
  227. /package/dist/{api → src/api}/metrics/LDAIMetrics.js +0 -0
  228. /package/dist/{api → src/api}/metrics/LDFeedbackKind.d.ts +0 -0
  229. /package/dist/{api → src/api}/metrics/LDFeedbackKind.js +0 -0
  230. /package/dist/{api → src/api}/metrics/LDTokenUsage.d.ts +0 -0
  231. /package/dist/{api → src/api}/metrics/LDTokenUsage.js +0 -0
  232. /package/dist/{api → src/api}/metrics/OpenAiUsage.d.ts +0 -0
  233. /package/dist/{api → src/api}/metrics/OpenAiUsage.js +0 -0
  234. /package/dist/{api → src/api}/metrics/VercelAISDKTokenUsage.d.ts +0 -0
  235. /package/dist/{api → src/api}/metrics/VercelAISDKTokenUsage.js +0 -0
  236. /package/dist/{api → src/api}/metrics/index.d.ts +0 -0
  237. /package/dist/{api → src/api}/metrics/index.js +0 -0
  238. /package/dist/{api → src/api}/providers/AIProviderFactory.js +0 -0
  239. /package/dist/{api → src/api}/providers/index.d.ts +0 -0
  240. /package/dist/{api → src/api}/providers/index.js +0 -0
  241. /package/dist/{index.d.ts → src/index.d.ts} +0 -0
  242. /package/dist/{index.js → src/index.js} +0 -0
@@ -1,382 +1,594 @@
1
1
  import { LDContext } from '@launchdarkly/js-server-sdk-common';
2
2
 
3
- import { LDAIAgentDefaults } from '../src/api/agents';
4
- import { LDAIDefaults } from '../src/api/config';
3
+ import {
4
+ LDAIAgentConfigDefault,
5
+ LDAICompletionConfigDefault,
6
+ LDAIJudgeConfigDefault,
7
+ } from '../src/api/config/types';
8
+ import { Judge } from '../src/api/judge/Judge';
9
+ import { AIProviderFactory } from '../src/api/providers/AIProviderFactory';
5
10
  import { LDAIClientImpl } from '../src/LDAIClientImpl';
6
11
  import { LDClientMin } from '../src/LDClientMin';
7
12
 
13
+ // Mock Judge and AIProviderFactory
14
+ jest.mock('../src/api/judge/Judge');
15
+ jest.mock('../src/api/providers/AIProviderFactory');
16
+
8
17
  const mockLdClient: jest.Mocked<LDClientMin> = {
9
18
  variation: jest.fn(),
10
19
  track: jest.fn(),
11
20
  };
12
21
 
22
+ // Reset mocks before each test
23
+ beforeEach(() => {
24
+ jest.clearAllMocks();
25
+ });
26
+
13
27
  const testContext: LDContext = { kind: 'user', key: 'test-user' };
14
28
 
15
- it('returns config with interpolated messages', async () => {
16
- const client = new LDAIClientImpl(mockLdClient);
17
- const key = 'test-flag';
18
- const defaultValue: LDAIDefaults = {
19
- model: { name: 'test', parameters: { name: 'test-model' } },
20
- messages: [],
21
- enabled: true,
22
- };
23
-
24
- const mockVariation = {
25
- model: {
26
- name: 'example-model',
27
- parameters: { name: 'imagination', temperature: 0.7, maxTokens: 4096 },
28
- },
29
- provider: {
30
- name: 'example-provider',
31
- },
32
- messages: [
33
- { role: 'system', content: 'Hello {{name}}' },
34
- { role: 'user', content: 'Score: {{score}}' },
35
- ],
36
- _ldMeta: {
37
- variationKey: 'v1',
38
- enabled: true,
39
- },
40
- };
41
-
42
- mockLdClient.variation.mockResolvedValue(mockVariation);
43
-
44
- const variables = { name: 'John', score: 42 };
45
- const result = await client.config(key, testContext, defaultValue, variables);
46
-
47
- expect(result).toEqual({
48
- model: {
49
- name: 'example-model',
50
- parameters: { name: 'imagination', temperature: 0.7, maxTokens: 4096 },
51
- },
52
- provider: {
53
- name: 'example-provider',
54
- },
55
- messages: [
29
+ describe('config evaluation', () => {
30
+ it('evaluates completion config successfully with variable interpolation', async () => {
31
+ const client = new LDAIClientImpl(mockLdClient);
32
+ const key = 'test-flag';
33
+ const defaultValue: LDAICompletionConfigDefault = {
34
+ enabled: false,
35
+ };
36
+
37
+ const mockVariation = {
38
+ model: {
39
+ name: 'example-model',
40
+ parameters: { name: 'imagination', temperature: 0.7, maxTokens: 4096 },
41
+ },
42
+ provider: {
43
+ name: 'example-provider',
44
+ },
45
+ messages: [
46
+ { role: 'system', content: 'Hello {{name}}' },
47
+ { role: 'user', content: 'Score: {{score}}' },
48
+ ],
49
+ _ldMeta: {
50
+ variationKey: 'v1',
51
+ enabled: true,
52
+ mode: 'completion',
53
+ },
54
+ };
55
+
56
+ mockLdClient.variation.mockResolvedValue(mockVariation);
57
+
58
+ const variables = { name: 'John', score: 42 };
59
+ const evaluateSpy = jest.spyOn(client as any, '_evaluate');
60
+ const result = await client.completionConfig(key, testContext, defaultValue, variables);
61
+
62
+ expect(evaluateSpy).toHaveBeenCalledWith(
63
+ key,
64
+ testContext,
65
+ defaultValue,
66
+ 'completion',
67
+ variables,
68
+ );
69
+ expect(result.messages).toEqual([
56
70
  { role: 'system', content: 'Hello John' },
57
71
  { role: 'user', content: 'Score: 42' },
58
- ],
59
- tracker: expect.any(Object),
60
- enabled: true,
61
- toVercelAISDK: expect.any(Function),
72
+ ]);
73
+ expect(result.tracker).toBeDefined();
74
+ expect(result.enabled).toBe(true);
75
+ evaluateSpy.mockRestore();
62
76
  });
63
77
 
64
- // Verify tracking was called
65
- expect(mockLdClient.track).toHaveBeenCalledWith(
66
- '$ld:ai:config:function:single',
67
- testContext,
68
- key,
69
- 1,
70
- );
71
- });
78
+ it('includes context (ldctx) in variables for message interpolation', async () => {
79
+ const client = new LDAIClientImpl(mockLdClient);
80
+ const key = 'test-flag';
81
+ const defaultValue: LDAICompletionConfigDefault = {
82
+ enabled: false,
83
+ };
84
+
85
+ const mockVariation = {
86
+ messages: [{ role: 'system', content: 'User key: {{ldctx.key}}' }],
87
+ _ldMeta: { variationKey: 'v1', enabled: true, mode: 'completion' },
88
+ };
72
89
 
73
- it('includes context in variables for messages interpolation', async () => {
74
- const client = new LDAIClientImpl(mockLdClient);
75
- const key = 'test-flag';
76
- const defaultValue: LDAIDefaults = {
77
- model: { name: 'test', parameters: { name: 'test-model' } },
78
- messages: [],
79
- };
90
+ mockLdClient.variation.mockResolvedValue(mockVariation);
80
91
 
81
- const mockVariation = {
82
- messages: [{ role: 'system', content: 'User key: {{ldctx.key}}' }],
83
- _ldMeta: { variationKey: 'v1', enabled: true },
84
- };
92
+ const result = await client.completionConfig(key, testContext, defaultValue);
85
93
 
86
- mockLdClient.variation.mockResolvedValue(mockVariation);
94
+ expect(result.messages?.[0].content).toBe('User key: test-user');
95
+ });
87
96
 
88
- const result = await client.config(key, testContext, defaultValue);
97
+ it('evaluates agent config successfully with instruction interpolation', async () => {
98
+ const client = new LDAIClientImpl(mockLdClient);
99
+ const key = 'test-agent';
100
+ const defaultValue: LDAIAgentConfigDefault = {
101
+ enabled: false,
102
+ };
89
103
 
90
- expect(result.messages?.[0].content).toBe('User key: test-user');
91
- expect(result.toVercelAISDK).toEqual(expect.any(Function));
92
- });
104
+ const mockVariation = {
105
+ model: {
106
+ name: 'example-model',
107
+ parameters: { temperature: 0.7, maxTokens: 4096 },
108
+ },
109
+ provider: {
110
+ name: 'example-provider',
111
+ },
112
+ instructions:
113
+ 'You are a helpful assistant. Your name is {{name}} and your score is {{score}}',
114
+ _ldMeta: {
115
+ variationKey: 'v1',
116
+ enabled: true,
117
+ mode: 'agent',
118
+ },
119
+ };
93
120
 
94
- it('handles missing metadata in variation', async () => {
95
- const client = new LDAIClientImpl(mockLdClient);
96
- const key = 'test-flag';
97
- const defaultValue: LDAIDefaults = {
98
- model: { name: 'test', parameters: { name: 'test-model' } },
99
- messages: [],
100
- };
101
-
102
- const mockVariation = {
103
- model: { name: 'example-provider', parameters: { name: 'imagination' } },
104
- messages: [{ role: 'system', content: 'Hello' }],
105
- };
106
-
107
- mockLdClient.variation.mockResolvedValue(mockVariation);
108
-
109
- const result = await client.config(key, testContext, defaultValue);
110
-
111
- expect(result).toEqual({
112
- model: { name: 'example-provider', parameters: { name: 'imagination' } },
113
- messages: [{ role: 'system', content: 'Hello' }],
114
- tracker: expect.any(Object),
115
- enabled: false,
116
- toVercelAISDK: expect.any(Function),
117
- });
118
- });
121
+ mockLdClient.variation.mockResolvedValue(mockVariation);
119
122
 
120
- it('passes the default value to the underlying client', async () => {
121
- const client = new LDAIClientImpl(mockLdClient);
122
- const key = 'non-existent-flag';
123
- const defaultValue: LDAIDefaults = {
124
- model: { name: 'default-model', parameters: { name: 'default' } },
125
- provider: { name: 'default-provider' },
126
- messages: [{ role: 'system', content: 'Default messages' }],
127
- enabled: true,
128
- };
129
-
130
- const expectedLDFlagValue = {
131
- _ldMeta: { enabled: true },
132
- model: defaultValue.model,
133
- messages: defaultValue.messages,
134
- provider: defaultValue.provider,
135
- };
136
-
137
- mockLdClient.variation.mockResolvedValue(expectedLDFlagValue);
138
-
139
- const result = await client.config(key, testContext, defaultValue);
140
-
141
- expect(result).toEqual({
142
- model: defaultValue.model,
143
- messages: defaultValue.messages,
144
- provider: defaultValue.provider,
145
- tracker: expect.any(Object),
146
- enabled: defaultValue.enabled,
147
- toVercelAISDK: expect.any(Function),
123
+ const variables = { name: 'John', score: 42 };
124
+ const evaluateSpy = jest.spyOn(client as any, '_evaluate');
125
+ const result = await client.agentConfig(key, testContext, defaultValue, variables);
126
+
127
+ expect(evaluateSpy).toHaveBeenCalledWith(key, testContext, defaultValue, 'agent', variables);
128
+ expect(result.instructions).toBe(
129
+ 'You are a helpful assistant. Your name is John and your score is 42',
130
+ );
131
+ expect(result.tracker).toBeDefined();
132
+ expect(result.enabled).toBe(true);
133
+ evaluateSpy.mockRestore();
148
134
  });
149
135
 
150
- expect(mockLdClient.variation).toHaveBeenCalledWith(key, testContext, expectedLDFlagValue);
151
- });
136
+ it('evaluates judge config successfully', async () => {
137
+ const client = new LDAIClientImpl(mockLdClient);
138
+ const key = 'test-judge';
139
+ const defaultValue: LDAIJudgeConfigDefault = {
140
+ enabled: false,
141
+ };
152
142
 
153
- // New agent-related tests
154
- it('returns single agent config with interpolated instructions', async () => {
155
- const client = new LDAIClientImpl(mockLdClient);
156
- const key = 'test-agent';
157
- const defaultValue: LDAIAgentDefaults = {
158
- model: { name: 'test', parameters: { name: 'test-model' } },
159
- instructions: 'You are a helpful assistant.',
160
- enabled: true,
161
- };
162
-
163
- const mockVariation = {
164
- model: {
165
- name: 'example-model',
166
- parameters: { name: 'imagination', temperature: 0.7, maxTokens: 4096 },
167
- },
168
- provider: {
169
- name: 'example-provider',
170
- },
171
- instructions: 'You are a helpful assistant. Your name is {{name}} and your score is {{score}}',
172
- _ldMeta: {
173
- variationKey: 'v1',
143
+ const mockVariation = {
174
144
  enabled: true,
175
- mode: 'agent',
176
- },
177
- };
178
-
179
- mockLdClient.variation.mockResolvedValue(mockVariation);
180
-
181
- const variables = { name: 'John', score: 42 };
182
- const result = await client.agent(key, testContext, defaultValue, variables);
183
-
184
- expect(result).toEqual({
185
- model: {
186
- name: 'example-model',
187
- parameters: { name: 'imagination', temperature: 0.7, maxTokens: 4096 },
188
- },
189
- provider: {
190
- name: 'example-provider',
191
- },
192
- instructions: 'You are a helpful assistant. Your name is John and your score is 42',
193
- tracker: expect.any(Object),
194
- enabled: true,
145
+ model: { name: 'gpt-4' },
146
+ provider: { name: 'openai' },
147
+ evaluationMetricKeys: ['relevance', 'accuracy'],
148
+ messages: [{ role: 'system', content: 'You are a judge.' }],
149
+ _ldMeta: {
150
+ variationKey: 'v1',
151
+ enabled: true,
152
+ mode: 'judge',
153
+ },
154
+ };
155
+
156
+ mockLdClient.variation.mockResolvedValue(mockVariation);
157
+
158
+ const evaluateSpy = jest.spyOn(client as any, '_evaluate');
159
+ const result = await client.judgeConfig(key, testContext, defaultValue);
160
+
161
+ expect(evaluateSpy).toHaveBeenCalledWith(key, testContext, defaultValue, 'judge', undefined);
162
+ expect(result.evaluationMetricKeys).toEqual(['relevance', 'accuracy']);
163
+ expect(result.tracker).toBeDefined();
164
+ expect(result.enabled).toBe(true);
165
+ evaluateSpy.mockRestore();
195
166
  });
196
167
 
197
- // Verify tracking was called
198
- expect(mockLdClient.track).toHaveBeenCalledWith(
199
- '$ld:ai:agent:function:single',
200
- testContext,
201
- key,
202
- 1,
203
- );
204
- });
168
+ it('handles mode mismatch by returning disabled config', async () => {
169
+ const client = new LDAIClientImpl(mockLdClient);
170
+ const key = 'test-flag';
171
+ const defaultValue: LDAICompletionConfigDefault = {
172
+ enabled: false,
173
+ };
205
174
 
206
- it('includes context in variables for agent instructions interpolation', async () => {
207
- const client = new LDAIClientImpl(mockLdClient);
208
- const key = 'test-agent';
209
- const defaultValue: LDAIAgentDefaults = {
210
- model: { name: 'test', parameters: { name: 'test-model' } },
211
- instructions: 'You are a helpful assistant.',
212
- enabled: true,
213
- };
175
+ const mockVariation = {
176
+ model: { name: 'example-provider', parameters: { name: 'imagination' } },
177
+ messages: [{ role: 'system', content: 'Hello' }],
178
+ _ldMeta: { variationKey: 'v1', enabled: true, mode: 'agent' }, // Wrong mode
179
+ };
214
180
 
215
- const mockVariation = {
216
- instructions: 'You are a helpful assistant. Your user key is {{ldctx.key}}',
217
- _ldMeta: { variationKey: 'v1', enabled: true, mode: 'agent' },
218
- };
181
+ mockLdClient.variation.mockResolvedValue(mockVariation);
219
182
 
220
- mockLdClient.variation.mockResolvedValue(mockVariation);
183
+ const result = await client.completionConfig(key, testContext, defaultValue);
221
184
 
222
- const result = await client.agent(key, testContext, defaultValue);
185
+ expect(result.enabled).toBe(false);
186
+ expect(result.tracker).toBeUndefined();
187
+ });
223
188
 
224
- expect(result.instructions).toBe('You are a helpful assistant. Your user key is test-user');
225
- });
189
+ it('handles missing metadata mode by defaulting to completion mode', async () => {
190
+ const client = new LDAIClientImpl(mockLdClient);
191
+ const key = 'test-flag';
192
+ const defaultValue: LDAICompletionConfigDefault = {
193
+ enabled: false,
194
+ };
195
+
196
+ const mockVariation = {
197
+ model: { name: 'example-provider', parameters: { name: 'imagination' } },
198
+ messages: [{ role: 'system', content: 'Hello' }],
199
+ // No _ldMeta - mode defaults to completion
200
+ };
226
201
 
227
- it('handles missing metadata in agent variation', async () => {
228
- const client = new LDAIClientImpl(mockLdClient);
229
- const key = 'test-agent';
230
- const defaultValue: LDAIAgentDefaults = {
231
- model: { name: 'test', parameters: { name: 'test-model' } },
232
- instructions: 'You are a helpful assistant.',
233
- enabled: true,
234
- };
235
-
236
- const mockVariation = {
237
- model: { name: 'example-provider', parameters: { name: 'imagination' } },
238
- instructions: 'Hello.',
239
- };
240
-
241
- mockLdClient.variation.mockResolvedValue(mockVariation);
242
-
243
- const result = await client.agent(key, testContext, defaultValue);
244
-
245
- expect(result).toEqual({
246
- model: { name: 'example-provider', parameters: { name: 'imagination' } },
247
- instructions: 'Hello.',
248
- tracker: expect.any(Object),
249
- enabled: false,
202
+ mockLdClient.variation.mockResolvedValue(mockVariation);
203
+
204
+ const result = await client.completionConfig(key, testContext, defaultValue);
205
+
206
+ expect(result.enabled).toBe(false);
207
+ expect(result.tracker).toBeDefined();
208
+ expect(result.messages).toEqual([{ role: 'system', content: 'Hello' }]);
209
+ expect(result.model).toEqual({ name: 'example-provider', parameters: { name: 'imagination' } });
250
210
  });
251
- });
252
211
 
253
- it('passes the default value to the underlying client for single agent', async () => {
254
- const client = new LDAIClientImpl(mockLdClient);
255
- const key = 'non-existent-agent';
256
- const defaultValue: LDAIAgentDefaults = {
257
- model: { name: 'default-model', parameters: { name: 'default' } },
258
- provider: { name: 'default-provider' },
259
- instructions: 'Default instructions',
260
- enabled: true,
261
- };
262
-
263
- const expectedLDFlagValue = {
264
- _ldMeta: { enabled: defaultValue.enabled },
265
- model: defaultValue.model,
266
- provider: defaultValue.provider,
267
- instructions: defaultValue.instructions,
268
- };
269
-
270
- mockLdClient.variation.mockResolvedValue(expectedLDFlagValue);
271
-
272
- const result = await client.agent(key, testContext, defaultValue);
273
-
274
- expect(result).toEqual({
275
- model: defaultValue.model,
276
- instructions: defaultValue.instructions,
277
- provider: defaultValue.provider,
278
- tracker: expect.any(Object),
279
- enabled: defaultValue.enabled,
212
+ it('uses default value when flag does not exist', async () => {
213
+ const client = new LDAIClientImpl(mockLdClient);
214
+ const key = 'non-existent-flag';
215
+ const defaultValue: LDAICompletionConfigDefault = {
216
+ model: { name: 'default-model', parameters: { name: 'default' } },
217
+ provider: { name: 'default-provider' },
218
+ messages: [{ role: 'system', content: 'Default messages' }],
219
+ enabled: true,
220
+ };
221
+
222
+ const expectedLDFlagValue = {
223
+ _ldMeta: { enabled: true, mode: 'completion', variationKey: '' },
224
+ model: defaultValue.model,
225
+ messages: defaultValue.messages,
226
+ provider: defaultValue.provider,
227
+ };
228
+
229
+ mockLdClient.variation.mockResolvedValue(expectedLDFlagValue);
230
+
231
+ const result = await client.completionConfig(key, testContext, defaultValue);
232
+
233
+ expect(result.model).toEqual(defaultValue.model);
234
+ expect(result.messages).toEqual(defaultValue.messages);
235
+ expect(result.provider).toEqual(defaultValue.provider);
236
+ expect(result.tracker).toBeDefined();
237
+ expect(result.enabled).toBe(defaultValue.enabled);
238
+ expect(mockLdClient.variation).toHaveBeenCalledWith(
239
+ key,
240
+ testContext,
241
+ expect.objectContaining({
242
+ model: defaultValue.model,
243
+ provider: defaultValue.provider,
244
+ }),
245
+ );
280
246
  });
247
+ });
248
+
249
+ describe('completionConfig method', () => {
250
+ it('calls _evaluate with correct parameters and tracks usage', async () => {
251
+ const client = new LDAIClientImpl(mockLdClient);
252
+ const key = 'test-flag';
253
+ const defaultValue: LDAICompletionConfigDefault = {
254
+ model: { name: 'test', parameters: { name: 'test-model' } },
255
+ messages: [],
256
+ enabled: true,
257
+ };
258
+ const variables = { var1: 'value1' };
281
259
 
282
- expect(mockLdClient.variation).toHaveBeenCalledWith(key, testContext, expectedLDFlagValue);
260
+ const mockConfig = {
261
+ model: { name: 'test-model' },
262
+ messages: [],
263
+ tracker: {} as any,
264
+ enabled: true,
265
+ };
266
+
267
+ const evaluateSpy = jest.spyOn(client as any, '_evaluate');
268
+ evaluateSpy.mockResolvedValue(mockConfig);
269
+
270
+ const result = await client.completionConfig(key, testContext, defaultValue, variables);
271
+
272
+ expect(mockLdClient.track).toHaveBeenCalledWith(
273
+ '$ld:ai:config:function:single',
274
+ testContext,
275
+ key,
276
+ 1,
277
+ );
278
+ expect(evaluateSpy).toHaveBeenCalledWith(
279
+ key,
280
+ testContext,
281
+ defaultValue,
282
+ 'completion',
283
+ variables,
284
+ );
285
+ expect(result).toBeDefined();
286
+ evaluateSpy.mockRestore();
287
+ });
283
288
  });
284
289
 
285
- it('returns multiple agents config with interpolated instructions', async () => {
286
- const client = new LDAIClientImpl(mockLdClient);
290
+ describe('agentConfig method', () => {
291
+ it('calls _evaluate with correct parameters and tracks usage', async () => {
292
+ const client = new LDAIClientImpl(mockLdClient);
293
+ const key = 'test-agent';
294
+ const defaultValue: LDAIAgentConfigDefault = {
295
+ model: { name: 'test', parameters: { name: 'test-model' } },
296
+ instructions: 'You are a helpful assistant.',
297
+ enabled: true,
298
+ };
299
+ const variables = { var1: 'value1' };
300
+
301
+ const mockConfig = {
302
+ model: { name: 'test-model' },
303
+ instructions: 'You are a helpful assistant.',
304
+ tracker: {} as any,
305
+ enabled: true,
306
+ };
307
+
308
+ const evaluateSpy = jest.spyOn(client as any, '_evaluate');
309
+ evaluateSpy.mockResolvedValue(mockConfig);
310
+
311
+ const result = await client.agentConfig(key, testContext, defaultValue, variables);
312
+
313
+ expect(mockLdClient.track).toHaveBeenCalledWith(
314
+ '$ld:ai:agent:function:single',
315
+ testContext,
316
+ key,
317
+ 1,
318
+ );
319
+ expect(evaluateSpy).toHaveBeenCalledWith(key, testContext, defaultValue, 'agent', variables);
320
+ expect(result).toBe(mockConfig);
321
+ evaluateSpy.mockRestore();
322
+ });
323
+ });
287
324
 
288
- const agentConfigs = [
289
- {
290
- key: 'research-agent',
291
- defaultValue: {
292
- model: { name: 'test', parameters: { name: 'test-model' } },
293
- instructions: 'You are a research assistant.',
325
+ describe('agents method', () => {
326
+ it('retrieves multiple agent configs with interpolated instructions', async () => {
327
+ const client = new LDAIClientImpl(mockLdClient);
328
+
329
+ const agentConfigs = [
330
+ {
331
+ key: 'research-agent',
332
+ defaultValue: {
333
+ enabled: false,
334
+ },
335
+ variables: { topic: 'climate change' },
336
+ },
337
+ {
338
+ key: 'writing-agent',
339
+ defaultValue: {
340
+ enabled: false,
341
+ },
342
+ variables: { style: 'academic' },
343
+ },
344
+ ] as const;
345
+
346
+ const mockVariations = {
347
+ 'research-agent': {
348
+ model: {
349
+ name: 'research-model',
350
+ parameters: { temperature: 0.3, maxTokens: 2048 },
351
+ },
352
+ provider: { name: 'openai' },
353
+ instructions: 'You are a research assistant specializing in {{topic}}.',
354
+ _ldMeta: { variationKey: 'v1', enabled: true, mode: 'agent' },
355
+ },
356
+ 'writing-agent': {
357
+ model: {
358
+ name: 'writing-model',
359
+ parameters: { temperature: 0.7, maxTokens: 1024 },
360
+ },
361
+ provider: { name: 'anthropic' },
362
+ instructions: 'You are a writing assistant with {{style}} style.',
363
+ _ldMeta: { variationKey: 'v2', enabled: true, mode: 'agent' },
364
+ },
365
+ };
366
+
367
+ mockLdClient.variation.mockImplementation((key) =>
368
+ Promise.resolve(mockVariations[key as keyof typeof mockVariations]),
369
+ );
370
+
371
+ const result = await client.agentConfigs(agentConfigs, testContext);
372
+
373
+ expect(result).toEqual({
374
+ 'research-agent': {
375
+ key: 'research-agent',
376
+ model: {
377
+ name: 'research-model',
378
+ parameters: { temperature: 0.3, maxTokens: 2048 },
379
+ },
380
+ provider: { name: 'openai' },
381
+ instructions: 'You are a research assistant specializing in climate change.',
382
+ tracker: expect.any(Object),
294
383
  enabled: true,
295
384
  },
296
- variables: { topic: 'climate change' },
297
- },
298
- {
299
- key: 'writing-agent',
300
- defaultValue: {
301
- model: { name: 'test', parameters: { name: 'test-model' } },
302
- instructions: 'You are a writing assistant.',
385
+ 'writing-agent': {
386
+ key: 'writing-agent',
387
+ model: {
388
+ name: 'writing-model',
389
+ parameters: { temperature: 0.7, maxTokens: 1024 },
390
+ },
391
+ provider: { name: 'anthropic' },
392
+ instructions: 'You are a writing assistant with academic style.',
393
+ tracker: expect.any(Object),
303
394
  enabled: true,
304
395
  },
305
- variables: { style: 'academic' },
306
- },
307
- ] as const;
396
+ });
397
+
398
+ expect(mockLdClient.track).toHaveBeenCalledWith(
399
+ '$ld:ai:agent:function:multiple',
400
+ testContext,
401
+ agentConfigs.length,
402
+ agentConfigs.length,
403
+ );
404
+ });
308
405
 
309
- const mockVariations = {
310
- 'research-agent': {
311
- model: {
312
- name: 'research-model',
313
- parameters: { temperature: 0.3, maxTokens: 2048 },
314
- },
315
- provider: { name: 'openai' },
316
- instructions: 'You are a research assistant specializing in {{topic}}.',
317
- _ldMeta: { variationKey: 'v1', enabled: true, mode: 'agent' },
318
- },
319
- 'writing-agent': {
320
- model: {
321
- name: 'writing-model',
322
- parameters: { temperature: 0.7, maxTokens: 1024 },
323
- },
324
- provider: { name: 'anthropic' },
325
- instructions: 'You are a writing assistant with {{style}} style.',
326
- _ldMeta: { variationKey: 'v2', enabled: true, mode: 'agent' },
327
- },
328
- };
406
+ it('handles empty agent configs array', async () => {
407
+ const client = new LDAIClientImpl(mockLdClient);
329
408
 
330
- mockLdClient.variation.mockImplementation((key) =>
331
- Promise.resolve(mockVariations[key as keyof typeof mockVariations]),
332
- );
409
+ const result = await client.agentConfigs([], testContext);
333
410
 
334
- const result = await client.agents(agentConfigs, testContext);
411
+ expect(result).toEqual({});
335
412
 
336
- expect(result).toEqual({
337
- 'research-agent': {
338
- model: {
339
- name: 'research-model',
340
- parameters: { temperature: 0.3, maxTokens: 2048 },
341
- },
342
- provider: { name: 'openai' },
343
- instructions: 'You are a research assistant specializing in climate change.',
344
- tracker: expect.any(Object),
413
+ expect(mockLdClient.track).toHaveBeenCalledWith(
414
+ '$ld:ai:agent:function:multiple',
415
+ testContext,
416
+ 0,
417
+ 0,
418
+ );
419
+ });
420
+ });
421
+
422
+ describe('judgeConfig method', () => {
423
+ it('calls _evaluate with correct parameters and tracks usage', async () => {
424
+ const client = new LDAIClientImpl(mockLdClient);
425
+ const key = 'test-judge';
426
+ const defaultValue: LDAIJudgeConfigDefault = {
427
+ enabled: false,
428
+ };
429
+ const variables = { metric: 'relevance' };
430
+
431
+ const mockJudgeConfig = {
345
432
  enabled: true,
346
- },
347
- 'writing-agent': {
348
- model: {
349
- name: 'writing-model',
350
- parameters: { temperature: 0.7, maxTokens: 1024 },
351
- },
352
- provider: { name: 'anthropic' },
353
- instructions: 'You are a writing assistant with academic style.',
354
- tracker: expect.any(Object),
433
+ model: { name: 'gpt-4' },
434
+ provider: { name: 'openai' },
435
+ evaluationMetricKeys: ['relevance'],
436
+ messages: [{ role: 'system' as const, content: 'You are a judge for {{metric}}.' }],
437
+ tracker: {} as any,
438
+ toVercelAISDK: jest.fn(),
439
+ };
440
+
441
+ const evaluateSpy = jest.spyOn(client as any, '_evaluate');
442
+ evaluateSpy.mockResolvedValue(mockJudgeConfig);
443
+
444
+ const result = await client.judgeConfig(key, testContext, defaultValue, variables);
445
+
446
+ expect(mockLdClient.track).toHaveBeenCalledWith(
447
+ '$ld:ai:judge:function:single',
448
+ testContext,
449
+ key,
450
+ 1,
451
+ );
452
+ expect(evaluateSpy).toHaveBeenCalledWith(key, testContext, defaultValue, 'judge', variables);
453
+ expect(result).toBe(mockJudgeConfig);
454
+ evaluateSpy.mockRestore();
455
+ });
456
+ });
457
+
458
+ describe('createJudge method', () => {
459
+ let mockProvider: jest.Mocked<any>;
460
+ let mockJudge: jest.Mocked<Judge>;
461
+
462
+ beforeEach(() => {
463
+ mockProvider = {
464
+ invokeStructuredModel: jest.fn(),
465
+ };
466
+
467
+ mockJudge = {
468
+ evaluate: jest.fn(),
469
+ evaluateMessages: jest.fn(),
470
+ } as any;
471
+
472
+ // Mock AIProviderFactory.create
473
+ (AIProviderFactory.create as jest.Mock).mockResolvedValue(mockProvider);
474
+
475
+ // Mock Judge constructor
476
+ (Judge as jest.MockedClass<typeof Judge>).mockImplementation(() => mockJudge);
477
+ });
478
+
479
+ it('initializes judge successfully', async () => {
480
+ const client = new LDAIClientImpl(mockLdClient);
481
+ const key = 'test-judge';
482
+ const defaultValue: LDAIJudgeConfigDefault = {
483
+ enabled: false,
484
+ };
485
+
486
+ const mockJudgeConfig = {
487
+ key: 'test-judge',
355
488
  enabled: true,
356
- },
489
+ model: { name: 'gpt-4' },
490
+ provider: { name: 'openai' },
491
+ evaluationMetricKeys: ['relevance', 'accuracy'],
492
+ messages: [{ role: 'system' as const, content: 'You are a judge.' }],
493
+ tracker: {} as any,
494
+ toVercelAISDK: jest.fn(),
495
+ };
496
+
497
+ const judgeConfigSpy = jest.spyOn(client, 'judgeConfig');
498
+ judgeConfigSpy.mockResolvedValue(mockJudgeConfig);
499
+
500
+ const result = await client.createJudge(key, testContext, defaultValue);
501
+
502
+ expect(mockLdClient.track).toHaveBeenCalledWith(
503
+ '$ld:ai:judge:function:createJudge',
504
+ testContext,
505
+ key,
506
+ 1,
507
+ );
508
+ expect(judgeConfigSpy).toHaveBeenCalledWith(key, testContext, defaultValue, {
509
+ message_history: '{{message_history}}',
510
+ response_to_evaluate: '{{response_to_evaluate}}',
511
+ });
512
+ expect(AIProviderFactory.create).toHaveBeenCalledWith(mockJudgeConfig, undefined, undefined);
513
+ expect(Judge).toHaveBeenCalledWith(
514
+ mockJudgeConfig,
515
+ mockJudgeConfig.tracker,
516
+ mockProvider,
517
+ undefined,
518
+ );
519
+ expect(result).toBe(mockJudge);
520
+ judgeConfigSpy.mockRestore();
357
521
  });
358
522
 
359
- // Verify tracking was called
360
- expect(mockLdClient.track).toHaveBeenCalledWith(
361
- '$ld:ai:agent:function:multiple',
362
- testContext,
363
- agentConfigs.length,
364
- agentConfigs.length,
365
- );
366
- });
523
+ it('returns undefined when judge configuration is disabled', async () => {
524
+ const client = new LDAIClientImpl(mockLdClient);
525
+ const key = 'test-judge';
526
+ const defaultValue: LDAIJudgeConfigDefault = {
527
+ enabled: false,
528
+ };
529
+
530
+ const mockJudgeConfig = {
531
+ key: 'test-judge',
532
+ enabled: false,
533
+ evaluationMetricKeys: [],
534
+ };
367
535
 
368
- it('handles empty agent configs array', async () => {
369
- const client = new LDAIClientImpl(mockLdClient);
536
+ const judgeConfigSpy = jest.spyOn(client, 'judgeConfig');
537
+ judgeConfigSpy.mockResolvedValue(mockJudgeConfig);
370
538
 
371
- const result = await client.agents([], testContext);
539
+ const result = await client.createJudge(key, testContext, defaultValue);
372
540
 
373
- expect(result).toEqual({});
541
+ expect(result).toBeUndefined();
542
+ expect(AIProviderFactory.create).not.toHaveBeenCalled();
543
+ expect(Judge).not.toHaveBeenCalled();
544
+ judgeConfigSpy.mockRestore();
545
+ });
546
+
547
+ it('returns undefined when AIProviderFactory.create fails', async () => {
548
+ const client = new LDAIClientImpl(mockLdClient);
549
+ const key = 'test-judge';
550
+ const defaultValue: LDAIJudgeConfigDefault = {
551
+ enabled: false,
552
+ };
374
553
 
375
- // Verify tracking was called with 0 agents
376
- expect(mockLdClient.track).toHaveBeenCalledWith(
377
- '$ld:ai:agent:function:multiple',
378
- testContext,
379
- 0,
380
- 0,
381
- );
554
+ const mockJudgeConfig = {
555
+ key: 'test-judge',
556
+ enabled: true,
557
+ model: { name: 'gpt-4' },
558
+ provider: { name: 'openai' },
559
+ evaluationMetricKeys: ['relevance'],
560
+ messages: [{ role: 'system' as const, content: 'You are a judge.' }],
561
+ tracker: {} as any,
562
+ toVercelAISDK: jest.fn(),
563
+ };
564
+
565
+ const judgeConfigSpy = jest.spyOn(client, 'judgeConfig');
566
+ judgeConfigSpy.mockResolvedValue(mockJudgeConfig);
567
+
568
+ (AIProviderFactory.create as jest.Mock).mockResolvedValue(undefined);
569
+
570
+ const result = await client.createJudge(key, testContext, defaultValue);
571
+
572
+ expect(result).toBeUndefined();
573
+ expect(AIProviderFactory.create).toHaveBeenCalledWith(mockJudgeConfig, undefined, undefined);
574
+ expect(Judge).not.toHaveBeenCalled();
575
+ judgeConfigSpy.mockRestore();
576
+ });
577
+
578
+ it('handles errors gracefully', async () => {
579
+ const client = new LDAIClientImpl(mockLdClient);
580
+ const key = 'test-judge';
581
+ const defaultValue: LDAIJudgeConfigDefault = {
582
+ enabled: false,
583
+ };
584
+
585
+ const error = new Error('Judge configuration error');
586
+ const judgeConfigSpy = jest.spyOn(client, 'judgeConfig');
587
+ judgeConfigSpy.mockRejectedValue(error);
588
+
589
+ const result = await client.createJudge(key, testContext, defaultValue);
590
+
591
+ expect(result).toBeUndefined();
592
+ judgeConfigSpy.mockRestore();
593
+ });
382
594
  });