speechflow 1.5.1 → 1.6.1
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/CHANGELOG.md +15 -0
- package/README.md +192 -171
- package/etc/claude.md +83 -46
- package/etc/speechflow.yaml +84 -84
- package/package.json +3 -3
- package/speechflow-cli/dst/speechflow-main-api.d.ts +12 -0
- package/speechflow-cli/dst/speechflow-main-api.js +319 -0
- package/speechflow-cli/dst/speechflow-main-api.js.map +1 -0
- package/speechflow-cli/dst/speechflow-main-cli.d.ts +28 -0
- package/speechflow-cli/dst/speechflow-main-cli.js +271 -0
- package/speechflow-cli/dst/speechflow-main-cli.js.map +1 -0
- package/speechflow-cli/dst/speechflow-main-config.d.ts +9 -0
- package/speechflow-cli/dst/speechflow-main-config.js +27 -0
- package/speechflow-cli/dst/speechflow-main-config.js.map +1 -0
- package/speechflow-cli/dst/speechflow-main-graph.d.ts +34 -0
- package/speechflow-cli/dst/speechflow-main-graph.js +367 -0
- package/speechflow-cli/dst/speechflow-main-graph.js.map +1 -0
- package/speechflow-cli/dst/speechflow-main-nodes.d.ts +10 -0
- package/speechflow-cli/dst/speechflow-main-nodes.js +60 -0
- package/speechflow-cli/dst/speechflow-main-nodes.js.map +1 -0
- package/speechflow-cli/dst/speechflow-main-status.d.ts +11 -0
- package/speechflow-cli/dst/speechflow-main-status.js +60 -0
- package/speechflow-cli/dst/speechflow-main-status.js.map +1 -0
- package/speechflow-cli/dst/speechflow-main.d.ts +7 -0
- package/speechflow-cli/dst/speechflow-main.js +127 -0
- package/speechflow-cli/dst/speechflow-main.js.map +1 -0
- package/speechflow-cli/dst/speechflow-node-a2a-compressor-wt.js +4 -4
- package/speechflow-cli/dst/speechflow-node-a2a-compressor-wt.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-compressor.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-compressor.js +8 -9
- package/speechflow-cli/dst/speechflow-node-a2a-compressor.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-expander-wt.js +5 -5
- package/speechflow-cli/dst/speechflow-node-a2a-expander-wt.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-expander.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-expander.js +8 -9
- package/speechflow-cli/dst/speechflow-node-a2a-expander.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-ffmpeg.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-ffmpeg.js +8 -8
- package/speechflow-cli/dst/speechflow-node-a2a-ffmpeg.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-filler.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-filler.js +6 -6
- package/speechflow-cli/dst/speechflow-node-a2a-filler.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-gain.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-gain.js +5 -5
- package/speechflow-cli/dst/speechflow-node-a2a-gain.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-gender.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-gender.js +7 -7
- package/speechflow-cli/dst/speechflow-node-a2a-gender.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-meter.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-meter.js +5 -5
- package/speechflow-cli/dst/speechflow-node-a2a-meter.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-mute.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-mute.js +3 -3
- package/speechflow-cli/dst/speechflow-node-a2a-mute.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-rnnoise.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-rnnoise.js +7 -7
- package/speechflow-cli/dst/speechflow-node-a2a-rnnoise.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-speex.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-speex.js +7 -7
- package/speechflow-cli/dst/speechflow-node-a2a-speex.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-vad.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-vad.js +7 -7
- package/speechflow-cli/dst/speechflow-node-a2a-vad.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-wav.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-wav.js +3 -3
- package/speechflow-cli/dst/speechflow-node-a2a-wav.js.map +1 -1
- package/speechflow-cli/dst/{speechflow-node-a2t-awstranscribe.d.ts → speechflow-node-a2t-amazon.d.ts} +1 -1
- package/speechflow-cli/dst/{speechflow-node-a2t-awstranscribe.js → speechflow-node-a2t-amazon.js} +11 -11
- package/speechflow-cli/dst/speechflow-node-a2t-amazon.js.map +1 -0
- package/speechflow-cli/dst/speechflow-node-a2t-deepgram.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-a2t-deepgram.js +7 -7
- package/speechflow-cli/dst/speechflow-node-a2t-deepgram.js.map +1 -1
- package/speechflow-cli/dst/{speechflow-node-a2t-openaitranscribe.d.ts → speechflow-node-a2t-openai.d.ts} +1 -1
- package/speechflow-cli/dst/{speechflow-node-a2t-openaitranscribe.js → speechflow-node-a2t-openai.js} +11 -11
- package/speechflow-cli/dst/speechflow-node-a2t-openai.js.map +1 -0
- package/speechflow-cli/dst/{speechflow-node-t2a-awspolly.d.ts → speechflow-node-t2a-amazon.d.ts} +1 -1
- package/speechflow-cli/dst/{speechflow-node-t2a-awspolly.js → speechflow-node-t2a-amazon.js} +9 -9
- package/speechflow-cli/dst/speechflow-node-t2a-amazon.js.map +1 -0
- package/speechflow-cli/dst/speechflow-node-t2a-elevenlabs.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-t2a-elevenlabs.js +5 -5
- package/speechflow-cli/dst/speechflow-node-t2a-elevenlabs.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-t2a-kokoro.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-t2a-kokoro.js +7 -7
- package/speechflow-cli/dst/speechflow-node-t2a-kokoro.js.map +1 -1
- package/speechflow-cli/dst/{speechflow-node-t2t-awstranslate.d.ts → speechflow-node-t2t-amazon.d.ts} +1 -1
- package/speechflow-cli/dst/{speechflow-node-t2t-awstranslate.js → speechflow-node-t2t-amazon.js} +7 -7
- package/speechflow-cli/dst/speechflow-node-t2t-amazon.js.map +1 -0
- package/speechflow-cli/dst/speechflow-node-t2t-deepl.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-t2t-deepl.js +5 -5
- package/speechflow-cli/dst/speechflow-node-t2t-deepl.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-t2t-format.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-t2t-format.js +3 -3
- package/speechflow-cli/dst/speechflow-node-t2t-format.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-t2t-google.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-t2t-google.js +8 -8
- package/speechflow-cli/dst/speechflow-node-t2t-google.js.map +1 -1
- package/speechflow-cli/dst/{speechflow-node-a2a-dynamics.d.ts → speechflow-node-t2t-modify.d.ts} +1 -5
- package/speechflow-cli/dst/speechflow-node-t2t-modify.js +111 -0
- package/speechflow-cli/dst/speechflow-node-t2t-modify.js.map +1 -0
- package/speechflow-cli/dst/speechflow-node-t2t-ollama.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-t2t-ollama.js +5 -5
- package/speechflow-cli/dst/speechflow-node-t2t-ollama.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-t2t-openai.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-t2t-openai.js +5 -5
- package/speechflow-cli/dst/speechflow-node-t2t-openai.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-t2t-sentence.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-t2t-sentence.js +5 -5
- package/speechflow-cli/dst/speechflow-node-t2t-sentence.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-t2t-subtitle.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-t2t-subtitle.js +5 -5
- package/speechflow-cli/dst/speechflow-node-t2t-subtitle.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-t2t-transformers.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-t2t-transformers.js +5 -5
- package/speechflow-cli/dst/speechflow-node-t2t-transformers.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-x2x-filter.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-x2x-filter.js +5 -5
- package/speechflow-cli/dst/speechflow-node-x2x-filter.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-x2x-trace.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-x2x-trace.js +3 -3
- package/speechflow-cli/dst/speechflow-node-x2x-trace.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-xio-device.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-xio-device.js +8 -8
- package/speechflow-cli/dst/speechflow-node-xio-device.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-xio-file.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-xio-file.js +50 -29
- package/speechflow-cli/dst/speechflow-node-xio-file.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-xio-mqtt.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-xio-mqtt.js +7 -7
- package/speechflow-cli/dst/speechflow-node-xio-mqtt.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-xio-websocket.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-xio-websocket.js +10 -10
- package/speechflow-cli/dst/speechflow-node-xio-websocket.js.map +1 -1
- package/speechflow-cli/dst/{speechflow-utils-audio-wt.js → speechflow-util-audio-wt.js} +1 -1
- package/speechflow-cli/dst/speechflow-util-audio-wt.js.map +1 -0
- package/speechflow-cli/dst/speechflow-util-audio.d.ts +22 -0
- package/speechflow-cli/dst/speechflow-util-audio.js +251 -0
- package/speechflow-cli/dst/speechflow-util-audio.js.map +1 -0
- package/speechflow-cli/dst/speechflow-util-error.d.ts +14 -0
- package/speechflow-cli/dst/speechflow-util-error.js +131 -0
- package/speechflow-cli/dst/speechflow-util-error.js.map +1 -0
- package/speechflow-cli/dst/speechflow-util-queue.d.ts +68 -0
- package/speechflow-cli/dst/speechflow-util-queue.js +338 -0
- package/speechflow-cli/dst/speechflow-util-queue.js.map +1 -0
- package/speechflow-cli/dst/speechflow-util-stream.d.ts +18 -0
- package/speechflow-cli/dst/speechflow-util-stream.js +219 -0
- package/speechflow-cli/dst/speechflow-util-stream.js.map +1 -0
- package/speechflow-cli/dst/speechflow-util-webaudio-wt.js +124 -0
- package/speechflow-cli/dst/speechflow-util-webaudio-wt.js.map +1 -0
- package/speechflow-cli/dst/{speechflow-utils-audio.js → speechflow-util-webaudio.js} +2 -2
- package/speechflow-cli/dst/speechflow-util-webaudio.js.map +1 -0
- package/speechflow-cli/dst/speechflow-util.d.ts +4 -0
- package/speechflow-cli/dst/speechflow-util.js +26 -0
- package/speechflow-cli/dst/speechflow-util.js.map +1 -0
- package/speechflow-cli/dst/speechflow.js +3 -906
- package/speechflow-cli/dst/speechflow.js.map +1 -1
- package/speechflow-cli/etc/oxlint.jsonc +4 -1
- package/speechflow-cli/package.json +12 -11
- package/speechflow-cli/src/speechflow-main-api.ts +315 -0
- package/speechflow-cli/src/speechflow-main-cli.ts +259 -0
- package/speechflow-cli/src/speechflow-main-config.ts +17 -0
- package/speechflow-cli/src/speechflow-main-graph.ts +372 -0
- package/speechflow-cli/src/speechflow-main-nodes.ts +61 -0
- package/speechflow-cli/src/speechflow-main-status.ts +70 -0
- package/speechflow-cli/src/speechflow-main.ts +106 -0
- package/speechflow-cli/src/speechflow-node-a2a-compressor-wt.ts +4 -4
- package/speechflow-cli/src/speechflow-node-a2a-compressor.ts +7 -8
- package/speechflow-cli/src/speechflow-node-a2a-expander-wt.ts +5 -5
- package/speechflow-cli/src/speechflow-node-a2a-expander.ts +7 -8
- package/speechflow-cli/src/speechflow-node-a2a-ffmpeg.ts +7 -7
- package/speechflow-cli/src/speechflow-node-a2a-filler.ts +6 -6
- package/speechflow-cli/src/speechflow-node-a2a-gain.ts +4 -4
- package/speechflow-cli/src/speechflow-node-a2a-gender.ts +6 -6
- package/speechflow-cli/src/speechflow-node-a2a-meter.ts +4 -4
- package/speechflow-cli/src/speechflow-node-a2a-mute.ts +2 -2
- package/speechflow-cli/src/speechflow-node-a2a-rnnoise.ts +6 -6
- package/speechflow-cli/src/speechflow-node-a2a-speex.ts +6 -6
- package/speechflow-cli/src/speechflow-node-a2a-vad.ts +6 -6
- package/speechflow-cli/src/speechflow-node-a2a-wav.ts +2 -2
- package/speechflow-cli/src/{speechflow-node-a2t-awstranscribe.ts → speechflow-node-a2t-amazon.ts} +10 -10
- package/speechflow-cli/src/speechflow-node-a2t-deepgram.ts +7 -7
- package/speechflow-cli/src/{speechflow-node-a2t-openaitranscribe.ts → speechflow-node-a2t-openai.ts} +10 -10
- package/speechflow-cli/src/{speechflow-node-t2a-awspolly.ts → speechflow-node-t2a-amazon.ts} +7 -7
- package/speechflow-cli/src/speechflow-node-t2a-elevenlabs.ts +4 -4
- package/speechflow-cli/src/speechflow-node-t2a-kokoro.ts +6 -6
- package/speechflow-cli/src/{speechflow-node-t2t-awstranslate.ts → speechflow-node-t2t-amazon.ts} +5 -5
- package/speechflow-cli/src/speechflow-node-t2t-deepl.ts +4 -4
- package/speechflow-cli/src/speechflow-node-t2t-format.ts +2 -2
- package/speechflow-cli/src/speechflow-node-t2t-google.ts +7 -7
- package/speechflow-cli/src/speechflow-node-t2t-modify.ts +84 -0
- package/speechflow-cli/src/speechflow-node-t2t-ollama.ts +4 -4
- package/speechflow-cli/src/speechflow-node-t2t-openai.ts +4 -4
- package/speechflow-cli/src/speechflow-node-t2t-sentence.ts +4 -4
- package/speechflow-cli/src/speechflow-node-t2t-subtitle.ts +9 -9
- package/speechflow-cli/src/speechflow-node-t2t-transformers.ts +4 -4
- package/speechflow-cli/src/speechflow-node-x2x-filter.ts +4 -4
- package/speechflow-cli/src/speechflow-node-x2x-trace.ts +2 -2
- package/speechflow-cli/src/speechflow-node-xio-device.ts +7 -7
- package/speechflow-cli/src/speechflow-node-xio-file.ts +49 -28
- package/speechflow-cli/src/speechflow-node-xio-mqtt.ts +7 -7
- package/speechflow-cli/src/speechflow-node-xio-websocket.ts +9 -9
- package/speechflow-cli/src/{speechflow-utils-audio.ts → speechflow-util-audio.ts} +131 -1
- package/speechflow-cli/src/speechflow-util-error.ts +184 -0
- package/speechflow-cli/src/speechflow-util-queue.ts +320 -0
- package/speechflow-cli/src/speechflow-util-stream.ts +197 -0
- package/speechflow-cli/src/speechflow-util.ts +10 -0
- package/speechflow-cli/src/speechflow.ts +3 -947
- package/speechflow-ui-db/package.json +3 -3
- package/speechflow-ui-st/dst/app-font-fa-brands-400.woff2 +0 -0
- package/speechflow-ui-st/dst/app-font-fa-regular-400.woff2 +0 -0
- package/speechflow-ui-st/dst/app-font-fa-solid-900.woff2 +0 -0
- package/speechflow-ui-st/dst/app-font-fa-v4compatibility.woff2 +0 -0
- package/speechflow-ui-st/dst/index.css +2 -2
- package/speechflow-ui-st/dst/index.js +32 -33
- package/speechflow-ui-st/package.json +4 -4
- package/speechflow-cli/dst/speechflow-node-a2a-dynamics-wt.js +0 -208
- package/speechflow-cli/dst/speechflow-node-a2a-dynamics-wt.js.map +0 -1
- package/speechflow-cli/dst/speechflow-node-a2a-dynamics.js +0 -312
- package/speechflow-cli/dst/speechflow-node-a2a-dynamics.js.map +0 -1
- package/speechflow-cli/dst/speechflow-node-a2t-awstranscribe.js.map +0 -1
- package/speechflow-cli/dst/speechflow-node-a2t-openaitranscribe.js.map +0 -1
- package/speechflow-cli/dst/speechflow-node-t2a-awspolly.js.map +0 -1
- package/speechflow-cli/dst/speechflow-node-t2t-awstranslate.js.map +0 -1
- package/speechflow-cli/dst/speechflow-utils-audio-wt.js.map +0 -1
- package/speechflow-cli/dst/speechflow-utils-audio.js.map +0 -1
- package/speechflow-cli/dst/speechflow-utils.d.ts +0 -108
- package/speechflow-cli/dst/speechflow-utils.js +0 -740
- package/speechflow-cli/dst/speechflow-utils.js.map +0 -1
- package/speechflow-cli/src/speechflow-utils.ts +0 -804
- /package/speechflow-cli/dst/{speechflow-node-a2a-dynamics-wt.d.ts → speechflow-util-audio-wt.d.ts} +0 -0
- /package/speechflow-cli/dst/{speechflow-utils-audio-wt.d.ts → speechflow-util-webaudio-wt.d.ts} +0 -0
- /package/speechflow-cli/dst/{speechflow-utils-audio.d.ts → speechflow-util-webaudio.d.ts} +0 -0
- /package/speechflow-cli/src/{speechflow-utils-audio-wt.ts → speechflow-util-audio-wt.ts} +0 -0
|
@@ -14,12 +14,12 @@ import { loadSpeexModule, SpeexPreprocessor } from "@sapphi-red/speex-preprocess
|
|
|
14
14
|
|
|
15
15
|
/* internal dependencies */
|
|
16
16
|
import SpeechFlowNode, { SpeechFlowChunk } from "./speechflow-node"
|
|
17
|
-
import * as
|
|
17
|
+
import * as util from "./speechflow-util"
|
|
18
18
|
|
|
19
19
|
/* SpeechFlow node for Speex based noise suppression in audio-to-audio passing */
|
|
20
|
-
export default class
|
|
20
|
+
export default class SpeechFlowNodeA2ASpeex extends SpeechFlowNode {
|
|
21
21
|
/* declare official node name */
|
|
22
|
-
public static name = "speex"
|
|
22
|
+
public static name = "a2a-speex"
|
|
23
23
|
|
|
24
24
|
/* internal state */
|
|
25
25
|
private destroyed = false
|
|
@@ -79,10 +79,10 @@ export default class SpeechFlowNodeSpeex extends SpeechFlowNode {
|
|
|
79
79
|
callback(new Error("invalid chunk payload type"))
|
|
80
80
|
else {
|
|
81
81
|
/* convert Buffer into Int16Array */
|
|
82
|
-
const payload =
|
|
82
|
+
const payload = util.convertBufToI16(chunk.payload)
|
|
83
83
|
|
|
84
84
|
/* process Int16Array in necessary fixed-size segments */
|
|
85
|
-
|
|
85
|
+
util.processInt16ArrayInSegments(payload, self.sampleSize, (segment) => {
|
|
86
86
|
if (self.destroyed)
|
|
87
87
|
throw new Error("stream already destroyed")
|
|
88
88
|
self.speexProcessor?.processInt16(segment)
|
|
@@ -92,7 +92,7 @@ export default class SpeechFlowNodeSpeex extends SpeechFlowNode {
|
|
|
92
92
|
throw new Error("stream already destroyed")
|
|
93
93
|
|
|
94
94
|
/* convert Int16Array back into Buffer */
|
|
95
|
-
const buf =
|
|
95
|
+
const buf = util.convertI16ToBuf(payload)
|
|
96
96
|
|
|
97
97
|
/* update chunk */
|
|
98
98
|
chunk.payload = buf
|
|
@@ -12,7 +12,7 @@ import { RealTimeVAD } from "@ericedouard/vad-node-realtime"
|
|
|
12
12
|
|
|
13
13
|
/* internal dependencies */
|
|
14
14
|
import SpeechFlowNode, { SpeechFlowChunk } from "./speechflow-node"
|
|
15
|
-
import * as
|
|
15
|
+
import * as util from "./speechflow-util"
|
|
16
16
|
|
|
17
17
|
/* audio stream queue element */
|
|
18
18
|
type AudioQueueElementSegment = {
|
|
@@ -30,13 +30,13 @@ type AudioQueueElement = {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
/* SpeechFlow node for VAD speech-to-speech processing */
|
|
33
|
-
export default class
|
|
33
|
+
export default class SpeechFlowNodeA2AVAD extends SpeechFlowNode {
|
|
34
34
|
/* declare official node name */
|
|
35
|
-
public static name = "vad"
|
|
35
|
+
public static name = "a2a-vad"
|
|
36
36
|
|
|
37
37
|
/* internal state */
|
|
38
38
|
private vad: RealTimeVAD | null = null
|
|
39
|
-
private queue = new
|
|
39
|
+
private queue = new util.Queue<AudioQueueElement>()
|
|
40
40
|
private queueRecv = this.queue.pointerUse("recv")
|
|
41
41
|
private queueVAD = this.queue.pointerUse("vad")
|
|
42
42
|
private queueSend = this.queue.pointerUse("send")
|
|
@@ -109,7 +109,7 @@ export default class SpeechFlowNodeVAD extends SpeechFlowNode {
|
|
|
109
109
|
onSpeechEnd: (audio) => {
|
|
110
110
|
if (this.destroyed)
|
|
111
111
|
return
|
|
112
|
-
const duration =
|
|
112
|
+
const duration = util.audioArrayDuration(audio, vadSampleRateTarget)
|
|
113
113
|
this.log("info", `VAD: speech end (duration: ${duration.toFixed(2)}s)`)
|
|
114
114
|
if (this.params.mode === "unplugged") {
|
|
115
115
|
tail = true
|
|
@@ -189,7 +189,7 @@ export default class SpeechFlowNodeVAD extends SpeechFlowNode {
|
|
|
189
189
|
else {
|
|
190
190
|
try {
|
|
191
191
|
/* convert audio samples from PCM/I16 to PCM/F32 */
|
|
192
|
-
const data =
|
|
192
|
+
const data = util.convertBufToF32(chunk.payload,
|
|
193
193
|
self.config.audioLittleEndian)
|
|
194
194
|
|
|
195
195
|
/* segment audio samples as individual VAD-sized frames */
|
|
@@ -83,9 +83,9 @@ const readWavHeader = (buffer: Buffer) => {
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
/* SpeechFlow node for WAV format conversion */
|
|
86
|
-
export default class
|
|
86
|
+
export default class SpeechFlowNodeA2AWAV extends SpeechFlowNode {
|
|
87
87
|
/* declare official node name */
|
|
88
|
-
public static name = "wav"
|
|
88
|
+
public static name = "a2a-wav"
|
|
89
89
|
|
|
90
90
|
/* construct node */
|
|
91
91
|
constructor (id: string, cfg: { [ id: string ]: any }, opts: { [ id: string ]: any }, args: any[]) {
|
package/speechflow-cli/src/{speechflow-node-a2t-awstranscribe.ts → speechflow-node-a2t-amazon.ts}
RENAMED
|
@@ -19,7 +19,7 @@ import { DateTime, Duration } from "luxon"
|
|
|
19
19
|
|
|
20
20
|
/* internal dependencies */
|
|
21
21
|
import SpeechFlowNode, { SpeechFlowChunk } from "./speechflow-node"
|
|
22
|
-
import * as
|
|
22
|
+
import * as util from "./speechflow-util"
|
|
23
23
|
|
|
24
24
|
/* helper class for an asynchronous queue */
|
|
25
25
|
class AsyncQueue<T> {
|
|
@@ -62,10 +62,10 @@ class AsyncQueue<T> {
|
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
/* SpeechFlow node for
|
|
66
|
-
export default class
|
|
65
|
+
/* SpeechFlow node for Amazon Transcribe speech-to-text conversion */
|
|
66
|
+
export default class SpeechFlowNodeA2TAmazon extends SpeechFlowNode {
|
|
67
67
|
/* declare official node name */
|
|
68
|
-
public static name = "
|
|
68
|
+
public static name = "a2t-amazon"
|
|
69
69
|
|
|
70
70
|
/* internal state */
|
|
71
71
|
private client: TranscribeStreamingClient | null = null
|
|
@@ -73,7 +73,7 @@ export default class SpeechFlowNodeAWSTranscribe extends SpeechFlowNode {
|
|
|
73
73
|
private destroyed = false
|
|
74
74
|
private initTimeout: ReturnType<typeof setTimeout> | null = null
|
|
75
75
|
private connectionTimeout: ReturnType<typeof setTimeout> | null = null
|
|
76
|
-
private queue:
|
|
76
|
+
private queue: util.SingleQueue<SpeechFlowChunk | null> | null = null
|
|
77
77
|
|
|
78
78
|
/* construct node */
|
|
79
79
|
constructor (id: string, cfg: { [ id: string ]: any }, opts: { [ id: string ]: any }, args: any[]) {
|
|
@@ -114,10 +114,10 @@ export default class SpeechFlowNodeAWSTranscribe extends SpeechFlowNode {
|
|
|
114
114
|
this.destroyed = false
|
|
115
115
|
|
|
116
116
|
/* create queue for results */
|
|
117
|
-
this.queue = new
|
|
117
|
+
this.queue = new util.SingleQueue<SpeechFlowChunk | null>()
|
|
118
118
|
|
|
119
119
|
/* create a store for the meta information */
|
|
120
|
-
const metastore = new
|
|
120
|
+
const metastore = new util.TimeStore<Map<string, any>>()
|
|
121
121
|
|
|
122
122
|
/* connect to Amazon Transcribe API */
|
|
123
123
|
this.client = new TranscribeStreamingClient({
|
|
@@ -225,7 +225,7 @@ export default class SpeechFlowNodeAWSTranscribe extends SpeechFlowNode {
|
|
|
225
225
|
metastore.store(chunk.timestampStart, chunk.timestampEnd, chunk.meta)
|
|
226
226
|
audioQueue.push(new Uint8Array(chunk.payload)) /* intentionally discard all time information */
|
|
227
227
|
ensureAudioStreamActive().catch((error: unknown) => {
|
|
228
|
-
self.log("error", `failed to start audio stream: ${
|
|
228
|
+
self.log("error", `failed to start audio stream: ${util.ensureError(error).message}`)
|
|
229
229
|
})
|
|
230
230
|
}
|
|
231
231
|
callback()
|
|
@@ -251,7 +251,7 @@ export default class SpeechFlowNodeAWSTranscribe extends SpeechFlowNode {
|
|
|
251
251
|
}
|
|
252
252
|
}).catch((error: unknown) => {
|
|
253
253
|
if (!self.destroyed)
|
|
254
|
-
self.log("error", `queue read error: ${
|
|
254
|
+
self.log("error", `queue read error: ${util.ensureError(error).message}`)
|
|
255
255
|
})
|
|
256
256
|
},
|
|
257
257
|
final (callback) {
|
|
@@ -259,7 +259,7 @@ export default class SpeechFlowNodeAWSTranscribe extends SpeechFlowNode {
|
|
|
259
259
|
callback()
|
|
260
260
|
return
|
|
261
261
|
}
|
|
262
|
-
|
|
262
|
+
util.run(
|
|
263
263
|
() => self.client!.destroy(),
|
|
264
264
|
(error: Error) => self.log("warning", `error closing Amazon Transcribe connection: ${error}`)
|
|
265
265
|
)
|
|
@@ -13,19 +13,19 @@ import { DateTime, Duration } from "luxon"
|
|
|
13
13
|
|
|
14
14
|
/* internal dependencies */
|
|
15
15
|
import SpeechFlowNode, { SpeechFlowChunk } from "./speechflow-node"
|
|
16
|
-
import * as
|
|
16
|
+
import * as util from "./speechflow-util"
|
|
17
17
|
|
|
18
18
|
/* SpeechFlow node for Deepgram speech-to-text conversion */
|
|
19
|
-
export default class
|
|
19
|
+
export default class SpeechFlowNodeA2TDeepgram extends SpeechFlowNode {
|
|
20
20
|
/* declare official node name */
|
|
21
|
-
public static name = "deepgram"
|
|
21
|
+
public static name = "a2t-deepgram"
|
|
22
22
|
|
|
23
23
|
/* internal state */
|
|
24
24
|
private dg: Deepgram.LiveClient | null = null
|
|
25
25
|
private destroyed = false
|
|
26
26
|
private initTimeout: ReturnType<typeof setTimeout> | null = null
|
|
27
27
|
private connectionTimeout: ReturnType<typeof setTimeout> | null = null
|
|
28
|
-
private queue:
|
|
28
|
+
private queue: util.SingleQueue<SpeechFlowChunk | null> | null = null
|
|
29
29
|
|
|
30
30
|
/* construct node */
|
|
31
31
|
constructor (id: string, cfg: { [ id: string ]: any }, opts: { [ id: string ]: any }, args: any[]) {
|
|
@@ -78,10 +78,10 @@ export default class SpeechFlowNodeDeepgram extends SpeechFlowNode {
|
|
|
78
78
|
this.destroyed = false
|
|
79
79
|
|
|
80
80
|
/* create queue for results */
|
|
81
|
-
this.queue = new
|
|
81
|
+
this.queue = new util.SingleQueue<SpeechFlowChunk | null>()
|
|
82
82
|
|
|
83
83
|
/* create a store for the meta information */
|
|
84
|
-
const metastore = new
|
|
84
|
+
const metastore = new util.TimeStore<Map<string, any>>()
|
|
85
85
|
|
|
86
86
|
/* connect to Deepgram API */
|
|
87
87
|
const deepgram = Deepgram.createClient(this.params.key)
|
|
@@ -234,7 +234,7 @@ export default class SpeechFlowNodeDeepgram extends SpeechFlowNode {
|
|
|
234
234
|
}
|
|
235
235
|
}).catch((error: unknown) => {
|
|
236
236
|
if (!self.destroyed)
|
|
237
|
-
self.log("error", `queue read error: ${
|
|
237
|
+
self.log("error", `queue read error: ${util.ensureError(error).message}`)
|
|
238
238
|
})
|
|
239
239
|
},
|
|
240
240
|
final (callback) {
|
package/speechflow-cli/src/{speechflow-node-a2t-openaitranscribe.ts → speechflow-node-a2t-openai.ts}
RENAMED
|
@@ -15,18 +15,18 @@ import ws from "ws"
|
|
|
15
15
|
|
|
16
16
|
/* internal dependencies */
|
|
17
17
|
import SpeechFlowNode, { SpeechFlowChunk } from "./speechflow-node"
|
|
18
|
-
import * as
|
|
18
|
+
import * as util from "./speechflow-util"
|
|
19
19
|
|
|
20
|
-
/* SpeechFlow node for OpenAI
|
|
21
|
-
export default class
|
|
20
|
+
/* SpeechFlow node for OpenAI speech-to-text conversion */
|
|
21
|
+
export default class SpeechFlowNodeA2TOpenAI extends SpeechFlowNode {
|
|
22
22
|
/* declare official node name */
|
|
23
|
-
public static name = "
|
|
23
|
+
public static name = "a2t-openai"
|
|
24
24
|
|
|
25
25
|
/* internal state */
|
|
26
26
|
private static speexInitialized = false
|
|
27
27
|
private openai: OpenAI | null = null
|
|
28
28
|
private ws: ws.WebSocket | null = null
|
|
29
|
-
private queue:
|
|
29
|
+
private queue: util.SingleQueue<SpeechFlowChunk | null> | null = null
|
|
30
30
|
private resampler: SpeexResampler | null = null
|
|
31
31
|
private destroyed = false
|
|
32
32
|
private connectionTimeout: ReturnType<typeof setTimeout> | null = null
|
|
@@ -64,17 +64,17 @@ export default class SpeechFlowNodeOpenAITranscribe extends SpeechFlowNode {
|
|
|
64
64
|
this.destroyed = false
|
|
65
65
|
|
|
66
66
|
/* create queue for results */
|
|
67
|
-
this.queue = new
|
|
67
|
+
this.queue = new util.SingleQueue<SpeechFlowChunk | null>()
|
|
68
68
|
|
|
69
69
|
/* create a store for the meta information */
|
|
70
|
-
const metastore = new
|
|
70
|
+
const metastore = new util.TimeStore<Map<string, any>>()
|
|
71
71
|
|
|
72
72
|
/* establish resampler from our standard audio sample rate (48Khz)
|
|
73
73
|
to OpenAI's maximum 24Khz input sample rate */
|
|
74
|
-
if (!
|
|
74
|
+
if (!SpeechFlowNodeA2TOpenAI.speexInitialized) {
|
|
75
75
|
/* at least once initialize resampler */
|
|
76
76
|
await SpeexResampler.initPromise
|
|
77
|
-
|
|
77
|
+
SpeechFlowNodeA2TOpenAI.speexInitialized = true
|
|
78
78
|
}
|
|
79
79
|
this.resampler = new SpeexResampler(1, this.config.audioSampleRate, 24000, 7)
|
|
80
80
|
|
|
@@ -281,7 +281,7 @@ export default class SpeechFlowNodeOpenAITranscribe extends SpeechFlowNode {
|
|
|
281
281
|
}
|
|
282
282
|
}).catch((error: unknown) => {
|
|
283
283
|
if (!self.destroyed)
|
|
284
|
-
self.log("error", `queue read error: ${
|
|
284
|
+
self.log("error", `queue read error: ${util.ensureError(error).message}`)
|
|
285
285
|
})
|
|
286
286
|
},
|
|
287
287
|
final (callback) {
|
package/speechflow-cli/src/{speechflow-node-t2a-awspolly.ts → speechflow-node-t2a-amazon.ts}
RENAMED
|
@@ -17,12 +17,12 @@ import {
|
|
|
17
17
|
|
|
18
18
|
/* internal dependencies */
|
|
19
19
|
import SpeechFlowNode, { SpeechFlowChunk } from "./speechflow-node"
|
|
20
|
-
import * as
|
|
20
|
+
import * as util from "./speechflow-util"
|
|
21
21
|
|
|
22
|
-
/* SpeechFlow node for
|
|
23
|
-
export default class
|
|
22
|
+
/* SpeechFlow node for Amazon Polly text-to-speech conversion */
|
|
23
|
+
export default class SpeechFlowNodeT2AAmazon extends SpeechFlowNode {
|
|
24
24
|
/* declare official node name */
|
|
25
|
-
public static name = "
|
|
25
|
+
public static name = "t2a-amazon"
|
|
26
26
|
|
|
27
27
|
/* internal state */
|
|
28
28
|
private client: PollyClient | null = null
|
|
@@ -114,10 +114,10 @@ export default class SpeechFlowNodeAWSPolly extends SpeechFlowNode {
|
|
|
114
114
|
|
|
115
115
|
/* establish resampler from AWS Polly's maximum 16Khz output
|
|
116
116
|
(for PCM output) to our standard audio sample rate (48KHz) */
|
|
117
|
-
if (!
|
|
117
|
+
if (!SpeechFlowNodeT2AAmazon.speexInitialized) {
|
|
118
118
|
/* at least once initialize resampler */
|
|
119
119
|
await SpeexResampler.initPromise
|
|
120
|
-
|
|
120
|
+
SpeechFlowNodeT2AAmazon.speexInitialized = true
|
|
121
121
|
}
|
|
122
122
|
this.resampler = new SpeexResampler(1, 16000, this.config.audioSampleRate, 7)
|
|
123
123
|
|
|
@@ -146,7 +146,7 @@ export default class SpeechFlowNodeAWSPolly extends SpeechFlowNode {
|
|
|
146
146
|
this.push(chunkNew)
|
|
147
147
|
callback()
|
|
148
148
|
}).catch((error: unknown) => {
|
|
149
|
-
callback(
|
|
149
|
+
callback(util.ensureError(error, "failed to send to AWS Polly"))
|
|
150
150
|
})
|
|
151
151
|
}
|
|
152
152
|
else
|
|
@@ -16,9 +16,9 @@ import SpeexResampler from "speex-resampler"
|
|
|
16
16
|
import SpeechFlowNode, { SpeechFlowChunk } from "./speechflow-node"
|
|
17
17
|
|
|
18
18
|
/* SpeechFlow node for Elevenlabs text-to-speech conversion */
|
|
19
|
-
export default class
|
|
19
|
+
export default class SpeechFlowNodeT2AElevenlabs extends SpeechFlowNode {
|
|
20
20
|
/* declare official node name */
|
|
21
|
-
public static name = "elevenlabs"
|
|
21
|
+
public static name = "t2a-elevenlabs"
|
|
22
22
|
|
|
23
23
|
/* internal state */
|
|
24
24
|
private elevenlabs: ElevenLabs.ElevenLabsClient | null = null
|
|
@@ -131,10 +131,10 @@ export default class SpeechFlowNodeElevenlabs extends SpeechFlowNode {
|
|
|
131
131
|
|
|
132
132
|
/* establish resampler from ElevenLabs's maximum 24Khz
|
|
133
133
|
output to our standard audio sample rate (48KHz) */
|
|
134
|
-
if (!
|
|
134
|
+
if (!SpeechFlowNodeT2AElevenlabs.speexInitialized) {
|
|
135
135
|
/* at least once initialize resampler */
|
|
136
136
|
await SpeexResampler.initPromise
|
|
137
|
-
|
|
137
|
+
SpeechFlowNodeT2AElevenlabs.speexInitialized = true
|
|
138
138
|
}
|
|
139
139
|
this.resampler = new SpeexResampler(1, maxSampleRate, this.config.audioSampleRate, 7)
|
|
140
140
|
|
|
@@ -13,12 +13,12 @@ import SpeexResampler from "speex-resampler"
|
|
|
13
13
|
|
|
14
14
|
/* internal dependencies */
|
|
15
15
|
import SpeechFlowNode, { SpeechFlowChunk } from "./speechflow-node"
|
|
16
|
-
import * as
|
|
16
|
+
import * as util from "./speechflow-util"
|
|
17
17
|
|
|
18
18
|
/* SpeechFlow node for Kokoro text-to-speech conversion */
|
|
19
|
-
export default class
|
|
19
|
+
export default class SpeechFlowNodeT2AKokoro extends SpeechFlowNode {
|
|
20
20
|
/* declare official node name */
|
|
21
|
-
public static name = "kokoro"
|
|
21
|
+
public static name = "t2a-kokoro"
|
|
22
22
|
|
|
23
23
|
/* internal state */
|
|
24
24
|
private kokoro: KokoroTTS | null = null
|
|
@@ -82,9 +82,9 @@ export default class SpeechFlowNodeKokoro extends SpeechFlowNode {
|
|
|
82
82
|
|
|
83
83
|
/* establish resampler from Kokoro's maximum 24Khz
|
|
84
84
|
output to our standard audio sample rate (48KHz) */
|
|
85
|
-
if (!
|
|
85
|
+
if (!SpeechFlowNodeT2AKokoro.speexInitialized) {
|
|
86
86
|
/* at least once initialize resampler */
|
|
87
|
-
|
|
87
|
+
SpeechFlowNodeT2AKokoro.speexInitialized = true
|
|
88
88
|
await SpeexResampler.initPromise
|
|
89
89
|
}
|
|
90
90
|
this.resampler = new SpeexResampler(1, 24000, this.config.audioSampleRate, 7)
|
|
@@ -143,7 +143,7 @@ export default class SpeechFlowNodeKokoro extends SpeechFlowNode {
|
|
|
143
143
|
this.push(chunk)
|
|
144
144
|
callback()
|
|
145
145
|
}).catch((error: unknown) => {
|
|
146
|
-
callback(
|
|
146
|
+
callback(util.ensureError(error))
|
|
147
147
|
})
|
|
148
148
|
}
|
|
149
149
|
},
|
package/speechflow-cli/src/{speechflow-node-t2t-awstranslate.ts → speechflow-node-t2t-amazon.ts}
RENAMED
|
@@ -12,12 +12,12 @@ import { TranslateClient, TranslateTextCommand } from "@aws-sdk/client-translate
|
|
|
12
12
|
|
|
13
13
|
/* internal dependencies */
|
|
14
14
|
import SpeechFlowNode, { SpeechFlowChunk } from "./speechflow-node"
|
|
15
|
-
import * as
|
|
15
|
+
import * as util from "./speechflow-util"
|
|
16
16
|
|
|
17
|
-
/* SpeechFlow node for
|
|
18
|
-
export default class
|
|
17
|
+
/* SpeechFlow node for Amazon Translate text-to-text translations */
|
|
18
|
+
export default class SpeechFlowNodeT2TAmazon extends SpeechFlowNode {
|
|
19
19
|
/* declare official node name */
|
|
20
|
-
public static name = "
|
|
20
|
+
public static name = "t2t-amazon"
|
|
21
21
|
|
|
22
22
|
/* internal state */
|
|
23
23
|
private client: TranslateClient | null = null
|
|
@@ -124,7 +124,7 @@ export default class SpeechFlowNodeAWSTranslate extends SpeechFlowNode {
|
|
|
124
124
|
this.push(chunkNew)
|
|
125
125
|
callback()
|
|
126
126
|
}).catch((error: unknown) => {
|
|
127
|
-
callback(
|
|
127
|
+
callback(util.ensureError(error))
|
|
128
128
|
})
|
|
129
129
|
}
|
|
130
130
|
},
|
|
@@ -12,12 +12,12 @@ import * as DeepL from "deepl-node"
|
|
|
12
12
|
|
|
13
13
|
/* internal dependencies */
|
|
14
14
|
import SpeechFlowNode, { SpeechFlowChunk } from "./speechflow-node"
|
|
15
|
-
import * as
|
|
15
|
+
import * as util from "./speechflow-util"
|
|
16
16
|
|
|
17
17
|
/* SpeechFlow node for DeepL text-to-text translations */
|
|
18
|
-
export default class
|
|
18
|
+
export default class SpeechFlowNodeT2TDeepL extends SpeechFlowNode {
|
|
19
19
|
/* declare official node name */
|
|
20
|
-
public static name = "deepl"
|
|
20
|
+
public static name = "t2t-deepl"
|
|
21
21
|
|
|
22
22
|
/* internal state */
|
|
23
23
|
private deepl: DeepL.Translator | null = null
|
|
@@ -95,7 +95,7 @@ export default class SpeechFlowNodeDeepL extends SpeechFlowNode {
|
|
|
95
95
|
this.push(chunkNew)
|
|
96
96
|
callback()
|
|
97
97
|
}).catch((error: unknown) => {
|
|
98
|
-
callback(
|
|
98
|
+
callback(util.ensureError(error))
|
|
99
99
|
})
|
|
100
100
|
}
|
|
101
101
|
},
|
|
@@ -14,9 +14,9 @@ import wrapText from "wrap-text"
|
|
|
14
14
|
import SpeechFlowNode, { SpeechFlowChunk } from "./speechflow-node"
|
|
15
15
|
|
|
16
16
|
/* SpeechFlow node for text-to-text formatting */
|
|
17
|
-
export default class
|
|
17
|
+
export default class SpeechFlowNodeT2TFormat extends SpeechFlowNode {
|
|
18
18
|
/* declare official node name */
|
|
19
|
-
public static name = "format"
|
|
19
|
+
public static name = "t2t-format"
|
|
20
20
|
|
|
21
21
|
/* construct node */
|
|
22
22
|
constructor (id: string, cfg: { [ id: string ]: any }, opts: { [ id: string ]: any }, args: any[]) {
|
|
@@ -13,12 +13,12 @@ import * as arktype from "arktype"
|
|
|
13
13
|
|
|
14
14
|
/* internal dependencies */
|
|
15
15
|
import SpeechFlowNode, { SpeechFlowChunk } from "./speechflow-node"
|
|
16
|
-
import * as
|
|
16
|
+
import * as util from "./speechflow-util"
|
|
17
17
|
|
|
18
18
|
/* SpeechFlow node for Google Translate text-to-text translations */
|
|
19
|
-
export default class
|
|
19
|
+
export default class SpeechFlowNodeT2TGoogle extends SpeechFlowNode {
|
|
20
20
|
/* declare official node name */
|
|
21
|
-
public static name = "google"
|
|
21
|
+
public static name = "t2t-google"
|
|
22
22
|
|
|
23
23
|
/* internal state */
|
|
24
24
|
private client: TranslationServiceClient | null = null
|
|
@@ -55,9 +55,9 @@ export default class SpeechFlowNodeGoogle extends SpeechFlowNode {
|
|
|
55
55
|
/* open node */
|
|
56
56
|
async open () {
|
|
57
57
|
/* instantiate Google Translate client */
|
|
58
|
-
const data =
|
|
58
|
+
const data = util.run("Google Cloud API credentials key", () =>
|
|
59
59
|
JSON.parse(this.params.key))
|
|
60
|
-
const credentials =
|
|
60
|
+
const credentials = util.importObject("Google Cloud API credentials key",
|
|
61
61
|
data,
|
|
62
62
|
arktype.type({
|
|
63
63
|
project_id: "string",
|
|
@@ -74,7 +74,7 @@ export default class SpeechFlowNodeGoogle extends SpeechFlowNode {
|
|
|
74
74
|
})
|
|
75
75
|
|
|
76
76
|
/* provide text-to-text translation */
|
|
77
|
-
const translate =
|
|
77
|
+
const translate = util.runner("Google Translate API", async (text: string) => {
|
|
78
78
|
const [ response ] = await this.client!.translateText({
|
|
79
79
|
parent: `projects/${credentials.project_id}/locations/global`,
|
|
80
80
|
contents: [ text ],
|
|
@@ -105,7 +105,7 @@ export default class SpeechFlowNodeGoogle extends SpeechFlowNode {
|
|
|
105
105
|
this.push(chunkNew)
|
|
106
106
|
callback()
|
|
107
107
|
}).catch((error: unknown) => {
|
|
108
|
-
callback(
|
|
108
|
+
callback(util.ensureError(error))
|
|
109
109
|
})
|
|
110
110
|
}
|
|
111
111
|
},
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/*
|
|
2
|
+
** SpeechFlow - Speech Processing Flow Graph
|
|
3
|
+
** Copyright (c) 2024-2025 Dr. Ralf S. Engelschall <rse@engelschall.com>
|
|
4
|
+
** Licensed under GPL 3.0 <https://spdx.org/licenses/GPL-3.0-only>
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/* standard dependencies */
|
|
8
|
+
import Stream from "node:stream"
|
|
9
|
+
|
|
10
|
+
/* internal dependencies */
|
|
11
|
+
import SpeechFlowNode, { SpeechFlowChunk } from "./speechflow-node"
|
|
12
|
+
import * as util from "./speechflow-util"
|
|
13
|
+
|
|
14
|
+
/* SpeechFlow node for text-to-text modification via regex */
|
|
15
|
+
export default class SpeechFlowNodeT2TModify extends SpeechFlowNode {
|
|
16
|
+
/* declare official node name */
|
|
17
|
+
public static name = "t2t-modify"
|
|
18
|
+
|
|
19
|
+
/* construct node */
|
|
20
|
+
constructor (id: string, cfg: { [ id: string ]: any }, opts: { [ id: string ]: any }, args: any[]) {
|
|
21
|
+
super(id, cfg, opts, args)
|
|
22
|
+
|
|
23
|
+
/* declare node configuration parameters */
|
|
24
|
+
this.configure({
|
|
25
|
+
match: { type: "string", val: "" },
|
|
26
|
+
replace: { type: "string", val: "" }
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
/* declare node input/output format */
|
|
30
|
+
this.input = "text"
|
|
31
|
+
this.output = "text"
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/* open node */
|
|
35
|
+
async open () {
|
|
36
|
+
/* validate parameters */
|
|
37
|
+
if (this.params.match === "")
|
|
38
|
+
throw new Error("match parameter cannot be empty")
|
|
39
|
+
|
|
40
|
+
/* compile regex pattern */
|
|
41
|
+
const regex = util.run("compiling regex pattern",
|
|
42
|
+
() => new RegExp(this.params.match, "g"))
|
|
43
|
+
|
|
44
|
+
/* apply regex-based modification */
|
|
45
|
+
const modify = (text: string): string =>
|
|
46
|
+
text.replace(regex, this.params.replace)
|
|
47
|
+
|
|
48
|
+
/* establish a duplex stream and connect it to text modification */
|
|
49
|
+
this.stream = new Stream.Transform({
|
|
50
|
+
readableObjectMode: true,
|
|
51
|
+
writableObjectMode: true,
|
|
52
|
+
decodeStrings: false,
|
|
53
|
+
highWaterMark: 1,
|
|
54
|
+
transform (chunk: SpeechFlowChunk, encoding, callback) {
|
|
55
|
+
if (Buffer.isBuffer(chunk.payload))
|
|
56
|
+
callback(new Error("invalid chunk payload type"))
|
|
57
|
+
else if (chunk.payload === "") {
|
|
58
|
+
this.push(chunk)
|
|
59
|
+
callback()
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
const payload = modify(chunk.payload)
|
|
63
|
+
const chunkNew = chunk.clone()
|
|
64
|
+
chunkNew.payload = payload
|
|
65
|
+
this.push(chunkNew)
|
|
66
|
+
callback()
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
final (callback) {
|
|
70
|
+
this.push(null)
|
|
71
|
+
callback()
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* close node */
|
|
77
|
+
async close () {
|
|
78
|
+
/* close stream */
|
|
79
|
+
if (this.stream !== null) {
|
|
80
|
+
this.stream.destroy()
|
|
81
|
+
this.stream = null
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -12,16 +12,16 @@ import { Ollama, type ListResponse } from "ollama"
|
|
|
12
12
|
|
|
13
13
|
/* internal dependencies */
|
|
14
14
|
import SpeechFlowNode, { SpeechFlowChunk } from "./speechflow-node"
|
|
15
|
-
import * as
|
|
15
|
+
import * as util from "./speechflow-util"
|
|
16
16
|
|
|
17
17
|
/* internal utility types */
|
|
18
18
|
type ConfigEntry = { systemPrompt: string, chat: Array<{ role: string, content: string }> }
|
|
19
19
|
type Config = { [ key: string ]: ConfigEntry }
|
|
20
20
|
|
|
21
21
|
/* SpeechFlow node for Ollama text-to-text translation */
|
|
22
|
-
export default class
|
|
22
|
+
export default class SpeechFlowNodeT2TOllama extends SpeechFlowNode {
|
|
23
23
|
/* declare official node name */
|
|
24
|
-
public static name = "ollama"
|
|
24
|
+
public static name = "t2t-ollama"
|
|
25
25
|
|
|
26
26
|
/* internal state */
|
|
27
27
|
private ollama: Ollama | null = null
|
|
@@ -252,7 +252,7 @@ export default class SpeechFlowNodeOllama extends SpeechFlowNode {
|
|
|
252
252
|
this.push(chunkNew)
|
|
253
253
|
callback()
|
|
254
254
|
}).catch((error: unknown) => {
|
|
255
|
-
callback(
|
|
255
|
+
callback(util.ensureError(error))
|
|
256
256
|
})
|
|
257
257
|
}
|
|
258
258
|
}
|
|
@@ -12,16 +12,16 @@ import OpenAI from "openai"
|
|
|
12
12
|
|
|
13
13
|
/* internal dependencies */
|
|
14
14
|
import SpeechFlowNode, { SpeechFlowChunk } from "./speechflow-node"
|
|
15
|
-
import * as
|
|
15
|
+
import * as util from "./speechflow-util"
|
|
16
16
|
|
|
17
17
|
/* internal utility types */
|
|
18
18
|
type ConfigEntry = { systemPrompt: string, chat: OpenAI.ChatCompletionMessageParam[] }
|
|
19
19
|
type Config = { [ key: string ]: ConfigEntry }
|
|
20
20
|
|
|
21
21
|
/* SpeechFlow node for OpenAI/GPT text-to-text translation */
|
|
22
|
-
export default class
|
|
22
|
+
export default class SpeechFlowNodeT2TOpenAI extends SpeechFlowNode {
|
|
23
23
|
/* declare official node name */
|
|
24
|
-
public static name = "openai"
|
|
24
|
+
public static name = "t2t-openai"
|
|
25
25
|
|
|
26
26
|
/* internal state */
|
|
27
27
|
private openai: OpenAI | null = null
|
|
@@ -221,7 +221,7 @@ export default class SpeechFlowNodeOpenAI extends SpeechFlowNode {
|
|
|
221
221
|
this.push(chunkNew)
|
|
222
222
|
callback()
|
|
223
223
|
}).catch((error: unknown) => {
|
|
224
|
-
callback(
|
|
224
|
+
callback(util.ensureError(error))
|
|
225
225
|
})
|
|
226
226
|
}
|
|
227
227
|
},
|
|
@@ -12,7 +12,7 @@ import { Duration } from "luxon"
|
|
|
12
12
|
|
|
13
13
|
/* internal dependencies */
|
|
14
14
|
import SpeechFlowNode, { SpeechFlowChunk } from "./speechflow-node"
|
|
15
|
-
import * as
|
|
15
|
+
import * as util from "./speechflow-util"
|
|
16
16
|
|
|
17
17
|
/* text stream queue element */
|
|
18
18
|
type TextQueueElement = {
|
|
@@ -24,12 +24,12 @@ type TextQueueElement = {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
/* SpeechFlow node for sentence splitting */
|
|
27
|
-
export default class
|
|
27
|
+
export default class SpeechFlowNodeT2TSentence extends SpeechFlowNode {
|
|
28
28
|
/* declare official node name */
|
|
29
|
-
public static name = "sentence"
|
|
29
|
+
public static name = "t2t-sentence"
|
|
30
30
|
|
|
31
31
|
/* internal state */
|
|
32
|
-
private queue = new
|
|
32
|
+
private queue = new util.Queue<TextQueueElement>()
|
|
33
33
|
private queueRecv = this.queue.pointerUse("recv")
|
|
34
34
|
private queueSplit = this.queue.pointerUse("split")
|
|
35
35
|
private queueSend = this.queue.pointerUse("send")
|