Skip to content

Commit 098e319

Browse files
committed
Merge branch 'ld-main' into feat/svelte-sdk-example
2 parents 18e8141 + 220b6d6 commit 098e319

File tree

11 files changed

+144
-24
lines changed

11 files changed

+144
-24
lines changed

.release-please-manifest.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@
1414
"packages/sdk/react-native": "10.9.3",
1515
"packages/telemetry/node-server-sdk-otel": "1.1.2",
1616
"packages/sdk/browser": "0.3.3",
17-
"packages/sdk/server-ai": "0.6.0"
17+
"packages/sdk/server-ai": "0.7.0"
1818
}

packages/sdk/server-ai/CHANGELOG.md

+7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# Changelog
22

3+
## [0.7.0](https://github.com/launchdarkly/js-core/compare/server-sdk-ai-v0.6.0...server-sdk-ai-v0.7.0) (2024-12-17)
4+
5+
6+
### Features
7+
8+
* Add support for tracking errors. ([#715](https://github.com/launchdarkly/js-core/issues/715)) ([02f1d3d](https://github.com/launchdarkly/js-core/commit/02f1d3daa711319a620a55b50481083980ab18f7))
9+
310
## [0.6.0](https://github.com/launchdarkly/js-core/compare/server-sdk-ai-v0.5.0...server-sdk-ai-v0.6.0) (2024-12-10)
411

512

packages/sdk/server-ai/__tests__/LDAIConfigTrackerImpl.test.ts

+85-3
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,39 @@ it('tracks OpenAI usage', async () => {
134134
);
135135
});
136136

137+
it('tracks error when OpenAI metrics function throws', async () => {
138+
const tracker = new LDAIConfigTrackerImpl(mockLdClient, configKey, variationKey, testContext);
139+
jest.spyOn(global.Date, 'now').mockReturnValueOnce(1000).mockReturnValueOnce(2000);
140+
141+
const error = new Error('OpenAI API error');
142+
await expect(
143+
tracker.trackOpenAIMetrics(async () => {
144+
throw error;
145+
}),
146+
).rejects.toThrow(error);
147+
148+
expect(mockTrack).toHaveBeenCalledWith(
149+
'$ld:ai:duration:total',
150+
testContext,
151+
{ configKey, variationKey },
152+
1000,
153+
);
154+
155+
expect(mockTrack).toHaveBeenCalledWith(
156+
'$ld:ai:generation',
157+
testContext,
158+
{ configKey, variationKey },
159+
1,
160+
);
161+
162+
expect(mockTrack).toHaveBeenCalledWith(
163+
'$ld:ai:generation:error',
164+
testContext,
165+
{ configKey, variationKey },
166+
1,
167+
);
168+
});
169+
137170
it('tracks Bedrock conversation with successful response', () => {
138171
const tracker = new LDAIConfigTrackerImpl(mockLdClient, configKey, variationKey, testContext);
139172

@@ -196,11 +229,22 @@ it('tracks Bedrock conversation with error response', () => {
196229
$metadata: { httpStatusCode: 400 },
197230
};
198231

199-
// TODO: We may want a track failure.
200-
201232
tracker.trackBedrockConverseMetrics(response);
202233

203-
expect(mockTrack).not.toHaveBeenCalled();
234+
expect(mockTrack).toHaveBeenCalledTimes(2);
235+
expect(mockTrack).toHaveBeenCalledWith(
236+
'$ld:ai:generation',
237+
testContext,
238+
{ configKey, variationKey },
239+
1,
240+
);
241+
242+
expect(mockTrack).toHaveBeenCalledWith(
243+
'$ld:ai:generation:error',
244+
testContext,
245+
{ configKey, variationKey },
246+
1,
247+
);
204248
});
205249

206250
it('tracks tokens', () => {
@@ -304,3 +348,41 @@ it('summarizes tracked metrics', () => {
304348
success: true,
305349
});
306350
});
351+
352+
it('tracks duration when async function throws', async () => {
353+
const tracker = new LDAIConfigTrackerImpl(mockLdClient, configKey, variationKey, testContext);
354+
jest.spyOn(global.Date, 'now').mockReturnValueOnce(1000).mockReturnValueOnce(2000);
355+
356+
const error = new Error('test error');
357+
await expect(
358+
tracker.trackDurationOf(async () => {
359+
throw error;
360+
}),
361+
).rejects.toThrow(error);
362+
363+
expect(mockTrack).toHaveBeenCalledWith(
364+
'$ld:ai:duration:total',
365+
testContext,
366+
{ configKey, variationKey },
367+
1000,
368+
);
369+
});
370+
371+
it('tracks error', () => {
372+
const tracker = new LDAIConfigTrackerImpl(mockLdClient, configKey, variationKey, testContext);
373+
tracker.trackError();
374+
375+
expect(mockTrack).toHaveBeenCalledWith(
376+
'$ld:ai:generation',
377+
testContext,
378+
{ configKey, variationKey },
379+
1,
380+
);
381+
382+
expect(mockTrack).toHaveBeenCalledWith(
383+
'$ld:ai:generation:error',
384+
testContext,
385+
{ configKey, variationKey },
386+
1,
387+
);
388+
});

packages/sdk/server-ai/examples/bedrock/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
"dependencies": {
2525
"@aws-sdk/client-bedrock-runtime": "^3.679.0",
2626
"@launchdarkly/node-server-sdk": "^9.7.1",
27-
"@launchdarkly/server-sdk-ai": "0.6.0"
27+
"@launchdarkly/server-sdk-ai": "0.7.0"
2828
},
2929
"devDependencies": {
3030
"@trivago/prettier-plugin-sort-imports": "^4.1.1",

packages/sdk/server-ai/examples/openai/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
"license": "Apache-2.0",
2323
"dependencies": {
2424
"@launchdarkly/node-server-sdk": "^9.7.1",
25-
"@launchdarkly/server-sdk-ai": "0.6.0",
25+
"@launchdarkly/server-sdk-ai": "0.7.0",
2626
"openai": "^4.58.1"
2727
},
2828
"devDependencies": {

packages/sdk/server-ai/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@launchdarkly/server-sdk-ai",
3-
"version": "0.6.0",
3+
"version": "0.7.0",
44
"description": "LaunchDarkly AI SDK for Server-Side JavaScript",
55
"homepage": "https://github.com/launchdarkly/js-core/tree/main/packages/sdk/server-ai",
66
"repository": {

packages/sdk/server-ai/src/LDAIConfigTrackerImpl.ts

+27-11
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,15 @@ export class LDAIConfigTrackerImpl implements LDAIConfigTracker {
3030

3131
async trackDurationOf<TRes>(func: () => Promise<TRes>): Promise<TRes> {
3232
const startTime = Date.now();
33-
const result = await func();
34-
const endTime = Date.now();
35-
const duration = endTime - startTime; // duration in milliseconds
36-
this.trackDuration(duration);
37-
return result;
33+
try {
34+
// Be sure to await here so that we can track the duration of the function and also handle errors.
35+
const result = await func();
36+
return result;
37+
} finally {
38+
const endTime = Date.now();
39+
const duration = endTime - startTime; // duration in milliseconds
40+
this.trackDuration(duration);
41+
}
3842
}
3943

4044
trackFeedback(feedback: { kind: LDFeedbackKind }): void {
@@ -49,6 +53,13 @@ export class LDAIConfigTrackerImpl implements LDAIConfigTracker {
4953
trackSuccess(): void {
5054
this._trackedMetrics.success = true;
5155
this._ldClient.track('$ld:ai:generation', this._context, this._getTrackData(), 1);
56+
this._ldClient.track('$ld:ai:generation:success', this._context, this._getTrackData(), 1);
57+
}
58+
59+
trackError(): void {
60+
this._trackedMetrics.success = false;
61+
this._ldClient.track('$ld:ai:generation', this._context, this._getTrackData(), 1);
62+
this._ldClient.track('$ld:ai:generation:error', this._context, this._getTrackData(), 1);
5263
}
5364

5465
async trackOpenAIMetrics<
@@ -60,12 +71,17 @@ export class LDAIConfigTrackerImpl implements LDAIConfigTracker {
6071
};
6172
},
6273
>(func: () => Promise<TRes>): Promise<TRes> {
63-
const result = await this.trackDurationOf(func);
64-
this.trackSuccess();
65-
if (result.usage) {
66-
this.trackTokens(createOpenAiUsage(result.usage));
74+
try {
75+
const result = await this.trackDurationOf(func);
76+
this.trackSuccess();
77+
if (result.usage) {
78+
this.trackTokens(createOpenAiUsage(result.usage));
79+
}
80+
return result;
81+
} catch (err) {
82+
this.trackError();
83+
throw err;
6784
}
68-
return result;
6985
}
7086

7187
trackBedrockConverseMetrics<
@@ -82,7 +98,7 @@ export class LDAIConfigTrackerImpl implements LDAIConfigTracker {
8298
if (res.$metadata?.httpStatusCode === 200) {
8399
this.trackSuccess();
84100
} else if (res.$metadata?.httpStatusCode && res.$metadata.httpStatusCode >= 400) {
85-
// Potentially add error tracking in the future.
101+
this.trackError();
86102
}
87103
if (res.metrics && res.metrics.latencyMs) {
88104
this.trackDuration(res.metrics.latencyMs);

packages/sdk/server-ai/src/api/config/LDAIConfigTracker.ts

+19
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ export interface LDAIConfigTracker {
5050
*/
5151
trackSuccess(): void;
5252

53+
/**
54+
* An error was encountered during generation.
55+
*/
56+
trackError(): void;
57+
5358
/**
5459
* Track sentiment about the generation.
5560
*
@@ -59,6 +64,12 @@ export interface LDAIConfigTracker {
5964

6065
/**
6166
* Track the duration of execution of the provided function.
67+
*
68+
* If the provided function throws, then this method will also throw.
69+
* In the case the provided function throws, this function will still record the duration.
70+
*
71+
* This function does not automatically record an error when the function throws.
72+
*
6273
* @param func The function to track the duration of.
6374
* @returns The result of the function.
6475
*/
@@ -67,6 +78,12 @@ export interface LDAIConfigTracker {
6778
/**
6879
* Track an OpenAI operation.
6980
*
81+
* This function will track the duration of the operation, the token usage, and the success or error status.
82+
*
83+
* If the provided function throws, then this method will also throw.
84+
* In the case the provided function throws, this function will record the duration and an error.
85+
* A failed operation will not have any token usage data.
86+
*
7087
* @param func Function which executes the operation.
7188
* @returns The result of the operation.
7289
*/
@@ -85,6 +102,8 @@ export interface LDAIConfigTracker {
85102
/**
86103
* Track an operation which uses Bedrock.
87104
*
105+
* This function will track the duration of the operation, the token usage, and the success or error status.
106+
*
88107
* @param res The result of the Bedrock operation.
89108
* @returns The input operation.
90109
*/

packages/sdk/server-node/src/platform/NodeRequests.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ function processProxyOptions(
5757
};
5858
if (proxyOptions.auth) {
5959
parsedOptions.headers = {
60-
'Proxy-Authorization': `Basic ${Buffer.from(proxyOptions.auth).toString('base64')}}`,
60+
'Proxy-Authorization': `Basic ${Buffer.from(proxyOptions.auth).toString('base64')}`,
6161
};
6262
}
6363

packages/sdk/svelte/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@launchdarkly/svelte-client-sdk",
3-
"version": "1.0.0",
3+
"version": "0.1.0",
44
"description": "Svelte LaunchDarkly SDK",
55
"homepage": "https://github.com/launchdarkly/js-core/tree/main/packages/sdk/svelte",
66
"repository": {

packages/shared/common/src/api/context/LDUser.ts

-4
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,6 @@ export interface LDUser {
4141

4242
/**
4343
* The user's IP address.
44-
*
45-
* If you provide an IP, LaunchDarkly will use a geolocation service to
46-
* automatically infer a `country` for the user, unless you've already
47-
* specified one.
4844
*/
4945
ip?: string;
5046

0 commit comments

Comments
 (0)