@launchdarkly/server-sdk-ai 0.14.1 → 0.15.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.
- package/CHANGELOG.md +22 -0
- package/dist/index.cjs +1117 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1022 -0
- package/dist/index.d.ts +1022 -0
- package/dist/index.js +1071 -0
- package/dist/index.js.map +1 -0
- package/package.json +23 -6
- package/__tests__/Judge.test.ts +0 -521
- package/__tests__/LDAIClientImpl.test.ts +0 -594
- package/__tests__/LDAIConfigTrackerImpl.test.ts +0 -815
- package/__tests__/TokenUsage.test.ts +0 -119
- package/__tests__/TrackedChat.test.ts +0 -231
- package/dist/package.json +0 -53
- package/dist/src/LDAIClientImpl.d.ts +0 -39
- package/dist/src/LDAIClientImpl.d.ts.map +0 -1
- package/dist/src/LDAIClientImpl.js +0 -164
- package/dist/src/LDAIClientImpl.js.map +0 -1
- package/dist/src/LDAIConfigTrackerImpl.d.ts +0 -74
- package/dist/src/LDAIConfigTrackerImpl.d.ts.map +0 -1
- package/dist/src/LDAIConfigTrackerImpl.js +0 -207
- package/dist/src/LDAIConfigTrackerImpl.js.map +0 -1
- package/dist/src/LDClientMin.d.ts +0 -11
- package/dist/src/LDClientMin.d.ts.map +0 -1
- package/dist/src/LDClientMin.js +0 -3
- package/dist/src/LDClientMin.js.map +0 -1
- package/dist/src/api/LDAIClient.d.ts +0 -258
- package/dist/src/api/LDAIClient.d.ts.map +0 -1
- package/dist/src/api/LDAIClient.js +0 -3
- package/dist/src/api/LDAIClient.js.map +0 -1
- package/dist/src/api/chat/TrackedChat.d.ts +0 -72
- package/dist/src/api/chat/TrackedChat.d.ts.map +0 -1
- package/dist/src/api/chat/TrackedChat.js +0 -125
- package/dist/src/api/chat/TrackedChat.js.map +0 -1
- package/dist/src/api/chat/index.d.ts +0 -3
- package/dist/src/api/chat/index.d.ts.map +0 -1
- package/dist/src/api/chat/index.js +0 -19
- package/dist/src/api/chat/index.js.map +0 -1
- package/dist/src/api/chat/types.d.ts +0 -22
- package/dist/src/api/chat/types.d.ts.map +0 -1
- package/dist/src/api/chat/types.js +0 -3
- package/dist/src/api/chat/types.js.map +0 -1
- package/dist/src/api/config/LDAIConfigTracker.d.ts +0 -209
- package/dist/src/api/config/LDAIConfigTracker.d.ts.map +0 -1
- package/dist/src/api/config/LDAIConfigTracker.js +0 -3
- package/dist/src/api/config/LDAIConfigTracker.js.map +0 -1
- package/dist/src/api/config/LDAIConfigUtils.d.ts +0 -2
- package/dist/src/api/config/LDAIConfigUtils.d.ts.map +0 -1
- package/dist/src/api/config/LDAIConfigUtils.js +0 -145
- package/dist/src/api/config/LDAIConfigUtils.js.map +0 -1
- package/dist/src/api/config/index.d.ts +0 -3
- package/dist/src/api/config/index.d.ts.map +0 -1
- package/dist/src/api/config/index.js +0 -18
- package/dist/src/api/config/index.js.map +0 -1
- package/dist/src/api/config/types.d.ts +0 -206
- package/dist/src/api/config/types.d.ts.map +0 -1
- package/dist/src/api/config/types.js +0 -3
- package/dist/src/api/config/types.js.map +0 -1
- package/dist/src/api/index.d.ts +0 -7
- package/dist/src/api/index.d.ts.map +0 -1
- package/dist/src/api/index.js +0 -23
- package/dist/src/api/index.js.map +0 -1
- package/dist/src/api/judge/EvaluationSchemaBuilder.d.ts +0 -11
- package/dist/src/api/judge/EvaluationSchemaBuilder.d.ts.map +0 -1
- package/dist/src/api/judge/EvaluationSchemaBuilder.js +0 -52
- package/dist/src/api/judge/EvaluationSchemaBuilder.js.map +0 -1
- package/dist/src/api/judge/Judge.d.ts +0 -63
- package/dist/src/api/judge/Judge.d.ts.map +0 -1
- package/dist/src/api/judge/Judge.js +0 -151
- package/dist/src/api/judge/Judge.js.map +0 -1
- package/dist/src/api/judge/index.d.ts +0 -3
- package/dist/src/api/judge/index.d.ts.map +0 -1
- package/dist/src/api/judge/index.js +0 -6
- package/dist/src/api/judge/index.js.map +0 -1
- package/dist/src/api/judge/types.d.ts +0 -37
- package/dist/src/api/judge/types.d.ts.map +0 -1
- package/dist/src/api/judge/types.js +0 -3
- package/dist/src/api/judge/types.js.map +0 -1
- package/dist/src/api/metrics/BedrockTokenUsage.d.ts +0 -7
- package/dist/src/api/metrics/BedrockTokenUsage.d.ts.map +0 -1
- package/dist/src/api/metrics/BedrockTokenUsage.js +0 -12
- package/dist/src/api/metrics/BedrockTokenUsage.js.map +0 -1
- package/dist/src/api/metrics/LDAIMetrics.d.ts +0 -17
- package/dist/src/api/metrics/LDAIMetrics.d.ts.map +0 -1
- package/dist/src/api/metrics/LDAIMetrics.js +0 -3
- package/dist/src/api/metrics/LDAIMetrics.js.map +0 -1
- package/dist/src/api/metrics/LDFeedbackKind.d.ts +0 -14
- package/dist/src/api/metrics/LDFeedbackKind.d.ts.map +0 -1
- package/dist/src/api/metrics/LDFeedbackKind.js +0 -18
- package/dist/src/api/metrics/LDFeedbackKind.js.map +0 -1
- package/dist/src/api/metrics/LDTokenUsage.d.ts +0 -18
- package/dist/src/api/metrics/LDTokenUsage.d.ts.map +0 -1
- package/dist/src/api/metrics/LDTokenUsage.js +0 -3
- package/dist/src/api/metrics/LDTokenUsage.js.map +0 -1
- package/dist/src/api/metrics/OpenAiUsage.d.ts +0 -7
- package/dist/src/api/metrics/OpenAiUsage.d.ts.map +0 -1
- package/dist/src/api/metrics/OpenAiUsage.js +0 -13
- package/dist/src/api/metrics/OpenAiUsage.js.map +0 -1
- package/dist/src/api/metrics/VercelAISDKTokenUsage.d.ts +0 -9
- package/dist/src/api/metrics/VercelAISDKTokenUsage.d.ts.map +0 -1
- package/dist/src/api/metrics/VercelAISDKTokenUsage.js +0 -13
- package/dist/src/api/metrics/VercelAISDKTokenUsage.js.map +0 -1
- package/dist/src/api/metrics/index.d.ts +0 -7
- package/dist/src/api/metrics/index.d.ts.map +0 -1
- package/dist/src/api/metrics/index.js +0 -23
- package/dist/src/api/metrics/index.js.map +0 -1
- package/dist/src/api/providers/AIProvider.d.ts +0 -52
- package/dist/src/api/providers/AIProvider.d.ts.map +0 -1
- package/dist/src/api/providers/AIProvider.js +0 -88
- package/dist/src/api/providers/AIProvider.js.map +0 -1
- package/dist/src/api/providers/AIProviderFactory.d.ts +0 -39
- package/dist/src/api/providers/AIProviderFactory.d.ts.map +0 -1
- package/dist/src/api/providers/AIProviderFactory.js +0 -102
- package/dist/src/api/providers/AIProviderFactory.js.map +0 -1
- package/dist/src/api/providers/index.d.ts +0 -3
- package/dist/src/api/providers/index.d.ts.map +0 -1
- package/dist/src/api/providers/index.js +0 -19
- package/dist/src/api/providers/index.js.map +0 -1
- package/dist/src/index.d.ts +0 -19
- package/dist/src/index.d.ts.map +0 -1
- package/dist/src/index.js +0 -29
- package/dist/src/index.js.map +0 -1
- package/docs/.nojekyll +0 -1
- package/docs/assets/highlight.css +0 -92
- package/docs/assets/main.js +0 -58
- package/docs/assets/search.js +0 -1
- package/docs/assets/style.css +0 -1379
- package/docs/classes/AIProvider.html +0 -210
- package/docs/classes/AIProviderFactory.html +0 -208
- package/docs/classes/Judge.html +0 -322
- package/docs/classes/TrackedChat.html +0 -322
- package/docs/enums/LDFeedbackKind.html +0 -115
- package/docs/functions/createBedrockTokenUsage.html +0 -94
- package/docs/functions/createOpenAiUsage.html +0 -94
- package/docs/functions/createVercelAISDKTokenUsage.html +0 -98
- package/docs/functions/initAi.html +0 -93
- package/docs/index.html +0 -136
- package/docs/interfaces/ChatResponse.html +0 -130
- package/docs/interfaces/EvalScore.html +0 -119
- package/docs/interfaces/JudgeResponse.html +0 -139
- package/docs/interfaces/LDAIAgentConfig.html +0 -178
- package/docs/interfaces/LDAIAgentConfigDefault.html +0 -155
- package/docs/interfaces/LDAIAgentRequestConfig.html +0 -129
- package/docs/interfaces/LDAIClient.html +0 -449
- package/docs/interfaces/LDAICompletionConfig.html +0 -178
- package/docs/interfaces/LDAICompletionConfigDefault.html +0 -155
- package/docs/interfaces/LDAIConfig.html +0 -158
- package/docs/interfaces/LDAIConfigDefault.html +0 -133
- package/docs/interfaces/LDAIConfigTracker.html +0 -530
- package/docs/interfaces/LDAIJudgeConfig.html +0 -178
- package/docs/interfaces/LDAIJudgeConfigDefault.html +0 -155
- package/docs/interfaces/LDAIMetrics.html +0 -121
- package/docs/interfaces/LDJudge.html +0 -119
- package/docs/interfaces/LDJudgeConfiguration.html +0 -109
- package/docs/interfaces/LDLogger.html +0 -189
- package/docs/interfaces/LDMessage.html +0 -119
- package/docs/interfaces/LDModelConfig.html +0 -139
- package/docs/interfaces/LDProviderConfig.html +0 -105
- package/docs/interfaces/LDTokenUsage.html +0 -129
- package/docs/interfaces/StructuredResponse.html +0 -129
- package/docs/types/LDAIConfigDefaultKind.html +0 -81
- package/docs/types/LDAIConfigKind.html +0 -81
- package/docs/types/LDAIConfigMode.html +0 -81
- package/docs/types/SupportedAIProvider.html +0 -81
- package/docs/variables/SUPPORTED_AI_PROVIDERS.html +0 -81
- package/jest.config.js +0 -7
- package/src/LDAIClientImpl.ts +0 -327
- package/src/LDAIConfigTrackerImpl.ts +0 -288
- package/src/LDClientMin.ts +0 -18
- package/src/api/LDAIClient.ts +0 -325
- package/src/api/chat/TrackedChat.ts +0 -163
- package/src/api/chat/index.ts +0 -2
- package/src/api/chat/types.ts +0 -24
- package/src/api/config/LDAIConfigTracker.ts +0 -238
- package/src/api/config/LDAIConfigUtils.ts +0 -212
- package/src/api/config/index.ts +0 -3
- package/src/api/config/types.ts +0 -260
- package/src/api/index.ts +0 -6
- package/src/api/judge/EvaluationSchemaBuilder.ts +0 -54
- package/src/api/judge/Judge.ts +0 -218
- package/src/api/judge/index.ts +0 -2
- package/src/api/judge/types.ts +0 -41
- package/src/api/metrics/BedrockTokenUsage.ts +0 -13
- package/src/api/metrics/LDAIMetrics.ts +0 -18
- package/src/api/metrics/LDFeedbackKind.ts +0 -13
- package/src/api/metrics/LDTokenUsage.ts +0 -19
- package/src/api/metrics/OpenAiUsage.ts +0 -13
- package/src/api/metrics/VercelAISDKTokenUsage.ts +0 -15
- package/src/api/metrics/index.ts +0 -6
- package/src/api/providers/AIProvider.ts +0 -94
- package/src/api/providers/AIProviderFactory.ts +0 -152
- package/src/api/providers/index.ts +0 -2
- package/src/index.ts +0 -24
- package/tsconfig.eslint.json +0 -5
- package/tsconfig.json +0 -21
- package/tsconfig.ref.json +0 -7
- package/typedoc.json +0 -5
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var src_exports = {};
|
|
32
|
+
__export(src_exports, {
|
|
33
|
+
AIProvider: () => AIProvider,
|
|
34
|
+
AIProviderFactory: () => AIProviderFactory,
|
|
35
|
+
Judge: () => Judge,
|
|
36
|
+
LDFeedbackKind: () => LDFeedbackKind,
|
|
37
|
+
SUPPORTED_AI_PROVIDERS: () => SUPPORTED_AI_PROVIDERS,
|
|
38
|
+
TrackedChat: () => TrackedChat,
|
|
39
|
+
createBedrockTokenUsage: () => createBedrockTokenUsage,
|
|
40
|
+
createOpenAiUsage: () => createOpenAiUsage,
|
|
41
|
+
createVercelAISDKTokenUsage: () => createVercelAISDKTokenUsage,
|
|
42
|
+
initAi: () => initAi
|
|
43
|
+
});
|
|
44
|
+
module.exports = __toCommonJS(src_exports);
|
|
45
|
+
|
|
46
|
+
// src/LDAIClientImpl.ts
|
|
47
|
+
var import_mustache2 = __toESM(require("mustache"), 1);
|
|
48
|
+
|
|
49
|
+
// src/api/chat/TrackedChat.ts
|
|
50
|
+
var TrackedChat = class {
|
|
51
|
+
constructor(aiConfig, tracker, provider, judges = {}, _logger) {
|
|
52
|
+
this.aiConfig = aiConfig;
|
|
53
|
+
this.tracker = tracker;
|
|
54
|
+
this.provider = provider;
|
|
55
|
+
this.judges = judges;
|
|
56
|
+
this._logger = _logger;
|
|
57
|
+
this.messages = [];
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Invoke the chat model with a prompt string.
|
|
61
|
+
* This method handles conversation management and tracking, delegating to the provider's invokeModel method.
|
|
62
|
+
*/
|
|
63
|
+
async invoke(prompt) {
|
|
64
|
+
const userMessage = {
|
|
65
|
+
role: "user",
|
|
66
|
+
content: prompt
|
|
67
|
+
};
|
|
68
|
+
this.messages.push(userMessage);
|
|
69
|
+
const configMessages = this.aiConfig.messages || [];
|
|
70
|
+
const allMessages = [...configMessages, ...this.messages];
|
|
71
|
+
const response = await this.tracker.trackMetricsOf(
|
|
72
|
+
(result) => result.metrics,
|
|
73
|
+
() => this.provider.invokeModel(allMessages)
|
|
74
|
+
);
|
|
75
|
+
if (this.aiConfig.judgeConfiguration?.judges && this.aiConfig.judgeConfiguration.judges.length > 0) {
|
|
76
|
+
response.evaluations = this._evaluateWithJudges(this.messages, response);
|
|
77
|
+
}
|
|
78
|
+
this.messages.push(response.message);
|
|
79
|
+
return response;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Evaluates the response with all configured judges.
|
|
83
|
+
* Returns a promise that resolves to an array of evaluation results.
|
|
84
|
+
*
|
|
85
|
+
* @param messages Array of messages representing the conversation history
|
|
86
|
+
* @param response The AI response to be evaluated
|
|
87
|
+
* @returns Promise resolving to array of judge evaluation results
|
|
88
|
+
*/
|
|
89
|
+
async _evaluateWithJudges(messages, response) {
|
|
90
|
+
const judgeConfigs = this.aiConfig.judgeConfiguration.judges;
|
|
91
|
+
const evaluationPromises = judgeConfigs.map(async (judgeConfig) => {
|
|
92
|
+
const judge = this.judges[judgeConfig.key];
|
|
93
|
+
if (!judge) {
|
|
94
|
+
this._logger?.warn(
|
|
95
|
+
`Judge configuration is not enabled: ${judgeConfig.key}`,
|
|
96
|
+
this.tracker.getTrackData()
|
|
97
|
+
);
|
|
98
|
+
return void 0;
|
|
99
|
+
}
|
|
100
|
+
const judgeResponse = await judge.evaluateMessages(
|
|
101
|
+
messages,
|
|
102
|
+
response,
|
|
103
|
+
judgeConfig.samplingRate
|
|
104
|
+
);
|
|
105
|
+
if (judgeResponse && judgeResponse.success) {
|
|
106
|
+
this.tracker.trackJudgeResponse(judgeResponse);
|
|
107
|
+
}
|
|
108
|
+
return judgeResponse;
|
|
109
|
+
});
|
|
110
|
+
const results = await Promise.allSettled(evaluationPromises);
|
|
111
|
+
return results.map((result) => result.status === "fulfilled" ? result.value : void 0);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Get the underlying AI configuration used to initialize this TrackedChat.
|
|
115
|
+
*/
|
|
116
|
+
getConfig() {
|
|
117
|
+
return this.aiConfig;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Get the underlying AI configuration tracker used to initialize this TrackedChat.
|
|
121
|
+
*/
|
|
122
|
+
getTracker() {
|
|
123
|
+
return this.tracker;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Get the underlying AI provider instance.
|
|
127
|
+
* This provides direct access to the provider for advanced use cases.
|
|
128
|
+
*/
|
|
129
|
+
getProvider() {
|
|
130
|
+
return this.provider;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Get the judges associated with this TrackedChat.
|
|
134
|
+
* Returns a record of judge instances keyed by their configuration keys.
|
|
135
|
+
*/
|
|
136
|
+
getJudges() {
|
|
137
|
+
return this.judges;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Append messages to the conversation history.
|
|
141
|
+
* Adds messages to the conversation history without invoking the model,
|
|
142
|
+
* which is useful for managing multi-turn conversations or injecting context.
|
|
143
|
+
*
|
|
144
|
+
* @param messages Array of messages to append to the conversation history
|
|
145
|
+
*/
|
|
146
|
+
appendMessages(messages) {
|
|
147
|
+
this.messages.push(...messages);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Get all messages in the conversation history.
|
|
151
|
+
*
|
|
152
|
+
* @param includeConfigMessages Whether to include the config messages from the AIConfig.
|
|
153
|
+
* Defaults to false.
|
|
154
|
+
* @returns Array of messages. When includeConfigMessages is true, returns both config
|
|
155
|
+
* messages and conversation history with config messages prepended. When false,
|
|
156
|
+
* returns only the conversation history messages.
|
|
157
|
+
*/
|
|
158
|
+
getMessages(includeConfigMessages = false) {
|
|
159
|
+
if (includeConfigMessages) {
|
|
160
|
+
const configMessages = this.aiConfig.messages || [];
|
|
161
|
+
return [...configMessages, ...this.messages];
|
|
162
|
+
}
|
|
163
|
+
return [...this.messages];
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
// src/api/config/LDAIConfigUtils.ts
|
|
168
|
+
var LDAIConfigUtils = class {
|
|
169
|
+
/**
|
|
170
|
+
* Converts a default AI configuration to a LaunchDarkly flag value format.
|
|
171
|
+
*
|
|
172
|
+
* @param config The default AI configuration to convert
|
|
173
|
+
* @param mode The mode for the configuration
|
|
174
|
+
* @returns The flag value structure for LaunchDarkly
|
|
175
|
+
*/
|
|
176
|
+
static toFlagValue(config, mode) {
|
|
177
|
+
const flagValue = {
|
|
178
|
+
_ldMeta: {
|
|
179
|
+
variationKey: "",
|
|
180
|
+
// Not available when converting from config
|
|
181
|
+
enabled: config.enabled ?? false,
|
|
182
|
+
mode
|
|
183
|
+
},
|
|
184
|
+
model: config.model
|
|
185
|
+
};
|
|
186
|
+
if ("messages" in config && config.messages !== void 0) {
|
|
187
|
+
flagValue.messages = config.messages;
|
|
188
|
+
}
|
|
189
|
+
if (config.provider !== void 0) {
|
|
190
|
+
flagValue.provider = config.provider;
|
|
191
|
+
}
|
|
192
|
+
if ("instructions" in config && config.instructions !== void 0) {
|
|
193
|
+
flagValue.instructions = config.instructions;
|
|
194
|
+
}
|
|
195
|
+
if ("evaluationMetricKeys" in config && config.evaluationMetricKeys !== void 0) {
|
|
196
|
+
flagValue.evaluationMetricKeys = config.evaluationMetricKeys;
|
|
197
|
+
}
|
|
198
|
+
if ("judgeConfiguration" in config && config.judgeConfiguration !== void 0) {
|
|
199
|
+
flagValue.judgeConfiguration = config.judgeConfiguration;
|
|
200
|
+
}
|
|
201
|
+
return flagValue;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Converts a LaunchDarkly flag value to the appropriate AI configuration type.
|
|
205
|
+
*
|
|
206
|
+
* @param flagValue The flag value from LaunchDarkly
|
|
207
|
+
* @param tracker The tracker to add to the config
|
|
208
|
+
* @returns The appropriate AI configuration type
|
|
209
|
+
*/
|
|
210
|
+
static fromFlagValue(key, flagValue, tracker) {
|
|
211
|
+
const flagValueMode = flagValue._ldMeta?.mode;
|
|
212
|
+
switch (flagValueMode) {
|
|
213
|
+
case "agent":
|
|
214
|
+
return this.toAgentConfig(key, flagValue, tracker);
|
|
215
|
+
case "judge":
|
|
216
|
+
return this.toJudgeConfig(key, flagValue, tracker);
|
|
217
|
+
case "completion":
|
|
218
|
+
default:
|
|
219
|
+
return this.toCompletionConfig(key, flagValue, tracker);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Creates a disabled configuration of the specified mode.
|
|
224
|
+
*
|
|
225
|
+
* @param mode The mode for the disabled config
|
|
226
|
+
* @returns A disabled config of the appropriate type
|
|
227
|
+
*/
|
|
228
|
+
static createDisabledConfig(key, mode) {
|
|
229
|
+
switch (mode) {
|
|
230
|
+
case "agent":
|
|
231
|
+
return {
|
|
232
|
+
key,
|
|
233
|
+
enabled: false,
|
|
234
|
+
tracker: void 0
|
|
235
|
+
};
|
|
236
|
+
case "judge":
|
|
237
|
+
return {
|
|
238
|
+
key,
|
|
239
|
+
enabled: false,
|
|
240
|
+
tracker: void 0,
|
|
241
|
+
evaluationMetricKeys: []
|
|
242
|
+
};
|
|
243
|
+
case "completion":
|
|
244
|
+
default:
|
|
245
|
+
return {
|
|
246
|
+
key,
|
|
247
|
+
enabled: false,
|
|
248
|
+
tracker: void 0
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Creates the base configuration that all config types share.
|
|
254
|
+
*
|
|
255
|
+
* @param flagValue The flag value from LaunchDarkly
|
|
256
|
+
* @returns Base configuration object
|
|
257
|
+
*/
|
|
258
|
+
static _toBaseConfig(key, flagValue) {
|
|
259
|
+
return {
|
|
260
|
+
key,
|
|
261
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
262
|
+
enabled: flagValue._ldMeta?.enabled ?? false,
|
|
263
|
+
model: flagValue.model,
|
|
264
|
+
provider: flagValue.provider
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Creates a completion config from flag value data.
|
|
269
|
+
*
|
|
270
|
+
* @param flagValue The flag value from LaunchDarkly
|
|
271
|
+
* @param tracker The tracker to add to the config
|
|
272
|
+
* @returns A completion configuration
|
|
273
|
+
*/
|
|
274
|
+
static toCompletionConfig(key, flagValue, tracker) {
|
|
275
|
+
return {
|
|
276
|
+
...this._toBaseConfig(key, flagValue),
|
|
277
|
+
tracker,
|
|
278
|
+
messages: flagValue.messages,
|
|
279
|
+
judgeConfiguration: flagValue.judgeConfiguration
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Creates an agent config from flag value data.
|
|
284
|
+
*
|
|
285
|
+
* @param flagValue The flag value from LaunchDarkly
|
|
286
|
+
* @param tracker The tracker to add to the config
|
|
287
|
+
* @returns An agent configuration
|
|
288
|
+
*/
|
|
289
|
+
static toAgentConfig(key, flagValue, tracker) {
|
|
290
|
+
return {
|
|
291
|
+
...this._toBaseConfig(key, flagValue),
|
|
292
|
+
tracker,
|
|
293
|
+
instructions: flagValue.instructions,
|
|
294
|
+
judgeConfiguration: flagValue.judgeConfiguration
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Creates a judge config from flag value data.
|
|
299
|
+
*
|
|
300
|
+
* @param flagValue The flag value from LaunchDarkly
|
|
301
|
+
* @param tracker The tracker to add to the config
|
|
302
|
+
* @returns A judge configuration
|
|
303
|
+
*/
|
|
304
|
+
static toJudgeConfig(key, flagValue, tracker) {
|
|
305
|
+
return {
|
|
306
|
+
...this._toBaseConfig(key, flagValue),
|
|
307
|
+
tracker,
|
|
308
|
+
messages: flagValue.messages,
|
|
309
|
+
evaluationMetricKeys: flagValue.evaluationMetricKeys || []
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
// src/api/judge/Judge.ts
|
|
315
|
+
var import_mustache = __toESM(require("mustache"), 1);
|
|
316
|
+
|
|
317
|
+
// src/api/judge/EvaluationSchemaBuilder.ts
|
|
318
|
+
var EvaluationSchemaBuilder = class {
|
|
319
|
+
static build(evaluationMetricKeys) {
|
|
320
|
+
return {
|
|
321
|
+
type: "object",
|
|
322
|
+
properties: {
|
|
323
|
+
evaluations: {
|
|
324
|
+
type: "object",
|
|
325
|
+
description: `Object containing evaluation results for ${evaluationMetricKeys.join(", ")} metrics`,
|
|
326
|
+
properties: this._buildKeyProperties(evaluationMetricKeys),
|
|
327
|
+
required: evaluationMetricKeys,
|
|
328
|
+
additionalProperties: false
|
|
329
|
+
}
|
|
330
|
+
},
|
|
331
|
+
required: ["evaluations"],
|
|
332
|
+
additionalProperties: false
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
static _buildKeyProperties(evaluationMetricKeys) {
|
|
336
|
+
return evaluationMetricKeys.reduce(
|
|
337
|
+
(acc, key) => {
|
|
338
|
+
acc[key] = this._buildKeySchema(key);
|
|
339
|
+
return acc;
|
|
340
|
+
},
|
|
341
|
+
{}
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
static _buildKeySchema(key) {
|
|
345
|
+
return {
|
|
346
|
+
type: "object",
|
|
347
|
+
properties: {
|
|
348
|
+
score: {
|
|
349
|
+
type: "number",
|
|
350
|
+
minimum: 0,
|
|
351
|
+
maximum: 1,
|
|
352
|
+
description: `Score between 0.0 and 1.0 for ${key}`
|
|
353
|
+
},
|
|
354
|
+
reasoning: {
|
|
355
|
+
type: "string",
|
|
356
|
+
description: `Reasoning behind the score for ${key}`
|
|
357
|
+
}
|
|
358
|
+
},
|
|
359
|
+
required: ["score", "reasoning"],
|
|
360
|
+
additionalProperties: false
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
// src/api/judge/Judge.ts
|
|
366
|
+
var Judge = class {
|
|
367
|
+
constructor(_aiConfig, _aiConfigTracker, _aiProvider, logger) {
|
|
368
|
+
this._aiConfig = _aiConfig;
|
|
369
|
+
this._aiConfigTracker = _aiConfigTracker;
|
|
370
|
+
this._aiProvider = _aiProvider;
|
|
371
|
+
this._logger = logger;
|
|
372
|
+
this._evaluationResponseStructure = EvaluationSchemaBuilder.build(
|
|
373
|
+
this._aiConfig.evaluationMetricKeys
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Evaluates an AI response using the judge's configuration.
|
|
378
|
+
*
|
|
379
|
+
* @param input The input prompt or question that was provided to the AI
|
|
380
|
+
* @param output The AI-generated response to be evaluated
|
|
381
|
+
* @param samplingRate Sampling rate (0-1) to determine if evaluation should be processed (defaults to 1)
|
|
382
|
+
* @returns Promise that resolves to evaluation results or undefined if not sampled
|
|
383
|
+
*/
|
|
384
|
+
async evaluate(input, output, samplingRate = 1) {
|
|
385
|
+
try {
|
|
386
|
+
if (!this._aiConfig.evaluationMetricKeys || this._aiConfig.evaluationMetricKeys.length === 0) {
|
|
387
|
+
this._logger?.warn(
|
|
388
|
+
"Judge configuration is missing required evaluationMetricKeys",
|
|
389
|
+
this._aiConfigTracker.getTrackData()
|
|
390
|
+
);
|
|
391
|
+
return void 0;
|
|
392
|
+
}
|
|
393
|
+
if (!this._aiConfig.messages) {
|
|
394
|
+
this._logger?.warn(
|
|
395
|
+
"Judge configuration must include messages",
|
|
396
|
+
this._aiConfigTracker.getTrackData()
|
|
397
|
+
);
|
|
398
|
+
return void 0;
|
|
399
|
+
}
|
|
400
|
+
if (Math.random() > samplingRate) {
|
|
401
|
+
this._logger?.debug(`Judge evaluation skipped due to sampling rate: ${samplingRate}`);
|
|
402
|
+
return void 0;
|
|
403
|
+
}
|
|
404
|
+
const messages = this._constructEvaluationMessages(input, output);
|
|
405
|
+
const response = await this._aiConfigTracker.trackMetricsOf(
|
|
406
|
+
(result) => result.metrics,
|
|
407
|
+
() => this._aiProvider.invokeStructuredModel(messages, this._evaluationResponseStructure)
|
|
408
|
+
);
|
|
409
|
+
let { success } = response.metrics;
|
|
410
|
+
const evals = this._parseEvaluationResponse(response.data);
|
|
411
|
+
if (Object.keys(evals).length !== this._aiConfig.evaluationMetricKeys.length) {
|
|
412
|
+
this._logger?.warn(
|
|
413
|
+
"Judge evaluation did not return all evaluations",
|
|
414
|
+
this._aiConfigTracker.getTrackData()
|
|
415
|
+
);
|
|
416
|
+
success = false;
|
|
417
|
+
}
|
|
418
|
+
return {
|
|
419
|
+
evals,
|
|
420
|
+
success,
|
|
421
|
+
judgeConfigKey: this._aiConfig.key
|
|
422
|
+
};
|
|
423
|
+
} catch (error) {
|
|
424
|
+
this._logger?.error("Judge evaluation failed:", error);
|
|
425
|
+
return {
|
|
426
|
+
evals: {},
|
|
427
|
+
success: false,
|
|
428
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
429
|
+
judgeConfigKey: this._aiConfig.key
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Evaluates an AI response from chat messages and response.
|
|
435
|
+
*
|
|
436
|
+
* @param messages Array of messages representing the conversation history
|
|
437
|
+
* @param response The AI response to be evaluated
|
|
438
|
+
* @param samplingRatio Sampling ratio (0-1) to determine if evaluation should be processed (defaults to 1)
|
|
439
|
+
* @returns Promise that resolves to evaluation results or undefined if not sampled
|
|
440
|
+
*/
|
|
441
|
+
async evaluateMessages(messages, response, samplingRatio = 1) {
|
|
442
|
+
const input = messages.length === 0 ? "" : messages.map((msg) => msg.content).join("\r\n");
|
|
443
|
+
const output = response.message.content;
|
|
444
|
+
return this.evaluate(input, output, samplingRatio);
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* Returns the AI Config used by this judge.
|
|
448
|
+
*/
|
|
449
|
+
getAIConfig() {
|
|
450
|
+
return this._aiConfig;
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* Returns the tracker associated with this judge.
|
|
454
|
+
*/
|
|
455
|
+
getTracker() {
|
|
456
|
+
return this._aiConfigTracker;
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* Returns the AI provider used by this judge.
|
|
460
|
+
*/
|
|
461
|
+
getProvider() {
|
|
462
|
+
return this._aiProvider;
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Constructs evaluation messages by combining judge's config messages with input/output.
|
|
466
|
+
*/
|
|
467
|
+
_constructEvaluationMessages(input, output) {
|
|
468
|
+
const messages = this._aiConfig.messages.map((msg) => ({
|
|
469
|
+
...msg,
|
|
470
|
+
content: this._interpolateMessage(msg.content, {
|
|
471
|
+
message_history: input,
|
|
472
|
+
response_to_evaluate: output
|
|
473
|
+
})
|
|
474
|
+
}));
|
|
475
|
+
return messages;
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* Interpolates message content with variables using Mustache templating.
|
|
479
|
+
*/
|
|
480
|
+
_interpolateMessage(content, variables) {
|
|
481
|
+
return import_mustache.default.render(content, variables, void 0, { escape: (item) => item });
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* Parses the structured evaluation response from the AI provider.
|
|
485
|
+
*/
|
|
486
|
+
_parseEvaluationResponse(data) {
|
|
487
|
+
const evaluations = data.evaluations;
|
|
488
|
+
const results = {};
|
|
489
|
+
if (!data.evaluations || typeof data.evaluations !== "object") {
|
|
490
|
+
this._logger?.warn("Invalid response: missing or invalid evaluations object");
|
|
491
|
+
return results;
|
|
492
|
+
}
|
|
493
|
+
this._aiConfig.evaluationMetricKeys.forEach((metricKey) => {
|
|
494
|
+
const evaluation = evaluations[metricKey];
|
|
495
|
+
if (!evaluation || typeof evaluation !== "object") {
|
|
496
|
+
this._logger?.warn(
|
|
497
|
+
`Missing evaluation for metric key: ${metricKey}`,
|
|
498
|
+
this._aiConfigTracker.getTrackData()
|
|
499
|
+
);
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
const evalData = evaluation;
|
|
503
|
+
if (typeof evalData.score !== "number" || evalData.score < 0 || evalData.score > 1) {
|
|
504
|
+
this._logger?.warn(
|
|
505
|
+
`Invalid score evaluated for ${metricKey}: ${evalData.score}. Score must be a number between 0 and 1 inclusive`,
|
|
506
|
+
this._aiConfigTracker.getTrackData()
|
|
507
|
+
);
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
if (typeof evalData.reasoning !== "string") {
|
|
511
|
+
this._logger?.warn(
|
|
512
|
+
`Invalid reasoning evaluated for ${metricKey}: ${evalData.reasoning}. Reasoning must be a string`,
|
|
513
|
+
this._aiConfigTracker.getTrackData()
|
|
514
|
+
);
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
results[metricKey] = {
|
|
518
|
+
score: evalData.score,
|
|
519
|
+
reasoning: evalData.reasoning
|
|
520
|
+
};
|
|
521
|
+
});
|
|
522
|
+
return results;
|
|
523
|
+
}
|
|
524
|
+
};
|
|
525
|
+
|
|
526
|
+
// src/api/providers/AIProvider.ts
|
|
527
|
+
var AIProvider = class {
|
|
528
|
+
constructor(logger) {
|
|
529
|
+
this.logger = logger;
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Invoke the chat model with an array of messages.
|
|
533
|
+
* This method should convert messages to provider format, invoke the model,
|
|
534
|
+
* and return a ChatResponse with the result and metrics.
|
|
535
|
+
*
|
|
536
|
+
* Default implementation takes no action and returns a placeholder response.
|
|
537
|
+
* Provider implementations should override this method.
|
|
538
|
+
*
|
|
539
|
+
* @param messages Array of LDMessage objects representing the conversation
|
|
540
|
+
* @returns Promise that resolves to a ChatResponse containing the model's response
|
|
541
|
+
*/
|
|
542
|
+
async invokeModel(_messages) {
|
|
543
|
+
this.logger?.warn("invokeModel not implemented by this provider");
|
|
544
|
+
return {
|
|
545
|
+
message: {
|
|
546
|
+
role: "assistant",
|
|
547
|
+
content: ""
|
|
548
|
+
},
|
|
549
|
+
metrics: {
|
|
550
|
+
success: false,
|
|
551
|
+
usage: {
|
|
552
|
+
total: 0,
|
|
553
|
+
input: 0,
|
|
554
|
+
output: 0
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* Invoke the chat model with structured output support.
|
|
561
|
+
* This method should convert messages to provider format, invoke the model with
|
|
562
|
+
* structured output configuration, and return a structured response.
|
|
563
|
+
*
|
|
564
|
+
* Default implementation takes no action and returns a placeholder response.
|
|
565
|
+
* Provider implementations should override this method.
|
|
566
|
+
*
|
|
567
|
+
* @param messages Array of LDMessage objects representing the conversation
|
|
568
|
+
* @param responseStructure Dictionary of output configurations keyed by output name
|
|
569
|
+
* @returns Promise that resolves to a structured response
|
|
570
|
+
*/
|
|
571
|
+
async invokeStructuredModel(_messages, _responseStructure) {
|
|
572
|
+
this.logger?.warn("invokeStructuredModel not implemented by this provider");
|
|
573
|
+
return {
|
|
574
|
+
data: {},
|
|
575
|
+
rawResponse: "",
|
|
576
|
+
metrics: {
|
|
577
|
+
success: false,
|
|
578
|
+
usage: {
|
|
579
|
+
total: 0,
|
|
580
|
+
input: 0,
|
|
581
|
+
output: 0
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* Static method that constructs an instance of the provider.
|
|
588
|
+
* Each provider implementation must provide their own static create method
|
|
589
|
+
* that accepts an AIConfig and returns a configured instance.
|
|
590
|
+
*
|
|
591
|
+
* @param aiConfig The LaunchDarkly AI configuration
|
|
592
|
+
* @param logger Optional logger for the provider
|
|
593
|
+
* @returns Promise that resolves to a configured provider instance
|
|
594
|
+
*/
|
|
595
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
596
|
+
static async create(aiConfig, logger) {
|
|
597
|
+
throw new Error("Provider implementations must override the static create method");
|
|
598
|
+
}
|
|
599
|
+
};
|
|
600
|
+
|
|
601
|
+
// src/api/providers/AIProviderFactory.ts
|
|
602
|
+
var SUPPORTED_AI_PROVIDERS = [
|
|
603
|
+
"openai",
|
|
604
|
+
// Multi-provider packages should be last in the list
|
|
605
|
+
"langchain",
|
|
606
|
+
"vercel"
|
|
607
|
+
];
|
|
608
|
+
var AIProviderFactory = class {
|
|
609
|
+
/**
|
|
610
|
+
* Create an AIProvider instance based on the AI configuration.
|
|
611
|
+
* This method attempts to load provider-specific implementations dynamically.
|
|
612
|
+
* Returns undefined if the provider is not supported.
|
|
613
|
+
*
|
|
614
|
+
* @param aiConfig The AI configuration
|
|
615
|
+
* @param logger Optional logger for logging provider initialization
|
|
616
|
+
* @param defaultAiProvider Optional default AI provider to use
|
|
617
|
+
*/
|
|
618
|
+
static async create(aiConfig, logger, defaultAiProvider) {
|
|
619
|
+
const providerName = aiConfig.provider?.name?.toLowerCase();
|
|
620
|
+
const providersToTry = this._getProvidersToTry(defaultAiProvider, providerName);
|
|
621
|
+
for (const providerType of providersToTry) {
|
|
622
|
+
logger?.debug(
|
|
623
|
+
`Attempting to create AIProvider for: ${aiConfig.provider?.name} with provider type: ${providerType}`
|
|
624
|
+
);
|
|
625
|
+
const provider = await this._tryCreateProvider(providerType, aiConfig, logger);
|
|
626
|
+
if (provider) {
|
|
627
|
+
logger?.debug(`Successfully created AIProvider for: ${aiConfig.provider?.name}`);
|
|
628
|
+
return provider;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
logger?.warn(
|
|
632
|
+
`Provider is not supported or failed to initialize: ${aiConfig.provider?.name ?? "unknown"}`
|
|
633
|
+
);
|
|
634
|
+
return void 0;
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* Determine which providers to try based on defaultAiProvider and providerName.
|
|
638
|
+
*/
|
|
639
|
+
static _getProvidersToTry(defaultAiProvider, providerName) {
|
|
640
|
+
if (defaultAiProvider) {
|
|
641
|
+
return [defaultAiProvider];
|
|
642
|
+
}
|
|
643
|
+
const providerSet = /* @__PURE__ */ new Set();
|
|
644
|
+
if (providerName && SUPPORTED_AI_PROVIDERS.includes(providerName)) {
|
|
645
|
+
providerSet.add(providerName);
|
|
646
|
+
}
|
|
647
|
+
const multiProviderPackages = ["langchain", "vercel"];
|
|
648
|
+
multiProviderPackages.forEach((provider) => {
|
|
649
|
+
providerSet.add(provider);
|
|
650
|
+
});
|
|
651
|
+
return Array.from(providerSet);
|
|
652
|
+
}
|
|
653
|
+
/**
|
|
654
|
+
* Try to create a provider of the specified type.
|
|
655
|
+
*/
|
|
656
|
+
static async _tryCreateProvider(providerType, aiConfig, logger) {
|
|
657
|
+
try {
|
|
658
|
+
let module2;
|
|
659
|
+
switch (providerType) {
|
|
660
|
+
case "openai": {
|
|
661
|
+
module2 = await import("@launchdarkly/server-sdk-ai-openai");
|
|
662
|
+
const provider = await module2.OpenAIProvider.create(aiConfig, logger);
|
|
663
|
+
return provider;
|
|
664
|
+
}
|
|
665
|
+
case "langchain": {
|
|
666
|
+
module2 = await import("@launchdarkly/server-sdk-ai-langchain");
|
|
667
|
+
const provider = await module2.LangChainProvider.create(aiConfig, logger);
|
|
668
|
+
return provider;
|
|
669
|
+
}
|
|
670
|
+
case "vercel": {
|
|
671
|
+
module2 = await import("@launchdarkly/server-sdk-ai-vercel");
|
|
672
|
+
const provider = await module2.VercelProvider.create(aiConfig, logger);
|
|
673
|
+
return provider;
|
|
674
|
+
}
|
|
675
|
+
default:
|
|
676
|
+
return void 0;
|
|
677
|
+
}
|
|
678
|
+
} catch (error) {
|
|
679
|
+
logger?.warn(
|
|
680
|
+
`Unable to create AIProvider. Check that you have installed the correct package. ${error.message}`
|
|
681
|
+
);
|
|
682
|
+
return void 0;
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
};
|
|
686
|
+
|
|
687
|
+
// src/api/metrics/BedrockTokenUsage.ts
|
|
688
|
+
function createBedrockTokenUsage(data) {
|
|
689
|
+
return {
|
|
690
|
+
total: data.totalTokens || 0,
|
|
691
|
+
input: data.inputTokens || 0,
|
|
692
|
+
output: data.outputTokens || 0
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
// src/api/metrics/OpenAiUsage.ts
|
|
697
|
+
function createOpenAiUsage(data) {
|
|
698
|
+
return {
|
|
699
|
+
total: data.total_tokens ?? 0,
|
|
700
|
+
input: data.prompt_tokens ?? 0,
|
|
701
|
+
output: data.completion_tokens ?? 0
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
// src/api/metrics/LDFeedbackKind.ts
|
|
706
|
+
var LDFeedbackKind = /* @__PURE__ */ ((LDFeedbackKind2) => {
|
|
707
|
+
LDFeedbackKind2["Positive"] = "positive";
|
|
708
|
+
LDFeedbackKind2["Negative"] = "negative";
|
|
709
|
+
return LDFeedbackKind2;
|
|
710
|
+
})(LDFeedbackKind || {});
|
|
711
|
+
|
|
712
|
+
// src/api/metrics/VercelAISDKTokenUsage.ts
|
|
713
|
+
function createVercelAISDKTokenUsage(data) {
|
|
714
|
+
return {
|
|
715
|
+
total: data.totalTokens ?? 0,
|
|
716
|
+
input: data.inputTokens ?? data.promptTokens ?? 0,
|
|
717
|
+
output: data.outputTokens ?? data.completionTokens ?? 0
|
|
718
|
+
};
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
// src/sdkInfo.ts
|
|
722
|
+
var aiSdkName = "@launchdarkly/server-sdk-ai";
|
|
723
|
+
var aiSdkVersion = "0.15.1";
|
|
724
|
+
|
|
725
|
+
// src/LDAIConfigTrackerImpl.ts
|
|
726
|
+
var LDAIConfigTrackerImpl = class {
|
|
727
|
+
constructor(_ldClient, _configKey, _variationKey, _version, _modelName, _providerName, _context) {
|
|
728
|
+
this._ldClient = _ldClient;
|
|
729
|
+
this._configKey = _configKey;
|
|
730
|
+
this._variationKey = _variationKey;
|
|
731
|
+
this._version = _version;
|
|
732
|
+
this._modelName = _modelName;
|
|
733
|
+
this._providerName = _providerName;
|
|
734
|
+
this._context = _context;
|
|
735
|
+
this._trackedMetrics = {};
|
|
736
|
+
}
|
|
737
|
+
getTrackData() {
|
|
738
|
+
return {
|
|
739
|
+
variationKey: this._variationKey,
|
|
740
|
+
configKey: this._configKey,
|
|
741
|
+
version: this._version,
|
|
742
|
+
modelName: this._modelName,
|
|
743
|
+
providerName: this._providerName,
|
|
744
|
+
aiSdkName,
|
|
745
|
+
aiSdkVersion
|
|
746
|
+
};
|
|
747
|
+
}
|
|
748
|
+
trackDuration(duration) {
|
|
749
|
+
this._trackedMetrics.durationMs = duration;
|
|
750
|
+
this._ldClient.track("$ld:ai:duration:total", this._context, this.getTrackData(), duration);
|
|
751
|
+
}
|
|
752
|
+
async trackDurationOf(func) {
|
|
753
|
+
const startTime = Date.now();
|
|
754
|
+
try {
|
|
755
|
+
const result = await func();
|
|
756
|
+
return result;
|
|
757
|
+
} finally {
|
|
758
|
+
const endTime = Date.now();
|
|
759
|
+
const duration = endTime - startTime;
|
|
760
|
+
this.trackDuration(duration);
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
trackTimeToFirstToken(timeToFirstTokenMs) {
|
|
764
|
+
this._trackedMetrics.timeToFirstTokenMs = timeToFirstTokenMs;
|
|
765
|
+
this._ldClient.track(
|
|
766
|
+
"$ld:ai:tokens:ttf",
|
|
767
|
+
this._context,
|
|
768
|
+
this.getTrackData(),
|
|
769
|
+
timeToFirstTokenMs
|
|
770
|
+
);
|
|
771
|
+
}
|
|
772
|
+
trackEvalScores(scores) {
|
|
773
|
+
Object.entries(scores).forEach(([metricKey, evalScore]) => {
|
|
774
|
+
this._ldClient.track(metricKey, this._context, this.getTrackData(), evalScore.score);
|
|
775
|
+
});
|
|
776
|
+
}
|
|
777
|
+
trackJudgeResponse(response) {
|
|
778
|
+
Object.entries(response.evals).forEach(([metricKey, evalScore]) => {
|
|
779
|
+
this._ldClient.track(
|
|
780
|
+
metricKey,
|
|
781
|
+
this._context,
|
|
782
|
+
{ ...this.getTrackData(), judgeConfigKey: response.judgeConfigKey },
|
|
783
|
+
evalScore.score
|
|
784
|
+
);
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
trackFeedback(feedback) {
|
|
788
|
+
this._trackedMetrics.feedback = feedback;
|
|
789
|
+
if (feedback.kind === "positive" /* Positive */) {
|
|
790
|
+
this._ldClient.track("$ld:ai:feedback:user:positive", this._context, this.getTrackData(), 1);
|
|
791
|
+
} else if (feedback.kind === "negative" /* Negative */) {
|
|
792
|
+
this._ldClient.track("$ld:ai:feedback:user:negative", this._context, this.getTrackData(), 1);
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
trackSuccess() {
|
|
796
|
+
this._trackedMetrics.success = true;
|
|
797
|
+
this._ldClient.track("$ld:ai:generation:success", this._context, this.getTrackData(), 1);
|
|
798
|
+
}
|
|
799
|
+
trackError() {
|
|
800
|
+
this._trackedMetrics.success = false;
|
|
801
|
+
this._ldClient.track("$ld:ai:generation:error", this._context, this.getTrackData(), 1);
|
|
802
|
+
}
|
|
803
|
+
async trackMetricsOf(metricsExtractor, func) {
|
|
804
|
+
let result;
|
|
805
|
+
try {
|
|
806
|
+
result = await this.trackDurationOf(func);
|
|
807
|
+
} catch (err) {
|
|
808
|
+
this.trackError();
|
|
809
|
+
throw err;
|
|
810
|
+
}
|
|
811
|
+
const metrics = metricsExtractor(result);
|
|
812
|
+
if (metrics.success) {
|
|
813
|
+
this.trackSuccess();
|
|
814
|
+
} else {
|
|
815
|
+
this.trackError();
|
|
816
|
+
}
|
|
817
|
+
if (metrics.usage) {
|
|
818
|
+
this.trackTokens(metrics.usage);
|
|
819
|
+
}
|
|
820
|
+
return result;
|
|
821
|
+
}
|
|
822
|
+
trackStreamMetricsOf(streamCreator, metricsExtractor) {
|
|
823
|
+
const startTime = Date.now();
|
|
824
|
+
try {
|
|
825
|
+
const stream = streamCreator();
|
|
826
|
+
this._trackStreamMetricsInBackground(stream, metricsExtractor, startTime);
|
|
827
|
+
return stream;
|
|
828
|
+
} catch (error) {
|
|
829
|
+
this.trackDuration(Date.now() - startTime);
|
|
830
|
+
this.trackError();
|
|
831
|
+
throw error;
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
async _trackStreamMetricsInBackground(stream, metricsExtractor, startTime) {
|
|
835
|
+
try {
|
|
836
|
+
const metrics = await metricsExtractor(stream);
|
|
837
|
+
if (metrics.success) {
|
|
838
|
+
this.trackSuccess();
|
|
839
|
+
} else {
|
|
840
|
+
this.trackError();
|
|
841
|
+
}
|
|
842
|
+
if (metrics.usage) {
|
|
843
|
+
this.trackTokens(metrics.usage);
|
|
844
|
+
}
|
|
845
|
+
} catch (error) {
|
|
846
|
+
this.trackError();
|
|
847
|
+
} finally {
|
|
848
|
+
this.trackDuration(Date.now() - startTime);
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
async trackOpenAIMetrics(func) {
|
|
852
|
+
try {
|
|
853
|
+
const result = await this.trackDurationOf(func);
|
|
854
|
+
this.trackSuccess();
|
|
855
|
+
if (result.usage) {
|
|
856
|
+
this.trackTokens(createOpenAiUsage(result.usage));
|
|
857
|
+
}
|
|
858
|
+
return result;
|
|
859
|
+
} catch (err) {
|
|
860
|
+
this.trackError();
|
|
861
|
+
throw err;
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
trackBedrockConverseMetrics(res) {
|
|
865
|
+
if (res.$metadata?.httpStatusCode === 200) {
|
|
866
|
+
this.trackSuccess();
|
|
867
|
+
} else if (res.$metadata?.httpStatusCode && res.$metadata.httpStatusCode >= 400) {
|
|
868
|
+
this.trackError();
|
|
869
|
+
}
|
|
870
|
+
if (res.metrics && res.metrics.latencyMs) {
|
|
871
|
+
this.trackDuration(res.metrics.latencyMs);
|
|
872
|
+
}
|
|
873
|
+
if (res.usage) {
|
|
874
|
+
this.trackTokens(createBedrockTokenUsage(res.usage));
|
|
875
|
+
}
|
|
876
|
+
return res;
|
|
877
|
+
}
|
|
878
|
+
async trackVercelAISDKGenerateTextMetrics(func) {
|
|
879
|
+
try {
|
|
880
|
+
const result = await this.trackDurationOf(func);
|
|
881
|
+
this.trackSuccess();
|
|
882
|
+
if (result.usage) {
|
|
883
|
+
this.trackTokens(createVercelAISDKTokenUsage(result.usage));
|
|
884
|
+
}
|
|
885
|
+
return result;
|
|
886
|
+
} catch (err) {
|
|
887
|
+
this.trackError();
|
|
888
|
+
throw err;
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
trackTokens(tokens) {
|
|
892
|
+
this._trackedMetrics.tokens = tokens;
|
|
893
|
+
const trackData = this.getTrackData();
|
|
894
|
+
if (tokens.total > 0) {
|
|
895
|
+
this._ldClient.track("$ld:ai:tokens:total", this._context, trackData, tokens.total);
|
|
896
|
+
}
|
|
897
|
+
if (tokens.input > 0) {
|
|
898
|
+
this._ldClient.track("$ld:ai:tokens:input", this._context, trackData, tokens.input);
|
|
899
|
+
}
|
|
900
|
+
if (tokens.output > 0) {
|
|
901
|
+
this._ldClient.track("$ld:ai:tokens:output", this._context, trackData, tokens.output);
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
/**
|
|
905
|
+
* Get a summary of the tracked metrics.
|
|
906
|
+
*/
|
|
907
|
+
getSummary() {
|
|
908
|
+
return { ...this._trackedMetrics };
|
|
909
|
+
}
|
|
910
|
+
};
|
|
911
|
+
|
|
912
|
+
// src/LDAIClientImpl.ts
|
|
913
|
+
var TRACK_CONFIG_SINGLE = "$ld:ai:config:function:single";
|
|
914
|
+
var TRACK_CONFIG_CREATE_CHAT = "$ld:ai:config:function:createChat";
|
|
915
|
+
var TRACK_JUDGE_SINGLE = "$ld:ai:judge:function:single";
|
|
916
|
+
var TRACK_JUDGE_CREATE = "$ld:ai:judge:function:createJudge";
|
|
917
|
+
var TRACK_AGENT_SINGLE = "$ld:ai:agent:function:single";
|
|
918
|
+
var TRACK_AGENT_MULTIPLE = "$ld:ai:agent:function:multiple";
|
|
919
|
+
var LDAIClientImpl = class {
|
|
920
|
+
constructor(_ldClient) {
|
|
921
|
+
this._ldClient = _ldClient;
|
|
922
|
+
this._logger = _ldClient.logger;
|
|
923
|
+
}
|
|
924
|
+
_interpolateTemplate(template, variables) {
|
|
925
|
+
return import_mustache2.default.render(template, variables, void 0, { escape: (item) => item });
|
|
926
|
+
}
|
|
927
|
+
async _evaluate(key, context, defaultValue, mode, variables) {
|
|
928
|
+
const ldFlagValue = LDAIConfigUtils.toFlagValue(defaultValue, mode);
|
|
929
|
+
const value = await this._ldClient.variation(key, context, ldFlagValue);
|
|
930
|
+
const flagMode = value._ldMeta?.mode ?? "completion";
|
|
931
|
+
if (flagMode !== mode) {
|
|
932
|
+
this._logger?.warn(
|
|
933
|
+
`AI Config mode mismatch for ${key}: expected ${mode}, got ${flagMode}. Returning disabled config.`
|
|
934
|
+
);
|
|
935
|
+
return LDAIConfigUtils.createDisabledConfig(key, mode);
|
|
936
|
+
}
|
|
937
|
+
const tracker = new LDAIConfigTrackerImpl(
|
|
938
|
+
this._ldClient,
|
|
939
|
+
key,
|
|
940
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
941
|
+
value._ldMeta?.variationKey ?? "",
|
|
942
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
943
|
+
value._ldMeta?.version ?? 1,
|
|
944
|
+
value.model?.name ?? "",
|
|
945
|
+
value.provider?.name ?? "",
|
|
946
|
+
context
|
|
947
|
+
);
|
|
948
|
+
const config = LDAIConfigUtils.fromFlagValue(key, value, tracker);
|
|
949
|
+
return this._applyInterpolation(config, context, variables);
|
|
950
|
+
}
|
|
951
|
+
_applyInterpolation(config, context, variables) {
|
|
952
|
+
const allVariables = { ...variables, ldctx: context };
|
|
953
|
+
if ("messages" in config && config.messages) {
|
|
954
|
+
return {
|
|
955
|
+
...config,
|
|
956
|
+
messages: config.messages.map((entry) => ({
|
|
957
|
+
...entry,
|
|
958
|
+
content: this._interpolateTemplate(entry.content, allVariables)
|
|
959
|
+
}))
|
|
960
|
+
};
|
|
961
|
+
}
|
|
962
|
+
if ("instructions" in config && config.instructions) {
|
|
963
|
+
return {
|
|
964
|
+
...config,
|
|
965
|
+
instructions: this._interpolateTemplate(config.instructions, allVariables)
|
|
966
|
+
};
|
|
967
|
+
}
|
|
968
|
+
return config;
|
|
969
|
+
}
|
|
970
|
+
async _initializeJudges(judgeConfigs, context, variables, defaultAiProvider) {
|
|
971
|
+
const judges = {};
|
|
972
|
+
const judgePromises = judgeConfigs.map(async (judgeConfig) => {
|
|
973
|
+
const judge = await this.createJudge(
|
|
974
|
+
judgeConfig.key,
|
|
975
|
+
context,
|
|
976
|
+
{ enabled: false },
|
|
977
|
+
variables,
|
|
978
|
+
defaultAiProvider
|
|
979
|
+
);
|
|
980
|
+
return judge ? { key: judgeConfig.key, judge } : null;
|
|
981
|
+
});
|
|
982
|
+
const results = await Promise.all(judgePromises);
|
|
983
|
+
results.forEach((result) => {
|
|
984
|
+
if (result) {
|
|
985
|
+
judges[result.key] = result.judge;
|
|
986
|
+
}
|
|
987
|
+
});
|
|
988
|
+
return judges;
|
|
989
|
+
}
|
|
990
|
+
async completionConfig(key, context, defaultValue, variables) {
|
|
991
|
+
this._ldClient.track(TRACK_CONFIG_SINGLE, context, key, 1);
|
|
992
|
+
const config = await this._evaluate(key, context, defaultValue, "completion", variables);
|
|
993
|
+
return config;
|
|
994
|
+
}
|
|
995
|
+
/**
|
|
996
|
+
* @deprecated Use `completionConfig` instead. This method will be removed in a future version.
|
|
997
|
+
*/
|
|
998
|
+
async config(key, context, defaultValue, variables) {
|
|
999
|
+
return this.completionConfig(key, context, defaultValue, variables);
|
|
1000
|
+
}
|
|
1001
|
+
async judgeConfig(key, context, defaultValue, variables) {
|
|
1002
|
+
this._ldClient.track(TRACK_JUDGE_SINGLE, context, key, 1);
|
|
1003
|
+
const config = await this._evaluate(key, context, defaultValue, "judge", variables);
|
|
1004
|
+
return config;
|
|
1005
|
+
}
|
|
1006
|
+
async agentConfig(key, context, defaultValue, variables) {
|
|
1007
|
+
this._ldClient.track(TRACK_AGENT_SINGLE, context, key, 1);
|
|
1008
|
+
const config = await this._evaluate(key, context, defaultValue, "agent", variables);
|
|
1009
|
+
return config;
|
|
1010
|
+
}
|
|
1011
|
+
/**
|
|
1012
|
+
* @deprecated Use `agentConfig` instead. This method will be removed in a future version.
|
|
1013
|
+
*/
|
|
1014
|
+
async agent(key, context, defaultValue, variables) {
|
|
1015
|
+
return this.agentConfig(key, context, defaultValue, variables);
|
|
1016
|
+
}
|
|
1017
|
+
async agentConfigs(agentConfigs, context) {
|
|
1018
|
+
this._ldClient.track(TRACK_AGENT_MULTIPLE, context, agentConfigs.length, agentConfigs.length);
|
|
1019
|
+
const agents = {};
|
|
1020
|
+
await Promise.all(
|
|
1021
|
+
agentConfigs.map(async (config) => {
|
|
1022
|
+
const agent = await this._evaluate(
|
|
1023
|
+
config.key,
|
|
1024
|
+
context,
|
|
1025
|
+
config.defaultValue,
|
|
1026
|
+
"agent",
|
|
1027
|
+
config.variables
|
|
1028
|
+
);
|
|
1029
|
+
agents[config.key] = agent;
|
|
1030
|
+
})
|
|
1031
|
+
);
|
|
1032
|
+
return agents;
|
|
1033
|
+
}
|
|
1034
|
+
/**
|
|
1035
|
+
* @deprecated Use `agentConfigs` instead. This method will be removed in a future version.
|
|
1036
|
+
*/
|
|
1037
|
+
async agents(agentConfigs, context) {
|
|
1038
|
+
return this.agentConfigs(agentConfigs, context);
|
|
1039
|
+
}
|
|
1040
|
+
async createChat(key, context, defaultValue, variables, defaultAiProvider) {
|
|
1041
|
+
this._ldClient.track(TRACK_CONFIG_CREATE_CHAT, context, key, 1);
|
|
1042
|
+
const config = await this.completionConfig(key, context, defaultValue, variables);
|
|
1043
|
+
if (!config.enabled || !config.tracker) {
|
|
1044
|
+
this._logger?.info(`Chat configuration is disabled: ${key}`);
|
|
1045
|
+
return void 0;
|
|
1046
|
+
}
|
|
1047
|
+
const provider = await AIProviderFactory.create(config, this._logger, defaultAiProvider);
|
|
1048
|
+
if (!provider) {
|
|
1049
|
+
return void 0;
|
|
1050
|
+
}
|
|
1051
|
+
const judges = await this._initializeJudges(
|
|
1052
|
+
config.judgeConfiguration?.judges ?? [],
|
|
1053
|
+
context,
|
|
1054
|
+
variables,
|
|
1055
|
+
defaultAiProvider
|
|
1056
|
+
);
|
|
1057
|
+
return new TrackedChat(config, config.tracker, provider, judges, this._logger);
|
|
1058
|
+
}
|
|
1059
|
+
async createJudge(key, context, defaultValue, variables, defaultAiProvider) {
|
|
1060
|
+
this._ldClient.track(TRACK_JUDGE_CREATE, context, key, 1);
|
|
1061
|
+
try {
|
|
1062
|
+
if (variables?.message_history !== void 0) {
|
|
1063
|
+
this._logger?.warn(
|
|
1064
|
+
"The variable 'message_history' is reserved by the judge and will be ignored."
|
|
1065
|
+
);
|
|
1066
|
+
}
|
|
1067
|
+
if (variables?.response_to_evaluate !== void 0) {
|
|
1068
|
+
this._logger?.warn(
|
|
1069
|
+
"The variable 'response_to_evaluate' is reserved by the judge and will be ignored."
|
|
1070
|
+
);
|
|
1071
|
+
}
|
|
1072
|
+
const extendedVariables = {
|
|
1073
|
+
...variables,
|
|
1074
|
+
message_history: "{{message_history}}",
|
|
1075
|
+
response_to_evaluate: "{{response_to_evaluate}}"
|
|
1076
|
+
};
|
|
1077
|
+
const judgeConfig = await this.judgeConfig(key, context, defaultValue, extendedVariables);
|
|
1078
|
+
if (!judgeConfig.enabled || !judgeConfig.tracker) {
|
|
1079
|
+
this._logger?.info(`Judge configuration is disabled: ${key}`);
|
|
1080
|
+
return void 0;
|
|
1081
|
+
}
|
|
1082
|
+
const provider = await AIProviderFactory.create(judgeConfig, this._logger, defaultAiProvider);
|
|
1083
|
+
if (!provider) {
|
|
1084
|
+
return void 0;
|
|
1085
|
+
}
|
|
1086
|
+
return new Judge(judgeConfig, judgeConfig.tracker, provider, this._logger);
|
|
1087
|
+
} catch (error) {
|
|
1088
|
+
this._logger?.error(`Failed to initialize judge ${key}:`, error);
|
|
1089
|
+
return void 0;
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
/**
|
|
1093
|
+
* @deprecated Use `createChat` instead. This method will be removed in a future version.
|
|
1094
|
+
*/
|
|
1095
|
+
async initChat(key, context, defaultValue, variables, defaultAiProvider) {
|
|
1096
|
+
return this.createChat(key, context, defaultValue, variables, defaultAiProvider);
|
|
1097
|
+
}
|
|
1098
|
+
};
|
|
1099
|
+
|
|
1100
|
+
// src/index.ts
|
|
1101
|
+
function initAi(ldClient) {
|
|
1102
|
+
return new LDAIClientImpl(ldClient);
|
|
1103
|
+
}
|
|
1104
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1105
|
+
0 && (module.exports = {
|
|
1106
|
+
AIProvider,
|
|
1107
|
+
AIProviderFactory,
|
|
1108
|
+
Judge,
|
|
1109
|
+
LDFeedbackKind,
|
|
1110
|
+
SUPPORTED_AI_PROVIDERS,
|
|
1111
|
+
TrackedChat,
|
|
1112
|
+
createBedrockTokenUsage,
|
|
1113
|
+
createOpenAiUsage,
|
|
1114
|
+
createVercelAISDKTokenUsage,
|
|
1115
|
+
initAi
|
|
1116
|
+
});
|
|
1117
|
+
//# sourceMappingURL=index.cjs.map
|