@livekit/agents 0.4.6 → 0.5.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 +17 -0
- package/dist/audio.cjs +77 -0
- package/dist/audio.cjs.map +1 -0
- package/dist/audio.js +48 -37
- package/dist/audio.js.map +1 -1
- package/dist/cli.cjs +131 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.js +96 -122
- package/dist/cli.js.map +1 -1
- package/dist/generator.cjs +36 -0
- package/dist/generator.cjs.map +1 -0
- package/dist/generator.js +8 -22
- package/dist/generator.js.map +1 -1
- package/dist/http_server.cjs +72 -0
- package/dist/http_server.cjs.map +1 -0
- package/dist/http_server.d.ts +1 -1
- package/dist/http_server.js +44 -47
- package/dist/http_server.js.map +1 -1
- package/dist/index.cjs +78 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.js +26 -28
- package/dist/index.js.map +1 -1
- package/dist/ipc/job_executor.cjs +33 -0
- package/dist/ipc/job_executor.cjs.map +1 -0
- package/dist/ipc/job_executor.js +7 -4
- package/dist/ipc/job_executor.js.map +1 -1
- package/dist/ipc/job_main.cjs +147 -0
- package/dist/ipc/job_main.cjs.map +1 -0
- package/dist/ipc/job_main.d.ts +1 -1
- package/dist/ipc/job_main.js +103 -103
- package/dist/ipc/job_main.js.map +1 -1
- package/dist/ipc/message.cjs +17 -0
- package/dist/ipc/message.cjs.map +1 -0
- package/dist/ipc/message.js +0 -1
- package/dist/ipc/message.js.map +1 -1
- package/dist/ipc/proc_job_executor.cjs +174 -0
- package/dist/ipc/proc_job_executor.cjs.map +1 -0
- package/dist/ipc/proc_job_executor.js +130 -126
- package/dist/ipc/proc_job_executor.js.map +1 -1
- package/dist/ipc/proc_pool.cjs +126 -0
- package/dist/ipc/proc_pool.cjs.map +1 -0
- package/dist/ipc/proc_pool.js +93 -96
- package/dist/ipc/proc_pool.js.map +1 -1
- package/dist/job.cjs +230 -0
- package/dist/job.cjs.map +1 -0
- package/dist/job.js +195 -198
- package/dist/job.js.map +1 -1
- package/dist/llm/chat_context.cjs +131 -0
- package/dist/llm/chat_context.cjs.map +1 -0
- package/dist/llm/chat_context.js +98 -86
- package/dist/llm/chat_context.js.map +1 -1
- package/dist/llm/function_context.cjs +103 -0
- package/dist/llm/function_context.cjs.map +1 -0
- package/dist/llm/function_context.js +72 -81
- package/dist/llm/function_context.js.map +1 -1
- package/dist/llm/function_context.test.cjs +218 -0
- package/dist/llm/function_context.test.cjs.map +1 -0
- package/dist/llm/function_context.test.js +209 -210
- package/dist/llm/function_context.test.js.map +1 -1
- package/dist/llm/index.cjs +43 -0
- package/dist/llm/index.cjs.map +1 -0
- package/dist/llm/index.js +22 -6
- package/dist/llm/index.js.map +1 -1
- package/dist/llm/llm.cjs +76 -0
- package/dist/llm/llm.cjs.map +1 -0
- package/dist/llm/llm.js +48 -42
- package/dist/llm/llm.js.map +1 -1
- package/dist/log.cjs +57 -0
- package/dist/log.cjs.map +1 -0
- package/dist/log.js +27 -26
- package/dist/log.js.map +1 -1
- package/dist/multimodal/agent_playout.cjs +228 -0
- package/dist/multimodal/agent_playout.cjs.map +1 -0
- package/dist/multimodal/agent_playout.d.ts +1 -1
- package/dist/multimodal/agent_playout.js +193 -180
- package/dist/multimodal/agent_playout.js.map +1 -1
- package/dist/multimodal/index.cjs +25 -0
- package/dist/multimodal/index.cjs.map +1 -0
- package/dist/multimodal/index.js +2 -5
- package/dist/multimodal/index.js.map +1 -1
- package/dist/multimodal/multimodal_agent.cjs +404 -0
- package/dist/multimodal/multimodal_agent.cjs.map +1 -0
- package/dist/multimodal/multimodal_agent.d.ts +1 -1
- package/dist/multimodal/multimodal_agent.js +351 -330
- package/dist/multimodal/multimodal_agent.js.map +1 -1
- package/dist/pipeline/agent_output.cjs +172 -0
- package/dist/pipeline/agent_output.cjs.map +1 -0
- package/dist/pipeline/agent_output.js +136 -138
- package/dist/pipeline/agent_output.js.map +1 -1
- package/dist/pipeline/agent_playout.cjs +169 -0
- package/dist/pipeline/agent_playout.cjs.map +1 -0
- package/dist/pipeline/agent_playout.js +126 -136
- package/dist/pipeline/agent_playout.js.map +1 -1
- package/dist/pipeline/human_input.cjs +158 -0
- package/dist/pipeline/human_input.cjs.map +1 -0
- package/dist/pipeline/human_input.js +124 -125
- package/dist/pipeline/human_input.js.map +1 -1
- package/dist/pipeline/index.cjs +31 -0
- package/dist/pipeline/index.cjs.map +1 -0
- package/dist/pipeline/index.js +8 -4
- package/dist/pipeline/index.js.map +1 -1
- package/dist/pipeline/pipeline_agent.cjs +642 -0
- package/dist/pipeline/pipeline_agent.cjs.map +1 -0
- package/dist/pipeline/pipeline_agent.js +595 -651
- package/dist/pipeline/pipeline_agent.js.map +1 -1
- package/dist/pipeline/speech_handle.cjs +128 -0
- package/dist/pipeline/speech_handle.cjs.map +1 -0
- package/dist/pipeline/speech_handle.js +102 -100
- package/dist/pipeline/speech_handle.js.map +1 -1
- package/dist/plugin.cjs +46 -0
- package/dist/plugin.cjs.map +1 -0
- package/dist/plugin.js +20 -20
- package/dist/plugin.js.map +1 -1
- package/dist/stt/index.cjs +38 -0
- package/dist/stt/index.cjs.map +1 -0
- package/dist/stt/index.js +13 -5
- package/dist/stt/index.js.map +1 -1
- package/dist/stt/stream_adapter.cjs +87 -0
- package/dist/stt/stream_adapter.cjs.map +1 -0
- package/dist/stt/stream_adapter.js +58 -55
- package/dist/stt/stream_adapter.js.map +1 -1
- package/dist/stt/stt.cjs +98 -0
- package/dist/stt/stt.cjs.map +1 -0
- package/dist/stt/stt.js +63 -98
- package/dist/stt/stt.js.map +1 -1
- package/dist/tokenize/basic/basic.cjs +98 -0
- package/dist/tokenize/basic/basic.cjs.map +1 -0
- package/dist/tokenize/basic/basic.js +56 -45
- package/dist/tokenize/basic/basic.js.map +1 -1
- package/dist/tokenize/basic/hyphenator.cjs +425 -0
- package/dist/tokenize/basic/hyphenator.cjs.map +1 -0
- package/dist/tokenize/basic/hyphenator.js +66 -82
- package/dist/tokenize/basic/hyphenator.js.map +1 -1
- package/dist/tokenize/basic/index.cjs +35 -0
- package/dist/tokenize/basic/index.cjs.map +1 -0
- package/dist/tokenize/basic/index.js +7 -4
- package/dist/tokenize/basic/index.js.map +1 -1
- package/dist/tokenize/basic/paragraph.cjs +57 -0
- package/dist/tokenize/basic/paragraph.cjs.map +1 -0
- package/dist/tokenize/basic/paragraph.js +30 -35
- package/dist/tokenize/basic/paragraph.js.map +1 -1
- package/dist/tokenize/basic/sentence.cjs +83 -0
- package/dist/tokenize/basic/sentence.cjs.map +1 -0
- package/dist/tokenize/basic/sentence.js +56 -57
- package/dist/tokenize/basic/sentence.js.map +1 -1
- package/dist/tokenize/basic/word.cjs +44 -0
- package/dist/tokenize/basic/word.cjs.map +1 -0
- package/dist/tokenize/basic/word.js +17 -20
- package/dist/tokenize/basic/word.js.map +1 -1
- package/dist/tokenize/index.cjs +55 -0
- package/dist/tokenize/index.cjs.map +1 -0
- package/dist/tokenize/index.js +18 -7
- package/dist/tokenize/index.js.map +1 -1
- package/dist/tokenize/token_stream.cjs +164 -0
- package/dist/tokenize/token_stream.cjs.map +1 -0
- package/dist/tokenize/token_stream.js +133 -139
- package/dist/tokenize/token_stream.js.map +1 -1
- package/dist/tokenize/tokenizer.cjs +184 -0
- package/dist/tokenize/tokenizer.cjs.map +1 -0
- package/dist/tokenize/tokenizer.js +138 -99
- package/dist/tokenize/tokenizer.js.map +1 -1
- package/dist/transcription.cjs +131 -0
- package/dist/transcription.cjs.map +1 -0
- package/dist/transcription.js +99 -96
- package/dist/transcription.js.map +1 -1
- package/dist/tts/index.cjs +38 -0
- package/dist/tts/index.cjs.map +1 -0
- package/dist/tts/index.js +13 -5
- package/dist/tts/index.js.map +1 -1
- package/dist/tts/stream_adapter.cjs +78 -0
- package/dist/tts/stream_adapter.cjs.map +1 -0
- package/dist/tts/stream_adapter.js +50 -47
- package/dist/tts/stream_adapter.js.map +1 -1
- package/dist/tts/tts.cjs +127 -0
- package/dist/tts/tts.cjs.map +1 -0
- package/dist/tts/tts.js +90 -120
- package/dist/tts/tts.js.map +1 -1
- package/dist/utils.cjs +284 -0
- package/dist/utils.cjs.map +1 -0
- package/dist/utils.js +242 -247
- package/dist/utils.js.map +1 -1
- package/dist/vad.cjs +92 -0
- package/dist/vad.cjs.map +1 -0
- package/dist/vad.js +57 -52
- package/dist/vad.js.map +1 -1
- package/dist/version.cjs +29 -0
- package/dist/version.cjs.map +1 -0
- package/dist/version.js +4 -4
- package/dist/version.js.map +1 -1
- package/dist/worker.cjs +576 -0
- package/dist/worker.cjs.map +1 -0
- package/dist/worker.d.ts +1 -1
- package/dist/worker.js +511 -484
- package/dist/worker.js.map +1 -1
- package/package.json +18 -8
- package/src/ipc/job_main.ts +66 -64
- package/src/pipeline/pipeline_agent.ts +23 -23
|
@@ -1,117 +1,156 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
import { AsyncIterableQueue } from "../utils.js";
|
|
2
|
+
const PUNCTUATIONS = [
|
|
3
|
+
"!",
|
|
4
|
+
'"',
|
|
5
|
+
"#",
|
|
6
|
+
"$",
|
|
7
|
+
"%",
|
|
8
|
+
"&",
|
|
9
|
+
"'",
|
|
10
|
+
"(",
|
|
11
|
+
")",
|
|
12
|
+
"*",
|
|
13
|
+
"+",
|
|
14
|
+
",",
|
|
15
|
+
"-",
|
|
16
|
+
".",
|
|
17
|
+
"/",
|
|
18
|
+
":",
|
|
19
|
+
";",
|
|
20
|
+
"<",
|
|
21
|
+
"=",
|
|
22
|
+
">",
|
|
23
|
+
"?",
|
|
24
|
+
"@",
|
|
25
|
+
"[",
|
|
26
|
+
"\\",
|
|
27
|
+
"]",
|
|
28
|
+
"^",
|
|
29
|
+
"_",
|
|
30
|
+
"`",
|
|
31
|
+
"{",
|
|
32
|
+
"|",
|
|
33
|
+
"}",
|
|
34
|
+
"~",
|
|
35
|
+
"\xB1",
|
|
36
|
+
"\u2014",
|
|
37
|
+
"\u2018",
|
|
38
|
+
"\u2019",
|
|
39
|
+
"\u201C",
|
|
40
|
+
"\u201D",
|
|
41
|
+
"\u2026"
|
|
10
42
|
];
|
|
11
|
-
|
|
43
|
+
class SentenceTokenizer {
|
|
12
44
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
45
|
+
class SentenceStream {
|
|
46
|
+
static FLUSH_SENTINEL = Symbol("FLUSH_SENTINEL");
|
|
47
|
+
input = new AsyncIterableQueue();
|
|
48
|
+
queue = new AsyncIterableQueue();
|
|
49
|
+
#closed = false;
|
|
50
|
+
get closed() {
|
|
51
|
+
return this.#closed;
|
|
52
|
+
}
|
|
53
|
+
/** Push a string of text to the tokenizer */
|
|
54
|
+
pushText(text) {
|
|
55
|
+
if (this.input.closed) {
|
|
56
|
+
throw new Error("Input is closed");
|
|
20
57
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if (this.input.closed) {
|
|
24
|
-
throw new Error('Input is closed');
|
|
25
|
-
}
|
|
26
|
-
if (this.#closed) {
|
|
27
|
-
throw new Error('Stream is closed');
|
|
28
|
-
}
|
|
29
|
-
this.input.put(text);
|
|
58
|
+
if (this.#closed) {
|
|
59
|
+
throw new Error("Stream is closed");
|
|
30
60
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
throw new Error('Stream is closed');
|
|
38
|
-
}
|
|
39
|
-
this.input.put(SentenceStream.FLUSH_SENTINEL);
|
|
61
|
+
this.input.put(text);
|
|
62
|
+
}
|
|
63
|
+
/** Flush the tokenizer, causing it to process all pending text */
|
|
64
|
+
flush() {
|
|
65
|
+
if (this.input.closed) {
|
|
66
|
+
throw new Error("Input is closed");
|
|
40
67
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if (this.input.closed) {
|
|
44
|
-
throw new Error('Input is closed');
|
|
45
|
-
}
|
|
46
|
-
if (this.#closed) {
|
|
47
|
-
throw new Error('Stream is closed');
|
|
48
|
-
}
|
|
49
|
-
this.input.close();
|
|
68
|
+
if (this.#closed) {
|
|
69
|
+
throw new Error("Stream is closed");
|
|
50
70
|
}
|
|
51
|
-
|
|
52
|
-
|
|
71
|
+
this.input.put(SentenceStream.FLUSH_SENTINEL);
|
|
72
|
+
}
|
|
73
|
+
/** Mark the input as ended and forbid additional pushes */
|
|
74
|
+
endInput() {
|
|
75
|
+
if (this.input.closed) {
|
|
76
|
+
throw new Error("Input is closed");
|
|
53
77
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
this.input.close();
|
|
57
|
-
this.queue.close();
|
|
58
|
-
this.#closed = true;
|
|
59
|
-
}
|
|
60
|
-
[Symbol.asyncIterator]() {
|
|
61
|
-
return this;
|
|
78
|
+
if (this.#closed) {
|
|
79
|
+
throw new Error("Stream is closed");
|
|
62
80
|
}
|
|
81
|
+
this.input.close();
|
|
82
|
+
}
|
|
83
|
+
next() {
|
|
84
|
+
return this.queue.next();
|
|
85
|
+
}
|
|
86
|
+
/** Close both the input and output of the tokenizer stream */
|
|
87
|
+
close() {
|
|
88
|
+
this.input.close();
|
|
89
|
+
this.queue.close();
|
|
90
|
+
this.#closed = true;
|
|
91
|
+
}
|
|
92
|
+
[Symbol.asyncIterator]() {
|
|
93
|
+
return this;
|
|
94
|
+
}
|
|
63
95
|
}
|
|
64
|
-
|
|
96
|
+
class WordTokenizer {
|
|
65
97
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
if (this.#closed) {
|
|
80
|
-
throw new Error('Stream is closed');
|
|
81
|
-
}
|
|
82
|
-
this.input.put(text);
|
|
98
|
+
class WordStream {
|
|
99
|
+
static FLUSH_SENTINEL = Symbol("FLUSH_SENTINEL");
|
|
100
|
+
input = new AsyncIterableQueue();
|
|
101
|
+
queue = new AsyncIterableQueue();
|
|
102
|
+
#closed = false;
|
|
103
|
+
get closed() {
|
|
104
|
+
return this.#closed;
|
|
105
|
+
}
|
|
106
|
+
/** Push a string of text to the tokenizer */
|
|
107
|
+
pushText(text) {
|
|
108
|
+
if (this.input.closed) {
|
|
109
|
+
throw new Error("Input is closed");
|
|
83
110
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
if (this.input.closed) {
|
|
87
|
-
throw new Error('Input is closed');
|
|
88
|
-
}
|
|
89
|
-
if (this.#closed) {
|
|
90
|
-
throw new Error('Stream is closed');
|
|
91
|
-
}
|
|
92
|
-
this.input.put(WordStream.FLUSH_SENTINEL);
|
|
111
|
+
if (this.#closed) {
|
|
112
|
+
throw new Error("Stream is closed");
|
|
93
113
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
throw new Error('Stream is closed');
|
|
101
|
-
}
|
|
102
|
-
this.input.close();
|
|
114
|
+
this.input.put(text);
|
|
115
|
+
}
|
|
116
|
+
/** Flush the tokenizer, causing it to process all pending text */
|
|
117
|
+
flush() {
|
|
118
|
+
if (this.input.closed) {
|
|
119
|
+
throw new Error("Input is closed");
|
|
103
120
|
}
|
|
104
|
-
|
|
105
|
-
|
|
121
|
+
if (this.#closed) {
|
|
122
|
+
throw new Error("Stream is closed");
|
|
106
123
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
124
|
+
this.input.put(WordStream.FLUSH_SENTINEL);
|
|
125
|
+
}
|
|
126
|
+
/** Mark the input as ended and forbid additional pushes */
|
|
127
|
+
endInput() {
|
|
128
|
+
if (this.input.closed) {
|
|
129
|
+
throw new Error("Input is closed");
|
|
112
130
|
}
|
|
113
|
-
|
|
114
|
-
|
|
131
|
+
if (this.#closed) {
|
|
132
|
+
throw new Error("Stream is closed");
|
|
115
133
|
}
|
|
134
|
+
this.input.close();
|
|
135
|
+
}
|
|
136
|
+
next() {
|
|
137
|
+
return this.queue.next();
|
|
138
|
+
}
|
|
139
|
+
/** Close both the input and output of the tokenizer stream */
|
|
140
|
+
close() {
|
|
141
|
+
this.input.close();
|
|
142
|
+
this.queue.close();
|
|
143
|
+
this.#closed = true;
|
|
144
|
+
}
|
|
145
|
+
[Symbol.asyncIterator]() {
|
|
146
|
+
return this;
|
|
147
|
+
}
|
|
116
148
|
}
|
|
149
|
+
export {
|
|
150
|
+
PUNCTUATIONS,
|
|
151
|
+
SentenceStream,
|
|
152
|
+
SentenceTokenizer,
|
|
153
|
+
WordStream,
|
|
154
|
+
WordTokenizer
|
|
155
|
+
};
|
|
117
156
|
//# sourceMappingURL=tokenizer.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"
|
|
1
|
+
{"version":3,"sources":["../../src/tokenize/tokenizer.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { AsyncIterableQueue } from '../utils.js';\n\n// prettier-ignore\nexport const PUNCTUATIONS = [\n '!', '\"', '#', '$', '%', '&', \"'\", '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=',\n '>', '?', '@', '[', '\\\\', ']', '^', '_', '`', '{', '|', '}', '~', '±', '—', '‘', '’', '“', '”',\n '…',\n]\n\nexport interface TokenData {\n segmentId: string;\n token: string;\n}\n\nexport abstract class SentenceTokenizer {\n abstract tokenize(text: string, language?: string): string[];\n\n /**\n * Returns a {@link SentenceStream} that can be used to push strings and receive smaller segments.\n */\n abstract stream(): SentenceStream;\n}\n\nexport abstract class SentenceStream {\n protected static readonly FLUSH_SENTINEL = Symbol('FLUSH_SENTINEL');\n protected input = new AsyncIterableQueue<string | typeof SentenceStream.FLUSH_SENTINEL>();\n protected queue = new AsyncIterableQueue<TokenData>();\n #closed = false;\n\n get closed(): boolean {\n return this.#closed;\n }\n\n /** Push a string of text to the tokenizer */\n pushText(text: string) {\n if (this.input.closed) {\n throw new Error('Input is closed');\n }\n if (this.#closed) {\n throw new Error('Stream is closed');\n }\n this.input.put(text);\n }\n\n /** Flush the tokenizer, causing it to process all pending text */\n flush() {\n if (this.input.closed) {\n throw new Error('Input is closed');\n }\n if (this.#closed) {\n throw new Error('Stream is closed');\n }\n this.input.put(SentenceStream.FLUSH_SENTINEL);\n }\n\n /** Mark the input as ended and forbid additional pushes */\n endInput() {\n if (this.input.closed) {\n throw new Error('Input is closed');\n }\n if (this.#closed) {\n throw new Error('Stream is closed');\n }\n this.input.close();\n }\n\n next(): Promise<IteratorResult<TokenData>> {\n return this.queue.next();\n }\n\n /** Close both the input and output of the tokenizer stream */\n close() {\n this.input.close();\n this.queue.close();\n this.#closed = true;\n }\n\n [Symbol.asyncIterator](): SentenceStream {\n return this;\n }\n}\n\nexport abstract class WordTokenizer {\n abstract tokenize(text: string, language?: string): string[];\n\n /**\n * Returns a {@link WordStream} that can be used to push words and receive smaller segments.\n */\n abstract stream(): WordStream;\n}\n\nexport abstract class WordStream {\n protected static readonly FLUSH_SENTINEL = Symbol('FLUSH_SENTINEL');\n protected input = new AsyncIterableQueue<string | typeof WordStream.FLUSH_SENTINEL>();\n protected queue = new AsyncIterableQueue<TokenData>();\n #closed = false;\n\n get closed(): boolean {\n return this.#closed;\n }\n\n /** Push a string of text to the tokenizer */\n pushText(text: string) {\n if (this.input.closed) {\n throw new Error('Input is closed');\n }\n if (this.#closed) {\n throw new Error('Stream is closed');\n }\n this.input.put(text);\n }\n\n /** Flush the tokenizer, causing it to process all pending text */\n flush() {\n if (this.input.closed) {\n throw new Error('Input is closed');\n }\n if (this.#closed) {\n throw new Error('Stream is closed');\n }\n this.input.put(WordStream.FLUSH_SENTINEL);\n }\n\n /** Mark the input as ended and forbid additional pushes */\n endInput() {\n if (this.input.closed) {\n throw new Error('Input is closed');\n }\n if (this.#closed) {\n throw new Error('Stream is closed');\n }\n this.input.close();\n }\n\n next(): Promise<IteratorResult<TokenData>> {\n return this.queue.next();\n }\n\n /** Close both the input and output of the tokenizer stream */\n close() {\n this.input.close();\n this.queue.close();\n this.#closed = true;\n }\n\n [Symbol.asyncIterator](): WordStream {\n return this;\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var transcription_exports = {};
|
|
20
|
+
__export(transcription_exports, {
|
|
21
|
+
BasicTranscriptionForwarder: () => BasicTranscriptionForwarder
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(transcription_exports);
|
|
24
|
+
var import_log = require("./log.cjs");
|
|
25
|
+
class BasicTranscriptionForwarder {
|
|
26
|
+
#room;
|
|
27
|
+
#participantIdentity;
|
|
28
|
+
#trackSid;
|
|
29
|
+
#currentText = "";
|
|
30
|
+
#totalAudioDuration = 0;
|
|
31
|
+
#currentPlayoutTime = 0;
|
|
32
|
+
#DEFAULT_CHARS_PER_SECOND = 16;
|
|
33
|
+
#charsPerSecond = this.#DEFAULT_CHARS_PER_SECOND;
|
|
34
|
+
#messageId;
|
|
35
|
+
#isRunning = false;
|
|
36
|
+
#logger = (0, import_log.log)();
|
|
37
|
+
currentCharacterIndex = 0;
|
|
38
|
+
constructor(room, participantIdentity, trackSid, messageId) {
|
|
39
|
+
this.#room = room;
|
|
40
|
+
this.#participantIdentity = participantIdentity;
|
|
41
|
+
this.#trackSid = trackSid;
|
|
42
|
+
this.#messageId = messageId;
|
|
43
|
+
}
|
|
44
|
+
get text() {
|
|
45
|
+
return this.#currentText;
|
|
46
|
+
}
|
|
47
|
+
start() {
|
|
48
|
+
if (!this.#isRunning) {
|
|
49
|
+
this.#isRunning = true;
|
|
50
|
+
this.#startPublishingLoop().catch((error) => {
|
|
51
|
+
this.#logger.error("Error in publishing loop:", error);
|
|
52
|
+
this.#isRunning = false;
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
pushAudio(frame) {
|
|
57
|
+
this.#totalAudioDuration += frame.samplesPerChannel / frame.sampleRate;
|
|
58
|
+
}
|
|
59
|
+
pushText(text) {
|
|
60
|
+
this.#currentText += text;
|
|
61
|
+
}
|
|
62
|
+
#textIsComplete = false;
|
|
63
|
+
#audioIsComplete = false;
|
|
64
|
+
markTextComplete() {
|
|
65
|
+
this.#textIsComplete = true;
|
|
66
|
+
this.#adjustTimingIfBothFinished();
|
|
67
|
+
}
|
|
68
|
+
markAudioComplete() {
|
|
69
|
+
this.#audioIsComplete = true;
|
|
70
|
+
this.#adjustTimingIfBothFinished();
|
|
71
|
+
}
|
|
72
|
+
#adjustTimingIfBothFinished() {
|
|
73
|
+
if (this.#textIsComplete && this.#audioIsComplete) {
|
|
74
|
+
const actualDuration = this.#totalAudioDuration;
|
|
75
|
+
if (actualDuration > 0 && this.#currentText.length > 0) {
|
|
76
|
+
this.#charsPerSecond = this.#currentText.length / actualDuration;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
#computeSleepInterval() {
|
|
81
|
+
return Math.min(Math.max(1 / this.#charsPerSecond, 0.0625), 0.5);
|
|
82
|
+
}
|
|
83
|
+
async #startPublishingLoop() {
|
|
84
|
+
this.#isRunning = true;
|
|
85
|
+
let sleepInterval = this.#computeSleepInterval();
|
|
86
|
+
let isComplete = false;
|
|
87
|
+
while (this.#isRunning && !isComplete) {
|
|
88
|
+
this.#currentPlayoutTime += sleepInterval;
|
|
89
|
+
this.currentCharacterIndex = Math.floor(this.#currentPlayoutTime * this.#charsPerSecond);
|
|
90
|
+
isComplete = this.#textIsComplete && this.currentCharacterIndex >= this.#currentText.length;
|
|
91
|
+
await this.#publishTranscription(false);
|
|
92
|
+
if (this.#isRunning && !isComplete) {
|
|
93
|
+
sleepInterval = this.#computeSleepInterval();
|
|
94
|
+
await new Promise((resolve) => setTimeout(resolve, sleepInterval * 1e3));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (this.#isRunning) {
|
|
98
|
+
this.close(false);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async #publishTranscription(final) {
|
|
102
|
+
var _a;
|
|
103
|
+
const textToPublish = this.#currentText.slice(0, this.currentCharacterIndex);
|
|
104
|
+
await ((_a = this.#room.localParticipant) == null ? void 0 : _a.publishTranscription({
|
|
105
|
+
participantIdentity: this.#participantIdentity,
|
|
106
|
+
trackSid: this.#trackSid,
|
|
107
|
+
segments: [
|
|
108
|
+
{
|
|
109
|
+
text: textToPublish,
|
|
110
|
+
final,
|
|
111
|
+
id: this.#messageId,
|
|
112
|
+
startTime: BigInt(0),
|
|
113
|
+
endTime: BigInt(0),
|
|
114
|
+
language: ""
|
|
115
|
+
}
|
|
116
|
+
]
|
|
117
|
+
}));
|
|
118
|
+
}
|
|
119
|
+
async close(interrupt) {
|
|
120
|
+
this.#isRunning = false;
|
|
121
|
+
if (!interrupt) {
|
|
122
|
+
this.currentCharacterIndex = this.#currentText.length;
|
|
123
|
+
}
|
|
124
|
+
await this.#publishTranscription(true);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
128
|
+
0 && (module.exports = {
|
|
129
|
+
BasicTranscriptionForwarder
|
|
130
|
+
});
|
|
131
|
+
//# sourceMappingURL=transcription.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/transcription.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { AudioFrame, Room } from '@livekit/rtc-node';\nimport { log } from './log.js';\n\nexport interface TranscriptionForwarder {\n start(): void;\n pushAudio(frame: AudioFrame): void;\n pushText(text: string): void;\n markTextComplete(): void;\n markAudioComplete(): void;\n close(interrupt: boolean): Promise<void>;\n currentCharacterIndex: number;\n text: string;\n}\n\nexport class BasicTranscriptionForwarder implements TranscriptionForwarder {\n #room: Room;\n #participantIdentity: string;\n #trackSid: string;\n #currentText: string = '';\n #totalAudioDuration: number = 0;\n #currentPlayoutTime: number = 0;\n #DEFAULT_CHARS_PER_SECOND = 16;\n #charsPerSecond: number = this.#DEFAULT_CHARS_PER_SECOND;\n #messageId: string;\n #isRunning: boolean = false;\n #logger = log();\n currentCharacterIndex: number = 0;\n\n constructor(room: Room, participantIdentity: string, trackSid: string, messageId: string) {\n this.#room = room;\n this.#participantIdentity = participantIdentity;\n this.#trackSid = trackSid;\n this.#messageId = messageId;\n }\n\n get text(): string {\n return this.#currentText;\n }\n\n start(): void {\n if (!this.#isRunning) {\n this.#isRunning = true;\n this.#startPublishingLoop().catch((error) => {\n this.#logger.error('Error in publishing loop:', error);\n this.#isRunning = false;\n });\n }\n }\n\n pushAudio(frame: AudioFrame): void {\n this.#totalAudioDuration += frame.samplesPerChannel / frame.sampleRate;\n }\n\n pushText(text: string): void {\n this.#currentText += text;\n }\n\n #textIsComplete: boolean = false;\n #audioIsComplete: boolean = false;\n\n markTextComplete(): void {\n this.#textIsComplete = true;\n this.#adjustTimingIfBothFinished();\n }\n\n markAudioComplete(): void {\n this.#audioIsComplete = true;\n this.#adjustTimingIfBothFinished();\n }\n\n #adjustTimingIfBothFinished(): void {\n if (this.#textIsComplete && this.#audioIsComplete) {\n const actualDuration = this.#totalAudioDuration;\n if (actualDuration > 0 && this.#currentText.length > 0) {\n this.#charsPerSecond = this.#currentText.length / actualDuration;\n }\n }\n }\n\n #computeSleepInterval(): number {\n return Math.min(Math.max(1 / this.#charsPerSecond, 0.0625), 0.5);\n }\n\n async #startPublishingLoop(): Promise<void> {\n this.#isRunning = true;\n let sleepInterval = this.#computeSleepInterval();\n let isComplete = false;\n while (this.#isRunning && !isComplete) {\n this.#currentPlayoutTime += sleepInterval;\n this.currentCharacterIndex = Math.floor(this.#currentPlayoutTime * this.#charsPerSecond);\n isComplete = this.#textIsComplete && this.currentCharacterIndex >= this.#currentText.length;\n await this.#publishTranscription(false);\n if (this.#isRunning && !isComplete) {\n sleepInterval = this.#computeSleepInterval();\n await new Promise((resolve) => setTimeout(resolve, sleepInterval * 1000));\n }\n }\n\n if (this.#isRunning) {\n this.close(false);\n }\n }\n\n async #publishTranscription(final: boolean): Promise<void> {\n const textToPublish = this.#currentText.slice(0, this.currentCharacterIndex);\n await this.#room.localParticipant?.publishTranscription({\n participantIdentity: this.#participantIdentity,\n trackSid: this.#trackSid,\n segments: [\n {\n text: textToPublish,\n final: final,\n id: this.#messageId,\n startTime: BigInt(0),\n endTime: BigInt(0),\n language: '',\n },\n ],\n });\n }\n\n async close(interrupt: boolean): Promise<void> {\n this.#isRunning = false;\n\n // Publish whatever we had as final\n if (!interrupt) {\n this.currentCharacterIndex = this.#currentText.length;\n }\n await this.#publishTranscription(true);\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/transcription.js
CHANGED
|
@@ -1,104 +1,107 @@
|
|
|
1
|
-
import { log } from
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
pushAudio(frame) {
|
|
34
|
-
this.#totalAudioDuration += frame.samplesPerChannel / frame.sampleRate;
|
|
35
|
-
}
|
|
36
|
-
pushText(text) {
|
|
37
|
-
this.#currentText += text;
|
|
38
|
-
}
|
|
39
|
-
#textIsComplete = false;
|
|
40
|
-
#audioIsComplete = false;
|
|
41
|
-
markTextComplete() {
|
|
42
|
-
this.#textIsComplete = true;
|
|
43
|
-
this.#adjustTimingIfBothFinished();
|
|
44
|
-
}
|
|
45
|
-
markAudioComplete() {
|
|
46
|
-
this.#audioIsComplete = true;
|
|
47
|
-
this.#adjustTimingIfBothFinished();
|
|
48
|
-
}
|
|
49
|
-
#adjustTimingIfBothFinished() {
|
|
50
|
-
if (this.#textIsComplete && this.#audioIsComplete) {
|
|
51
|
-
const actualDuration = this.#totalAudioDuration;
|
|
52
|
-
if (actualDuration > 0 && this.#currentText.length > 0) {
|
|
53
|
-
this.#charsPerSecond = this.#currentText.length / actualDuration;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
1
|
+
import { log } from "./log.js";
|
|
2
|
+
class BasicTranscriptionForwarder {
|
|
3
|
+
#room;
|
|
4
|
+
#participantIdentity;
|
|
5
|
+
#trackSid;
|
|
6
|
+
#currentText = "";
|
|
7
|
+
#totalAudioDuration = 0;
|
|
8
|
+
#currentPlayoutTime = 0;
|
|
9
|
+
#DEFAULT_CHARS_PER_SECOND = 16;
|
|
10
|
+
#charsPerSecond = this.#DEFAULT_CHARS_PER_SECOND;
|
|
11
|
+
#messageId;
|
|
12
|
+
#isRunning = false;
|
|
13
|
+
#logger = log();
|
|
14
|
+
currentCharacterIndex = 0;
|
|
15
|
+
constructor(room, participantIdentity, trackSid, messageId) {
|
|
16
|
+
this.#room = room;
|
|
17
|
+
this.#participantIdentity = participantIdentity;
|
|
18
|
+
this.#trackSid = trackSid;
|
|
19
|
+
this.#messageId = messageId;
|
|
20
|
+
}
|
|
21
|
+
get text() {
|
|
22
|
+
return this.#currentText;
|
|
23
|
+
}
|
|
24
|
+
start() {
|
|
25
|
+
if (!this.#isRunning) {
|
|
26
|
+
this.#isRunning = true;
|
|
27
|
+
this.#startPublishingLoop().catch((error) => {
|
|
28
|
+
this.#logger.error("Error in publishing loop:", error);
|
|
29
|
+
this.#isRunning = false;
|
|
30
|
+
});
|
|
56
31
|
}
|
|
57
|
-
|
|
58
|
-
|
|
32
|
+
}
|
|
33
|
+
pushAudio(frame) {
|
|
34
|
+
this.#totalAudioDuration += frame.samplesPerChannel / frame.sampleRate;
|
|
35
|
+
}
|
|
36
|
+
pushText(text) {
|
|
37
|
+
this.#currentText += text;
|
|
38
|
+
}
|
|
39
|
+
#textIsComplete = false;
|
|
40
|
+
#audioIsComplete = false;
|
|
41
|
+
markTextComplete() {
|
|
42
|
+
this.#textIsComplete = true;
|
|
43
|
+
this.#adjustTimingIfBothFinished();
|
|
44
|
+
}
|
|
45
|
+
markAudioComplete() {
|
|
46
|
+
this.#audioIsComplete = true;
|
|
47
|
+
this.#adjustTimingIfBothFinished();
|
|
48
|
+
}
|
|
49
|
+
#adjustTimingIfBothFinished() {
|
|
50
|
+
if (this.#textIsComplete && this.#audioIsComplete) {
|
|
51
|
+
const actualDuration = this.#totalAudioDuration;
|
|
52
|
+
if (actualDuration > 0 && this.#currentText.length > 0) {
|
|
53
|
+
this.#charsPerSecond = this.#currentText.length / actualDuration;
|
|
54
|
+
}
|
|
59
55
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
56
|
+
}
|
|
57
|
+
#computeSleepInterval() {
|
|
58
|
+
return Math.min(Math.max(1 / this.#charsPerSecond, 0.0625), 0.5);
|
|
59
|
+
}
|
|
60
|
+
async #startPublishingLoop() {
|
|
61
|
+
this.#isRunning = true;
|
|
62
|
+
let sleepInterval = this.#computeSleepInterval();
|
|
63
|
+
let isComplete = false;
|
|
64
|
+
while (this.#isRunning && !isComplete) {
|
|
65
|
+
this.#currentPlayoutTime += sleepInterval;
|
|
66
|
+
this.currentCharacterIndex = Math.floor(this.#currentPlayoutTime * this.#charsPerSecond);
|
|
67
|
+
isComplete = this.#textIsComplete && this.currentCharacterIndex >= this.#currentText.length;
|
|
68
|
+
await this.#publishTranscription(false);
|
|
69
|
+
if (this.#isRunning && !isComplete) {
|
|
70
|
+
sleepInterval = this.#computeSleepInterval();
|
|
71
|
+
await new Promise((resolve) => setTimeout(resolve, sleepInterval * 1e3));
|
|
72
|
+
}
|
|
77
73
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
await this.#room.localParticipant?.publishTranscription({
|
|
81
|
-
participantIdentity: this.#participantIdentity,
|
|
82
|
-
trackSid: this.#trackSid,
|
|
83
|
-
segments: [
|
|
84
|
-
{
|
|
85
|
-
text: textToPublish,
|
|
86
|
-
final: final,
|
|
87
|
-
id: this.#messageId,
|
|
88
|
-
startTime: BigInt(0),
|
|
89
|
-
endTime: BigInt(0),
|
|
90
|
-
language: '',
|
|
91
|
-
},
|
|
92
|
-
],
|
|
93
|
-
});
|
|
74
|
+
if (this.#isRunning) {
|
|
75
|
+
this.close(false);
|
|
94
76
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
77
|
+
}
|
|
78
|
+
async #publishTranscription(final) {
|
|
79
|
+
var _a;
|
|
80
|
+
const textToPublish = this.#currentText.slice(0, this.currentCharacterIndex);
|
|
81
|
+
await ((_a = this.#room.localParticipant) == null ? void 0 : _a.publishTranscription({
|
|
82
|
+
participantIdentity: this.#participantIdentity,
|
|
83
|
+
trackSid: this.#trackSid,
|
|
84
|
+
segments: [
|
|
85
|
+
{
|
|
86
|
+
text: textToPublish,
|
|
87
|
+
final,
|
|
88
|
+
id: this.#messageId,
|
|
89
|
+
startTime: BigInt(0),
|
|
90
|
+
endTime: BigInt(0),
|
|
91
|
+
language: ""
|
|
100
92
|
}
|
|
101
|
-
|
|
93
|
+
]
|
|
94
|
+
}));
|
|
95
|
+
}
|
|
96
|
+
async close(interrupt) {
|
|
97
|
+
this.#isRunning = false;
|
|
98
|
+
if (!interrupt) {
|
|
99
|
+
this.currentCharacterIndex = this.#currentText.length;
|
|
102
100
|
}
|
|
101
|
+
await this.#publishTranscription(true);
|
|
102
|
+
}
|
|
103
103
|
}
|
|
104
|
+
export {
|
|
105
|
+
BasicTranscriptionForwarder
|
|
106
|
+
};
|
|
104
107
|
//# sourceMappingURL=transcription.js.map
|