@ourlu/assistant-sdk 0.2.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.
- package/README.md +109 -0
- package/dist/esm/index.js +436 -0
- package/dist/iife/audio.v1.js +179 -0
- package/dist/iife/engine.v1.js +645 -0
- package/dist/iife/loader.v1.js +71 -0
- package/dist/iife/ui.v1.js +919 -0
- package/package.json +39 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
(function() {
|
|
2
|
+
"use strict";
|
|
3
|
+
var runtime = window.__CompanionWidgetRuntimeV1 || (window.__CompanionWidgetRuntimeV1 = {});
|
|
4
|
+
if (!runtime.utils) {
|
|
5
|
+
throw new Error("Widget runtime utils module must be loaded first.");
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
var mergeTranscript = runtime.utils.mergeTranscript;
|
|
9
|
+
var resolveRecorderMimeType = runtime.utils.resolveRecorderMimeType;
|
|
10
|
+
var encodeArrayBufferToBase64 = runtime.utils.encodeArrayBufferToBase64;
|
|
11
|
+
|
|
12
|
+
function WidgetAudioManager(state, api, ui) {
|
|
13
|
+
this.state = state;
|
|
14
|
+
this.api = api;
|
|
15
|
+
this.ui = ui;
|
|
16
|
+
this.stream = null;
|
|
17
|
+
this.recorder = null;
|
|
18
|
+
this.chunks = [];
|
|
19
|
+
this.stopDraftStream = null;
|
|
20
|
+
this.activeAudioSessionId = "";
|
|
21
|
+
this.cleanupTimer = null;
|
|
22
|
+
this._starting = false;
|
|
23
|
+
this._stopping = false;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
WidgetAudioManager.prototype.isSupported = function() {
|
|
27
|
+
return !!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia && typeof MediaRecorder !== "undefined");
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
WidgetAudioManager.prototype.toggle = async function() {
|
|
31
|
+
if (this._starting || this._stopping) return;
|
|
32
|
+
if (this.state.listening) return this.stop();
|
|
33
|
+
return this.start();
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
WidgetAudioManager.prototype.start = async function() {
|
|
37
|
+
if (!this.isSupported() || this.state.sending || this._starting || this._stopping) return;
|
|
38
|
+
this._starting = true;
|
|
39
|
+
this.ui.setMicListening(true);
|
|
40
|
+
try {
|
|
41
|
+
this.ui.showError("");
|
|
42
|
+
this.clearDraftCleanup();
|
|
43
|
+
if (this.activeAudioSessionId && this.state.sessionId) {
|
|
44
|
+
try {
|
|
45
|
+
await this.api.closeAudioSession(this.state.sessionId, this.activeAudioSessionId);
|
|
46
|
+
} catch (_) { /* best-effort */ }
|
|
47
|
+
this.activeAudioSessionId = "";
|
|
48
|
+
}
|
|
49
|
+
var sessionId = await this.api.ensureSession();
|
|
50
|
+
if (this.stopDraftStream) this.stopDraftStream();
|
|
51
|
+
this.stopDraftStream = await this.api.streamAudioDraft(sessionId, {
|
|
52
|
+
onDelta: this.handleDraftDelta.bind(this),
|
|
53
|
+
onComplete: this.handleDraftComplete.bind(this),
|
|
54
|
+
onError: this.handleDraftError.bind(this),
|
|
55
|
+
onTransportError: this.handleDraftTransportError.bind(this)
|
|
56
|
+
});
|
|
57
|
+
var audioSession = await this.api.startAudioSession(sessionId);
|
|
58
|
+
this.activeAudioSessionId = audioSession.audio_session_id || "";
|
|
59
|
+
this.stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
60
|
+
this.chunks = [];
|
|
61
|
+
var mimeType = resolveRecorderMimeType();
|
|
62
|
+
this.recorder = new MediaRecorder(this.stream, mimeType ? { mimeType: mimeType } : {});
|
|
63
|
+
var self = this;
|
|
64
|
+
this.recorder.ondataavailable = function(event) {
|
|
65
|
+
if (event.data && event.data.size > 0) self.chunks.push(event.data);
|
|
66
|
+
};
|
|
67
|
+
this.recorder.start(250);
|
|
68
|
+
this.state.listening = true;
|
|
69
|
+
} catch (error) {
|
|
70
|
+
this.cleanupMedia();
|
|
71
|
+
this.activeAudioSessionId = "";
|
|
72
|
+
this.state.listening = false;
|
|
73
|
+
this.ui.setMicListening(false);
|
|
74
|
+
this.ui.showError("Erreur démarrage audio : " + (error.message || error));
|
|
75
|
+
} finally {
|
|
76
|
+
this._starting = false;
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
WidgetAudioManager.prototype.stop = async function() {
|
|
81
|
+
if (!this.state.listening || this._stopping) return;
|
|
82
|
+
this._stopping = true;
|
|
83
|
+
this.state.listening = false;
|
|
84
|
+
this.ui.setMicListening(false);
|
|
85
|
+
var sessionId = this.state.sessionId;
|
|
86
|
+
var audioSessionId = this.activeAudioSessionId;
|
|
87
|
+
try {
|
|
88
|
+
var blob = await this.stopRecorder();
|
|
89
|
+
if (blob && blob.size > 0 && sessionId && audioSessionId) {
|
|
90
|
+
var base64 = encodeArrayBufferToBase64(await blob.arrayBuffer());
|
|
91
|
+
await this.api.sendAudioChunk(sessionId, audioSessionId, base64);
|
|
92
|
+
}
|
|
93
|
+
if (sessionId && audioSessionId) await this.api.closeAudioSession(sessionId, audioSessionId);
|
|
94
|
+
} catch (error) {
|
|
95
|
+
this.ui.showError("Erreur fermeture audio : " + (error.message || error));
|
|
96
|
+
} finally {
|
|
97
|
+
this.activeAudioSessionId = "";
|
|
98
|
+
this._stopping = false;
|
|
99
|
+
this.scheduleDraftCleanup();
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
WidgetAudioManager.prototype.stopRecorder = function() {
|
|
104
|
+
var self = this;
|
|
105
|
+
if (!this.recorder) {
|
|
106
|
+
this.cleanupMedia();
|
|
107
|
+
return Promise.resolve(new Blob());
|
|
108
|
+
}
|
|
109
|
+
return new Promise(function(resolve) {
|
|
110
|
+
var recorder = self.recorder;
|
|
111
|
+
function done() {
|
|
112
|
+
var blob = new Blob(self.chunks, { type: recorder.mimeType || "audio/webm" });
|
|
113
|
+
self.cleanupMedia();
|
|
114
|
+
resolve(blob);
|
|
115
|
+
}
|
|
116
|
+
if (recorder.state === "inactive") return done();
|
|
117
|
+
recorder.addEventListener("stop", done, { once: true });
|
|
118
|
+
recorder.stop();
|
|
119
|
+
});
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
WidgetAudioManager.prototype.cleanupMedia = function() {
|
|
123
|
+
if (this.recorder) {
|
|
124
|
+
this.recorder.ondataavailable = null;
|
|
125
|
+
this.recorder = null;
|
|
126
|
+
}
|
|
127
|
+
if (this.stream) {
|
|
128
|
+
this.stream.getTracks().forEach(function(track) { track.stop(); });
|
|
129
|
+
this.stream = null;
|
|
130
|
+
}
|
|
131
|
+
this.chunks = [];
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
WidgetAudioManager.prototype.handleDraftDelta = function(payload) {
|
|
135
|
+
if (!payload || !payload.delta) return;
|
|
136
|
+
this.ui.setInput(mergeTranscript(this.ui.inputValue(), payload.delta));
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
WidgetAudioManager.prototype.handleDraftComplete = function(payload) {
|
|
140
|
+
if (payload && payload.text) this.ui.setInput(String(payload.text).trim());
|
|
141
|
+
this.scheduleDraftCleanup();
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
WidgetAudioManager.prototype.handleDraftError = function(payload) {
|
|
145
|
+
this.ui.showError((payload && payload.message) || "Erreur transcription audio");
|
|
146
|
+
this.scheduleDraftCleanup();
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
WidgetAudioManager.prototype.isExpectedStreamAbortMessage = function(message) {
|
|
150
|
+
var normalized = String(message || "").toLowerCase();
|
|
151
|
+
return normalized.indexOf("aborted") !== -1 ||
|
|
152
|
+
normalized.indexOf("aborterror") !== -1 ||
|
|
153
|
+
normalized.indexOf("body stream buffer was aborted") !== -1 ||
|
|
154
|
+
normalized.indexOf("the operation was aborted") !== -1;
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
WidgetAudioManager.prototype.handleDraftTransportError = function(message) {
|
|
158
|
+
if (this.isExpectedStreamAbortMessage(message)) return;
|
|
159
|
+
this.ui.showError(message || "Erreur stream audio");
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
WidgetAudioManager.prototype.clearDraftCleanup = function() {
|
|
163
|
+
if (!this.cleanupTimer) return;
|
|
164
|
+
clearTimeout(this.cleanupTimer);
|
|
165
|
+
this.cleanupTimer = null;
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
WidgetAudioManager.prototype.scheduleDraftCleanup = function() {
|
|
169
|
+
var self = this;
|
|
170
|
+
this.clearDraftCleanup();
|
|
171
|
+
this.cleanupTimer = setTimeout(function() {
|
|
172
|
+
if (self.stopDraftStream) self.stopDraftStream();
|
|
173
|
+
self.stopDraftStream = null;
|
|
174
|
+
self.cleanupTimer = null;
|
|
175
|
+
}, 3000);
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
runtime.WidgetAudioManager = WidgetAudioManager;
|
|
179
|
+
})();
|