Skip to content

Commit c89473a

Browse files
authored
Merge pull request #132 from anam-org/alpha
Release 3.1.0
2 parents 3d50edd + e361cb4 commit c89473a

16 files changed

+646
-117
lines changed

package-lock.json

Lines changed: 19 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
"webpack-cli": "^5.1.4"
6666
},
6767
"dependencies": {
68-
"buffer": "^6.0.3"
68+
"buffer": "^6.0.3",
69+
"nanoid": "^5.1.5"
6970
}
7071
}

src/AnamClient.ts

Lines changed: 62 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1+
import { nanoid } from 'nanoid';
2+
import { ClientError, ErrorCode } from './lib/ClientError';
13
import {
2-
ClientError,
4+
ClientMetricMeasurement,
35
DEFAULT_ANAM_API_VERSION,
46
DEFAULT_ANAM_METRICS_BASE_URL,
5-
ErrorCode,
6-
setErrorMetricsBaseUrl,
7-
setCurrentSessionInfo,
8-
} from './lib/ClientError';
7+
setClientMetricsBaseUrl,
8+
sendClientMetric,
9+
setMetricsContext,
10+
} from './lib/ClientMetrics';
911
import {
1012
CoreApiRestClient,
1113
InternalEventEmitter,
@@ -21,6 +23,7 @@ import {
2123
PersonaConfig,
2224
StartSessionOptions,
2325
StartSessionResponse,
26+
ConnectionClosedCode,
2427
} from './types';
2528
import { TalkMessageStream } from './types/TalkMessageStream';
2629
import { Buffer } from 'buffer';
@@ -64,7 +67,7 @@ export default class AnamClient {
6467
this.clientOptions = options;
6568

6669
if (options?.api?.baseUrl || options?.api?.apiVersion) {
67-
setErrorMetricsBaseUrl(
70+
setClientMetricsBaseUrl(
6871
options.api.baseUrl || DEFAULT_ANAM_METRICS_BASE_URL,
6972
options.api.apiVersion || DEFAULT_ANAM_API_VERSION,
7073
);
@@ -114,7 +117,9 @@ export default class AnamClient {
114117
if (sessionToken) {
115118
const decodedToken = this.decodeJwt(sessionToken);
116119
this.organizationId = decodedToken.accountId;
117-
setCurrentSessionInfo(this.sessionId, this.organizationId);
120+
setMetricsContext({
121+
organizationId: this.organizationId,
122+
});
118123

119124
const tokenType = decodedToken.type?.toLowerCase();
120125

@@ -187,6 +192,11 @@ export default class AnamClient {
187192
const { heartbeatIntervalSeconds, maxWsReconnectionAttempts, iceServers } =
188193
clientConfig;
189194

195+
this.sessionId = sessionId;
196+
setMetricsContext({
197+
sessionId: this.sessionId,
198+
});
199+
190200
try {
191201
this.streamingClient = new StreamingClient(
192202
sessionId,
@@ -212,11 +222,22 @@ export default class AnamClient {
212222
audioDeviceId: this.clientOptions?.audioDeviceId,
213223
disableInputAudio: this.clientOptions?.disableInputAudio,
214224
},
225+
metrics: {
226+
showPeerConnectionStatsReport:
227+
this.clientOptions?.metrics?.showPeerConnectionStatsReport ??
228+
false,
229+
peerConnectionStatsReportOutputFormat:
230+
this.clientOptions?.metrics
231+
?.peerConnectionStatsReportOutputFormat ?? 'console',
232+
},
215233
},
216234
this.publicEventEmitter,
217235
this.internalEventEmitter,
218236
);
219237
} catch (error) {
238+
setMetricsContext({
239+
sessionId: null,
240+
});
220241
throw new ClientError(
221242
'Failed to initialize streaming client',
222243
ErrorCode.CLIENT_ERROR_CODE_SERVER_ERROR,
@@ -228,8 +249,6 @@ export default class AnamClient {
228249
);
229250
}
230251

231-
this.sessionId = sessionId;
232-
setCurrentSessionInfo(this.sessionId, this.organizationId);
233252
return sessionId;
234253
}
235254

@@ -253,15 +272,26 @@ export default class AnamClient {
253272
public async stream(
254273
userProvidedAudioStream?: MediaStream,
255274
): Promise<MediaStream[]> {
275+
if (this._isStreaming) {
276+
throw new Error('Already streaming');
277+
}
278+
// generate a new ID here to track the attempt
279+
const attemptCorrelationId = nanoid();
280+
setMetricsContext({
281+
attemptCorrelationId,
282+
sessionId: null, // reset sessionId
283+
});
284+
sendClientMetric(
285+
ClientMetricMeasurement.CLIENT_METRIC_MEASUREMENT_SESSION_ATTEMPT,
286+
'1',
287+
);
256288
if (this.clientOptions?.disableInputAudio && userProvidedAudioStream) {
257289
console.warn(
258290
'AnamClient:Input audio is disabled. User provided audio stream will be ignored.',
259291
);
260292
}
261293
await this.startSessionIfNeeded(userProvidedAudioStream);
262-
if (this._isStreaming) {
263-
throw new Error('Already streaming');
264-
}
294+
265295
this._isStreaming = true;
266296
return new Promise<MediaStream[]>((resolve) => {
267297
// set stream callbacks to capture the stream
@@ -311,6 +341,16 @@ export default class AnamClient {
311341
videoElementId: string,
312342
userProvidedAudioStream?: MediaStream,
313343
): Promise<void> {
344+
// generate a new ID here to track the attempt
345+
const attemptCorrelationId = nanoid();
346+
setMetricsContext({
347+
attemptCorrelationId,
348+
sessionId: null, // reset sessionId
349+
});
350+
sendClientMetric(
351+
ClientMetricMeasurement.CLIENT_METRIC_MEASUREMENT_SESSION_ATTEMPT,
352+
'1',
353+
);
314354
if (this.clientOptions?.disableInputAudio && userProvidedAudioStream) {
315355
console.warn(
316356
'AnamClient:Input audio is disabled. User provided audio stream will be ignored.',
@@ -371,10 +411,18 @@ export default class AnamClient {
371411

372412
public async stopStreaming(): Promise<void> {
373413
if (this.streamingClient) {
374-
this.streamingClient.stopConnection();
414+
this.publicEventEmitter.emit(
415+
AnamEvent.CONNECTION_CLOSED,
416+
ConnectionClosedCode.NORMAL,
417+
);
418+
await this.streamingClient.stopConnection();
375419
this.streamingClient = null;
376420
this.sessionId = null;
377-
setCurrentSessionInfo(null, this.organizationId);
421+
setMetricsContext({
422+
attemptCorrelationId: null,
423+
sessionId: null,
424+
organizationId: this.organizationId,
425+
});
378426
this._isStreaming = false;
379427
}
380428
}

src/lib/ClientError.ts

Lines changed: 2 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { CLIENT_METADATA } from './constants';
1+
import { ClientMetricMeasurement, sendClientMetric } from './ClientMetrics';
22

33
export enum ErrorCode {
44
CLIENT_ERROR_CODE_USAGE_LIMIT_REACHED = 'CLIENT_ERROR_CODE_USAGE_LIMIT_REACHED',
@@ -12,75 +12,6 @@ export enum ErrorCode {
1212
CLIENT_ERROR_CODE_CONFIGURATION_ERROR = 'CLIENT_ERROR_CODE_CONFIGURATION_ERROR',
1313
}
1414

15-
export const DEFAULT_ANAM_METRICS_BASE_URL = 'https://api.anam.ai';
16-
export const DEFAULT_ANAM_API_VERSION = '/v1';
17-
18-
export enum ClientMetricMeasurement {
19-
CLIENT_METRIC_MEASUREMENT_ERROR = 'client_error',
20-
CLIENT_METRIC_MEASUREMENT_CONNECTION_CLOSED = 'client_connection_closed',
21-
CLIENT_METRIC_MEASUREMENT_CONNECTION_ESTABLISHED = 'client_connection_established',
22-
}
23-
24-
let anamCurrentBaseUrl = DEFAULT_ANAM_METRICS_BASE_URL;
25-
let anamCurrentApiVersion = DEFAULT_ANAM_API_VERSION;
26-
27-
let currentSessionId: string | null = null;
28-
let currentOrganizationId: string | null = null;
29-
30-
export const setErrorMetricsBaseUrl = (
31-
baseUrl: string,
32-
apiVersion: string = DEFAULT_ANAM_API_VERSION,
33-
) => {
34-
anamCurrentBaseUrl = baseUrl;
35-
anamCurrentApiVersion = apiVersion;
36-
};
37-
38-
export const setCurrentSessionInfo = (
39-
sessionId: string | null,
40-
organizationId: string | null,
41-
) => {
42-
currentSessionId = sessionId;
43-
currentOrganizationId = organizationId;
44-
};
45-
46-
export const sendErrorMetric = async (
47-
name: string,
48-
value: string,
49-
tags?: Record<string, string | number>,
50-
) => {
51-
try {
52-
const metricTags: Record<string, string | number> = {
53-
...CLIENT_METADATA,
54-
...tags,
55-
};
56-
57-
// Add session and organization IDs if available
58-
if (currentSessionId) {
59-
metricTags.sessionId = currentSessionId;
60-
}
61-
if (currentOrganizationId) {
62-
metricTags.organizationId = currentOrganizationId;
63-
}
64-
65-
await fetch(
66-
`${anamCurrentBaseUrl}${anamCurrentApiVersion}/metrics/client`,
67-
{
68-
method: 'POST',
69-
headers: {
70-
'Content-Type': 'application/json',
71-
},
72-
body: JSON.stringify({
73-
name,
74-
value,
75-
tags: metricTags,
76-
}),
77-
},
78-
);
79-
} catch (error) {
80-
console.error('Failed to send error metric:', error);
81-
}
82-
};
83-
8415
export class ClientError extends Error {
8516
code: ErrorCode;
8617
statusCode: number;
@@ -101,7 +32,7 @@ export class ClientError extends Error {
10132
Object.setPrototypeOf(this, ClientError.prototype);
10233

10334
// Send error metric when error is created
104-
sendErrorMetric(
35+
sendClientMetric(
10536
ClientMetricMeasurement.CLIENT_METRIC_MEASUREMENT_ERROR,
10637
code,
10738
{

0 commit comments

Comments
 (0)