@decartai/sdk 0.0.67 → 0.1.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.
Files changed (45) hide show
  1. package/README.md +55 -5
  2. package/dist/index.d.ts +7 -5
  3. package/dist/index.js +43 -28
  4. package/dist/process/client.js +1 -3
  5. package/dist/process/request.js +1 -3
  6. package/dist/queue/client.js +1 -3
  7. package/dist/queue/polling.js +1 -2
  8. package/dist/queue/request.js +1 -3
  9. package/dist/realtime/client.d.ts +23 -13
  10. package/dist/realtime/client.js +74 -244
  11. package/dist/realtime/config-realtime.js +49 -0
  12. package/dist/realtime/event-buffer.js +1 -3
  13. package/dist/realtime/initial-state-gate.js +21 -0
  14. package/dist/realtime/media-channel.js +82 -0
  15. package/dist/realtime/methods.js +12 -42
  16. package/dist/realtime/mirror-stream.js +1 -2
  17. package/dist/realtime/observability/diagnostics.d.ts +39 -0
  18. package/dist/realtime/observability/livekit-stats-provider.js +25 -0
  19. package/dist/realtime/observability/realtime-observability.js +173 -0
  20. package/dist/realtime/{telemetry-reporter.js → observability/telemetry-reporter.js} +12 -31
  21. package/dist/realtime/observability/webrtc-stats.d.ts +148 -0
  22. package/dist/realtime/observability/webrtc-stats.js +276 -0
  23. package/dist/realtime/signaling-channel.js +286 -0
  24. package/dist/realtime/stream-session.js +252 -0
  25. package/dist/realtime/subscribe-client.d.ts +2 -1
  26. package/dist/realtime/subscribe-client.js +115 -11
  27. package/dist/realtime/types.d.ts +25 -1
  28. package/dist/shared/model.d.ts +11 -1
  29. package/dist/shared/model.js +51 -14
  30. package/dist/shared/request.js +1 -3
  31. package/dist/shared/types.js +1 -3
  32. package/dist/tokens/client.js +1 -3
  33. package/dist/utils/env.js +1 -2
  34. package/dist/utils/errors.js +1 -2
  35. package/dist/utils/logger.js +1 -2
  36. package/dist/utils/media.js +43 -0
  37. package/dist/utils/platform.js +13 -0
  38. package/dist/utils/user-agent.js +1 -3
  39. package/dist/version.js +1 -2
  40. package/package.json +2 -1
  41. package/dist/realtime/diagnostics.d.ts +0 -78
  42. package/dist/realtime/webrtc-connection.js +0 -501
  43. package/dist/realtime/webrtc-manager.js +0 -189
  44. package/dist/realtime/webrtc-stats.d.ts +0 -59
  45. package/dist/realtime/webrtc-stats.js +0 -154
@@ -1,189 +0,0 @@
1
- import { WebRTCConnection } from "./webrtc-connection.js";
2
- import pRetry, { AbortError } from "p-retry";
3
-
4
- //#region src/realtime/webrtc-manager.ts
5
- const PERMANENT_ERRORS = [
6
- "permission denied",
7
- "not allowed",
8
- "invalid session",
9
- "401",
10
- "invalid api key",
11
- "unauthorized"
12
- ];
13
- const CONNECTION_TIMEOUT = 6e4 * 5;
14
- const RETRY_OPTIONS = {
15
- retries: 5,
16
- factor: 2,
17
- minTimeout: 1e3,
18
- maxTimeout: 1e4
19
- };
20
- var WebRTCManager = class {
21
- connection;
22
- config;
23
- logger;
24
- localStream = null;
25
- subscribeMode = false;
26
- managerState = "disconnected";
27
- hasConnected = false;
28
- isReconnecting = false;
29
- intentionalDisconnect = false;
30
- reconnectGeneration = 0;
31
- constructor(config) {
32
- this.config = config;
33
- this.logger = config.logger ?? {
34
- debug() {},
35
- info() {},
36
- warn() {},
37
- error() {}
38
- };
39
- this.connection = new WebRTCConnection({
40
- onRemoteStream: config.onRemoteStream,
41
- onStateChange: (state) => this.handleConnectionStateChange(state),
42
- onError: config.onError,
43
- customizeOffer: config.customizeOffer,
44
- vp8MinBitrate: config.vp8MinBitrate,
45
- vp8StartBitrate: config.vp8StartBitrate,
46
- initialImage: config.initialImage,
47
- initialPrompt: config.initialPrompt,
48
- logger: this.logger,
49
- onDiagnostic: config.onDiagnostic
50
- });
51
- }
52
- emitState(state) {
53
- if (this.managerState !== state) {
54
- this.managerState = state;
55
- if (state === "connected" || state === "generating") this.hasConnected = true;
56
- this.config.onConnectionStateChange?.(state);
57
- }
58
- }
59
- handleConnectionStateChange(state) {
60
- if (this.intentionalDisconnect) {
61
- this.emitState("disconnected");
62
- return;
63
- }
64
- if (this.isReconnecting) {
65
- if (state === "connected" || state === "generating") {
66
- this.isReconnecting = false;
67
- this.emitState(state);
68
- }
69
- return;
70
- }
71
- if (state === "disconnected" && !this.intentionalDisconnect && this.hasConnected) {
72
- this.reconnect();
73
- return;
74
- }
75
- this.emitState(state);
76
- }
77
- async reconnect() {
78
- if (this.isReconnecting || this.intentionalDisconnect) return;
79
- if (!this.subscribeMode && !this.localStream) return;
80
- const reconnectGeneration = ++this.reconnectGeneration;
81
- this.isReconnecting = true;
82
- this.emitState("reconnecting");
83
- const reconnectStart = performance.now();
84
- try {
85
- let attemptCount = 0;
86
- await pRetry(async () => {
87
- attemptCount++;
88
- if (this.intentionalDisconnect || reconnectGeneration !== this.reconnectGeneration) throw new AbortError("Reconnect cancelled");
89
- if (!this.subscribeMode && !this.localStream) throw new AbortError("Reconnect cancelled: no local stream");
90
- this.connection.cleanup();
91
- await this.connection.connect(this.config.webrtcUrl, this.localStream, CONNECTION_TIMEOUT, this.config.integration);
92
- if (this.intentionalDisconnect || reconnectGeneration !== this.reconnectGeneration) {
93
- this.connection.cleanup();
94
- throw new AbortError("Reconnect cancelled");
95
- }
96
- }, {
97
- ...RETRY_OPTIONS,
98
- onFailedAttempt: (error) => {
99
- if (this.intentionalDisconnect || reconnectGeneration !== this.reconnectGeneration) return;
100
- this.logger.warn("Reconnect attempt failed", {
101
- error: error.message,
102
- attempt: error.attemptNumber
103
- });
104
- this.config.onDiagnostic?.("reconnect", {
105
- attempt: error.attemptNumber,
106
- maxAttempts: RETRY_OPTIONS.retries + 1,
107
- durationMs: performance.now() - reconnectStart,
108
- success: false,
109
- error: error.message
110
- });
111
- this.connection.cleanup();
112
- },
113
- shouldRetry: (error) => {
114
- if (this.intentionalDisconnect || reconnectGeneration !== this.reconnectGeneration) return false;
115
- const msg = error.message.toLowerCase();
116
- return !PERMANENT_ERRORS.some((err) => msg.includes(err));
117
- }
118
- });
119
- this.config.onDiagnostic?.("reconnect", {
120
- attempt: attemptCount,
121
- maxAttempts: RETRY_OPTIONS.retries + 1,
122
- durationMs: performance.now() - reconnectStart,
123
- success: true
124
- });
125
- } catch (error) {
126
- this.isReconnecting = false;
127
- if (this.intentionalDisconnect || reconnectGeneration !== this.reconnectGeneration) return;
128
- this.emitState("disconnected");
129
- this.config.onError?.(error instanceof Error ? error : new Error(String(error)));
130
- }
131
- }
132
- async connect(localStream) {
133
- this.localStream = localStream;
134
- this.subscribeMode = localStream === null;
135
- this.intentionalDisconnect = false;
136
- this.hasConnected = false;
137
- this.isReconnecting = false;
138
- this.reconnectGeneration += 1;
139
- this.emitState("connecting");
140
- return pRetry(async () => {
141
- if (this.intentionalDisconnect) throw new AbortError("Connect cancelled");
142
- await this.connection.connect(this.config.webrtcUrl, localStream, CONNECTION_TIMEOUT, this.config.integration);
143
- return true;
144
- }, {
145
- ...RETRY_OPTIONS,
146
- onFailedAttempt: (error) => {
147
- this.logger.warn("Connection attempt failed", {
148
- error: error.message,
149
- attempt: error.attemptNumber
150
- });
151
- this.connection.cleanup();
152
- },
153
- shouldRetry: (error) => {
154
- if (this.intentionalDisconnect) return false;
155
- const msg = error.message.toLowerCase();
156
- return !PERMANENT_ERRORS.some((err) => msg.includes(err));
157
- }
158
- });
159
- }
160
- sendMessage(message) {
161
- return this.connection.send(message);
162
- }
163
- cleanup() {
164
- this.intentionalDisconnect = true;
165
- this.isReconnecting = false;
166
- this.reconnectGeneration += 1;
167
- this.connection.cleanup();
168
- this.localStream = null;
169
- this.emitState("disconnected");
170
- }
171
- isConnected() {
172
- return this.managerState === "connected" || this.managerState === "generating";
173
- }
174
- getConnectionState() {
175
- return this.managerState;
176
- }
177
- getPeerConnection() {
178
- return this.connection.getPeerConnection();
179
- }
180
- getWebsocketMessageEmitter() {
181
- return this.connection.websocketMessagesEmitter;
182
- }
183
- setImage(imageBase64, options) {
184
- return this.connection.setImageBase64(imageBase64, options);
185
- }
186
- };
187
-
188
- //#endregion
189
- export { WebRTCManager };
@@ -1,59 +0,0 @@
1
- //#region src/realtime/webrtc-stats.d.ts
2
- type WebRTCStats = {
3
- timestamp: number;
4
- video: {
5
- framesDecoded: number;
6
- framesDropped: number;
7
- framesPerSecond: number;
8
- frameWidth: number;
9
- frameHeight: number;
10
- bytesReceived: number;
11
- packetsReceived: number;
12
- packetsLost: number;
13
- jitter: number;
14
- /** Estimated inbound bitrate in bits/sec, computed from bytesReceived delta. */
15
- bitrate: number;
16
- freezeCount: number;
17
- totalFreezesDuration: number;
18
- /** Delta: packets lost since previous sample. */
19
- packetsLostDelta: number;
20
- /** Delta: frames dropped since previous sample. */
21
- framesDroppedDelta: number;
22
- /** Delta: freeze count since previous sample. */
23
- freezeCountDelta: number;
24
- /** Delta: freeze duration (seconds) since previous sample. */
25
- freezeDurationDelta: number;
26
- } | null;
27
- audio: {
28
- bytesReceived: number;
29
- packetsReceived: number;
30
- packetsLost: number;
31
- jitter: number;
32
- /** Estimated inbound bitrate in bits/sec, computed from bytesReceived delta. */
33
- bitrate: number;
34
- /** Delta: packets lost since previous sample. */
35
- packetsLostDelta: number;
36
- } | null;
37
- /** Outbound video track stats (from the local camera/screen share being sent). */
38
- outboundVideo: {
39
- /** Why the encoder is limiting quality: "none", "bandwidth", "cpu", or "other". */
40
- qualityLimitationReason: string;
41
- /** Cumulative time (seconds) spent in each quality limitation state. */
42
- qualityLimitationDurations: Record<string, number>;
43
- bytesSent: number;
44
- packetsSent: number;
45
- framesPerSecond: number;
46
- frameWidth: number;
47
- frameHeight: number;
48
- /** Estimated outbound bitrate in bits/sec, computed from bytesSent delta. */
49
- bitrate: number;
50
- } | null;
51
- connection: {
52
- /** Current round-trip time in seconds, or null if unavailable. */
53
- currentRoundTripTime: number | null;
54
- /** Available outgoing bitrate estimate in bits/sec, or null if unavailable. */
55
- availableOutgoingBitrate: number | null;
56
- };
57
- };
58
- //#endregion
59
- export { WebRTCStats };
@@ -1,154 +0,0 @@
1
- //#region src/realtime/webrtc-stats.ts
2
- const DEFAULT_INTERVAL_MS = 1e3;
3
- const MIN_INTERVAL_MS = 500;
4
- var WebRTCStatsCollector = class {
5
- pc = null;
6
- intervalId = null;
7
- prevBytesVideo = 0;
8
- prevBytesAudio = 0;
9
- prevBytesSentVideo = 0;
10
- prevTimestamp = 0;
11
- prevPacketsLostVideo = 0;
12
- prevFramesDropped = 0;
13
- prevFreezeCount = 0;
14
- prevFreezeDuration = 0;
15
- prevPacketsLostAudio = 0;
16
- onStats = null;
17
- intervalMs;
18
- constructor(options = {}) {
19
- this.intervalMs = Math.max(options.intervalMs ?? DEFAULT_INTERVAL_MS, MIN_INTERVAL_MS);
20
- }
21
- /** Attach to a peer connection and start polling. */
22
- start(pc, onStats) {
23
- this.stop();
24
- this.pc = pc;
25
- this.onStats = onStats;
26
- this.prevBytesVideo = 0;
27
- this.prevBytesAudio = 0;
28
- this.prevBytesSentVideo = 0;
29
- this.prevTimestamp = 0;
30
- this.prevPacketsLostVideo = 0;
31
- this.prevFramesDropped = 0;
32
- this.prevFreezeCount = 0;
33
- this.prevFreezeDuration = 0;
34
- this.prevPacketsLostAudio = 0;
35
- this.intervalId = setInterval(() => this.collect(), this.intervalMs);
36
- }
37
- /** Stop polling and release resources. */
38
- stop() {
39
- if (this.intervalId !== null) {
40
- clearInterval(this.intervalId);
41
- this.intervalId = null;
42
- }
43
- this.pc = null;
44
- this.onStats = null;
45
- }
46
- isRunning() {
47
- return this.intervalId !== null;
48
- }
49
- async collect() {
50
- if (!this.pc || !this.onStats) return;
51
- try {
52
- const rawStats = await this.pc.getStats();
53
- const stats = this.parse(rawStats);
54
- this.onStats(stats);
55
- } catch {
56
- this.stop();
57
- }
58
- }
59
- parse(rawStats) {
60
- const now = performance.now();
61
- const elapsed = this.prevTimestamp > 0 ? (now - this.prevTimestamp) / 1e3 : 0;
62
- let video = null;
63
- let audio = null;
64
- let outboundVideo = null;
65
- const connection = {
66
- currentRoundTripTime: null,
67
- availableOutgoingBitrate: null
68
- };
69
- rawStats.forEach((report) => {
70
- if (report.type === "inbound-rtp" && report.kind === "video") {
71
- const bytesReceived = report.bytesReceived ?? 0;
72
- const bitrate = elapsed > 0 ? (bytesReceived - this.prevBytesVideo) * 8 / elapsed : 0;
73
- this.prevBytesVideo = bytesReceived;
74
- const r = report;
75
- const packetsLost = r.packetsLost ?? 0;
76
- const framesDropped = r.framesDropped ?? 0;
77
- const freezeCount = r.freezeCount ?? 0;
78
- const freezeDuration = r.totalFreezesDuration ?? 0;
79
- video = {
80
- framesDecoded: r.framesDecoded ?? 0,
81
- framesDropped,
82
- framesPerSecond: r.framesPerSecond ?? 0,
83
- frameWidth: r.frameWidth ?? 0,
84
- frameHeight: r.frameHeight ?? 0,
85
- bytesReceived,
86
- packetsReceived: r.packetsReceived ?? 0,
87
- packetsLost,
88
- jitter: r.jitter ?? 0,
89
- bitrate: Math.round(bitrate),
90
- freezeCount,
91
- totalFreezesDuration: freezeDuration,
92
- packetsLostDelta: Math.max(0, packetsLost - this.prevPacketsLostVideo),
93
- framesDroppedDelta: Math.max(0, framesDropped - this.prevFramesDropped),
94
- freezeCountDelta: Math.max(0, freezeCount - this.prevFreezeCount),
95
- freezeDurationDelta: Math.max(0, freezeDuration - this.prevFreezeDuration)
96
- };
97
- this.prevPacketsLostVideo = packetsLost;
98
- this.prevFramesDropped = framesDropped;
99
- this.prevFreezeCount = freezeCount;
100
- this.prevFreezeDuration = freezeDuration;
101
- }
102
- if (report.type === "outbound-rtp" && report.kind === "video") {
103
- const r = report;
104
- const bytesSent = r.bytesSent ?? 0;
105
- const outBitrate = elapsed > 0 ? (bytesSent - this.prevBytesSentVideo) * 8 / elapsed : 0;
106
- this.prevBytesSentVideo = bytesSent;
107
- outboundVideo = {
108
- qualityLimitationReason: r.qualityLimitationReason ?? "none",
109
- qualityLimitationDurations: r.qualityLimitationDurations ?? {},
110
- bytesSent,
111
- packetsSent: r.packetsSent ?? 0,
112
- framesPerSecond: r.framesPerSecond ?? 0,
113
- frameWidth: r.frameWidth ?? 0,
114
- frameHeight: r.frameHeight ?? 0,
115
- bitrate: Math.round(outBitrate)
116
- };
117
- }
118
- if (report.type === "inbound-rtp" && report.kind === "audio") {
119
- const bytesReceived = report.bytesReceived ?? 0;
120
- const bitrate = elapsed > 0 ? (bytesReceived - this.prevBytesAudio) * 8 / elapsed : 0;
121
- this.prevBytesAudio = bytesReceived;
122
- const r = report;
123
- const audioPacketsLost = r.packetsLost ?? 0;
124
- audio = {
125
- bytesReceived,
126
- packetsReceived: r.packetsReceived ?? 0,
127
- packetsLost: audioPacketsLost,
128
- jitter: r.jitter ?? 0,
129
- bitrate: Math.round(bitrate),
130
- packetsLostDelta: Math.max(0, audioPacketsLost - this.prevPacketsLostAudio)
131
- };
132
- this.prevPacketsLostAudio = audioPacketsLost;
133
- }
134
- if (report.type === "candidate-pair") {
135
- const r = report;
136
- if (r.state === "succeeded") {
137
- connection.currentRoundTripTime = r.currentRoundTripTime ?? null;
138
- connection.availableOutgoingBitrate = r.availableOutgoingBitrate ?? null;
139
- }
140
- }
141
- });
142
- this.prevTimestamp = now;
143
- return {
144
- timestamp: Date.now(),
145
- video,
146
- audio,
147
- outboundVideo,
148
- connection
149
- };
150
- }
151
- };
152
-
153
- //#endregion
154
- export { WebRTCStatsCollector };