@azure/ai-voicelive 1.0.0-alpha.20251117.2
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/LICENSE +21 -0
- package/README.md +358 -0
- package/dist/browser/auth/credentialHandler.d.ts +43 -0
- package/dist/browser/auth/credentialHandler.js +147 -0
- package/dist/browser/auth/credentialHandler.js.map +1 -0
- package/dist/browser/errors/connectionErrors.d.ts +68 -0
- package/dist/browser/errors/connectionErrors.js +136 -0
- package/dist/browser/errors/connectionErrors.js.map +1 -0
- package/dist/browser/errors/index.d.ts +2 -0
- package/dist/browser/errors/index.js +4 -0
- package/dist/browser/errors/index.js.map +1 -0
- package/dist/browser/handlers/sessionHandlers.d.ts +250 -0
- package/dist/browser/handlers/sessionHandlers.js +4 -0
- package/dist/browser/handlers/sessionHandlers.js.map +1 -0
- package/dist/browser/handlers/subscriptionManager.d.ts +54 -0
- package/dist/browser/handlers/subscriptionManager.js +250 -0
- package/dist/browser/handlers/subscriptionManager.js.map +1 -0
- package/dist/browser/index.d.ts +7 -0
- package/dist/browser/index.js +12 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/browser/logger.d.ts +2 -0
- package/dist/browser/logger.js +5 -0
- package/dist/browser/logger.js.map +1 -0
- package/dist/browser/models/index.d.ts +2 -0
- package/dist/browser/models/index.js +4 -0
- package/dist/browser/models/index.js.map +1 -0
- package/dist/browser/models/models.d.ts +2154 -0
- package/dist/browser/models/models.js +2251 -0
- package/dist/browser/models/models.js.map +1 -0
- package/dist/browser/package.json +3 -0
- package/dist/browser/protocol/messageParser.d.ts +42 -0
- package/dist/browser/protocol/messageParser.js +150 -0
- package/dist/browser/protocol/messageParser.js.map +1 -0
- package/dist/browser/voiceLiveClient.d.ts +65 -0
- package/dist/browser/voiceLiveClient.js +81 -0
- package/dist/browser/voiceLiveClient.js.map +1 -0
- package/dist/browser/voiceLiveSession.d.ts +138 -0
- package/dist/browser/voiceLiveSession.js +429 -0
- package/dist/browser/voiceLiveSession.js.map +1 -0
- package/dist/browser/websocket/connectionManager.d.ts +88 -0
- package/dist/browser/websocket/connectionManager.js +183 -0
- package/dist/browser/websocket/connectionManager.js.map +1 -0
- package/dist/browser/websocket/websocketBrowser.d.ts +26 -0
- package/dist/browser/websocket/websocketBrowser.js +175 -0
- package/dist/browser/websocket/websocketBrowser.js.map +1 -0
- package/dist/browser/websocket/websocketFactory.d.ts +23 -0
- package/dist/browser/websocket/websocketFactory.js +80 -0
- package/dist/browser/websocket/websocketFactory.js.map +1 -0
- package/dist/browser/websocket/websocketLike.d.ts +78 -0
- package/dist/browser/websocket/websocketLike.js +13 -0
- package/dist/browser/websocket/websocketLike.js.map +1 -0
- package/dist/browser/websocket/websocketNode.d.ts +26 -0
- package/dist/browser/websocket/websocketNode.js +180 -0
- package/dist/browser/websocket/websocketNode.js.map +1 -0
- package/dist/commonjs/auth/credentialHandler.d.ts +43 -0
- package/dist/commonjs/auth/credentialHandler.js +151 -0
- package/dist/commonjs/auth/credentialHandler.js.map +1 -0
- package/dist/commonjs/errors/connectionErrors.d.ts +68 -0
- package/dist/commonjs/errors/connectionErrors.js +146 -0
- package/dist/commonjs/errors/connectionErrors.js.map +1 -0
- package/dist/commonjs/errors/index.d.ts +2 -0
- package/dist/commonjs/errors/index.js +7 -0
- package/dist/commonjs/errors/index.js.map +1 -0
- package/dist/commonjs/handlers/sessionHandlers.d.ts +250 -0
- package/dist/commonjs/handlers/sessionHandlers.js +5 -0
- package/dist/commonjs/handlers/sessionHandlers.js.map +1 -0
- package/dist/commonjs/handlers/subscriptionManager.d.ts +54 -0
- package/dist/commonjs/handlers/subscriptionManager.js +255 -0
- package/dist/commonjs/handlers/subscriptionManager.js.map +1 -0
- package/dist/commonjs/index.d.ts +7 -0
- package/dist/commonjs/index.js +45 -0
- package/dist/commonjs/index.js.map +1 -0
- package/dist/commonjs/logger.d.ts +2 -0
- package/dist/commonjs/logger.js +8 -0
- package/dist/commonjs/logger.js.map +1 -0
- package/dist/commonjs/models/index.d.ts +2 -0
- package/dist/commonjs/models/index.js +27 -0
- package/dist/commonjs/models/index.js.map +1 -0
- package/dist/commonjs/models/models.d.ts +2154 -0
- package/dist/commonjs/models/models.js +2463 -0
- package/dist/commonjs/models/models.js.map +1 -0
- package/dist/commonjs/package.json +3 -0
- package/dist/commonjs/protocol/messageParser.d.ts +42 -0
- package/dist/commonjs/protocol/messageParser.js +154 -0
- package/dist/commonjs/protocol/messageParser.js.map +1 -0
- package/dist/commonjs/tsdoc-metadata.json +11 -0
- package/dist/commonjs/voiceLiveClient.d.ts +65 -0
- package/dist/commonjs/voiceLiveClient.js +85 -0
- package/dist/commonjs/voiceLiveClient.js.map +1 -0
- package/dist/commonjs/voiceLiveSession.d.ts +138 -0
- package/dist/commonjs/voiceLiveSession.js +433 -0
- package/dist/commonjs/voiceLiveSession.js.map +1 -0
- package/dist/commonjs/websocket/connectionManager.d.ts +88 -0
- package/dist/commonjs/websocket/connectionManager.js +187 -0
- package/dist/commonjs/websocket/connectionManager.js.map +1 -0
- package/dist/commonjs/websocket/websocketBrowser.d.ts +26 -0
- package/dist/commonjs/websocket/websocketBrowser.js +179 -0
- package/dist/commonjs/websocket/websocketBrowser.js.map +1 -0
- package/dist/commonjs/websocket/websocketFactory.d.ts +23 -0
- package/dist/commonjs/websocket/websocketFactory.js +86 -0
- package/dist/commonjs/websocket/websocketFactory.js.map +1 -0
- package/dist/commonjs/websocket/websocketLike.d.ts +78 -0
- package/dist/commonjs/websocket/websocketLike.js +16 -0
- package/dist/commonjs/websocket/websocketLike.js.map +1 -0
- package/dist/commonjs/websocket/websocketNode.d.ts +26 -0
- package/dist/commonjs/websocket/websocketNode.js +185 -0
- package/dist/commonjs/websocket/websocketNode.js.map +1 -0
- package/dist/esm/auth/credentialHandler.d.ts +43 -0
- package/dist/esm/auth/credentialHandler.js +147 -0
- package/dist/esm/auth/credentialHandler.js.map +1 -0
- package/dist/esm/errors/connectionErrors.d.ts +68 -0
- package/dist/esm/errors/connectionErrors.js +136 -0
- package/dist/esm/errors/connectionErrors.js.map +1 -0
- package/dist/esm/errors/index.d.ts +2 -0
- package/dist/esm/errors/index.js +4 -0
- package/dist/esm/errors/index.js.map +1 -0
- package/dist/esm/handlers/sessionHandlers.d.ts +250 -0
- package/dist/esm/handlers/sessionHandlers.js +4 -0
- package/dist/esm/handlers/sessionHandlers.js.map +1 -0
- package/dist/esm/handlers/subscriptionManager.d.ts +54 -0
- package/dist/esm/handlers/subscriptionManager.js +250 -0
- package/dist/esm/handlers/subscriptionManager.js.map +1 -0
- package/dist/esm/index.d.ts +7 -0
- package/dist/esm/index.js +12 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/logger.d.ts +2 -0
- package/dist/esm/logger.js +5 -0
- package/dist/esm/logger.js.map +1 -0
- package/dist/esm/models/index.d.ts +2 -0
- package/dist/esm/models/index.js +4 -0
- package/dist/esm/models/index.js.map +1 -0
- package/dist/esm/models/models.d.ts +2154 -0
- package/dist/esm/models/models.js +2251 -0
- package/dist/esm/models/models.js.map +1 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/protocol/messageParser.d.ts +42 -0
- package/dist/esm/protocol/messageParser.js +150 -0
- package/dist/esm/protocol/messageParser.js.map +1 -0
- package/dist/esm/voiceLiveClient.d.ts +65 -0
- package/dist/esm/voiceLiveClient.js +81 -0
- package/dist/esm/voiceLiveClient.js.map +1 -0
- package/dist/esm/voiceLiveSession.d.ts +138 -0
- package/dist/esm/voiceLiveSession.js +429 -0
- package/dist/esm/voiceLiveSession.js.map +1 -0
- package/dist/esm/websocket/connectionManager.d.ts +88 -0
- package/dist/esm/websocket/connectionManager.js +183 -0
- package/dist/esm/websocket/connectionManager.js.map +1 -0
- package/dist/esm/websocket/websocketBrowser.d.ts +26 -0
- package/dist/esm/websocket/websocketBrowser.js +175 -0
- package/dist/esm/websocket/websocketBrowser.js.map +1 -0
- package/dist/esm/websocket/websocketFactory.d.ts +23 -0
- package/dist/esm/websocket/websocketFactory.js +80 -0
- package/dist/esm/websocket/websocketFactory.js.map +1 -0
- package/dist/esm/websocket/websocketLike.d.ts +78 -0
- package/dist/esm/websocket/websocketLike.js +13 -0
- package/dist/esm/websocket/websocketLike.js.map +1 -0
- package/dist/esm/websocket/websocketNode.d.ts +26 -0
- package/dist/esm/websocket/websocketNode.js +180 -0
- package/dist/esm/websocket/websocketNode.js.map +1 -0
- package/dist/react-native/auth/credentialHandler.d.ts +43 -0
- package/dist/react-native/auth/credentialHandler.js +147 -0
- package/dist/react-native/auth/credentialHandler.js.map +1 -0
- package/dist/react-native/errors/connectionErrors.d.ts +68 -0
- package/dist/react-native/errors/connectionErrors.js +136 -0
- package/dist/react-native/errors/connectionErrors.js.map +1 -0
- package/dist/react-native/errors/index.d.ts +2 -0
- package/dist/react-native/errors/index.js +4 -0
- package/dist/react-native/errors/index.js.map +1 -0
- package/dist/react-native/handlers/sessionHandlers.d.ts +250 -0
- package/dist/react-native/handlers/sessionHandlers.js +4 -0
- package/dist/react-native/handlers/sessionHandlers.js.map +1 -0
- package/dist/react-native/handlers/subscriptionManager.d.ts +54 -0
- package/dist/react-native/handlers/subscriptionManager.js +250 -0
- package/dist/react-native/handlers/subscriptionManager.js.map +1 -0
- package/dist/react-native/index.d.ts +7 -0
- package/dist/react-native/index.js +12 -0
- package/dist/react-native/index.js.map +1 -0
- package/dist/react-native/logger.d.ts +2 -0
- package/dist/react-native/logger.js +5 -0
- package/dist/react-native/logger.js.map +1 -0
- package/dist/react-native/models/index.d.ts +2 -0
- package/dist/react-native/models/index.js +4 -0
- package/dist/react-native/models/index.js.map +1 -0
- package/dist/react-native/models/models.d.ts +2154 -0
- package/dist/react-native/models/models.js +2251 -0
- package/dist/react-native/models/models.js.map +1 -0
- package/dist/react-native/package.json +3 -0
- package/dist/react-native/protocol/messageParser.d.ts +42 -0
- package/dist/react-native/protocol/messageParser.js +150 -0
- package/dist/react-native/protocol/messageParser.js.map +1 -0
- package/dist/react-native/voiceLiveClient.d.ts +65 -0
- package/dist/react-native/voiceLiveClient.js +81 -0
- package/dist/react-native/voiceLiveClient.js.map +1 -0
- package/dist/react-native/voiceLiveSession.d.ts +138 -0
- package/dist/react-native/voiceLiveSession.js +429 -0
- package/dist/react-native/voiceLiveSession.js.map +1 -0
- package/dist/react-native/websocket/connectionManager.d.ts +88 -0
- package/dist/react-native/websocket/connectionManager.js +183 -0
- package/dist/react-native/websocket/connectionManager.js.map +1 -0
- package/dist/react-native/websocket/websocketBrowser.d.ts +26 -0
- package/dist/react-native/websocket/websocketBrowser.js +175 -0
- package/dist/react-native/websocket/websocketBrowser.js.map +1 -0
- package/dist/react-native/websocket/websocketFactory.d.ts +23 -0
- package/dist/react-native/websocket/websocketFactory.js +80 -0
- package/dist/react-native/websocket/websocketFactory.js.map +1 -0
- package/dist/react-native/websocket/websocketLike.d.ts +78 -0
- package/dist/react-native/websocket/websocketLike.js +13 -0
- package/dist/react-native/websocket/websocketLike.js.map +1 -0
- package/dist/react-native/websocket/websocketNode.d.ts +26 -0
- package/dist/react-native/websocket/websocketNode.js +180 -0
- package/dist/react-native/websocket/websocketNode.js.map +1 -0
- package/package.json +150 -0
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (c) Microsoft Corporation.
|
|
3
|
+
// Licensed under the MIT License.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.VoiceLiveSession = void 0;
|
|
6
|
+
const connectionManager_js_1 = require("./websocket/connectionManager.js");
|
|
7
|
+
const websocketFactory_js_1 = require("./websocket/websocketFactory.js");
|
|
8
|
+
const messageParser_js_1 = require("./protocol/messageParser.js");
|
|
9
|
+
const credentialHandler_js_1 = require("./auth/credentialHandler.js");
|
|
10
|
+
const index_js_1 = require("./errors/index.js");
|
|
11
|
+
const logger_js_1 = require("./logger.js");
|
|
12
|
+
const subscriptionManager_js_1 = require("./handlers/subscriptionManager.js");
|
|
13
|
+
/**
|
|
14
|
+
* Represents a WebSocket-based session for real-time voice communication with the Azure VoiceLive service.
|
|
15
|
+
*
|
|
16
|
+
* This class manages the connection, handles real-time communication, and provides access to all
|
|
17
|
+
* interactive features including audio streaming, conversation management, and avatar control.
|
|
18
|
+
*/
|
|
19
|
+
class VoiceLiveSession {
|
|
20
|
+
_endpoint;
|
|
21
|
+
_credentialHandler;
|
|
22
|
+
_options;
|
|
23
|
+
_messageParser;
|
|
24
|
+
_model;
|
|
25
|
+
_connectionManager;
|
|
26
|
+
_sessionId;
|
|
27
|
+
_activeTurnId;
|
|
28
|
+
_disposed = false;
|
|
29
|
+
// Handler-based subscription management
|
|
30
|
+
_subscriptionManager;
|
|
31
|
+
/**
|
|
32
|
+
* Creates an instance of VoiceLiveSession.
|
|
33
|
+
*
|
|
34
|
+
* @param endpoint - The Voice Live service endpoint URL
|
|
35
|
+
* @param credential - Azure TokenCredential or KeyCredential for authentication
|
|
36
|
+
* @param apiVersion - API version to use for the Voice Live service
|
|
37
|
+
* @param model - The model name to use for the session
|
|
38
|
+
* @param options - Optional configuration for the session
|
|
39
|
+
*/
|
|
40
|
+
constructor(endpoint, credential, apiVersion, model, options = {}) {
|
|
41
|
+
this._endpoint = this._normalizeEndpoint(endpoint);
|
|
42
|
+
this._credentialHandler = new credentialHandler_js_1.CredentialHandler(credential);
|
|
43
|
+
this._options = this._buildDefaultOptions(options);
|
|
44
|
+
this._messageParser = new messageParser_js_1.VoiceLiveMessageParser();
|
|
45
|
+
this._model = model;
|
|
46
|
+
// Initialize handler-based subscription management
|
|
47
|
+
this._subscriptionManager = new subscriptionManager_js_1.SubscriptionManager();
|
|
48
|
+
logger_js_1.logger.info("VoiceLiveSession created", {
|
|
49
|
+
endpoint: this._endpoint,
|
|
50
|
+
model: this._model,
|
|
51
|
+
apiVersion: apiVersion,
|
|
52
|
+
enableDebugLogging: this._options.enableDebugLogging,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Establishes connection to the Voice Live service with authentication.
|
|
57
|
+
*/
|
|
58
|
+
async connect(options = {}) {
|
|
59
|
+
this._ensureNotDisposed();
|
|
60
|
+
if (this.isConnected) {
|
|
61
|
+
logger_js_1.logger.info("VoiceLiveSession already connected");
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
logger_js_1.logger.info("Connecting to Voice Live service", {
|
|
66
|
+
endpoint: this._endpoint,
|
|
67
|
+
model: this._model,
|
|
68
|
+
});
|
|
69
|
+
// Get WebSocket URL with authentication and model
|
|
70
|
+
const wsUrl = await this._credentialHandler.getWebSocketUrl(this._endpoint, "2025-10-01", // TODO: make this configurable through constructor
|
|
71
|
+
this._model);
|
|
72
|
+
const authHeaders = await this._credentialHandler.getAuthHeaders();
|
|
73
|
+
// Create connection manager
|
|
74
|
+
const websocketFactory = new websocketFactory_js_1.VoiceLiveWebSocketFactory();
|
|
75
|
+
this._connectionManager = new connectionManager_js_1.ConnectionManager(() => websocketFactory.create({
|
|
76
|
+
headers: { ...authHeaders },
|
|
77
|
+
connectionTimeoutInMs: this._options.connectionTimeoutInMs,
|
|
78
|
+
compression: true,
|
|
79
|
+
}), {
|
|
80
|
+
endpoint: wsUrl,
|
|
81
|
+
connectionTimeout: options.timeoutInMs || this._options.connectionTimeoutInMs,
|
|
82
|
+
});
|
|
83
|
+
// Setup connection event handlers
|
|
84
|
+
this._setupConnectionEventHandlers();
|
|
85
|
+
// Connect with proper error handling
|
|
86
|
+
await this._connectionManager.connect(options.abortSignal);
|
|
87
|
+
logger_js_1.logger.info("Successfully connected to Voice Live service");
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
logger_js_1.logger.error("Failed to connect to Voice Live service", { error });
|
|
91
|
+
// Use error classification
|
|
92
|
+
if (error instanceof index_js_1.VoiceLiveConnectionError) {
|
|
93
|
+
throw error;
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
throw (0, index_js_1.classifyConnectionError)(error);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Disconnects from the Voice Live service and cleans up resources.
|
|
102
|
+
*/
|
|
103
|
+
async disconnect() {
|
|
104
|
+
if (!this._connectionManager) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
logger_js_1.logger.info("Disconnecting from Voice Live service");
|
|
108
|
+
try {
|
|
109
|
+
await this._connectionManager.disconnect();
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
logger_js_1.logger.error("Error during disconnect", { error });
|
|
113
|
+
}
|
|
114
|
+
finally {
|
|
115
|
+
this._connectionManager = undefined;
|
|
116
|
+
this._sessionId = undefined;
|
|
117
|
+
this._activeTurnId = undefined;
|
|
118
|
+
logger_js_1.logger.info("Disconnected from Voice Live service");
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Subscribe to VoiceLive session events using strongly-typed handlers.
|
|
123
|
+
* This follows the Azure SDK pattern used by EventHub, Service Bus, etc.
|
|
124
|
+
*
|
|
125
|
+
* @param handlers - Handler functions for different types of events
|
|
126
|
+
* @returns A subscription object that can be used to stop receiving events
|
|
127
|
+
*/
|
|
128
|
+
subscribe(handlers) {
|
|
129
|
+
this._ensureNotDisposed();
|
|
130
|
+
logger_js_1.logger.info("Creating VoiceLive session subscription");
|
|
131
|
+
return this._subscriptionManager.createSubscription(handlers);
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Sends a custom client event to the service.
|
|
135
|
+
*/
|
|
136
|
+
async sendEvent(event, options = {}) {
|
|
137
|
+
this._ensureConnected();
|
|
138
|
+
await this._sendEvent(event, options);
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Updates the session configuration with the service.
|
|
142
|
+
*/
|
|
143
|
+
async updateSession(session, options = {}) {
|
|
144
|
+
this._ensureConnected();
|
|
145
|
+
const updateEvent = {
|
|
146
|
+
type: "session.update",
|
|
147
|
+
session: session,
|
|
148
|
+
eventId: this._generateEventId(),
|
|
149
|
+
};
|
|
150
|
+
await this._sendEvent(updateEvent, options);
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Sends audio data to the service using turn-based or buffer-based approach.
|
|
154
|
+
*/
|
|
155
|
+
async sendAudio(audioData, options = {}) {
|
|
156
|
+
this._ensureConnected();
|
|
157
|
+
const audioBase64 = this._arrayBufferToBase64(audioData);
|
|
158
|
+
if (options.turnId) {
|
|
159
|
+
// Turn-based audio
|
|
160
|
+
const appendEvent = {
|
|
161
|
+
type: "input_audio.turn.append",
|
|
162
|
+
audio: audioBase64,
|
|
163
|
+
turnId: options.turnId,
|
|
164
|
+
eventId: this._generateEventId(),
|
|
165
|
+
};
|
|
166
|
+
await this._sendEvent(appendEvent, options);
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
// Buffer-based audio (VAD mode)
|
|
170
|
+
const bufferEvent = {
|
|
171
|
+
type: "input_audio_buffer.append",
|
|
172
|
+
audio: audioBase64,
|
|
173
|
+
eventId: this._generateEventId(),
|
|
174
|
+
};
|
|
175
|
+
await this._sendEvent(bufferEvent, options);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Starts a new audio turn for turn-based audio input.
|
|
180
|
+
*/
|
|
181
|
+
async startAudioTurn(options = {}) {
|
|
182
|
+
this._ensureConnected();
|
|
183
|
+
const turnId = options.turnId || this._generateTurnId();
|
|
184
|
+
this._activeTurnId = turnId;
|
|
185
|
+
const startEvent = {
|
|
186
|
+
type: "input_audio.turn.start",
|
|
187
|
+
turnId: turnId,
|
|
188
|
+
eventId: this._generateEventId(),
|
|
189
|
+
};
|
|
190
|
+
await this._sendEvent(startEvent, options);
|
|
191
|
+
return turnId;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Ends the current audio turn.
|
|
195
|
+
*/
|
|
196
|
+
async endAudioTurn(turnId, options = {}) {
|
|
197
|
+
this._ensureConnected();
|
|
198
|
+
const targetTurnId = turnId || this._activeTurnId;
|
|
199
|
+
if (!targetTurnId) {
|
|
200
|
+
throw new index_js_1.VoiceLiveConnectionError("No active audio turn to end", "INVALID_STATE");
|
|
201
|
+
}
|
|
202
|
+
const endEvent = {
|
|
203
|
+
type: "input_audio.turn.end",
|
|
204
|
+
turnId: targetTurnId,
|
|
205
|
+
eventId: this._generateEventId(),
|
|
206
|
+
};
|
|
207
|
+
await this._sendEvent(endEvent, options);
|
|
208
|
+
if (targetTurnId === this._activeTurnId) {
|
|
209
|
+
this._activeTurnId = undefined;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Adds a conversation item (message) to the conversation.
|
|
214
|
+
*/
|
|
215
|
+
async addConversationItem(item, options = {}) {
|
|
216
|
+
this._ensureConnected();
|
|
217
|
+
const createEvent = {
|
|
218
|
+
type: "conversation.item.create",
|
|
219
|
+
item: item,
|
|
220
|
+
eventId: this._generateEventId(),
|
|
221
|
+
};
|
|
222
|
+
await this._sendEvent(createEvent, options);
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Indicates whether the session is currently connected to the Voice Live service.
|
|
226
|
+
*/
|
|
227
|
+
get isConnected() {
|
|
228
|
+
return this._connectionManager?.isConnected || false;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Gets the current connection state of the session.
|
|
232
|
+
*/
|
|
233
|
+
get connectionState() {
|
|
234
|
+
return this._connectionManager?.state || connectionManager_js_1.ConnectionState.Disconnected;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Gets the current session ID.
|
|
238
|
+
*/
|
|
239
|
+
get sessionId() {
|
|
240
|
+
return this._sessionId;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Gets the current active audio turn ID.
|
|
244
|
+
*/
|
|
245
|
+
get activeTurnId() {
|
|
246
|
+
return this._activeTurnId;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Disposes the session and cleans up resources.
|
|
250
|
+
*/
|
|
251
|
+
async dispose() {
|
|
252
|
+
if (this._disposed) {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
logger_js_1.logger.info("Disposing VoiceLiveSession");
|
|
256
|
+
try {
|
|
257
|
+
// Close all subscriptions first
|
|
258
|
+
await this._subscriptionManager.closeAll();
|
|
259
|
+
// Then disconnect
|
|
260
|
+
await this.disconnect();
|
|
261
|
+
}
|
|
262
|
+
catch (error) {
|
|
263
|
+
logger_js_1.logger.error("Error during session disposal", { error });
|
|
264
|
+
}
|
|
265
|
+
finally {
|
|
266
|
+
this._disposed = true;
|
|
267
|
+
logger_js_1.logger.info("VoiceLiveSession disposed");
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
// Private methods
|
|
271
|
+
_buildDefaultOptions(options) {
|
|
272
|
+
return {
|
|
273
|
+
connectionTimeoutInMs: options.connectionTimeoutInMs || 30000,
|
|
274
|
+
enableDebugLogging: options.enableDebugLogging ?? false,
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
_normalizeEndpoint(endpoint) {
|
|
278
|
+
// Ensure endpoint has proper protocol
|
|
279
|
+
if (!endpoint.startsWith("http://") && !endpoint.startsWith("https://")) {
|
|
280
|
+
endpoint = `https://${endpoint}`;
|
|
281
|
+
}
|
|
282
|
+
// Remove trailing slash
|
|
283
|
+
return endpoint.replace(/\/$/, "");
|
|
284
|
+
}
|
|
285
|
+
_setupConnectionEventHandlers() {
|
|
286
|
+
if (!this._connectionManager)
|
|
287
|
+
return;
|
|
288
|
+
this._connectionManager.updateEventHandlers({
|
|
289
|
+
onStateChange: (state, previousState) => {
|
|
290
|
+
logger_js_1.logger.info("Connection state changed", { state, previousState });
|
|
291
|
+
// Handle connection state changes for handler-based subscriptions
|
|
292
|
+
if (state === connectionManager_js_1.ConnectionState.Connected && previousState === connectionManager_js_1.ConnectionState.Connecting) {
|
|
293
|
+
this._notifyConnectionEvent("connected", {
|
|
294
|
+
connectionId: `conn_${Date.now()}`,
|
|
295
|
+
timestamp: new Date(),
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
else if (state === connectionManager_js_1.ConnectionState.Disconnected &&
|
|
299
|
+
previousState === connectionManager_js_1.ConnectionState.Connected) {
|
|
300
|
+
this._notifyConnectionEvent("disconnected", {
|
|
301
|
+
code: 1006, // Abnormal closure
|
|
302
|
+
reason: "Connection lost during session",
|
|
303
|
+
wasClean: false,
|
|
304
|
+
timestamp: new Date(),
|
|
305
|
+
});
|
|
306
|
+
this._markSessionAsDead("Connection lost during session - session is permanently unusable");
|
|
307
|
+
}
|
|
308
|
+
},
|
|
309
|
+
onMessage: (data) => {
|
|
310
|
+
this._handleIncomingMessage(data);
|
|
311
|
+
},
|
|
312
|
+
onError: (error) => {
|
|
313
|
+
logger_js_1.logger.error("Connection error - marking session as dead", { error });
|
|
314
|
+
this._notifyConnectionEvent("error", {
|
|
315
|
+
error: error,
|
|
316
|
+
context: "WebSocket connection error",
|
|
317
|
+
recoverable: false,
|
|
318
|
+
timestamp: new Date(),
|
|
319
|
+
});
|
|
320
|
+
this._markSessionAsDead(`Connection error: ${error.message}`);
|
|
321
|
+
},
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
_markSessionAsDead(reason) {
|
|
325
|
+
if (this._disposed)
|
|
326
|
+
return;
|
|
327
|
+
logger_js_1.logger.error("Session marked as permanently dead", { reason });
|
|
328
|
+
// Mark as disposed to prevent further use
|
|
329
|
+
this._disposed = true;
|
|
330
|
+
// Clean up connection manager
|
|
331
|
+
this._connectionManager = undefined;
|
|
332
|
+
this._sessionId = undefined;
|
|
333
|
+
this._activeTurnId = undefined;
|
|
334
|
+
}
|
|
335
|
+
_handleIncomingMessage(data) {
|
|
336
|
+
try {
|
|
337
|
+
logger_js_1.logger.info("Message received", {
|
|
338
|
+
type: typeof data,
|
|
339
|
+
size: typeof data === "string" ? data.length : data.byteLength,
|
|
340
|
+
});
|
|
341
|
+
// Parse and process the message
|
|
342
|
+
const parsed = this._messageParser.parseIncomingMessage(data);
|
|
343
|
+
if (parsed && parsed.type === "server") {
|
|
344
|
+
// Handle server events
|
|
345
|
+
this._handleServerEvent(parsed.event);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
catch (error) {
|
|
349
|
+
logger_js_1.logger.error("Error handling incoming message", { error });
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
_handleServerEvent(event) {
|
|
353
|
+
// Extract session information from events
|
|
354
|
+
if (event.type === "session.created" && event.session?.id) {
|
|
355
|
+
this._sessionId = event.session.id;
|
|
356
|
+
logger_js_1.logger.info("Session created", { sessionId: this._sessionId });
|
|
357
|
+
}
|
|
358
|
+
// Notify handler-based subscriptions
|
|
359
|
+
this._notifyServerEvent(event);
|
|
360
|
+
}
|
|
361
|
+
_notifyConnectionEvent(eventType, args) {
|
|
362
|
+
const context = {
|
|
363
|
+
endpoint: this._endpoint,
|
|
364
|
+
sessionId: this._sessionId,
|
|
365
|
+
timestamp: new Date(),
|
|
366
|
+
model: this._model,
|
|
367
|
+
};
|
|
368
|
+
// Fire and forget - don't await to avoid blocking
|
|
369
|
+
this._subscriptionManager.processConnectionEvent(eventType, args, context).catch((error) => {
|
|
370
|
+
logger_js_1.logger.error("Error processing connection event in handlers", { eventType, error });
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
_notifyServerEvent(event) {
|
|
374
|
+
if (!this._sessionId) {
|
|
375
|
+
// Can't notify server events without a session ID
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
const context = {
|
|
379
|
+
endpoint: this._endpoint,
|
|
380
|
+
sessionId: this._sessionId,
|
|
381
|
+
timestamp: new Date(),
|
|
382
|
+
model: this._model,
|
|
383
|
+
conversationId: undefined, // Could extract from event if available
|
|
384
|
+
};
|
|
385
|
+
// Fire and forget - don't await to avoid blocking
|
|
386
|
+
this._subscriptionManager.processServerEvent(event, context).catch((error) => {
|
|
387
|
+
logger_js_1.logger.error("Error processing server event in handlers", { eventType: event.type, error });
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
async _sendEvent(event, options) {
|
|
391
|
+
if (!this._connectionManager?.isConnected) {
|
|
392
|
+
throw new index_js_1.VoiceLiveConnectionError("Not connected to Voice Live service", "NOT_CONNECTED");
|
|
393
|
+
}
|
|
394
|
+
try {
|
|
395
|
+
const serialized = this._messageParser.serializeOutgoingMessage(event);
|
|
396
|
+
await this._connectionManager.send(serialized, options.abortSignal);
|
|
397
|
+
logger_js_1.logger.info("Sent event", { type: event.type, eventId: event.eventId });
|
|
398
|
+
}
|
|
399
|
+
catch (error) {
|
|
400
|
+
if (error instanceof index_js_1.VoiceLiveConnectionError) {
|
|
401
|
+
throw error;
|
|
402
|
+
}
|
|
403
|
+
throw (0, index_js_1.classifyConnectionError)(error);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
_ensureConnected() {
|
|
407
|
+
this._ensureNotDisposed();
|
|
408
|
+
if (!this.isConnected) {
|
|
409
|
+
throw new index_js_1.VoiceLiveConnectionError("Must be connected to Voice Live service", "NOT_CONNECTED");
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
_ensureNotDisposed() {
|
|
413
|
+
if (this._disposed) {
|
|
414
|
+
throw new index_js_1.VoiceLiveConnectionError("Session is permanently dead and cannot be used. Create a new session to continue.", "SESSION_DEAD");
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
_generateEventId() {
|
|
418
|
+
return `evt_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
|
|
419
|
+
}
|
|
420
|
+
_generateTurnId() {
|
|
421
|
+
return `turn_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
|
|
422
|
+
}
|
|
423
|
+
_arrayBufferToBase64(buffer) {
|
|
424
|
+
const bytes = buffer instanceof ArrayBuffer ? new Uint8Array(buffer) : buffer;
|
|
425
|
+
let binary = "";
|
|
426
|
+
for (let i = 0; i < bytes.byteLength; i++) {
|
|
427
|
+
binary += String.fromCharCode(bytes[i]);
|
|
428
|
+
}
|
|
429
|
+
return btoa(binary);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
exports.VoiceLiveSession = VoiceLiveSession;
|
|
433
|
+
//# sourceMappingURL=voiceLiveSession.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"voiceLiveSession.js","sourceRoot":"","sources":["../../src/voiceLiveSession.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport type { KeyCredential, TokenCredential } from \"@azure/core-auth\";\nimport type { AbortSignalLike } from \"@azure/abort-controller\";\nimport type {\n RequestSession,\n ClientEventSessionUpdate,\n ClientEventUnion,\n ClientEventInputAudioBufferAppend,\n ClientEventInputAudioTurnStart,\n ClientEventInputAudioTurnAppend,\n ClientEventInputAudioTurnEnd,\n ConversationRequestItem,\n ClientEventConversationItemCreate,\n ServerEventUnion,\n} from \"./models/index.js\";\nimport { ConnectionManager, ConnectionState } from \"./websocket/connectionManager.js\";\nimport { VoiceLiveWebSocketFactory } from \"./websocket/websocketFactory.js\";\nimport { VoiceLiveMessageParser } from \"./protocol/messageParser.js\";\nimport { CredentialHandler } from \"./auth/credentialHandler.js\";\nimport { VoiceLiveConnectionError, classifyConnectionError } from \"./errors/index.js\";\nimport { logger } from \"./logger.js\";\nimport type {\n VoiceLiveSessionHandlers,\n VoiceLiveSubscription,\n ConnectionContext,\n SessionContext,\n ConnectedEventArgs,\n DisconnectedEventArgs,\n ErrorEventArgs,\n} from \"./handlers/sessionHandlers.js\";\nimport { SubscriptionManager } from \"./handlers/subscriptionManager.js\";\n\nexport interface VoiceLiveSessionOptions {\n /** Connection timeout in milliseconds */\n connectionTimeoutInMs?: number;\n /** Enable debug logging for development */\n enableDebugLogging?: boolean;\n}\n\nexport interface CreateSessionOptions extends VoiceLiveSessionOptions {}\n\nexport interface StartSessionOptions extends VoiceLiveSessionOptions {}\nexport interface ConnectOptions {\n /** Abort signal to cancel connection attempt */\n abortSignal?: AbortSignalLike;\n /** Override connection timeout for this operation */\n timeoutInMs?: number;\n}\n\nexport interface SendEventOptions {\n /** Abort signal to cancel send operation */\n abortSignal?: AbortSignalLike;\n /** Timeout for send operation */\n timeoutInMs?: number;\n}\n\nexport interface AudioStreamOptions extends SendEventOptions {\n /** Turn ID for turn-based audio (if not provided, uses buffer mode) */\n turnId?: string;\n}\n\nexport interface TurnOptions extends SendEventOptions {\n /** Custom turn ID (if not provided, one will be generated) */\n turnId?: string;\n}\n\n/**\n * Represents a WebSocket-based session for real-time voice communication with the Azure VoiceLive service.\n *\n * This class manages the connection, handles real-time communication, and provides access to all\n * interactive features including audio streaming, conversation management, and avatar control.\n */\nexport class VoiceLiveSession {\n private readonly _endpoint: string;\n private readonly _credentialHandler: CredentialHandler;\n private readonly _options: Required<VoiceLiveSessionOptions>;\n private readonly _messageParser: VoiceLiveMessageParser;\n private readonly _model: string;\n private _connectionManager?: ConnectionManager;\n private _sessionId?: string;\n private _activeTurnId?: string;\n private _disposed = false;\n\n // Handler-based subscription management\n private readonly _subscriptionManager: SubscriptionManager;\n\n /**\n * Creates an instance of VoiceLiveSession.\n *\n * @param endpoint - The Voice Live service endpoint URL\n * @param credential - Azure TokenCredential or KeyCredential for authentication\n * @param apiVersion - API version to use for the Voice Live service\n * @param model - The model name to use for the session\n * @param options - Optional configuration for the session\n */\n constructor(\n endpoint: string,\n credential: TokenCredential | KeyCredential,\n apiVersion: string,\n model: string,\n options: VoiceLiveSessionOptions = {},\n ) {\n this._endpoint = this._normalizeEndpoint(endpoint);\n this._credentialHandler = new CredentialHandler(credential);\n this._options = this._buildDefaultOptions(options);\n this._messageParser = new VoiceLiveMessageParser();\n this._model = model;\n\n // Initialize handler-based subscription management\n this._subscriptionManager = new SubscriptionManager();\n\n logger.info(\"VoiceLiveSession created\", {\n endpoint: this._endpoint,\n model: this._model,\n apiVersion: apiVersion,\n enableDebugLogging: this._options.enableDebugLogging,\n });\n }\n\n /**\n * Establishes connection to the Voice Live service with authentication.\n */\n async connect(options: ConnectOptions = {}): Promise<void> {\n this._ensureNotDisposed();\n\n if (this.isConnected) {\n logger.info(\"VoiceLiveSession already connected\");\n return;\n }\n\n try {\n logger.info(\"Connecting to Voice Live service\", {\n endpoint: this._endpoint,\n model: this._model,\n });\n\n // Get WebSocket URL with authentication and model\n const wsUrl = await this._credentialHandler.getWebSocketUrl(\n this._endpoint,\n \"2025-10-01\", // TODO: make this configurable through constructor\n this._model,\n );\n const authHeaders = await this._credentialHandler.getAuthHeaders();\n\n // Create connection manager\n const websocketFactory = new VoiceLiveWebSocketFactory();\n this._connectionManager = new ConnectionManager(\n () =>\n websocketFactory.create({\n headers: { ...authHeaders },\n connectionTimeoutInMs: this._options.connectionTimeoutInMs,\n compression: true,\n }),\n {\n endpoint: wsUrl,\n connectionTimeout: options.timeoutInMs || this._options.connectionTimeoutInMs,\n },\n );\n\n // Setup connection event handlers\n this._setupConnectionEventHandlers();\n\n // Connect with proper error handling\n await this._connectionManager.connect(options.abortSignal);\n\n logger.info(\"Successfully connected to Voice Live service\");\n } catch (error) {\n logger.error(\"Failed to connect to Voice Live service\", { error });\n\n // Use error classification\n if (error instanceof VoiceLiveConnectionError) {\n throw error;\n } else {\n throw classifyConnectionError(error);\n }\n }\n }\n\n /**\n * Disconnects from the Voice Live service and cleans up resources.\n */\n async disconnect(): Promise<void> {\n if (!this._connectionManager) {\n return;\n }\n\n logger.info(\"Disconnecting from Voice Live service\");\n\n try {\n await this._connectionManager.disconnect();\n } catch (error) {\n logger.error(\"Error during disconnect\", { error });\n } finally {\n this._connectionManager = undefined;\n this._sessionId = undefined;\n this._activeTurnId = undefined;\n logger.info(\"Disconnected from Voice Live service\");\n }\n }\n\n /**\n * Subscribe to VoiceLive session events using strongly-typed handlers.\n * This follows the Azure SDK pattern used by EventHub, Service Bus, etc.\n *\n * @param handlers - Handler functions for different types of events\n * @returns A subscription object that can be used to stop receiving events\n */\n subscribe(handlers: VoiceLiveSessionHandlers): VoiceLiveSubscription {\n this._ensureNotDisposed();\n\n logger.info(\"Creating VoiceLive session subscription\");\n return this._subscriptionManager.createSubscription(handlers);\n }\n\n /**\n * Sends a custom client event to the service.\n */\n async sendEvent(event: ClientEventUnion, options: SendEventOptions = {}): Promise<void> {\n this._ensureConnected();\n await this._sendEvent(event, options);\n }\n\n /**\n * Updates the session configuration with the service.\n */\n async updateSession(session: RequestSession, options: SendEventOptions = {}): Promise<void> {\n this._ensureConnected();\n\n const updateEvent: ClientEventSessionUpdate = {\n type: \"session.update\",\n session: session,\n eventId: this._generateEventId(),\n };\n\n await this._sendEvent(updateEvent, options);\n }\n\n /**\n * Sends audio data to the service using turn-based or buffer-based approach.\n */\n async sendAudio(\n audioData: ArrayBuffer | Uint8Array,\n options: AudioStreamOptions = {},\n ): Promise<void> {\n this._ensureConnected();\n\n const audioBase64 = this._arrayBufferToBase64(audioData);\n\n if (options.turnId) {\n // Turn-based audio\n const appendEvent: ClientEventInputAudioTurnAppend = {\n type: \"input_audio.turn.append\",\n audio: audioBase64,\n turnId: options.turnId,\n eventId: this._generateEventId(),\n };\n await this._sendEvent(appendEvent, options);\n } else {\n // Buffer-based audio (VAD mode)\n const bufferEvent: ClientEventInputAudioBufferAppend = {\n type: \"input_audio_buffer.append\",\n audio: audioBase64,\n eventId: this._generateEventId(),\n };\n await this._sendEvent(bufferEvent, options);\n }\n }\n\n /**\n * Starts a new audio turn for turn-based audio input.\n */\n async startAudioTurn(options: TurnOptions = {}): Promise<string> {\n this._ensureConnected();\n\n const turnId = options.turnId || this._generateTurnId();\n this._activeTurnId = turnId;\n\n const startEvent: ClientEventInputAudioTurnStart = {\n type: \"input_audio.turn.start\",\n turnId: turnId,\n eventId: this._generateEventId(),\n };\n\n await this._sendEvent(startEvent, options);\n return turnId;\n }\n\n /**\n * Ends the current audio turn.\n */\n async endAudioTurn(turnId?: string, options: SendEventOptions = {}): Promise<void> {\n this._ensureConnected();\n\n const targetTurnId = turnId || this._activeTurnId;\n if (!targetTurnId) {\n throw new VoiceLiveConnectionError(\"No active audio turn to end\", \"INVALID_STATE\");\n }\n\n const endEvent: ClientEventInputAudioTurnEnd = {\n type: \"input_audio.turn.end\",\n turnId: targetTurnId,\n eventId: this._generateEventId(),\n };\n\n await this._sendEvent(endEvent, options);\n\n if (targetTurnId === this._activeTurnId) {\n this._activeTurnId = undefined;\n }\n }\n\n /**\n * Adds a conversation item (message) to the conversation.\n */\n async addConversationItem(\n item: ConversationRequestItem,\n options: SendEventOptions = {},\n ): Promise<void> {\n this._ensureConnected();\n\n const createEvent: ClientEventConversationItemCreate = {\n type: \"conversation.item.create\",\n item: item,\n eventId: this._generateEventId(),\n };\n\n await this._sendEvent(createEvent, options);\n }\n\n /**\n * Indicates whether the session is currently connected to the Voice Live service.\n */\n get isConnected(): boolean {\n return this._connectionManager?.isConnected || false;\n }\n\n /**\n * Gets the current connection state of the session.\n */\n get connectionState(): ConnectionState {\n return this._connectionManager?.state || ConnectionState.Disconnected;\n }\n\n /**\n * Gets the current session ID.\n */\n get sessionId(): string | undefined {\n return this._sessionId;\n }\n\n /**\n * Gets the current active audio turn ID.\n */\n get activeTurnId(): string | undefined {\n return this._activeTurnId;\n }\n\n /**\n * Disposes the session and cleans up resources.\n */\n async dispose(): Promise<void> {\n if (this._disposed) {\n return;\n }\n\n logger.info(\"Disposing VoiceLiveSession\");\n\n try {\n // Close all subscriptions first\n await this._subscriptionManager.closeAll();\n\n // Then disconnect\n await this.disconnect();\n } catch (error) {\n logger.error(\"Error during session disposal\", { error });\n } finally {\n this._disposed = true;\n logger.info(\"VoiceLiveSession disposed\");\n }\n }\n\n // Private methods\n\n private _buildDefaultOptions(\n options: VoiceLiveSessionOptions,\n ): Required<VoiceLiveSessionOptions> {\n return {\n connectionTimeoutInMs: options.connectionTimeoutInMs || 30000,\n enableDebugLogging: options.enableDebugLogging ?? false,\n };\n }\n\n private _normalizeEndpoint(endpoint: string): string {\n // Ensure endpoint has proper protocol\n if (!endpoint.startsWith(\"http://\") && !endpoint.startsWith(\"https://\")) {\n endpoint = `https://${endpoint}`;\n }\n\n // Remove trailing slash\n return endpoint.replace(/\\/$/, \"\");\n }\n\n private _setupConnectionEventHandlers(): void {\n if (!this._connectionManager) return;\n\n this._connectionManager.updateEventHandlers({\n onStateChange: (state, previousState) => {\n logger.info(\"Connection state changed\", { state, previousState });\n\n // Handle connection state changes for handler-based subscriptions\n if (state === ConnectionState.Connected && previousState === ConnectionState.Connecting) {\n this._notifyConnectionEvent(\"connected\", {\n connectionId: `conn_${Date.now()}`,\n timestamp: new Date(),\n });\n } else if (\n state === ConnectionState.Disconnected &&\n previousState === ConnectionState.Connected\n ) {\n this._notifyConnectionEvent(\"disconnected\", {\n code: 1006, // Abnormal closure\n reason: \"Connection lost during session\",\n wasClean: false,\n timestamp: new Date(),\n });\n this._markSessionAsDead(\n \"Connection lost during session - session is permanently unusable\",\n );\n }\n },\n onMessage: (data) => {\n this._handleIncomingMessage(data);\n },\n onError: (error) => {\n logger.error(\"Connection error - marking session as dead\", { error });\n this._notifyConnectionEvent(\"error\", {\n error: error,\n context: \"WebSocket connection error\",\n recoverable: false,\n timestamp: new Date(),\n });\n this._markSessionAsDead(`Connection error: ${error.message}`);\n },\n });\n }\n\n private _markSessionAsDead(reason: string): void {\n if (this._disposed) return;\n\n logger.error(\"Session marked as permanently dead\", { reason });\n\n // Mark as disposed to prevent further use\n this._disposed = true;\n\n // Clean up connection manager\n this._connectionManager = undefined;\n this._sessionId = undefined;\n this._activeTurnId = undefined;\n }\n\n private _handleIncomingMessage(data: string | ArrayBuffer): void {\n try {\n logger.info(\"Message received\", {\n type: typeof data,\n size: typeof data === \"string\" ? data.length : data.byteLength,\n });\n\n // Parse and process the message\n const parsed = this._messageParser.parseIncomingMessage(data);\n if (parsed && parsed.type === \"server\") {\n // Handle server events\n this._handleServerEvent(parsed.event);\n }\n } catch (error) {\n logger.error(\"Error handling incoming message\", { error });\n }\n }\n\n private _handleServerEvent(event: any): void {\n // Extract session information from events\n if (event.type === \"session.created\" && event.session?.id) {\n this._sessionId = event.session.id;\n logger.info(\"Session created\", { sessionId: this._sessionId });\n }\n\n // Notify handler-based subscriptions\n this._notifyServerEvent(event);\n }\n\n private _notifyConnectionEvent(\n eventType: \"connected\" | \"disconnected\" | \"error\",\n args: ConnectedEventArgs | DisconnectedEventArgs | ErrorEventArgs,\n ): void {\n const context: ConnectionContext = {\n endpoint: this._endpoint,\n sessionId: this._sessionId,\n timestamp: new Date(),\n model: this._model,\n };\n\n // Fire and forget - don't await to avoid blocking\n this._subscriptionManager.processConnectionEvent(eventType, args, context).catch((error) => {\n logger.error(\"Error processing connection event in handlers\", { eventType, error });\n });\n }\n\n private _notifyServerEvent(event: ServerEventUnion): void {\n if (!this._sessionId) {\n // Can't notify server events without a session ID\n return;\n }\n\n const context: SessionContext = {\n endpoint: this._endpoint,\n sessionId: this._sessionId,\n timestamp: new Date(),\n model: this._model,\n conversationId: undefined, // Could extract from event if available\n };\n\n // Fire and forget - don't await to avoid blocking\n this._subscriptionManager.processServerEvent(event, context).catch((error) => {\n logger.error(\"Error processing server event in handlers\", { eventType: event.type, error });\n });\n }\n\n private async _sendEvent(event: ClientEventUnion, options: SendEventOptions): Promise<void> {\n if (!this._connectionManager?.isConnected) {\n throw new VoiceLiveConnectionError(\"Not connected to Voice Live service\", \"NOT_CONNECTED\");\n }\n\n try {\n const serialized = this._messageParser.serializeOutgoingMessage(event);\n await this._connectionManager.send(serialized, options.abortSignal);\n\n logger.info(\"Sent event\", { type: event.type, eventId: (event as any).eventId });\n } catch (error) {\n if (error instanceof VoiceLiveConnectionError) {\n throw error;\n }\n\n throw classifyConnectionError(error);\n }\n }\n\n private _ensureConnected(): void {\n this._ensureNotDisposed();\n if (!this.isConnected) {\n throw new VoiceLiveConnectionError(\n \"Must be connected to Voice Live service\",\n \"NOT_CONNECTED\",\n );\n }\n }\n\n private _ensureNotDisposed(): void {\n if (this._disposed) {\n throw new VoiceLiveConnectionError(\n \"Session is permanently dead and cannot be used. Create a new session to continue.\",\n \"SESSION_DEAD\",\n );\n }\n }\n\n private _generateEventId(): string {\n return `evt_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;\n }\n\n private _generateTurnId(): string {\n return `turn_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;\n }\n\n private _arrayBufferToBase64(buffer: ArrayBuffer | Uint8Array): string {\n const bytes = buffer instanceof ArrayBuffer ? new Uint8Array(buffer) : buffer;\n let binary = \"\";\n for (let i = 0; i < bytes.byteLength; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary);\n }\n}\n"]}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { AbortSignalLike } from "@azure/abort-controller";
|
|
2
|
+
import type { VoiceLiveWebSocketLike } from "./websocketLike.js";
|
|
3
|
+
import { VoiceLiveConnectionError } from "../errors/index.js";
|
|
4
|
+
/**
|
|
5
|
+
* Connection state enumeration for lifecycle management
|
|
6
|
+
*/
|
|
7
|
+
export declare enum ConnectionState {
|
|
8
|
+
Disconnected = "disconnected",
|
|
9
|
+
Connecting = "connecting",
|
|
10
|
+
Connected = "connected",
|
|
11
|
+
Disconnecting = "disconnecting"
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Configuration options for connection management
|
|
15
|
+
*/
|
|
16
|
+
export interface ConnectionOptions {
|
|
17
|
+
/** WebSocket endpoint URL */
|
|
18
|
+
endpoint: string;
|
|
19
|
+
/** WebSocket protocols to use */
|
|
20
|
+
protocols?: string[];
|
|
21
|
+
/** Connection timeout in milliseconds */
|
|
22
|
+
connectionTimeout?: number;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Event handlers for connection lifecycle events
|
|
26
|
+
*/
|
|
27
|
+
export interface ConnectionEventHandlers {
|
|
28
|
+
/** Called when connection state changes */
|
|
29
|
+
onStateChange?: (state: ConnectionState, previousState: ConnectionState) => void;
|
|
30
|
+
/** Called when a message is received */
|
|
31
|
+
onMessage?: (data: string | ArrayBuffer) => void;
|
|
32
|
+
/** Called when an error occurs or connection is lost */
|
|
33
|
+
onError?: (error: VoiceLiveConnectionError) => void;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Manages WebSocket connection lifecycle with fail-fast semantics
|
|
37
|
+
*/
|
|
38
|
+
export declare class ConnectionManager {
|
|
39
|
+
private _websocketFactory;
|
|
40
|
+
private _options;
|
|
41
|
+
private _eventHandlers;
|
|
42
|
+
private _state;
|
|
43
|
+
private _previousState;
|
|
44
|
+
private _websocket?;
|
|
45
|
+
private _abortController?;
|
|
46
|
+
constructor(_websocketFactory: () => VoiceLiveWebSocketLike, _options: ConnectionOptions, _eventHandlers?: ConnectionEventHandlers);
|
|
47
|
+
/**
|
|
48
|
+
* Initiates a WebSocket connection
|
|
49
|
+
*/
|
|
50
|
+
connect(abortSignal?: AbortSignalLike): Promise<void>;
|
|
51
|
+
/**
|
|
52
|
+
* Disconnects the WebSocket connection
|
|
53
|
+
*/
|
|
54
|
+
disconnect(_abortSignal?: AbortSignalLike): Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* Sends data through the WebSocket connection
|
|
57
|
+
*/
|
|
58
|
+
send(data: string | ArrayBuffer, abortSignal?: AbortSignalLike): Promise<void>;
|
|
59
|
+
/**
|
|
60
|
+
* Sets up event handlers for the WebSocket instance
|
|
61
|
+
*/
|
|
62
|
+
private _setupWebSocketHandlers;
|
|
63
|
+
/**
|
|
64
|
+
* Handles any connection loss - always fatal in fail-fast model
|
|
65
|
+
*/
|
|
66
|
+
private _handleConnectionLost;
|
|
67
|
+
/**
|
|
68
|
+
* Handles WebSocket errors
|
|
69
|
+
*/
|
|
70
|
+
private _handleConnectionError;
|
|
71
|
+
/**
|
|
72
|
+
* Updates the connection state and notifies handlers
|
|
73
|
+
*/
|
|
74
|
+
private _setState;
|
|
75
|
+
/**
|
|
76
|
+
* Gets the current connection state
|
|
77
|
+
*/
|
|
78
|
+
get state(): ConnectionState;
|
|
79
|
+
/**
|
|
80
|
+
* Checks if the connection is currently established
|
|
81
|
+
*/
|
|
82
|
+
get isConnected(): boolean;
|
|
83
|
+
/**
|
|
84
|
+
* Updates the event handlers
|
|
85
|
+
*/
|
|
86
|
+
updateEventHandlers(handlers: Partial<ConnectionEventHandlers>): void;
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=connectionManager.d.ts.map
|