@launchdarkly/server-sdk-ai 0.12.3 → 0.14.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/CHANGELOG.md +44 -0
- package/README.md +2 -2
- package/__tests__/Judge.test.ts +496 -0
- package/__tests__/LDAIClientImpl.test.ts +530 -323
- package/__tests__/LDAIConfigTrackerImpl.test.ts +50 -290
- package/__tests__/TrackedChat.test.ts +4 -5
- package/dist/package.json +53 -0
- package/dist/src/LDAIClientImpl.d.ts +39 -0
- package/dist/src/LDAIClientImpl.d.ts.map +1 -0
- package/dist/src/LDAIClientImpl.js +164 -0
- package/dist/src/LDAIClientImpl.js.map +1 -0
- package/dist/{LDAIConfigTrackerImpl.d.ts → src/LDAIConfigTrackerImpl.d.ts} +13 -11
- package/dist/src/LDAIConfigTrackerImpl.d.ts.map +1 -0
- package/dist/{LDAIConfigTrackerImpl.js → src/LDAIConfigTrackerImpl.js} +59 -44
- package/dist/src/LDAIConfigTrackerImpl.js.map +1 -0
- package/dist/src/LDClientMin.d.ts.map +1 -0
- package/dist/{LDClientMin.js.map → src/LDClientMin.js.map} +1 -1
- package/dist/src/api/LDAIClient.d.ts +258 -0
- package/dist/src/api/LDAIClient.d.ts.map +1 -0
- package/dist/{api → src/api}/LDAIClient.js.map +1 -1
- package/dist/{api → src/api}/chat/TrackedChat.d.ts +22 -4
- package/dist/src/api/chat/TrackedChat.d.ts.map +1 -0
- package/dist/{api → src/api}/chat/TrackedChat.js +43 -2
- package/dist/src/api/chat/TrackedChat.js.map +1 -0
- package/dist/src/api/chat/index.d.ts.map +1 -0
- package/dist/src/api/chat/index.js.map +1 -0
- package/dist/src/api/chat/types.d.ts +22 -0
- package/dist/src/api/chat/types.d.ts.map +1 -0
- package/dist/{api → src/api}/chat/types.js.map +1 -1
- package/dist/{api → src/api}/config/LDAIConfigTracker.d.ts +41 -22
- package/dist/src/api/config/LDAIConfigTracker.d.ts.map +1 -0
- package/dist/src/api/config/LDAIConfigTracker.js.map +1 -0
- package/dist/src/api/config/LDAIConfigUtils.d.ts +2 -0
- package/dist/src/api/config/LDAIConfigUtils.d.ts.map +1 -0
- package/dist/src/api/config/LDAIConfigUtils.js +141 -0
- package/dist/src/api/config/LDAIConfigUtils.js.map +1 -0
- package/dist/src/api/config/index.d.ts +3 -0
- package/dist/src/api/config/index.d.ts.map +1 -0
- package/dist/{api/agents → src/api/config}/index.js +1 -1
- package/dist/src/api/config/index.js.map +1 -0
- package/dist/src/api/config/types.d.ts +202 -0
- package/dist/src/api/config/types.d.ts.map +1 -0
- package/dist/{api/agents/LDAIAgent.js → src/api/config/types.js} +1 -1
- package/dist/src/api/config/types.js.map +1 -0
- package/dist/{api → src/api}/index.d.ts +1 -1
- package/dist/src/api/index.d.ts.map +1 -0
- package/dist/{api → src/api}/index.js +1 -1
- package/dist/src/api/index.js.map +1 -0
- package/dist/src/api/judge/EvaluationSchemaBuilder.d.ts +11 -0
- package/dist/src/api/judge/EvaluationSchemaBuilder.d.ts.map +1 -0
- package/dist/src/api/judge/EvaluationSchemaBuilder.js +52 -0
- package/dist/src/api/judge/EvaluationSchemaBuilder.js.map +1 -0
- package/dist/src/api/judge/Judge.d.ts +63 -0
- package/dist/src/api/judge/Judge.d.ts.map +1 -0
- package/dist/src/api/judge/Judge.js +149 -0
- package/dist/src/api/judge/Judge.js.map +1 -0
- package/dist/src/api/judge/index.d.ts +3 -0
- package/dist/src/api/judge/index.d.ts.map +1 -0
- package/dist/src/api/judge/index.js +6 -0
- package/dist/src/api/judge/index.js.map +1 -0
- package/dist/src/api/judge/types.d.ts +35 -0
- package/dist/src/api/judge/types.d.ts.map +1 -0
- package/dist/{api/config/LDAIConfig.js → src/api/judge/types.js} +1 -1
- package/dist/src/api/judge/types.js.map +1 -0
- package/dist/src/api/metrics/BedrockTokenUsage.d.ts.map +1 -0
- package/dist/src/api/metrics/BedrockTokenUsage.js.map +1 -0
- package/dist/src/api/metrics/LDAIMetrics.d.ts.map +1 -0
- package/dist/src/api/metrics/LDAIMetrics.js.map +1 -0
- package/dist/src/api/metrics/LDFeedbackKind.d.ts.map +1 -0
- package/dist/src/api/metrics/LDFeedbackKind.js.map +1 -0
- package/dist/src/api/metrics/LDTokenUsage.d.ts.map +1 -0
- package/dist/src/api/metrics/LDTokenUsage.js.map +1 -0
- package/dist/src/api/metrics/OpenAiUsage.d.ts.map +1 -0
- package/dist/src/api/metrics/OpenAiUsage.js.map +1 -0
- package/dist/src/api/metrics/VercelAISDKTokenUsage.d.ts.map +1 -0
- package/dist/src/api/metrics/VercelAISDKTokenUsage.js.map +1 -0
- package/dist/src/api/metrics/index.d.ts.map +1 -0
- package/dist/src/api/metrics/index.js.map +1 -0
- package/dist/{api → src/api}/providers/AIProvider.d.ts +20 -3
- package/dist/src/api/providers/AIProvider.d.ts.map +1 -0
- package/dist/src/api/providers/AIProvider.js +88 -0
- package/dist/src/api/providers/AIProvider.js.map +1 -0
- package/dist/{api → src/api}/providers/AIProviderFactory.d.ts +2 -2
- package/dist/src/api/providers/AIProviderFactory.d.ts.map +1 -0
- package/dist/src/api/providers/AIProviderFactory.js.map +1 -0
- package/dist/src/api/providers/index.d.ts.map +1 -0
- package/dist/src/api/providers/index.js.map +1 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js.map +1 -0
- package/docs/assets/search.js +1 -1
- package/docs/classes/AIProvider.html +55 -20
- package/docs/classes/AIProviderFactory.html +27 -17
- package/docs/classes/Judge.html +322 -0
- package/docs/classes/TrackedChat.html +97 -29
- package/docs/enums/LDFeedbackKind.html +22 -12
- package/docs/functions/createBedrockTokenUsage.html +20 -10
- package/docs/functions/createOpenAiUsage.html +20 -10
- package/docs/functions/createVercelAISDKTokenUsage.html +20 -10
- package/docs/functions/initAi.html +20 -10
- package/docs/index.html +36 -16
- package/docs/interfaces/ChatResponse.html +35 -14
- package/docs/interfaces/EvalScore.html +119 -0
- package/docs/interfaces/JudgeResponse.html +129 -0
- package/docs/interfaces/LDAIAgentConfig.html +81 -33
- package/docs/interfaces/{LDAIAgent.html → LDAIAgentConfigDefault.html} +51 -41
- package/docs/interfaces/LDAIAgentRequestConfig.html +129 -0
- package/docs/interfaces/LDAIClient.html +234 -40
- package/docs/interfaces/{VercelAISDKConfig.html → LDAICompletionConfig.html} +86 -86
- package/docs/interfaces/LDAICompletionConfigDefault.html +155 -0
- package/docs/interfaces/LDAIConfig.html +45 -75
- package/docs/interfaces/LDAIConfigDefault.html +133 -0
- package/docs/interfaces/LDAIConfigTracker.html +140 -59
- package/docs/interfaces/LDAIJudgeConfig.html +167 -0
- package/docs/interfaces/LDAIJudgeConfigDefault.html +155 -0
- package/docs/interfaces/LDAIMetrics.html +22 -12
- package/docs/interfaces/LDJudge.html +119 -0
- package/docs/interfaces/{VercelAISDKMapOptions.html → LDJudgeConfiguration.html} +36 -19
- package/docs/interfaces/LDLogger.html +19 -9
- package/docs/interfaces/LDMessage.html +22 -12
- package/docs/interfaces/LDModelConfig.html +23 -13
- package/docs/interfaces/LDProviderConfig.html +21 -11
- package/docs/interfaces/LDTokenUsage.html +23 -13
- package/docs/interfaces/StructuredResponse.html +129 -0
- package/docs/types/{VercelAISDKProvider.html → LDAIConfigDefaultKind.html} +26 -31
- package/docs/types/{LDAIAgentDefaults.html → LDAIConfigKind.html} +24 -14
- package/docs/types/{LDAIDefaults.html → LDAIConfigMode.html} +24 -24
- package/docs/types/SupportedAIProvider.html +20 -10
- package/docs/variables/SUPPORTED_AI_PROVIDERS.html +20 -10
- package/package.json +3 -3
- package/src/LDAIClientImpl.ts +222 -176
- package/src/LDAIConfigTrackerImpl.ts +73 -54
- package/src/api/LDAIClient.ts +166 -33
- package/src/api/chat/TrackedChat.ts +64 -5
- package/src/api/chat/types.ts +8 -1
- package/src/api/config/LDAIConfigTracker.ts +46 -27
- package/src/api/config/LDAIConfigUtils.ts +201 -0
- package/src/api/config/index.ts +2 -2
- package/src/api/config/types.ts +256 -0
- package/src/api/index.ts +1 -1
- package/src/api/judge/EvaluationSchemaBuilder.ts +54 -0
- package/src/api/judge/Judge.ts +216 -0
- package/src/api/judge/index.ts +2 -0
- package/src/api/judge/types.ts +39 -0
- package/src/api/providers/AIProvider.ts +54 -3
- package/src/api/providers/AIProviderFactory.ts +4 -4
- package/tsconfig.json +3 -3
- package/tsconfig.ref.json +1 -1
- package/__tests__/LDAIConfigMapper.test.ts +0 -159
- package/dist/LDAIClientImpl.d.ts +0 -23
- package/dist/LDAIClientImpl.d.ts.map +0 -1
- package/dist/LDAIClientImpl.js +0 -128
- package/dist/LDAIClientImpl.js.map +0 -1
- package/dist/LDAIConfigMapper.d.ts +0 -10
- package/dist/LDAIConfigMapper.d.ts.map +0 -1
- package/dist/LDAIConfigMapper.js +0 -55
- package/dist/LDAIConfigMapper.js.map +0 -1
- package/dist/LDAIConfigTrackerImpl.d.ts.map +0 -1
- package/dist/LDAIConfigTrackerImpl.js.map +0 -1
- package/dist/LDClientMin.d.ts.map +0 -1
- package/dist/api/LDAIClient.d.ts +0 -169
- package/dist/api/LDAIClient.d.ts.map +0 -1
- package/dist/api/agents/LDAIAgent.d.ts +0 -32
- package/dist/api/agents/LDAIAgent.d.ts.map +0 -1
- package/dist/api/agents/LDAIAgent.js.map +0 -1
- package/dist/api/agents/index.d.ts +0 -2
- package/dist/api/agents/index.d.ts.map +0 -1
- package/dist/api/agents/index.js.map +0 -1
- package/dist/api/chat/TrackedChat.d.ts.map +0 -1
- package/dist/api/chat/TrackedChat.js.map +0 -1
- package/dist/api/chat/index.d.ts.map +0 -1
- package/dist/api/chat/index.js.map +0 -1
- package/dist/api/chat/types.d.ts +0 -16
- package/dist/api/chat/types.d.ts.map +0 -1
- package/dist/api/config/LDAIConfig.d.ts +0 -92
- package/dist/api/config/LDAIConfig.d.ts.map +0 -1
- package/dist/api/config/LDAIConfig.js.map +0 -1
- package/dist/api/config/LDAIConfigTracker.d.ts.map +0 -1
- package/dist/api/config/LDAIConfigTracker.js.map +0 -1
- package/dist/api/config/VercelAISDK.d.ts +0 -19
- package/dist/api/config/VercelAISDK.d.ts.map +0 -1
- package/dist/api/config/VercelAISDK.js +0 -3
- package/dist/api/config/VercelAISDK.js.map +0 -1
- package/dist/api/config/index.d.ts +0 -4
- package/dist/api/config/index.d.ts.map +0 -1
- package/dist/api/config/index.js +0 -19
- package/dist/api/config/index.js.map +0 -1
- package/dist/api/index.d.ts.map +0 -1
- package/dist/api/index.js.map +0 -1
- package/dist/api/metrics/BedrockTokenUsage.d.ts.map +0 -1
- package/dist/api/metrics/BedrockTokenUsage.js.map +0 -1
- package/dist/api/metrics/LDAIMetrics.d.ts.map +0 -1
- package/dist/api/metrics/LDAIMetrics.js.map +0 -1
- package/dist/api/metrics/LDFeedbackKind.d.ts.map +0 -1
- package/dist/api/metrics/LDFeedbackKind.js.map +0 -1
- package/dist/api/metrics/LDTokenUsage.d.ts.map +0 -1
- package/dist/api/metrics/LDTokenUsage.js.map +0 -1
- package/dist/api/metrics/OpenAiUsage.d.ts.map +0 -1
- package/dist/api/metrics/OpenAiUsage.js.map +0 -1
- package/dist/api/metrics/VercelAISDKTokenUsage.d.ts.map +0 -1
- package/dist/api/metrics/VercelAISDKTokenUsage.js.map +0 -1
- package/dist/api/metrics/index.d.ts.map +0 -1
- package/dist/api/metrics/index.js.map +0 -1
- package/dist/api/providers/AIProvider.d.ts.map +0 -1
- package/dist/api/providers/AIProvider.js +0 -31
- package/dist/api/providers/AIProvider.js.map +0 -1
- package/dist/api/providers/AIProviderFactory.d.ts.map +0 -1
- package/dist/api/providers/AIProviderFactory.js.map +0 -1
- package/dist/api/providers/index.d.ts.map +0 -1
- package/dist/api/providers/index.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/src/LDAIConfigMapper.ts +0 -65
- package/src/api/agents/LDAIAgent.ts +0 -36
- package/src/api/agents/index.ts +0 -1
- package/src/api/config/LDAIConfig.ts +0 -101
- package/src/api/config/VercelAISDK.ts +0 -21
- /package/dist/{LDClientMin.d.ts → src/LDClientMin.d.ts} +0 -0
- /package/dist/{LDClientMin.js → src/LDClientMin.js} +0 -0
- /package/dist/{api → src/api}/LDAIClient.js +0 -0
- /package/dist/{api → src/api}/chat/index.d.ts +0 -0
- /package/dist/{api → src/api}/chat/index.js +0 -0
- /package/dist/{api → src/api}/chat/types.js +0 -0
- /package/dist/{api → src/api}/config/LDAIConfigTracker.js +0 -0
- /package/dist/{api → src/api}/metrics/BedrockTokenUsage.d.ts +0 -0
- /package/dist/{api → src/api}/metrics/BedrockTokenUsage.js +0 -0
- /package/dist/{api → src/api}/metrics/LDAIMetrics.d.ts +0 -0
- /package/dist/{api → src/api}/metrics/LDAIMetrics.js +0 -0
- /package/dist/{api → src/api}/metrics/LDFeedbackKind.d.ts +0 -0
- /package/dist/{api → src/api}/metrics/LDFeedbackKind.js +0 -0
- /package/dist/{api → src/api}/metrics/LDTokenUsage.d.ts +0 -0
- /package/dist/{api → src/api}/metrics/LDTokenUsage.js +0 -0
- /package/dist/{api → src/api}/metrics/OpenAiUsage.d.ts +0 -0
- /package/dist/{api → src/api}/metrics/OpenAiUsage.js +0 -0
- /package/dist/{api → src/api}/metrics/VercelAISDKTokenUsage.d.ts +0 -0
- /package/dist/{api → src/api}/metrics/VercelAISDKTokenUsage.js +0 -0
- /package/dist/{api → src/api}/metrics/index.d.ts +0 -0
- /package/dist/{api → src/api}/metrics/index.js +0 -0
- /package/dist/{api → src/api}/providers/AIProviderFactory.js +0 -0
- /package/dist/{api → src/api}/providers/index.d.ts +0 -0
- /package/dist/{api → src/api}/providers/index.js +0 -0
- /package/dist/{index.d.ts → src/index.d.ts} +0 -0
- /package/dist/{index.js → src/index.js} +0 -0
|
@@ -1,382 +1,589 @@
|
|
|
1
1
|
import { LDContext } from '@launchdarkly/js-server-sdk-common';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
60
|
-
enabled
|
|
61
|
-
|
|
72
|
+
]);
|
|
73
|
+
expect(result.tracker).toBeDefined();
|
|
74
|
+
expect(result.enabled).toBe(true);
|
|
75
|
+
evaluateSpy.mockRestore();
|
|
62
76
|
});
|
|
63
77
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
'
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
94
|
+
expect(result.messages?.[0].content).toBe('User key: test-user');
|
|
95
|
+
});
|
|
87
96
|
|
|
88
|
-
|
|
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
|
-
|
|
91
|
-
|
|
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
|
-
|
|
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
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
enabled
|
|
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
|
-
|
|
198
|
-
|
|
199
|
-
'
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
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
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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
|
-
|
|
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
|
-
|
|
183
|
+
const result = await client.completionConfig(key, testContext, defaultValue);
|
|
221
184
|
|
|
222
|
-
|
|
185
|
+
expect(result.enabled).toBe(false);
|
|
186
|
+
expect(result.tracker).toBeUndefined();
|
|
187
|
+
});
|
|
223
188
|
|
|
224
|
-
|
|
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
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
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('
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
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
|
-
|
|
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
|
-
|
|
286
|
-
|
|
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
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
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
|
+
model: {
|
|
376
|
+
name: 'research-model',
|
|
377
|
+
parameters: { temperature: 0.3, maxTokens: 2048 },
|
|
378
|
+
},
|
|
379
|
+
provider: { name: 'openai' },
|
|
380
|
+
instructions: 'You are a research assistant specializing in climate change.',
|
|
381
|
+
tracker: expect.any(Object),
|
|
294
382
|
enabled: true,
|
|
295
383
|
},
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
instructions: 'You are a writing assistant.',
|
|
384
|
+
'writing-agent': {
|
|
385
|
+
model: {
|
|
386
|
+
name: 'writing-model',
|
|
387
|
+
parameters: { temperature: 0.7, maxTokens: 1024 },
|
|
388
|
+
},
|
|
389
|
+
provider: { name: 'anthropic' },
|
|
390
|
+
instructions: 'You are a writing assistant with academic style.',
|
|
391
|
+
tracker: expect.any(Object),
|
|
303
392
|
enabled: true,
|
|
304
393
|
},
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
expect(mockLdClient.track).toHaveBeenCalledWith(
|
|
397
|
+
'$ld:ai:agent:function:multiple',
|
|
398
|
+
testContext,
|
|
399
|
+
agentConfigs.length,
|
|
400
|
+
agentConfigs.length,
|
|
401
|
+
);
|
|
402
|
+
});
|
|
308
403
|
|
|
309
|
-
|
|
310
|
-
|
|
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
|
-
};
|
|
404
|
+
it('handles empty agent configs array', async () => {
|
|
405
|
+
const client = new LDAIClientImpl(mockLdClient);
|
|
329
406
|
|
|
330
|
-
|
|
331
|
-
Promise.resolve(mockVariations[key as keyof typeof mockVariations]),
|
|
332
|
-
);
|
|
407
|
+
const result = await client.agentConfigs([], testContext);
|
|
333
408
|
|
|
334
|
-
|
|
409
|
+
expect(result).toEqual({});
|
|
335
410
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
411
|
+
expect(mockLdClient.track).toHaveBeenCalledWith(
|
|
412
|
+
'$ld:ai:agent:function:multiple',
|
|
413
|
+
testContext,
|
|
414
|
+
0,
|
|
415
|
+
0,
|
|
416
|
+
);
|
|
417
|
+
});
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
describe('judgeConfig method', () => {
|
|
421
|
+
it('calls _evaluate with correct parameters and tracks usage', async () => {
|
|
422
|
+
const client = new LDAIClientImpl(mockLdClient);
|
|
423
|
+
const key = 'test-judge';
|
|
424
|
+
const defaultValue: LDAIJudgeConfigDefault = {
|
|
425
|
+
enabled: false,
|
|
426
|
+
};
|
|
427
|
+
const variables = { metric: 'relevance' };
|
|
428
|
+
|
|
429
|
+
const mockJudgeConfig = {
|
|
345
430
|
enabled: true,
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
431
|
+
model: { name: 'gpt-4' },
|
|
432
|
+
provider: { name: 'openai' },
|
|
433
|
+
evaluationMetricKeys: ['relevance'],
|
|
434
|
+
messages: [{ role: 'system' as const, content: 'You are a judge for {{metric}}.' }],
|
|
435
|
+
tracker: {} as any,
|
|
436
|
+
toVercelAISDK: jest.fn(),
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
const evaluateSpy = jest.spyOn(client as any, '_evaluate');
|
|
440
|
+
evaluateSpy.mockResolvedValue(mockJudgeConfig);
|
|
441
|
+
|
|
442
|
+
const result = await client.judgeConfig(key, testContext, defaultValue, variables);
|
|
443
|
+
|
|
444
|
+
expect(mockLdClient.track).toHaveBeenCalledWith(
|
|
445
|
+
'$ld:ai:judge:function:single',
|
|
446
|
+
testContext,
|
|
447
|
+
key,
|
|
448
|
+
1,
|
|
449
|
+
);
|
|
450
|
+
expect(evaluateSpy).toHaveBeenCalledWith(key, testContext, defaultValue, 'judge', variables);
|
|
451
|
+
expect(result).toBe(mockJudgeConfig);
|
|
452
|
+
evaluateSpy.mockRestore();
|
|
453
|
+
});
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
describe('createJudge method', () => {
|
|
457
|
+
let mockProvider: jest.Mocked<any>;
|
|
458
|
+
let mockJudge: jest.Mocked<Judge>;
|
|
459
|
+
|
|
460
|
+
beforeEach(() => {
|
|
461
|
+
mockProvider = {
|
|
462
|
+
invokeStructuredModel: jest.fn(),
|
|
463
|
+
};
|
|
464
|
+
|
|
465
|
+
mockJudge = {
|
|
466
|
+
evaluate: jest.fn(),
|
|
467
|
+
evaluateMessages: jest.fn(),
|
|
468
|
+
} as any;
|
|
469
|
+
|
|
470
|
+
// Mock AIProviderFactory.create
|
|
471
|
+
(AIProviderFactory.create as jest.Mock).mockResolvedValue(mockProvider);
|
|
472
|
+
|
|
473
|
+
// Mock Judge constructor
|
|
474
|
+
(Judge as jest.MockedClass<typeof Judge>).mockImplementation(() => mockJudge);
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
it('initializes judge successfully', async () => {
|
|
478
|
+
const client = new LDAIClientImpl(mockLdClient);
|
|
479
|
+
const key = 'test-judge';
|
|
480
|
+
const defaultValue: LDAIJudgeConfigDefault = {
|
|
481
|
+
enabled: false,
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
const mockJudgeConfig = {
|
|
355
485
|
enabled: true,
|
|
356
|
-
|
|
486
|
+
model: { name: 'gpt-4' },
|
|
487
|
+
provider: { name: 'openai' },
|
|
488
|
+
evaluationMetricKeys: ['relevance', 'accuracy'],
|
|
489
|
+
messages: [{ role: 'system' as const, content: 'You are a judge.' }],
|
|
490
|
+
tracker: {} as any,
|
|
491
|
+
toVercelAISDK: jest.fn(),
|
|
492
|
+
};
|
|
493
|
+
|
|
494
|
+
const judgeConfigSpy = jest.spyOn(client, 'judgeConfig');
|
|
495
|
+
judgeConfigSpy.mockResolvedValue(mockJudgeConfig);
|
|
496
|
+
|
|
497
|
+
const result = await client.createJudge(key, testContext, defaultValue);
|
|
498
|
+
|
|
499
|
+
expect(mockLdClient.track).toHaveBeenCalledWith(
|
|
500
|
+
'$ld:ai:judge:function:createJudge',
|
|
501
|
+
testContext,
|
|
502
|
+
key,
|
|
503
|
+
1,
|
|
504
|
+
);
|
|
505
|
+
expect(judgeConfigSpy).toHaveBeenCalledWith(key, testContext, defaultValue, {
|
|
506
|
+
message_history: '{{message_history}}',
|
|
507
|
+
response_to_evaluate: '{{response_to_evaluate}}',
|
|
508
|
+
});
|
|
509
|
+
expect(AIProviderFactory.create).toHaveBeenCalledWith(mockJudgeConfig, undefined, undefined);
|
|
510
|
+
expect(Judge).toHaveBeenCalledWith(
|
|
511
|
+
mockJudgeConfig,
|
|
512
|
+
mockJudgeConfig.tracker,
|
|
513
|
+
mockProvider,
|
|
514
|
+
undefined,
|
|
515
|
+
);
|
|
516
|
+
expect(result).toBe(mockJudge);
|
|
517
|
+
judgeConfigSpy.mockRestore();
|
|
357
518
|
});
|
|
358
519
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
'
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
520
|
+
it('returns undefined when judge configuration is disabled', async () => {
|
|
521
|
+
const client = new LDAIClientImpl(mockLdClient);
|
|
522
|
+
const key = 'test-judge';
|
|
523
|
+
const defaultValue: LDAIJudgeConfigDefault = {
|
|
524
|
+
enabled: false,
|
|
525
|
+
};
|
|
526
|
+
|
|
527
|
+
const mockJudgeConfig = {
|
|
528
|
+
enabled: false,
|
|
529
|
+
evaluationMetricKeys: [],
|
|
530
|
+
};
|
|
367
531
|
|
|
368
|
-
|
|
369
|
-
|
|
532
|
+
const judgeConfigSpy = jest.spyOn(client, 'judgeConfig');
|
|
533
|
+
judgeConfigSpy.mockResolvedValue(mockJudgeConfig);
|
|
370
534
|
|
|
371
|
-
|
|
535
|
+
const result = await client.createJudge(key, testContext, defaultValue);
|
|
372
536
|
|
|
373
|
-
|
|
537
|
+
expect(result).toBeUndefined();
|
|
538
|
+
expect(AIProviderFactory.create).not.toHaveBeenCalled();
|
|
539
|
+
expect(Judge).not.toHaveBeenCalled();
|
|
540
|
+
judgeConfigSpy.mockRestore();
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
it('returns undefined when AIProviderFactory.create fails', async () => {
|
|
544
|
+
const client = new LDAIClientImpl(mockLdClient);
|
|
545
|
+
const key = 'test-judge';
|
|
546
|
+
const defaultValue: LDAIJudgeConfigDefault = {
|
|
547
|
+
enabled: false,
|
|
548
|
+
};
|
|
374
549
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
550
|
+
const mockJudgeConfig = {
|
|
551
|
+
enabled: true,
|
|
552
|
+
model: { name: 'gpt-4' },
|
|
553
|
+
provider: { name: 'openai' },
|
|
554
|
+
evaluationMetricKeys: ['relevance'],
|
|
555
|
+
messages: [{ role: 'system' as const, content: 'You are a judge.' }],
|
|
556
|
+
tracker: {} as any,
|
|
557
|
+
toVercelAISDK: jest.fn(),
|
|
558
|
+
};
|
|
559
|
+
|
|
560
|
+
const judgeConfigSpy = jest.spyOn(client, 'judgeConfig');
|
|
561
|
+
judgeConfigSpy.mockResolvedValue(mockJudgeConfig);
|
|
562
|
+
|
|
563
|
+
(AIProviderFactory.create as jest.Mock).mockResolvedValue(undefined);
|
|
564
|
+
|
|
565
|
+
const result = await client.createJudge(key, testContext, defaultValue);
|
|
566
|
+
|
|
567
|
+
expect(result).toBeUndefined();
|
|
568
|
+
expect(AIProviderFactory.create).toHaveBeenCalledWith(mockJudgeConfig, undefined, undefined);
|
|
569
|
+
expect(Judge).not.toHaveBeenCalled();
|
|
570
|
+
judgeConfigSpy.mockRestore();
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
it('handles errors gracefully', async () => {
|
|
574
|
+
const client = new LDAIClientImpl(mockLdClient);
|
|
575
|
+
const key = 'test-judge';
|
|
576
|
+
const defaultValue: LDAIJudgeConfigDefault = {
|
|
577
|
+
enabled: false,
|
|
578
|
+
};
|
|
579
|
+
|
|
580
|
+
const error = new Error('Judge configuration error');
|
|
581
|
+
const judgeConfigSpy = jest.spyOn(client, 'judgeConfig');
|
|
582
|
+
judgeConfigSpy.mockRejectedValue(error);
|
|
583
|
+
|
|
584
|
+
const result = await client.createJudge(key, testContext, defaultValue);
|
|
585
|
+
|
|
586
|
+
expect(result).toBeUndefined();
|
|
587
|
+
judgeConfigSpy.mockRestore();
|
|
588
|
+
});
|
|
382
589
|
});
|