@ariaflowagents/livekit-plugin-transport-http 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 +28 -0
- package/dist/audio_input.d.ts.map +1 -0
- package/dist/audio_input.js +103 -0
- package/dist/audio_input.js.map +1 -0
- package/dist/audio_output.d.ts +18 -0
- package/dist/audio_output.d.ts.map +1 -0
- package/dist/audio_output.js +58 -0
- package/dist/audio_output.js.map +1 -0
- package/dist/handler.d.ts +29 -0
- package/dist/handler.d.ts.map +1 -0
- package/dist/handler.js +140 -0
- package/dist/handler.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/hono.d.ts +40 -0
- package/dist/integrations/hono.d.ts.map +1 -0
- package/dist/integrations/hono.js +51 -0
- package/dist/integrations/hono.js.map +1 -0
- package/dist/sse.d.ts +33 -0
- package/dist/sse.d.ts.map +1 -0
- package/dist/sse.js +26 -0
- package/dist/sse.js.map +1 -0
- package/dist/text_output.d.ts +16 -0
- package/dist/text_output.d.ts.map +1 -0
- package/dist/text_output.js +44 -0
- package/dist/text_output.js.map +1 -0
- package/dist/transport_adapter.d.ts +28 -0
- package/dist/transport_adapter.d.ts.map +1 -0
- package/dist/transport_adapter.js +64 -0
- package/dist/transport_adapter.js.map +1 -0
- package/dist/types.d.ts +28 -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 +39 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { AudioInput } from '@ariaflow/livekit-plugin';
|
|
2
|
+
/**
|
|
3
|
+
* Receives audio from HTTP POST requests and provides it as a
|
|
4
|
+
* ReadableStream<AudioFrame> to the AgentSession's STT pipeline.
|
|
5
|
+
*
|
|
6
|
+
* Audio can arrive as a complete blob (base64 in JSON POST) for
|
|
7
|
+
* push-to-talk, or as chunked streaming for lower latency.
|
|
8
|
+
*/
|
|
9
|
+
export declare class HTTPAudioInput extends AudioInput {
|
|
10
|
+
private byteStream;
|
|
11
|
+
private currentWriter;
|
|
12
|
+
private currentStreamId;
|
|
13
|
+
constructor(sampleRate?: number, numChannels?: number);
|
|
14
|
+
/**
|
|
15
|
+
* Push a complete audio buffer from a POST request body.
|
|
16
|
+
* Creates a new input stream, writes all frames, and closes it.
|
|
17
|
+
*/
|
|
18
|
+
pushAudioBuffer(pcmData: ArrayBuffer, sampleRate: number, numChannels: number): void;
|
|
19
|
+
/**
|
|
20
|
+
* Start a streaming audio input for chunked transfer encoding.
|
|
21
|
+
*/
|
|
22
|
+
startStreamingInput(): {
|
|
23
|
+
write: (chunk: ArrayBuffer) => void;
|
|
24
|
+
end: () => void;
|
|
25
|
+
};
|
|
26
|
+
close(): Promise<void>;
|
|
27
|
+
}
|
|
28
|
+
//# 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;AAGxF;;;;;;GAMG;AACH,qBAAa,cAAe,SAAQ,UAAU;IAC5C,OAAO,CAAC,UAAU,CAAkB;IACpC,OAAO,CAAC,aAAa,CAAwD;IAC7E,OAAO,CAAC,eAAe,CAAuB;gBAG5C,UAAU,GAAE,MAAc,EAC1B,WAAW,GAAE,MAAU;IAMzB;;;OAGG;IACH,eAAe,CACb,OAAO,EAAE,WAAW,EACpB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,GAClB,IAAI;IAmBP;;OAEG;IACH,mBAAmB,IAAI;QACrB,KAAK,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;QACpC,GAAG,EAAE,MAAM,IAAI,CAAC;KACjB;IAmDK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAY7B"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { AudioInput, AudioByteStream } from '@ariaflow/livekit-plugin';
|
|
2
|
+
import { TransformStream } from 'node:stream/web';
|
|
3
|
+
/**
|
|
4
|
+
* Receives audio from HTTP POST requests and provides it as a
|
|
5
|
+
* ReadableStream<AudioFrame> to the AgentSession's STT pipeline.
|
|
6
|
+
*
|
|
7
|
+
* Audio can arrive as a complete blob (base64 in JSON POST) for
|
|
8
|
+
* push-to-talk, or as chunked streaming for lower latency.
|
|
9
|
+
*/
|
|
10
|
+
export class HTTPAudioInput extends AudioInput {
|
|
11
|
+
byteStream;
|
|
12
|
+
currentWriter = null;
|
|
13
|
+
currentStreamId = null;
|
|
14
|
+
constructor(sampleRate = 24000, numChannels = 1) {
|
|
15
|
+
super();
|
|
16
|
+
this.byteStream = new AudioByteStream(sampleRate, numChannels);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Push a complete audio buffer from a POST request body.
|
|
20
|
+
* Creates a new input stream, writes all frames, and closes it.
|
|
21
|
+
*/
|
|
22
|
+
pushAudioBuffer(pcmData, sampleRate, numChannels) {
|
|
23
|
+
const localByteStream = new AudioByteStream(sampleRate, numChannels);
|
|
24
|
+
const frames = localByteStream.write(pcmData);
|
|
25
|
+
frames.push(...localByteStream.flush());
|
|
26
|
+
if (frames.length === 0)
|
|
27
|
+
return;
|
|
28
|
+
const { readable, writable } = new TransformStream();
|
|
29
|
+
const writer = writable.getWriter();
|
|
30
|
+
this.multiStream.addInputStream(readable);
|
|
31
|
+
(async () => {
|
|
32
|
+
for (const frame of frames) {
|
|
33
|
+
await writer.write(frame);
|
|
34
|
+
}
|
|
35
|
+
await writer.close();
|
|
36
|
+
})();
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Start a streaming audio input for chunked transfer encoding.
|
|
40
|
+
*/
|
|
41
|
+
startStreamingInput() {
|
|
42
|
+
const { readable, writable } = new TransformStream();
|
|
43
|
+
this.currentWriter = writable.getWriter();
|
|
44
|
+
this.currentStreamId = this.multiStream.addInputStream(readable);
|
|
45
|
+
return {
|
|
46
|
+
write: (chunk) => {
|
|
47
|
+
const frames = this.byteStream.write(chunk);
|
|
48
|
+
for (const frame of frames) {
|
|
49
|
+
this.currentWriter?.write(frame).catch((err) => {
|
|
50
|
+
// Stream may be closed if client disconnected - this is expected
|
|
51
|
+
if (this.currentWriter) {
|
|
52
|
+
console.error('[HTTPAudioInput] Error writing frame:', {
|
|
53
|
+
error: err instanceof Error ? err.message : String(err),
|
|
54
|
+
streamId: this.currentStreamId,
|
|
55
|
+
timestamp: new Date().toISOString(),
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
end: () => {
|
|
62
|
+
const frames = this.byteStream.flush();
|
|
63
|
+
for (const frame of frames) {
|
|
64
|
+
this.currentWriter?.write(frame).catch((err) => {
|
|
65
|
+
// Stream may be closed - this is expected
|
|
66
|
+
if (this.currentWriter) {
|
|
67
|
+
console.error('[HTTPAudioInput] Error writing flush frame:', {
|
|
68
|
+
error: err instanceof Error ? err.message : String(err),
|
|
69
|
+
streamId: this.currentStreamId,
|
|
70
|
+
timestamp: new Date().toISOString(),
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
this.currentWriter?.close().catch((err) => {
|
|
76
|
+
// Stream may already be closed - this is expected
|
|
77
|
+
if (this.currentWriter) {
|
|
78
|
+
console.error('[HTTPAudioInput] Error closing writer:', {
|
|
79
|
+
error: err instanceof Error ? err.message : String(err),
|
|
80
|
+
streamId: this.currentStreamId,
|
|
81
|
+
timestamp: new Date().toISOString(),
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
this.currentWriter = null;
|
|
86
|
+
this.currentStreamId = null;
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
async close() {
|
|
91
|
+
if (this.currentWriter) {
|
|
92
|
+
await this.currentWriter.close().catch((err) => {
|
|
93
|
+
console.error('[HTTPAudioInput] Error closing during cleanup:', {
|
|
94
|
+
error: err instanceof Error ? err.message : String(err),
|
|
95
|
+
streamId: this.currentStreamId,
|
|
96
|
+
timestamp: new Date().toISOString(),
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
await super.close();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
//# 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,18 @@
|
|
|
1
|
+
import { AudioOutput, type AudioFrame } from '@ariaflow/livekit-plugin';
|
|
2
|
+
import type { SSEWriter } from './sse.js';
|
|
3
|
+
/**
|
|
4
|
+
* Sends audio frames from the TTS pipeline to the client as SSE events
|
|
5
|
+
* containing base64-encoded PCM audio.
|
|
6
|
+
*/
|
|
7
|
+
export declare class HTTPAudioOutput extends AudioOutput {
|
|
8
|
+
private segmentSamplesSent;
|
|
9
|
+
private closed;
|
|
10
|
+
private sseWriter;
|
|
11
|
+
constructor(sampleRate?: number);
|
|
12
|
+
setSSEWriter(writer: SSEWriter): void;
|
|
13
|
+
captureFrame(frame: AudioFrame): Promise<void>;
|
|
14
|
+
flush(): void;
|
|
15
|
+
clearBuffer(): void;
|
|
16
|
+
close(): Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
//# 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,EAAmB,MAAM,UAAU,CAAC;AAE3D;;;GAGG;AACH,qBAAa,eAAgB,SAAQ,WAAW;IAC9C,OAAO,CAAC,kBAAkB,CAAa;IACvC,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,SAAS,CAA0B;gBAE/B,UAAU,GAAE,MAAc;IAItC,YAAY,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI;IAI/B,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBpD,KAAK,IAAI,IAAI;IAeb,WAAW,IAAI,IAAI;IAab,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { AudioOutput } from '@ariaflow/livekit-plugin';
|
|
2
|
+
/**
|
|
3
|
+
* Sends audio frames from the TTS pipeline to the client as SSE events
|
|
4
|
+
* containing base64-encoded PCM audio.
|
|
5
|
+
*/
|
|
6
|
+
export class HTTPAudioOutput extends AudioOutput {
|
|
7
|
+
segmentSamplesSent = 0;
|
|
8
|
+
closed = false;
|
|
9
|
+
sseWriter = null;
|
|
10
|
+
constructor(sampleRate = 24000) {
|
|
11
|
+
super(sampleRate);
|
|
12
|
+
}
|
|
13
|
+
setSSEWriter(writer) {
|
|
14
|
+
this.sseWriter = writer;
|
|
15
|
+
}
|
|
16
|
+
async captureFrame(frame) {
|
|
17
|
+
await super.captureFrame(frame);
|
|
18
|
+
if (this.closed || !this.sseWriter)
|
|
19
|
+
return;
|
|
20
|
+
if (this.segmentSamplesSent === 0) {
|
|
21
|
+
this.onPlaybackStarted(Date.now());
|
|
22
|
+
}
|
|
23
|
+
const bytes = new Uint8Array(frame.data.buffer);
|
|
24
|
+
const base64 = Buffer.from(bytes).toString('base64');
|
|
25
|
+
const event = {
|
|
26
|
+
audio: base64,
|
|
27
|
+
sampleRate: frame.sampleRate,
|
|
28
|
+
numChannels: frame.channels,
|
|
29
|
+
};
|
|
30
|
+
this.sseWriter.writeEvent('agent_audio', event);
|
|
31
|
+
this.segmentSamplesSent += frame.samplesPerChannel;
|
|
32
|
+
}
|
|
33
|
+
flush() {
|
|
34
|
+
super.flush();
|
|
35
|
+
const playbackDuration = this.sampleRate
|
|
36
|
+
? this.segmentSamplesSent / this.sampleRate
|
|
37
|
+
: 0;
|
|
38
|
+
this.onPlaybackFinished({
|
|
39
|
+
playbackPosition: playbackDuration,
|
|
40
|
+
interrupted: false,
|
|
41
|
+
});
|
|
42
|
+
this.segmentSamplesSent = 0;
|
|
43
|
+
}
|
|
44
|
+
clearBuffer() {
|
|
45
|
+
const playbackDuration = this.sampleRate
|
|
46
|
+
? this.segmentSamplesSent / this.sampleRate
|
|
47
|
+
: 0;
|
|
48
|
+
this.onPlaybackFinished({
|
|
49
|
+
playbackPosition: playbackDuration,
|
|
50
|
+
interrupted: true,
|
|
51
|
+
});
|
|
52
|
+
this.segmentSamplesSent = 0;
|
|
53
|
+
}
|
|
54
|
+
async close() {
|
|
55
|
+
this.closed = true;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=audio_output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audio_output.js","sourceRoot":"","sources":["../src/audio_output.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAmB,MAAM,0BAA0B,CAAC;AAGxE;;;GAGG;AACH,MAAM,OAAO,eAAgB,SAAQ,WAAW;IACtC,kBAAkB,GAAW,CAAC,CAAC;IAC/B,MAAM,GAAY,KAAK,CAAC;IACxB,SAAS,GAAqB,IAAI,CAAC;IAE3C,YAAY,aAAqB,KAAK;QACpC,KAAK,CAAC,UAAU,CAAC,CAAC;IACpB,CAAC;IAED,YAAY,CAAC,MAAiB;QAC5B,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC;IAC1B,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,KAAiB;QAClC,MAAM,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;QAChC,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,SAAS;YAAE,OAAO;QAE3C,IAAI,IAAI,CAAC,kBAAkB,KAAK,CAAC,EAAE,CAAC;YAClC,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;QACrC,CAAC;QAED,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAChD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAErD,MAAM,KAAK,GAAoB;YAC7B,KAAK,EAAE,MAAM;YACb,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,WAAW,EAAE,KAAK,CAAC,QAAQ;SAC5B,CAAC;QAEF,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC;QAChD,IAAI,CAAC,kBAAkB,IAAI,KAAK,CAAC,iBAAiB,CAAC;IACrD,CAAC;IAED,KAAK;QACH,KAAK,CAAC,KAAK,EAAE,CAAC;QAEd,MAAM,gBAAgB,GAAG,IAAI,CAAC,UAAU;YACtC,CAAC,CAAC,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC,UAAU;YAC3C,CAAC,CAAC,CAAC,CAAC;QAEN,IAAI,CAAC,kBAAkB,CAAC;YACtB,gBAAgB,EAAE,gBAAgB;YAClC,WAAW,EAAE,KAAK;SACnB,CAAC,CAAC;QAEH,IAAI,CAAC,kBAAkB,GAAG,CAAC,CAAC;IAC9B,CAAC;IAED,WAAW;QACT,MAAM,gBAAgB,GAAG,IAAI,CAAC,UAAU;YACtC,CAAC,CAAC,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC,UAAU;YAC3C,CAAC,CAAC,CAAC,CAAC;QAEN,IAAI,CAAC,kBAAkB,CAAC;YACtB,gBAAgB,EAAE,gBAAgB;YAClC,WAAW,EAAE,IAAI;SAClB,CAAC,CAAC;QAEH,IAAI,CAAC,kBAAkB,GAAG,CAAC,CAAC;IAC9B,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;IACrB,CAAC;CACF"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { SessionManager, AriaFlowVoiceSession } from '@ariaflow/livekit-plugin';
|
|
2
|
+
import type { HTTPTransportOptions } from './types.js';
|
|
3
|
+
export interface AgentHandlerOptions {
|
|
4
|
+
/** Factory function that creates an AriaFlowVoiceSession for each new session. */
|
|
5
|
+
agent: () => AriaFlowVoiceSession;
|
|
6
|
+
transportOptions?: HTTPTransportOptions;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Framework-agnostic HTTP request handler for AriaFlow voice sessions.
|
|
10
|
+
*
|
|
11
|
+
* Manages two kinds of requests:
|
|
12
|
+
* GET /session?id=<id> -- Establish SSE stream for server->client events
|
|
13
|
+
* POST /session?id=<id> -- Send user input (text or audio)
|
|
14
|
+
*/
|
|
15
|
+
export declare class AgentHandler {
|
|
16
|
+
private options;
|
|
17
|
+
private sessionManager;
|
|
18
|
+
private adapters;
|
|
19
|
+
constructor(options: AgentHandlerOptions);
|
|
20
|
+
/** Handle GET request to establish SSE connection. */
|
|
21
|
+
handleSSE(request: Request): Promise<Response>;
|
|
22
|
+
/** Handle POST request with user input (text or audio). */
|
|
23
|
+
handleInput(request: Request): Promise<Response>;
|
|
24
|
+
private closeSession;
|
|
25
|
+
get sessions(): SessionManager;
|
|
26
|
+
close(): Promise<void>;
|
|
27
|
+
}
|
|
28
|
+
export declare function createAgentHandler(options: AgentHandlerOptions): AgentHandler;
|
|
29
|
+
//# sourceMappingURL=handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../src/handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAIhF,OAAO,KAAK,EAAE,oBAAoB,EAAe,MAAM,YAAY,CAAC;AAEpE,MAAM,WAAW,mBAAmB;IAClC,kFAAkF;IAClF,KAAK,EAAE,MAAM,oBAAoB,CAAC;IAClC,gBAAgB,CAAC,EAAE,oBAAoB,CAAC;CACzC;AAED;;;;;;GAMG;AACH,qBAAa,YAAY;IAIX,OAAO,CAAC,OAAO;IAH3B,OAAO,CAAC,cAAc,CAAwC;IAC9D,OAAO,CAAC,QAAQ,CAAgD;gBAE5C,OAAO,EAAE,mBAAmB;IAEhD,sDAAsD;IAChD,SAAS,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC;IAiDpD,2DAA2D;IACrD,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC;YAyExC,YAAY;IAQ1B,IAAI,QAAQ,IAAI,cAAc,CAE7B;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAU7B;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,GAAG,YAAY,CAE7E"}
|
package/dist/handler.js
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { SessionManager } from '@ariaflow/livekit-plugin';
|
|
2
|
+
import { HTTPTransportAdapter } from './transport_adapter.js';
|
|
3
|
+
import { createSSEWriter } from './sse.js';
|
|
4
|
+
/**
|
|
5
|
+
* Framework-agnostic HTTP request handler for AriaFlow voice sessions.
|
|
6
|
+
*
|
|
7
|
+
* Manages two kinds of requests:
|
|
8
|
+
* GET /session?id=<id> -- Establish SSE stream for server->client events
|
|
9
|
+
* POST /session?id=<id> -- Send user input (text or audio)
|
|
10
|
+
*/
|
|
11
|
+
export class AgentHandler {
|
|
12
|
+
options;
|
|
13
|
+
sessionManager = new SessionManager();
|
|
14
|
+
adapters = new Map();
|
|
15
|
+
constructor(options) {
|
|
16
|
+
this.options = options;
|
|
17
|
+
}
|
|
18
|
+
/** Handle GET request to establish SSE connection. */
|
|
19
|
+
async handleSSE(request) {
|
|
20
|
+
const url = new URL(request.url, 'http://localhost');
|
|
21
|
+
let sessionId = url.searchParams.get('id');
|
|
22
|
+
if (!sessionId) {
|
|
23
|
+
sessionId = crypto.randomUUID();
|
|
24
|
+
}
|
|
25
|
+
let adapter = this.adapters.get(sessionId);
|
|
26
|
+
if (!adapter) {
|
|
27
|
+
adapter = new HTTPTransportAdapter({
|
|
28
|
+
id: sessionId,
|
|
29
|
+
sampleRate: this.options.transportOptions?.defaultSampleRate,
|
|
30
|
+
numChannels: this.options.transportOptions?.defaultNumChannels,
|
|
31
|
+
sessionTimeout: this.options.transportOptions?.sessionTimeout,
|
|
32
|
+
});
|
|
33
|
+
this.adapters.set(sessionId, adapter);
|
|
34
|
+
// Create AriaFlow voice session
|
|
35
|
+
const voiceSession = this.options.agent();
|
|
36
|
+
// Start the session
|
|
37
|
+
await this.sessionManager.startSession(adapter, voiceSession);
|
|
38
|
+
}
|
|
39
|
+
const { readable, writable } = new TransformStream();
|
|
40
|
+
const sseWriter = createSSEWriter(writable);
|
|
41
|
+
adapter.attachSSE(sseWriter);
|
|
42
|
+
sseWriter.writeEvent('session_started', {
|
|
43
|
+
sessionId,
|
|
44
|
+
});
|
|
45
|
+
request.signal?.addEventListener('abort', () => {
|
|
46
|
+
this.closeSession(sessionId);
|
|
47
|
+
});
|
|
48
|
+
return new Response(readable, {
|
|
49
|
+
status: 200,
|
|
50
|
+
headers: {
|
|
51
|
+
'Content-Type': 'text/event-stream',
|
|
52
|
+
'Cache-Control': 'no-cache',
|
|
53
|
+
Connection: 'keep-alive',
|
|
54
|
+
'X-Session-Id': sessionId,
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
/** Handle POST request with user input (text or audio). */
|
|
59
|
+
async handleInput(request) {
|
|
60
|
+
const url = new URL(request.url, 'http://localhost');
|
|
61
|
+
const sessionId = url.searchParams.get('id');
|
|
62
|
+
if (!sessionId) {
|
|
63
|
+
return new Response(JSON.stringify({ error: 'Missing session ID' }), {
|
|
64
|
+
status: 400,
|
|
65
|
+
headers: { 'Content-Type': 'application/json' },
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
const adapter = this.adapters.get(sessionId);
|
|
69
|
+
if (!adapter || !adapter.isOpen) {
|
|
70
|
+
return new Response(JSON.stringify({ error: 'Session not found' }), {
|
|
71
|
+
status: 404,
|
|
72
|
+
headers: { 'Content-Type': 'application/json' },
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
adapter.touch();
|
|
76
|
+
let body;
|
|
77
|
+
try {
|
|
78
|
+
body = (await request.json());
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return new Response(JSON.stringify({ error: 'Invalid JSON body' }), {
|
|
82
|
+
status: 400,
|
|
83
|
+
headers: { 'Content-Type': 'application/json' },
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
switch (body.type) {
|
|
87
|
+
case 'user_text': {
|
|
88
|
+
// Text input is processed via session.generateReply()
|
|
89
|
+
const session = this.sessionManager.getSession(sessionId);
|
|
90
|
+
if (session) {
|
|
91
|
+
try {
|
|
92
|
+
await session.generateReply({ userInput: body.text });
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
console.error('[AgentHandler] generateReply error:', err instanceof Error ? err.message : String(err));
|
|
96
|
+
return new Response(JSON.stringify({ error: 'Failed to generate reply' }), {
|
|
97
|
+
status: 500,
|
|
98
|
+
headers: { 'Content-Type': 'application/json' },
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
case 'user_audio': {
|
|
105
|
+
const audioBytes = Buffer.from(body.audio, 'base64');
|
|
106
|
+
adapter.audioInput.pushAudioBuffer(audioBytes.buffer.slice(audioBytes.byteOffset, audioBytes.byteOffset + audioBytes.byteLength), body.sampleRate, body.numChannels);
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
case 'end_session':
|
|
110
|
+
await this.closeSession(sessionId);
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
return new Response(JSON.stringify({ ok: true }), {
|
|
114
|
+
status: 200,
|
|
115
|
+
headers: { 'Content-Type': 'application/json' },
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
async closeSession(sessionId) {
|
|
119
|
+
const adapter = this.adapters.get(sessionId);
|
|
120
|
+
if (!adapter)
|
|
121
|
+
return;
|
|
122
|
+
this.adapters.delete(sessionId);
|
|
123
|
+
await this.sessionManager.closeSession(sessionId);
|
|
124
|
+
}
|
|
125
|
+
get sessions() {
|
|
126
|
+
return this.sessionManager;
|
|
127
|
+
}
|
|
128
|
+
async close() {
|
|
129
|
+
const closePromises = Array.from(this.adapters.keys()).map((id) => this.closeSession(id).catch((err) => {
|
|
130
|
+
console.warn('[AgentHandler] Error closing session during shutdown:', id, err);
|
|
131
|
+
}));
|
|
132
|
+
await Promise.allSettled(closePromises);
|
|
133
|
+
this.adapters.clear();
|
|
134
|
+
await this.sessionManager.closeAll();
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
export function createAgentHandler(options) {
|
|
138
|
+
return new AgentHandler(options);
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.js","sourceRoot":"","sources":["../src/handler.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { AgentHandler, createAgentHandler } from './handler.js';
|
|
2
|
+
export type { AgentHandlerOptions } from './handler.js';
|
|
3
|
+
export { HTTPTransportAdapter } from './transport_adapter.js';
|
|
4
|
+
export { HTTPAudioInput } from './audio_input.js';
|
|
5
|
+
export { HTTPAudioOutput } from './audio_output.js';
|
|
6
|
+
export { HTTPTextOutput } from './text_output.js';
|
|
7
|
+
export { createSSEWriter } from './sse.js';
|
|
8
|
+
export type { SSEWriter, SessionStartedEvent, AgentTextEvent, AgentAudioEvent, UserTranscriptionEvent, AgentStateEvent, SessionEndedEvent, ErrorSSEEvent, } from './sse.js';
|
|
9
|
+
export type { UserTextInput, UserAudioInput, EndSessionInput, ClientInput, HTTPTransportOptions, } from './types.js';
|
|
10
|
+
//# 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,YAAY,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAChE,YAAY,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAExD,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC3C,YAAY,EACV,SAAS,EACT,mBAAmB,EACnB,cAAc,EACd,eAAe,EACf,sBAAsB,EACtB,eAAe,EACf,iBAAiB,EACjB,aAAa,GACd,MAAM,UAAU,CAAC;AAElB,YAAY,EACV,aAAa,EACb,cAAc,EACd,eAAe,EACf,WAAW,EACX,oBAAoB,GACrB,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { AgentHandler, createAgentHandler } from './handler.js';
|
|
2
|
+
export { HTTPTransportAdapter } from './transport_adapter.js';
|
|
3
|
+
export { HTTPAudioInput } from './audio_input.js';
|
|
4
|
+
export { HTTPAudioOutput } from './audio_output.js';
|
|
5
|
+
export { HTTPTextOutput } from './text_output.js';
|
|
6
|
+
export { createSSEWriter } from './sse.js';
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAGhE,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { AgentHandler, type AgentHandlerOptions } from '../handler.js';
|
|
2
|
+
/**
|
|
3
|
+
* Mount the agent handler on a Hono app.
|
|
4
|
+
*
|
|
5
|
+
* Creates two routes:
|
|
6
|
+
* GET <path> -- SSE stream
|
|
7
|
+
* POST <path> -- User input
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* import { Hono } from 'hono';
|
|
11
|
+
* import { mountAgent } from '@ariaflow/livekit-plugin-transport-http/hono';
|
|
12
|
+
* import { AriaFlowVoiceSession } from '@ariaflow/livekit-plugin';
|
|
13
|
+
* import { Runtime } from '@ariaflowagents/core';
|
|
14
|
+
* import { openai } from '@ai-sdk/openai';
|
|
15
|
+
* import { GeminiLiveSTT, GeminiLiveTTS } from '@ariaflow/livekit-plugin/gemini';
|
|
16
|
+
*
|
|
17
|
+
* const app = new Hono();
|
|
18
|
+
*
|
|
19
|
+
* const runtime = new Runtime({
|
|
20
|
+
* agents: [{
|
|
21
|
+
* id: 'assistant',
|
|
22
|
+
* name: 'Assistant',
|
|
23
|
+
* model: openai('gpt-4o-mini'),
|
|
24
|
+
* prompt: 'You are helpful.',
|
|
25
|
+
* }],
|
|
26
|
+
* defaultAgentId: 'assistant',
|
|
27
|
+
* defaultModel: openai('gpt-4o-mini'),
|
|
28
|
+
* });
|
|
29
|
+
*
|
|
30
|
+
* mountAgent(app, '/voice', {
|
|
31
|
+
* agent: () => new AriaFlowVoiceSession({
|
|
32
|
+
* runtime: runtime,
|
|
33
|
+
* stt: new GeminiLiveSTT(),
|
|
34
|
+
* tts: new GeminiLiveTTS(),
|
|
35
|
+
* greeting: 'Hello!',
|
|
36
|
+
* }),
|
|
37
|
+
* });
|
|
38
|
+
*/
|
|
39
|
+
export declare function mountAgent(app: any, path: string, options: AgentHandlerOptions): AgentHandler;
|
|
40
|
+
//# sourceMappingURL=hono.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hono.d.ts","sourceRoot":"","sources":["../../src/integrations/hono.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,KAAK,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAEvE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,UAAU,CACxB,GAAG,EAAE,GAAG,EACR,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,mBAAmB,GAC3B,YAAY,CAcd"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { AgentHandler } from '../handler.js';
|
|
2
|
+
/**
|
|
3
|
+
* Mount the agent handler on a Hono app.
|
|
4
|
+
*
|
|
5
|
+
* Creates two routes:
|
|
6
|
+
* GET <path> -- SSE stream
|
|
7
|
+
* POST <path> -- User input
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* import { Hono } from 'hono';
|
|
11
|
+
* import { mountAgent } from '@ariaflow/livekit-plugin-transport-http/hono';
|
|
12
|
+
* import { AriaFlowVoiceSession } from '@ariaflow/livekit-plugin';
|
|
13
|
+
* import { Runtime } from '@ariaflowagents/core';
|
|
14
|
+
* import { openai } from '@ai-sdk/openai';
|
|
15
|
+
* import { GeminiLiveSTT, GeminiLiveTTS } from '@ariaflow/livekit-plugin/gemini';
|
|
16
|
+
*
|
|
17
|
+
* const app = new Hono();
|
|
18
|
+
*
|
|
19
|
+
* const runtime = new Runtime({
|
|
20
|
+
* agents: [{
|
|
21
|
+
* id: 'assistant',
|
|
22
|
+
* name: 'Assistant',
|
|
23
|
+
* model: openai('gpt-4o-mini'),
|
|
24
|
+
* prompt: 'You are helpful.',
|
|
25
|
+
* }],
|
|
26
|
+
* defaultAgentId: 'assistant',
|
|
27
|
+
* defaultModel: openai('gpt-4o-mini'),
|
|
28
|
+
* });
|
|
29
|
+
*
|
|
30
|
+
* mountAgent(app, '/voice', {
|
|
31
|
+
* agent: () => new AriaFlowVoiceSession({
|
|
32
|
+
* runtime: runtime,
|
|
33
|
+
* stt: new GeminiLiveSTT(),
|
|
34
|
+
* tts: new GeminiLiveTTS(),
|
|
35
|
+
* greeting: 'Hello!',
|
|
36
|
+
* }),
|
|
37
|
+
* });
|
|
38
|
+
*/
|
|
39
|
+
export function mountAgent(app, path, options) {
|
|
40
|
+
const handler = new AgentHandler(options);
|
|
41
|
+
app.get(path, async (c) => {
|
|
42
|
+
const response = await handler.handleSSE(c.req.raw);
|
|
43
|
+
return response;
|
|
44
|
+
});
|
|
45
|
+
app.post(path, async (c) => {
|
|
46
|
+
const response = await handler.handleInput(c.req.raw);
|
|
47
|
+
return response;
|
|
48
|
+
});
|
|
49
|
+
return handler;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=hono.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hono.js","sourceRoot":"","sources":["../../src/integrations/hono.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAA4B,MAAM,eAAe,CAAC;AAEvE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,MAAM,UAAU,UAAU,CACxB,GAAQ,EACR,IAAY,EACZ,OAA4B;IAE5B,MAAM,OAAO,GAAG,IAAI,YAAY,CAAC,OAAO,CAAC,CAAC;IAE1C,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,CAAM,EAAE,EAAE;QAC7B,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACpD,OAAO,QAAQ,CAAC;IAClB,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,CAAM,EAAE,EAAE;QAC9B,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACtD,OAAO,QAAQ,CAAC;IAClB,CAAC,CAAC,CAAC;IAEH,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
package/dist/sse.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export interface SSEWriter {
|
|
2
|
+
writeEvent(event: string, data: unknown): void;
|
|
3
|
+
close(): void;
|
|
4
|
+
}
|
|
5
|
+
export declare function createSSEWriter(writable: WritableStream<Uint8Array>): SSEWriter;
|
|
6
|
+
export interface SessionStartedEvent {
|
|
7
|
+
sessionId: string;
|
|
8
|
+
}
|
|
9
|
+
export interface AgentTextEvent {
|
|
10
|
+
text: string;
|
|
11
|
+
isFinal: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface AgentAudioEvent {
|
|
14
|
+
/** Base64-encoded raw PCM audio */
|
|
15
|
+
audio: string;
|
|
16
|
+
sampleRate: number;
|
|
17
|
+
numChannels: number;
|
|
18
|
+
}
|
|
19
|
+
export interface UserTranscriptionEvent {
|
|
20
|
+
text: string;
|
|
21
|
+
isFinal: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface AgentStateEvent {
|
|
24
|
+
state: 'initializing' | 'idle' | 'listening' | 'thinking' | 'speaking';
|
|
25
|
+
}
|
|
26
|
+
export interface SessionEndedEvent {
|
|
27
|
+
reason: 'completed' | 'error' | 'client_disconnect' | 'server_shutdown';
|
|
28
|
+
}
|
|
29
|
+
export interface ErrorSSEEvent {
|
|
30
|
+
message: string;
|
|
31
|
+
code?: string;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=sse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../src/sse.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS;IACxB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAAC;IAC/C,KAAK,IAAI,IAAI,CAAC;CACf;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,cAAc,CAAC,UAAU,CAAC,GAAG,SAAS,CA0B/E;AAID,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,cAAc,GAAG,MAAM,GAAG,WAAW,GAAG,UAAU,GAAG,UAAU,CAAC;CACxE;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,WAAW,GAAG,OAAO,GAAG,mBAAmB,GAAG,iBAAiB,CAAC;CACzE;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf"}
|
package/dist/sse.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export function createSSEWriter(writable) {
|
|
2
|
+
const encoder = new TextEncoder();
|
|
3
|
+
const writer = writable.getWriter();
|
|
4
|
+
return {
|
|
5
|
+
writeEvent(event, data) {
|
|
6
|
+
const json = JSON.stringify(data);
|
|
7
|
+
const message = `event: ${event}\ndata: ${json}\n\n`;
|
|
8
|
+
writer.write(encoder.encode(message)).catch((err) => {
|
|
9
|
+
console.error('[SSEWriter] Error writing event:', {
|
|
10
|
+
event,
|
|
11
|
+
error: err instanceof Error ? err.message : String(err),
|
|
12
|
+
timestamp: new Date().toISOString(),
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
},
|
|
16
|
+
close() {
|
|
17
|
+
writer.close().catch((err) => {
|
|
18
|
+
console.error('[SSEWriter] Error closing writer:', {
|
|
19
|
+
error: err instanceof Error ? err.message : String(err),
|
|
20
|
+
timestamp: new Date().toISOString(),
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=sse.js.map
|
package/dist/sse.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sse.js","sourceRoot":"","sources":["../src/sse.ts"],"names":[],"mappings":"AAKA,MAAM,UAAU,eAAe,CAAC,QAAoC;IAClE,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;IAClC,MAAM,MAAM,GAAG,QAAQ,CAAC,SAAS,EAAE,CAAC;IAEpC,OAAO;QACL,UAAU,CAAC,KAAa,EAAE,IAAa;YACrC,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;YAClC,MAAM,OAAO,GAAG,UAAU,KAAK,WAAW,IAAI,MAAM,CAAC;YACrD,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;gBAClD,OAAO,CAAC,KAAK,CAAC,kCAAkC,EAAE;oBAChD,KAAK;oBACL,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;oBACvD,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;iBACpC,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;QACL,CAAC;QAED,KAAK;YACH,MAAM,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;gBAC3B,OAAO,CAAC,KAAK,CAAC,mCAAmC,EAAE;oBACjD,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;oBACvD,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;iBACpC,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { TextOutput, type TimedString } from '@ariaflow/livekit-plugin';
|
|
2
|
+
import type { SSEWriter } from './sse.js';
|
|
3
|
+
/**
|
|
4
|
+
* Streams agent text responses to the client via SSE events.
|
|
5
|
+
* Primary output for text-only chatbots.
|
|
6
|
+
*/
|
|
7
|
+
export declare class HTTPTextOutput extends TextOutput {
|
|
8
|
+
private sseWriter;
|
|
9
|
+
private closed;
|
|
10
|
+
constructor(nextInChain?: TextOutput);
|
|
11
|
+
setSSEWriter(writer: SSEWriter): void;
|
|
12
|
+
captureText(text: string | TimedString): Promise<void>;
|
|
13
|
+
flush(): void;
|
|
14
|
+
close(): Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=text_output.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text_output.d.ts","sourceRoot":"","sources":["../src/text_output.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAiB,KAAK,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvF,OAAO,KAAK,EAAE,SAAS,EAAkB,MAAM,UAAU,CAAC;AAE1D;;;GAGG;AACH,qBAAa,cAAe,SAAQ,UAAU;IAC5C,OAAO,CAAC,SAAS,CAA0B;IAC3C,OAAO,CAAC,MAAM,CAAkB;gBAEpB,WAAW,CAAC,EAAE,UAAU;IAIpC,YAAY,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI;IAI/B,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAiB5D,KAAK,IAAI,IAAI;IAcP,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { TextOutput, isTimedString } from '@ariaflow/livekit-plugin';
|
|
2
|
+
/**
|
|
3
|
+
* Streams agent text responses to the client via SSE events.
|
|
4
|
+
* Primary output for text-only chatbots.
|
|
5
|
+
*/
|
|
6
|
+
export class HTTPTextOutput extends TextOutput {
|
|
7
|
+
sseWriter = null;
|
|
8
|
+
closed = false;
|
|
9
|
+
constructor(nextInChain) {
|
|
10
|
+
super(nextInChain);
|
|
11
|
+
}
|
|
12
|
+
setSSEWriter(writer) {
|
|
13
|
+
this.sseWriter = writer;
|
|
14
|
+
}
|
|
15
|
+
async captureText(text) {
|
|
16
|
+
if (this.closed || !this.sseWriter)
|
|
17
|
+
return;
|
|
18
|
+
const textContent = isTimedString(text) ? text.text : text;
|
|
19
|
+
const event = {
|
|
20
|
+
text: textContent,
|
|
21
|
+
isFinal: false,
|
|
22
|
+
};
|
|
23
|
+
this.sseWriter.writeEvent('agent_text', event);
|
|
24
|
+
if (this.nextInChain) {
|
|
25
|
+
await this.nextInChain.captureText(text);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
flush() {
|
|
29
|
+
if (!this.closed && this.sseWriter) {
|
|
30
|
+
const event = {
|
|
31
|
+
text: '',
|
|
32
|
+
isFinal: true,
|
|
33
|
+
};
|
|
34
|
+
this.sseWriter.writeEvent('agent_text', event);
|
|
35
|
+
}
|
|
36
|
+
if (this.nextInChain) {
|
|
37
|
+
this.nextInChain.flush();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async close() {
|
|
41
|
+
this.closed = true;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=text_output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text_output.js","sourceRoot":"","sources":["../src/text_output.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,aAAa,EAAoB,MAAM,0BAA0B,CAAC;AAGvF;;;GAGG;AACH,MAAM,OAAO,cAAe,SAAQ,UAAU;IACpC,SAAS,GAAqB,IAAI,CAAC;IACnC,MAAM,GAAY,KAAK,CAAC;IAEhC,YAAY,WAAwB;QAClC,KAAK,CAAC,WAAW,CAAC,CAAC;IACrB,CAAC;IAED,YAAY,CAAC,MAAiB;QAC5B,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC;IAC1B,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,IAA0B;QAC1C,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,SAAS;YAAE,OAAO;QAE3C,MAAM,WAAW,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;QAE3D,MAAM,KAAK,GAAmB;YAC5B,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE,KAAK;SACf,CAAC;QAEF,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;QAE/C,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,MAAO,IAAI,CAAC,WAA0B,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC;IAED,KAAK;QACH,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACnC,MAAM,KAAK,GAAmB;gBAC5B,IAAI,EAAE,EAAE;gBACR,OAAO,EAAE,IAAI;aACd,CAAC;YACF,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;QACjD,CAAC;QAED,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACpB,IAAI,CAAC,WAA0B,CAAC,KAAK,EAAE,CAAC;QAC3C,CAAC;IACH,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;IACrB,CAAC;CACF"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { TransportAdapter, type TransportAdapterConfig } from '@ariaflow/livekit-plugin';
|
|
2
|
+
import { HTTPAudioInput } from './audio_input.js';
|
|
3
|
+
import { HTTPAudioOutput } from './audio_output.js';
|
|
4
|
+
import { HTTPTextOutput } from './text_output.js';
|
|
5
|
+
import type { SSEWriter } from './sse.js';
|
|
6
|
+
export declare class HTTPTransportAdapter extends TransportAdapter {
|
|
7
|
+
readonly id: string;
|
|
8
|
+
readonly audioInput: HTTPAudioInput;
|
|
9
|
+
readonly audioOutput: HTTPAudioOutput;
|
|
10
|
+
readonly textOutput: HTTPTextOutput;
|
|
11
|
+
readonly config: TransportAdapterConfig;
|
|
12
|
+
private _isOpen;
|
|
13
|
+
private timeoutTimer;
|
|
14
|
+
private sessionTimeout;
|
|
15
|
+
constructor(options?: {
|
|
16
|
+
id?: string;
|
|
17
|
+
sampleRate?: number;
|
|
18
|
+
numChannels?: number;
|
|
19
|
+
sessionTimeout?: number;
|
|
20
|
+
});
|
|
21
|
+
/** Attach SSE writer to audio and text outputs. */
|
|
22
|
+
attachSSE(writer: SSEWriter): void;
|
|
23
|
+
/** Reset session timeout on activity. */
|
|
24
|
+
touch(): void;
|
|
25
|
+
get isOpen(): boolean;
|
|
26
|
+
close(): Promise<void>;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=transport_adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport_adapter.d.ts","sourceRoot":"","sources":["../src/transport_adapter.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,KAAK,sBAAsB,EAC5B,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAG1C,qBAAa,oBAAqB,SAAQ,gBAAgB;IACxD,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,UAAU,EAAE,cAAc,CAAC;IACpC,QAAQ,CAAC,WAAW,EAAE,eAAe,CAAC;IACtC,QAAQ,CAAC,UAAU,EAAE,cAAc,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE,sBAAsB,CAAC;IAExC,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,YAAY,CAA8C;IAClE,OAAO,CAAC,cAAc,CAAS;gBAG7B,OAAO,GAAE;QACP,EAAE,CAAC,EAAE,MAAM,CAAC;QACZ,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,cAAc,CAAC,EAAE,MAAM,CAAC;KACpB;IAyBR,mDAAmD;IACnD,SAAS,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI;IAKlC,yCAAyC;IACzC,KAAK,IAAI,IAAI;IASb,IAAI,MAAM,IAAI,OAAO,CAEpB;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAa7B"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { TransportAdapter, } from '@ariaflow/livekit-plugin';
|
|
2
|
+
import { HTTPAudioInput } from './audio_input.js';
|
|
3
|
+
import { HTTPAudioOutput } from './audio_output.js';
|
|
4
|
+
import { HTTPTextOutput } from './text_output.js';
|
|
5
|
+
import { randomUUID } from 'node:crypto';
|
|
6
|
+
export class HTTPTransportAdapter extends TransportAdapter {
|
|
7
|
+
id;
|
|
8
|
+
audioInput;
|
|
9
|
+
audioOutput;
|
|
10
|
+
textOutput;
|
|
11
|
+
config;
|
|
12
|
+
_isOpen = true;
|
|
13
|
+
timeoutTimer = null;
|
|
14
|
+
sessionTimeout;
|
|
15
|
+
constructor(options = {}) {
|
|
16
|
+
super();
|
|
17
|
+
this.id = options.id ?? randomUUID();
|
|
18
|
+
const sampleRate = options.sampleRate ?? 24000;
|
|
19
|
+
const numChannels = options.numChannels ?? 1;
|
|
20
|
+
this.sessionTimeout = options.sessionTimeout ?? 300000;
|
|
21
|
+
this.config = {
|
|
22
|
+
sampleRate,
|
|
23
|
+
numChannels,
|
|
24
|
+
encoding: 'pcm_s16le',
|
|
25
|
+
samplesPerChannel: null,
|
|
26
|
+
};
|
|
27
|
+
this.audioInput = new HTTPAudioInput(sampleRate, numChannels);
|
|
28
|
+
this.audioOutput = new HTTPAudioOutput(sampleRate);
|
|
29
|
+
this.textOutput = new HTTPTextOutput();
|
|
30
|
+
this.timeoutTimer = setTimeout(() => {
|
|
31
|
+
this.close();
|
|
32
|
+
}, this.sessionTimeout);
|
|
33
|
+
}
|
|
34
|
+
/** Attach SSE writer to audio and text outputs. */
|
|
35
|
+
attachSSE(writer) {
|
|
36
|
+
this.audioOutput.setSSEWriter(writer);
|
|
37
|
+
this.textOutput.setSSEWriter(writer);
|
|
38
|
+
}
|
|
39
|
+
/** Reset session timeout on activity. */
|
|
40
|
+
touch() {
|
|
41
|
+
if (this.timeoutTimer) {
|
|
42
|
+
clearTimeout(this.timeoutTimer);
|
|
43
|
+
this.timeoutTimer = setTimeout(() => {
|
|
44
|
+
this.close();
|
|
45
|
+
}, this.sessionTimeout);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
get isOpen() {
|
|
49
|
+
return this._isOpen;
|
|
50
|
+
}
|
|
51
|
+
async close() {
|
|
52
|
+
if (!this._isOpen)
|
|
53
|
+
return;
|
|
54
|
+
this._isOpen = false;
|
|
55
|
+
if (this.timeoutTimer) {
|
|
56
|
+
clearTimeout(this.timeoutTimer);
|
|
57
|
+
this.timeoutTimer = null;
|
|
58
|
+
}
|
|
59
|
+
await this.audioInput.close();
|
|
60
|
+
await this.audioOutput.close();
|
|
61
|
+
await this.textOutput.close();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=transport_adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport_adapter.js","sourceRoot":"","sources":["../src/transport_adapter.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,GAEjB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,OAAO,oBAAqB,SAAQ,gBAAgB;IAC/C,EAAE,CAAS;IACX,UAAU,CAAiB;IAC3B,WAAW,CAAkB;IAC7B,UAAU,CAAiB;IAC3B,MAAM,CAAyB;IAEhC,OAAO,GAAY,IAAI,CAAC;IACxB,YAAY,GAAyC,IAAI,CAAC;IAC1D,cAAc,CAAS;IAE/B,YACE,UAKI,EAAE;QAEN,KAAK,EAAE,CAAC;QAER,IAAI,CAAC,EAAE,GAAG,OAAO,CAAC,EAAE,IAAI,UAAU,EAAE,CAAC;QACrC,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,KAAK,CAAC;QAC/C,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,CAAC,CAAC;QAC7C,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,MAAM,CAAC;QAEvD,IAAI,CAAC,MAAM,GAAG;YACZ,UAAU;YACV,WAAW;YACX,QAAQ,EAAE,WAAW;YACrB,iBAAiB,EAAE,IAAI;SACxB,CAAC;QAEF,IAAI,CAAC,UAAU,GAAG,IAAI,cAAc,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;QAC9D,IAAI,CAAC,WAAW,GAAG,IAAI,eAAe,CAAC,UAAU,CAAC,CAAC;QACnD,IAAI,CAAC,UAAU,GAAG,IAAI,cAAc,EAAE,CAAC;QAEvC,IAAI,CAAC,YAAY,GAAG,UAAU,CAAC,GAAG,EAAE;YAClC,IAAI,CAAC,KAAK,EAAE,CAAC;QACf,CAAC,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;IAC1B,CAAC;IAED,mDAAmD;IACnD,SAAS,CAAC,MAAiB;QACzB,IAAI,CAAC,WAAW,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;QACtC,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IACvC,CAAC;IAED,yCAAyC;IACzC,KAAK;QACH,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,YAAY,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAChC,IAAI,CAAC,YAAY,GAAG,UAAU,CAAC,GAAG,EAAE;gBAClC,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,CAAC,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;IAED,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAC1B,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;QAErB,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,YAAY,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAChC,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QAC3B,CAAC;QAED,MAAM,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;QAC9B,MAAM,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC;QAC/B,MAAM,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;IAChC,CAAC;CACF"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export interface UserTextInput {
|
|
2
|
+
type: 'user_text';
|
|
3
|
+
text: string;
|
|
4
|
+
}
|
|
5
|
+
export interface UserAudioInput {
|
|
6
|
+
type: 'user_audio';
|
|
7
|
+
/** Base64-encoded raw PCM audio */
|
|
8
|
+
audio: string;
|
|
9
|
+
sampleRate: number;
|
|
10
|
+
numChannels: number;
|
|
11
|
+
}
|
|
12
|
+
export interface EndSessionInput {
|
|
13
|
+
type: 'end_session';
|
|
14
|
+
}
|
|
15
|
+
export type ClientInput = UserTextInput | UserAudioInput | EndSessionInput;
|
|
16
|
+
export interface HTTPTransportOptions {
|
|
17
|
+
/** Base path for session endpoints. Default: '/session'. */
|
|
18
|
+
basePath?: string;
|
|
19
|
+
/** Default audio sample rate. Default: 24000. */
|
|
20
|
+
defaultSampleRate?: number;
|
|
21
|
+
/** Default number of audio channels. Default: 1. */
|
|
22
|
+
defaultNumChannels?: number;
|
|
23
|
+
/** Maximum request body size in bytes. Default: 10MB. */
|
|
24
|
+
maxBodySize?: number;
|
|
25
|
+
/** Session timeout in milliseconds. Default: 300000 (5 minutes). */
|
|
26
|
+
sessionTimeout?: number;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,WAAW,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,YAAY,CAAC;IACnB,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,aAAa,CAAC;CACrB;AAED,MAAM,MAAM,WAAW,GAAG,aAAa,GAAG,cAAc,GAAG,eAAe,CAAC;AAE3E,MAAM,WAAW,oBAAoB;IACnC,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iDAAiD;IACjD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,oDAAoD;IACpD,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,yDAAyD;IACzD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oEAAoE;IACpE,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ariaflowagents/livekit-plugin-transport-http",
|
|
3
|
+
"version": "0.9.0",
|
|
4
|
+
"description": "HTTP/SSE transport adapter for LiveKit Agents. Run text chatbots and voice agents over standard HTTP with Server-Sent Events.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./hono": {
|
|
14
|
+
"types": "./dist/integrations/hono.d.ts",
|
|
15
|
+
"import": "./dist/integrations/hono.js"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"files": ["dist"],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsc",
|
|
21
|
+
"clean": "rm -rf dist"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {},
|
|
24
|
+
"peerDependencies": {
|
|
25
|
+
"@ariaflowagents/livekit-plugin": "workspace:*",
|
|
26
|
+
"@livekit/agents": ">=1.0.0"
|
|
27
|
+
},
|
|
28
|
+
"peerDependenciesMeta": {
|
|
29
|
+
"hono": {
|
|
30
|
+
"optional": true
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@hono/node-server": "^1.19.11",
|
|
35
|
+
"typescript": "^5.5.0",
|
|
36
|
+
"hono": "^4.0.0"
|
|
37
|
+
},
|
|
38
|
+
"license": "Apache-2.0"
|
|
39
|
+
}
|