@ariaflowagents/livekit-plugin-transport-ws 0.9.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/dist/audio_input.d.ts +25 -0
- package/dist/audio_input.d.ts.map +1 -0
- package/dist/audio_input.js +93 -0
- package/dist/audio_input.js.map +1 -0
- package/dist/audio_output.d.ts +26 -0
- package/dist/audio_output.d.ts.map +1 -0
- package/dist/audio_output.js +134 -0
- package/dist/audio_output.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/protocol.d.ts +65 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +44 -0
- package/dist/protocol.js.map +1 -0
- package/dist/realtime_bridge.d.ts +43 -0
- package/dist/realtime_bridge.d.ts.map +1 -0
- package/dist/realtime_bridge.js +192 -0
- package/dist/realtime_bridge.js.map +1 -0
- package/dist/server.d.ts +73 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +247 -0
- package/dist/server.js.map +1 -0
- package/dist/text_output.d.ts +17 -0
- package/dist/text_output.d.ts.map +1 -0
- package/dist/text_output.js +77 -0
- package/dist/text_output.js.map +1 -0
- package/dist/transport_adapter.d.ts +34 -0
- package/dist/transport_adapter.d.ts.map +1 -0
- package/dist/transport_adapter.js +72 -0
- package/dist/transport_adapter.js.map +1 -0
- package/dist/types.d.ts +14 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +47 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { AudioInput } from '@ariaflow/livekit-plugin';
|
|
2
|
+
import type { WebSocket } from 'ws';
|
|
3
|
+
/**
|
|
4
|
+
* Receives audio from a WebSocket connection and provides it as a
|
|
5
|
+
* ReadableStream<AudioFrame> to the AgentSession's STT pipeline.
|
|
6
|
+
*
|
|
7
|
+
* Binary WebSocket messages are raw PCM bytes. AudioByteStream accumulates
|
|
8
|
+
* bytes and emits AudioFrame objects at 100ms intervals.
|
|
9
|
+
*/
|
|
10
|
+
export declare class WebSocketAudioInput extends AudioInput {
|
|
11
|
+
private ws;
|
|
12
|
+
private byteStream;
|
|
13
|
+
private currentStreamId;
|
|
14
|
+
private streamWriter;
|
|
15
|
+
private closed;
|
|
16
|
+
constructor(ws: WebSocket, sampleRate?: number, numChannels?: number);
|
|
17
|
+
private setupListeners;
|
|
18
|
+
private handleAudioData;
|
|
19
|
+
private startNewStream;
|
|
20
|
+
private endCurrentStream;
|
|
21
|
+
/** Called when client sends 'end_of_audio'. Flushes remaining audio. */
|
|
22
|
+
endOfAudio(): void;
|
|
23
|
+
close(): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=audio_input.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audio_input.d.ts","sourceRoot":"","sources":["../src/audio_input.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAoC,MAAM,0BAA0B,CAAC;AAExF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAEpC;;;;;;GAMG;AACH,qBAAa,mBAAoB,SAAQ,UAAU;IAO/C,OAAO,CAAC,EAAE;IANZ,OAAO,CAAC,UAAU,CAAkB;IACpC,OAAO,CAAC,eAAe,CAAuB;IAC9C,OAAO,CAAC,YAAY,CAAwD;IAC5E,OAAO,CAAC,MAAM,CAAkB;gBAGtB,EAAE,EAAE,SAAS,EACrB,UAAU,GAAE,MAAc,EAC1B,WAAW,GAAE,MAAU;IAOzB,OAAO,CAAC,cAAc;IAetB,OAAO,CAAC,eAAe;IAuBvB,OAAO,CAAC,cAAc;IAMtB,OAAO,CAAC,gBAAgB;IAcxB,wEAAwE;IACxE,UAAU,IAAI,IAAI;IAgBZ,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAK7B"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { AudioInput, AudioByteStream } from '@ariaflow/livekit-plugin';
|
|
2
|
+
import { TransformStream } from 'node:stream/web';
|
|
3
|
+
/**
|
|
4
|
+
* Receives audio from a WebSocket connection and provides it as a
|
|
5
|
+
* ReadableStream<AudioFrame> to the AgentSession's STT pipeline.
|
|
6
|
+
*
|
|
7
|
+
* Binary WebSocket messages are raw PCM bytes. AudioByteStream accumulates
|
|
8
|
+
* bytes and emits AudioFrame objects at 100ms intervals.
|
|
9
|
+
*/
|
|
10
|
+
export class WebSocketAudioInput extends AudioInput {
|
|
11
|
+
ws;
|
|
12
|
+
byteStream;
|
|
13
|
+
currentStreamId = null;
|
|
14
|
+
streamWriter = null;
|
|
15
|
+
closed = false;
|
|
16
|
+
constructor(ws, sampleRate = 24000, numChannels = 1) {
|
|
17
|
+
super();
|
|
18
|
+
this.ws = ws;
|
|
19
|
+
this.byteStream = new AudioByteStream(sampleRate, numChannels);
|
|
20
|
+
this.setupListeners();
|
|
21
|
+
}
|
|
22
|
+
setupListeners() {
|
|
23
|
+
this.ws.on('message', (data, isBinary) => {
|
|
24
|
+
if (!isBinary || this.closed)
|
|
25
|
+
return;
|
|
26
|
+
this.handleAudioData(data);
|
|
27
|
+
});
|
|
28
|
+
this.ws.on('close', () => {
|
|
29
|
+
this.endCurrentStream();
|
|
30
|
+
});
|
|
31
|
+
this.ws.on('error', () => {
|
|
32
|
+
this.endCurrentStream();
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
handleAudioData(data) {
|
|
36
|
+
if (!this.currentStreamId) {
|
|
37
|
+
this.startNewStream();
|
|
38
|
+
}
|
|
39
|
+
const frames = this.byteStream.write(data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength));
|
|
40
|
+
for (const frame of frames) {
|
|
41
|
+
if (this.streamWriter) {
|
|
42
|
+
this.streamWriter.write(frame).catch((err) => {
|
|
43
|
+
console.error('[WebSocketAudioInput] Error writing frame:', {
|
|
44
|
+
error: err instanceof Error ? err.message : String(err),
|
|
45
|
+
streamId: this.currentStreamId,
|
|
46
|
+
timestamp: new Date().toISOString(),
|
|
47
|
+
});
|
|
48
|
+
this.endCurrentStream();
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
startNewStream() {
|
|
54
|
+
const { readable, writable } = new TransformStream();
|
|
55
|
+
this.streamWriter = writable.getWriter();
|
|
56
|
+
this.currentStreamId = this.multiStream.addInputStream(readable);
|
|
57
|
+
}
|
|
58
|
+
endCurrentStream() {
|
|
59
|
+
if (this.streamWriter) {
|
|
60
|
+
this.streamWriter.close().catch((err) => {
|
|
61
|
+
console.error('[WebSocketAudioInput] Error closing writer:', {
|
|
62
|
+
error: err instanceof Error ? err.message : String(err),
|
|
63
|
+
streamId: this.currentStreamId,
|
|
64
|
+
timestamp: new Date().toISOString(),
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
this.streamWriter = null;
|
|
68
|
+
}
|
|
69
|
+
this.currentStreamId = null;
|
|
70
|
+
}
|
|
71
|
+
/** Called when client sends 'end_of_audio'. Flushes remaining audio. */
|
|
72
|
+
endOfAudio() {
|
|
73
|
+
const frames = this.byteStream.flush();
|
|
74
|
+
for (const frame of frames) {
|
|
75
|
+
if (this.streamWriter) {
|
|
76
|
+
this.streamWriter.write(frame).catch((err) => {
|
|
77
|
+
console.error('[WebSocketAudioInput] Error writing flush frame:', {
|
|
78
|
+
error: err instanceof Error ? err.message : String(err),
|
|
79
|
+
streamId: this.currentStreamId,
|
|
80
|
+
timestamp: new Date().toISOString(),
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
this.endCurrentStream();
|
|
86
|
+
}
|
|
87
|
+
async close() {
|
|
88
|
+
this.closed = true;
|
|
89
|
+
this.endCurrentStream();
|
|
90
|
+
await super.close();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=audio_input.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audio_input.js","sourceRoot":"","sources":["../src/audio_input.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { AudioOutput, type AudioFrame } from '@ariaflow/livekit-plugin';
|
|
2
|
+
import type { WebSocket } from 'ws';
|
|
3
|
+
/**
|
|
4
|
+
* Sends audio frames from the TTS pipeline to the WebSocket client
|
|
5
|
+
* as binary messages containing raw PCM data.
|
|
6
|
+
*/
|
|
7
|
+
export declare class WebSocketAudioOutput extends AudioOutput {
|
|
8
|
+
private ws;
|
|
9
|
+
private contextLabel;
|
|
10
|
+
private sendQueue;
|
|
11
|
+
private sending;
|
|
12
|
+
private segmentSamplesSent;
|
|
13
|
+
private flushed;
|
|
14
|
+
private closed;
|
|
15
|
+
private segmentIndex;
|
|
16
|
+
private segmentStartedAt;
|
|
17
|
+
private frameCountInSegment;
|
|
18
|
+
constructor(ws: WebSocket, contextLabel?: string, sampleRate?: number);
|
|
19
|
+
captureFrame(frame: AudioFrame): Promise<void>;
|
|
20
|
+
private processSendQueue;
|
|
21
|
+
private drainAsync;
|
|
22
|
+
flush(): void;
|
|
23
|
+
clearBuffer(): void;
|
|
24
|
+
close(): Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=audio_output.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audio_output.d.ts","sourceRoot":"","sources":["../src/audio_output.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACxE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAEpC;;;GAGG;AACH,qBAAa,oBAAqB,SAAQ,WAAW;IAWjD,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,YAAY;IAXtB,OAAO,CAAC,SAAS,CAAoB;IACrC,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,kBAAkB,CAAa;IACvC,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,mBAAmB,CAAK;gBAGtB,EAAE,EAAE,SAAS,EACb,YAAY,GAAE,MAAkB,EACxC,UAAU,GAAE,MAAc;IAKtB,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAWpD,OAAO,CAAC,gBAAgB;IAMxB,OAAO,CAAC,UAAU;IAkDlB,KAAK,IAAI,IAAI;IA+Bb,WAAW,IAAI,IAAI;IA2Bb,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAI7B"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { AudioOutput } from '@ariaflow/livekit-plugin';
|
|
2
|
+
/**
|
|
3
|
+
* Sends audio frames from the TTS pipeline to the WebSocket client
|
|
4
|
+
* as binary messages containing raw PCM data.
|
|
5
|
+
*/
|
|
6
|
+
export class WebSocketAudioOutput extends AudioOutput {
|
|
7
|
+
ws;
|
|
8
|
+
contextLabel;
|
|
9
|
+
sendQueue = [];
|
|
10
|
+
sending = false;
|
|
11
|
+
segmentSamplesSent = 0;
|
|
12
|
+
flushed = false;
|
|
13
|
+
closed = false;
|
|
14
|
+
segmentIndex = 0;
|
|
15
|
+
segmentStartedAt = null;
|
|
16
|
+
frameCountInSegment = 0;
|
|
17
|
+
constructor(ws, contextLabel = 'unknown', sampleRate = 24000) {
|
|
18
|
+
super(sampleRate);
|
|
19
|
+
this.ws = ws;
|
|
20
|
+
this.contextLabel = contextLabel;
|
|
21
|
+
}
|
|
22
|
+
async captureFrame(frame) {
|
|
23
|
+
await super.captureFrame(frame);
|
|
24
|
+
if (this.closed)
|
|
25
|
+
return;
|
|
26
|
+
this.sendQueue.push(frame);
|
|
27
|
+
if (!this.sending) {
|
|
28
|
+
this.processSendQueue();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
processSendQueue() {
|
|
32
|
+
if (this.sending || this.closed)
|
|
33
|
+
return;
|
|
34
|
+
this.sending = true;
|
|
35
|
+
this.drainAsync();
|
|
36
|
+
}
|
|
37
|
+
drainAsync() {
|
|
38
|
+
if (this.closed || this.sendQueue.length === 0) {
|
|
39
|
+
this.sending = false;
|
|
40
|
+
if (this.flushed) {
|
|
41
|
+
const playbackDuration = this.sampleRate
|
|
42
|
+
? this.segmentSamplesSent / this.sampleRate
|
|
43
|
+
: 0;
|
|
44
|
+
this.onPlaybackFinished({
|
|
45
|
+
playbackPosition: playbackDuration,
|
|
46
|
+
interrupted: false,
|
|
47
|
+
});
|
|
48
|
+
this.segmentSamplesSent = 0;
|
|
49
|
+
this.flushed = false;
|
|
50
|
+
}
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const frame = this.sendQueue.shift();
|
|
54
|
+
if (this.segmentSamplesSent === 0) {
|
|
55
|
+
this.segmentIndex += 1;
|
|
56
|
+
this.segmentStartedAt = Date.now();
|
|
57
|
+
this.frameCountInSegment = 0;
|
|
58
|
+
this.onPlaybackStarted(Date.now());
|
|
59
|
+
console.info('[WebSocketAudioOutput] first frame enqueued', {
|
|
60
|
+
context: this.contextLabel,
|
|
61
|
+
segmentIndex: this.segmentIndex,
|
|
62
|
+
frameBytes: frame.data.byteLength,
|
|
63
|
+
timestamp: new Date().toISOString(),
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
const buffer = Buffer.from(frame.data.buffer, frame.data.byteOffset, frame.data.byteLength);
|
|
67
|
+
try {
|
|
68
|
+
this.ws.send(buffer, { binary: true });
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
this.sending = false;
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
this.segmentSamplesSent += frame.samplesPerChannel;
|
|
75
|
+
this.frameCountInSegment += 1;
|
|
76
|
+
// Yield to the event loop between sends to avoid blocking
|
|
77
|
+
setImmediate(() => this.drainAsync());
|
|
78
|
+
}
|
|
79
|
+
flush() {
|
|
80
|
+
super.flush();
|
|
81
|
+
this.flushed = true;
|
|
82
|
+
if (this.sendQueue.length === 0) {
|
|
83
|
+
const playbackDuration = this.sampleRate
|
|
84
|
+
? this.segmentSamplesSent / this.sampleRate
|
|
85
|
+
: 0;
|
|
86
|
+
this.onPlaybackFinished({
|
|
87
|
+
playbackPosition: playbackDuration,
|
|
88
|
+
interrupted: false,
|
|
89
|
+
});
|
|
90
|
+
this.segmentSamplesSent = 0;
|
|
91
|
+
this.flushed = false;
|
|
92
|
+
if (this.segmentStartedAt !== null) {
|
|
93
|
+
console.info('[WebSocketAudioOutput] segment flushed', {
|
|
94
|
+
context: this.contextLabel,
|
|
95
|
+
segmentIndex: this.segmentIndex,
|
|
96
|
+
frames: this.frameCountInSegment,
|
|
97
|
+
durationMs: Date.now() - this.segmentStartedAt,
|
|
98
|
+
playbackDurationMs: Math.round(playbackDuration * 1000),
|
|
99
|
+
timestamp: new Date().toISOString(),
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
this.segmentStartedAt = null;
|
|
103
|
+
this.frameCountInSegment = 0;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
clearBuffer() {
|
|
107
|
+
const playbackDuration = this.sampleRate
|
|
108
|
+
? this.segmentSamplesSent / this.sampleRate
|
|
109
|
+
: 0;
|
|
110
|
+
this.sendQueue = [];
|
|
111
|
+
this.onPlaybackFinished({
|
|
112
|
+
playbackPosition: playbackDuration,
|
|
113
|
+
interrupted: true,
|
|
114
|
+
});
|
|
115
|
+
if (this.segmentStartedAt !== null) {
|
|
116
|
+
console.warn('[WebSocketAudioOutput] segment interrupted', {
|
|
117
|
+
context: this.contextLabel,
|
|
118
|
+
segmentIndex: this.segmentIndex,
|
|
119
|
+
frames: this.frameCountInSegment,
|
|
120
|
+
durationMs: Date.now() - this.segmentStartedAt,
|
|
121
|
+
timestamp: new Date().toISOString(),
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
this.segmentSamplesSent = 0;
|
|
125
|
+
this.flushed = false;
|
|
126
|
+
this.segmentStartedAt = null;
|
|
127
|
+
this.frameCountInSegment = 0;
|
|
128
|
+
}
|
|
129
|
+
async close() {
|
|
130
|
+
this.closed = true;
|
|
131
|
+
this.sendQueue = [];
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=audio_output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audio_output.js","sourceRoot":"","sources":["../src/audio_output.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { WebSocketAgentServer } from './server.js';
|
|
2
|
+
export type { NativeSessionOptions } from './server.js';
|
|
3
|
+
export type { WebSocketServerOptions } from './types.js';
|
|
4
|
+
export { WebSocketTransportAdapter } from './transport_adapter.js';
|
|
5
|
+
export { WebSocketAudioInput } from './audio_input.js';
|
|
6
|
+
export { WebSocketAudioOutput } from './audio_output.js';
|
|
7
|
+
export { WebSocketTextOutput } from './text_output.js';
|
|
8
|
+
export { bridgeWebSocketToRealtimeTransport, bridgeAdapterToRealtimeTransport, float32ToInt16Bytes, int16BytesToFloat32, } from './realtime_bridge.js';
|
|
9
|
+
export type { ClientMessage, ServerMessage, ConfigureMessage, UserTextMessage, EndOfAudioMessage, SessionStartedMessage, AgentTextMessage, UserTranscriptionMessage, AgentStateMessage, ErrorMessage, SessionEndedMessage, } from './protocol.js';
|
|
10
|
+
export { parseClientMessage, serializeServerMessage } from './protocol.js';
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,YAAY,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACxD,YAAY,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAEzD,OAAO,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EACL,kCAAkC,EAClC,gCAAgC,EAChC,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,sBAAsB,CAAC;AAE9B,YAAY,EACV,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EACjB,qBAAqB,EACrB,gBAAgB,EAChB,wBAAwB,EACxB,iBAAiB,EACjB,YAAY,EACZ,mBAAmB,GACpB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { WebSocketAgentServer } from './server.js';
|
|
2
|
+
export { WebSocketTransportAdapter } from './transport_adapter.js';
|
|
3
|
+
export { WebSocketAudioInput } from './audio_input.js';
|
|
4
|
+
export { WebSocketAudioOutput } from './audio_output.js';
|
|
5
|
+
export { WebSocketTextOutput } from './text_output.js';
|
|
6
|
+
export { bridgeWebSocketToRealtimeTransport, bridgeAdapterToRealtimeTransport, float32ToInt16Bytes, int16BytesToFloat32, } from './realtime_bridge.js';
|
|
7
|
+
export { parseClientMessage, serializeServerMessage } from './protocol.js';
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAInD,OAAO,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EACL,kCAAkC,EAClC,gCAAgC,EAChC,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,sBAAsB,CAAC;AAgB9B,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export interface ConfigureMessage {
|
|
2
|
+
type: 'configure';
|
|
3
|
+
sampleRate?: number;
|
|
4
|
+
numChannels?: number;
|
|
5
|
+
encoding?: 'pcm_s16le' | 'mulaw' | 'alaw';
|
|
6
|
+
}
|
|
7
|
+
export interface UserTextMessage {
|
|
8
|
+
type: 'user_text';
|
|
9
|
+
text: string;
|
|
10
|
+
}
|
|
11
|
+
export interface EndOfAudioMessage {
|
|
12
|
+
type: 'end_of_audio';
|
|
13
|
+
}
|
|
14
|
+
export interface SessionStartedMessage {
|
|
15
|
+
type: 'session_started';
|
|
16
|
+
sessionId: string;
|
|
17
|
+
config: {
|
|
18
|
+
sampleRate: number;
|
|
19
|
+
numChannels: number;
|
|
20
|
+
encoding: string;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export interface AgentTextMessage {
|
|
24
|
+
type: 'agent_text';
|
|
25
|
+
text: string;
|
|
26
|
+
isFinal: boolean;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* @experimental Not currently emitted by the runtime. STT transcription events
|
|
30
|
+
* flow through the LiveKit AgentSession internal pipeline and are not surfaced
|
|
31
|
+
* to the WS client. Reserved for future observability needs.
|
|
32
|
+
*/
|
|
33
|
+
export interface UserTranscriptionMessage {
|
|
34
|
+
type: 'user_transcription';
|
|
35
|
+
text: string;
|
|
36
|
+
isFinal: boolean;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* @experimental Not currently emitted by the runtime. State machine transitions
|
|
40
|
+
* are implicit inside the LiveKit AgentSession. Reserved for future UI state
|
|
41
|
+
* indicator support.
|
|
42
|
+
*/
|
|
43
|
+
export interface AgentStateMessage {
|
|
44
|
+
type: 'agent_state';
|
|
45
|
+
state: 'initializing' | 'idle' | 'listening' | 'thinking' | 'speaking';
|
|
46
|
+
}
|
|
47
|
+
export interface ErrorMessage {
|
|
48
|
+
type: 'error';
|
|
49
|
+
message: string;
|
|
50
|
+
code?: string;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* @experimental Not currently emitted by the runtime. Session end is communicated
|
|
54
|
+
* via the WebSocket close frame. The client discovers session end when the WS
|
|
55
|
+
* connection closes. Reserved for explicit lifecycle signaling if needed.
|
|
56
|
+
*/
|
|
57
|
+
export interface SessionEndedMessage {
|
|
58
|
+
type: 'session_ended';
|
|
59
|
+
reason: 'completed' | 'error' | 'client_disconnect' | 'server_shutdown';
|
|
60
|
+
}
|
|
61
|
+
export type ClientMessage = ConfigureMessage | UserTextMessage | EndOfAudioMessage;
|
|
62
|
+
export type ServerMessage = SessionStartedMessage | AgentTextMessage | UserTranscriptionMessage | AgentStateMessage | ErrorMessage | SessionEndedMessage;
|
|
63
|
+
export declare function parseClientMessage(data: string): ClientMessage | null;
|
|
64
|
+
export declare function serializeServerMessage(msg: ServerMessage): string;
|
|
65
|
+
//# sourceMappingURL=protocol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,WAAW,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,WAAW,GAAG,OAAO,GAAG,MAAM,CAAC;CAC3C;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,WAAW,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,cAAc,CAAC;CACtB;AAID,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,iBAAiB,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE;QACN,UAAU,EAAE,MAAM,CAAC;QACnB,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;CACH;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,YAAY,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;GAIG;AACH,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,oBAAoB,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,aAAa,CAAC;IACpB,KAAK,EAAE,cAAc,GAAG,MAAM,GAAG,WAAW,GAAG,UAAU,GAAG,UAAU,CAAC;CACxE;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,eAAe,CAAC;IACtB,MAAM,EAAE,WAAW,GAAG,OAAO,GAAG,mBAAmB,GAAG,iBAAiB,CAAC;CACzE;AAID,MAAM,MAAM,aAAa,GAAG,gBAAgB,GAAG,eAAe,GAAG,iBAAiB,CAAC;AACnF,MAAM,MAAM,aAAa,GACrB,qBAAqB,GACrB,gBAAgB,GAChB,wBAAwB,GACxB,iBAAiB,GACjB,YAAY,GACZ,mBAAmB,CAAC;AAUxB,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CA+BrE;AAED,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAEjE"}
|
package/dist/protocol.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// --- Client -> Server Messages ---
|
|
2
|
+
function isFinitePositiveNumber(value) {
|
|
3
|
+
return typeof value === 'number' && Number.isFinite(value) && value > 0;
|
|
4
|
+
}
|
|
5
|
+
function isValidEncoding(value) {
|
|
6
|
+
return value === 'pcm_s16le' || value === 'mulaw' || value === 'alaw';
|
|
7
|
+
}
|
|
8
|
+
export function parseClientMessage(data) {
|
|
9
|
+
try {
|
|
10
|
+
const msg = JSON.parse(data);
|
|
11
|
+
if (!msg || typeof msg.type !== 'string')
|
|
12
|
+
return null;
|
|
13
|
+
switch (msg.type) {
|
|
14
|
+
case 'configure': {
|
|
15
|
+
if (msg.sampleRate !== undefined && !isFinitePositiveNumber(msg.sampleRate)) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
if (msg.numChannels !== undefined && !isFinitePositiveNumber(msg.numChannels)) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
if (msg.encoding !== undefined && !isValidEncoding(msg.encoding)) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
return msg;
|
|
25
|
+
}
|
|
26
|
+
case 'user_text':
|
|
27
|
+
if (typeof msg.text !== 'string') {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
return msg;
|
|
31
|
+
case 'end_of_audio':
|
|
32
|
+
return msg;
|
|
33
|
+
default:
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export function serializeServerMessage(msg) {
|
|
42
|
+
return JSON.stringify(msg);
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=protocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA,oCAAoC;AAoFpC,SAAS,sBAAsB,CAAC,KAAc;IAC5C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC;AAC1E,CAAC;AAED,SAAS,eAAe,CAAC,KAAc;IACrC,OAAO,KAAK,KAAK,WAAW,IAAI,KAAK,KAAK,OAAO,IAAI,KAAK,KAAK,MAAM,CAAC;AACxE,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC7B,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QAEtD,QAAQ,GAAG,CAAC,IAAI,EAAE,CAAC;YACjB,KAAK,WAAW,CAAC,CAAC,CAAC;gBACjB,IAAI,GAAG,CAAC,UAAU,KAAK,SAAS,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;oBAC5E,OAAO,IAAI,CAAC;gBACd,CAAC;gBACD,IAAI,GAAG,CAAC,WAAW,KAAK,SAAS,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC;oBAC9E,OAAO,IAAI,CAAC;gBACd,CAAC;gBACD,IAAI,GAAG,CAAC,QAAQ,KAAK,SAAS,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;oBACjE,OAAO,IAAI,CAAC;gBACd,CAAC;gBACD,OAAO,GAAuB,CAAC;YACjC,CAAC;YACD,KAAK,WAAW;gBACd,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;oBACjC,OAAO,IAAI,CAAC;gBACd,CAAC;gBACD,OAAO,GAAsB,CAAC;YAChC,KAAK,cAAc;gBACjB,OAAO,GAAwB,CAAC;YAClC;gBACE,OAAO,IAAI,CAAC;QAChB,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,GAAkB;IACvD,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;AAC7B,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { WebSocket } from 'ws';
|
|
2
|
+
import type { RealtimeTransportSession } from '@ariaflowagents/core/realtime';
|
|
3
|
+
import type { WebSocketTransportAdapter } from './transport_adapter.js';
|
|
4
|
+
/**
|
|
5
|
+
* Convert float32 PCM samples to int16 PCM bytes (Uint8Array).
|
|
6
|
+
* GeminiLiveSession and RealtimeRuntime expect int16 PCM.
|
|
7
|
+
* LiveKit AudioFrame uses float32 samples internally.
|
|
8
|
+
*/
|
|
9
|
+
export declare function float32ToInt16Bytes(float32: Float32Array): Uint8Array;
|
|
10
|
+
/**
|
|
11
|
+
* Convert int16 PCM bytes (Uint8Array) to float32 PCM samples.
|
|
12
|
+
*/
|
|
13
|
+
export declare function int16BytesToFloat32(pcmBytes: Uint8Array): Float32Array;
|
|
14
|
+
/**
|
|
15
|
+
* Bridge a raw WebSocket connection to a RealtimeTransportSession.
|
|
16
|
+
*
|
|
17
|
+
* This is the primary bridge used by WebSocketAgentServer.startNativeSession().
|
|
18
|
+
* Binary WS messages are passed through directly as Uint8Array (int16 PCM)
|
|
19
|
+
* without intermediate float32 conversion.
|
|
20
|
+
*
|
|
21
|
+
* The WebSocket client and the model client must agree on sample rate and
|
|
22
|
+
* encoding. No resampling is performed in this bridge.
|
|
23
|
+
*/
|
|
24
|
+
export declare function bridgeWebSocketToRealtimeTransport(ws: WebSocket, options?: {
|
|
25
|
+
sessionId?: string;
|
|
26
|
+
}): RealtimeTransportSession;
|
|
27
|
+
/**
|
|
28
|
+
* Bridge a WebSocketTransportAdapter to a RealtimeTransportSession.
|
|
29
|
+
*
|
|
30
|
+
* This variant works at the adapter level, converting between LiveKit's
|
|
31
|
+
* float32 AudioFrame format and RealtimeRuntime's int16 PCM Uint8Array.
|
|
32
|
+
*
|
|
33
|
+
* Use this when you already have a WebSocketTransportAdapter instance
|
|
34
|
+
* (e.g., from the server's onConnection callback) and want to route it
|
|
35
|
+
* through RealtimeRuntime instead of AriaFlowVoiceSession.
|
|
36
|
+
*
|
|
37
|
+
* Note: This bridge subscribes to the raw WS binary messages directly
|
|
38
|
+
* (same as the simple bridge) because WebSocketAudioInput doesn't expose
|
|
39
|
+
* a frame event. Output is sent as raw binary on the WS, bypassing
|
|
40
|
+
* WebSocketAudioOutput's AudioFrame machinery.
|
|
41
|
+
*/
|
|
42
|
+
export declare function bridgeAdapterToRealtimeTransport(adapter: WebSocketTransportAdapter): RealtimeTransportSession;
|
|
43
|
+
//# sourceMappingURL=realtime_bridge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"realtime_bridge.d.ts","sourceRoot":"","sources":["../src/realtime_bridge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AACpC,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAC9E,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AAIxE;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,YAAY,GAAG,UAAU,CAOrE;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,UAAU,GAAG,YAAY,CAWtE;AAID;;;;;;;;;GASG;AACH,wBAAgB,kCAAkC,CAChD,EAAE,EAAE,SAAS,EACb,OAAO,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/B,wBAAwB,CAsF1B;AAID;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gCAAgC,CAC9C,OAAO,EAAE,yBAAyB,GACjC,wBAAwB,CAqE1B"}
|