speechflow 1.5.1 → 1.6.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/CHANGELOG.md +8 -0
- package/README.md +191 -170
- package/etc/claude.md +83 -46
- package/etc/speechflow.yaml +84 -84
- package/package.json +3 -3
- package/speechflow-cli/dst/speechflow-node-a2a-compressor.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-compressor.js +3 -3
- package/speechflow-cli/dst/speechflow-node-a2a-compressor.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 +3 -3
- 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 +3 -3
- 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 +3 -3
- 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 +3 -3
- 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 +3 -3
- 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 +3 -3
- 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 +3 -3
- 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 +3 -3
- 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 +3 -3
- 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-amazon.d.ts +18 -0
- package/speechflow-cli/dst/speechflow-node-a2t-amazon.js +312 -0
- package/speechflow-cli/dst/speechflow-node-a2t-amazon.js.map +1 -0
- package/speechflow-cli/dst/speechflow-node-a2t-awstranscribe.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-a2t-awstranscribe.js +3 -3
- package/speechflow-cli/dst/speechflow-node-a2t-awstranscribe.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2t-deepgram.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-a2t-deepgram.js +3 -3
- package/speechflow-cli/dst/speechflow-node-a2t-deepgram.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2t-openai.d.ts +19 -0
- package/speechflow-cli/dst/speechflow-node-a2t-openai.js +351 -0
- package/speechflow-cli/dst/speechflow-node-a2t-openai.js.map +1 -0
- package/speechflow-cli/dst/speechflow-node-a2t-openaitranscribe.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-a2t-openaitranscribe.js +5 -5
- package/speechflow-cli/dst/speechflow-node-a2t-openaitranscribe.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-t2a-amazon.d.ts +16 -0
- package/speechflow-cli/dst/speechflow-node-t2a-amazon.js +204 -0
- package/speechflow-cli/dst/speechflow-node-t2a-amazon.js.map +1 -0
- package/speechflow-cli/dst/speechflow-node-t2a-awspolly.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-t2a-awspolly.js +5 -5
- package/speechflow-cli/dst/speechflow-node-t2a-awspolly.js.map +1 -1
- 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 +5 -5
- package/speechflow-cli/dst/speechflow-node-t2a-kokoro.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-t2t-amazon.d.ts +13 -0
- package/speechflow-cli/dst/speechflow-node-t2t-amazon.js +175 -0
- package/speechflow-cli/dst/speechflow-node-t2t-amazon.js.map +1 -0
- package/speechflow-cli/dst/speechflow-node-t2t-awstranslate.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-t2t-awstranslate.js +3 -3
- package/speechflow-cli/dst/speechflow-node-t2t-awstranslate.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-t2t-deepl.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-node-t2t-deepl.js +3 -3
- 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 +3 -3
- package/speechflow-cli/dst/speechflow-node-t2t-google.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-t2t-modify.d.ts +11 -0
- 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 +3 -3
- 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 +3 -3
- 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 +3 -3
- 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 +3 -3
- 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 +3 -3
- 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 +3 -3
- 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 +3 -3
- 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 +43 -22
- 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 +3 -3
- 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 +3 -3
- package/speechflow-cli/dst/speechflow-node-xio-websocket.js.map +1 -1
- package/speechflow-cli/dst/speechflow-utils.js +11 -5
- package/speechflow-cli/dst/speechflow-utils.js.map +1 -1
- package/speechflow-cli/dst/speechflow.js +13 -7
- package/speechflow-cli/dst/speechflow.js.map +1 -1
- package/speechflow-cli/package.json +11 -11
- package/speechflow-cli/src/speechflow-node-a2a-compressor.ts +2 -2
- package/speechflow-cli/src/speechflow-node-a2a-expander.ts +2 -2
- package/speechflow-cli/src/speechflow-node-a2a-ffmpeg.ts +2 -2
- package/speechflow-cli/src/speechflow-node-a2a-filler.ts +2 -2
- package/speechflow-cli/src/speechflow-node-a2a-gain.ts +2 -2
- package/speechflow-cli/src/speechflow-node-a2a-gender.ts +2 -2
- package/speechflow-cli/src/speechflow-node-a2a-meter.ts +2 -2
- package/speechflow-cli/src/speechflow-node-a2a-mute.ts +2 -2
- package/speechflow-cli/src/speechflow-node-a2a-rnnoise.ts +2 -2
- package/speechflow-cli/src/speechflow-node-a2a-speex.ts +2 -2
- package/speechflow-cli/src/speechflow-node-a2a-vad.ts +2 -2
- 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} +3 -3
- package/speechflow-cli/src/speechflow-node-a2t-deepgram.ts +2 -2
- package/speechflow-cli/src/{speechflow-node-a2t-openaitranscribe.ts → speechflow-node-a2t-openai.ts} +5 -5
- package/speechflow-cli/src/{speechflow-node-t2a-awspolly.ts → speechflow-node-t2a-amazon.ts} +5 -5
- package/speechflow-cli/src/speechflow-node-t2a-elevenlabs.ts +4 -4
- package/speechflow-cli/src/speechflow-node-t2a-kokoro.ts +4 -4
- package/speechflow-cli/src/{speechflow-node-t2t-awstranslate.ts → speechflow-node-t2t-amazon.ts} +3 -3
- package/speechflow-cli/src/speechflow-node-t2t-deepl.ts +2 -2
- package/speechflow-cli/src/speechflow-node-t2t-format.ts +2 -2
- package/speechflow-cli/src/speechflow-node-t2t-google.ts +2 -2
- package/speechflow-cli/src/speechflow-node-t2t-modify.ts +84 -0
- package/speechflow-cli/src/speechflow-node-t2t-ollama.ts +2 -2
- package/speechflow-cli/src/speechflow-node-t2t-openai.ts +2 -2
- package/speechflow-cli/src/speechflow-node-t2t-sentence.ts +2 -2
- package/speechflow-cli/src/speechflow-node-t2t-subtitle.ts +7 -7
- package/speechflow-cli/src/speechflow-node-t2t-transformers.ts +2 -2
- package/speechflow-cli/src/speechflow-node-x2x-filter.ts +2 -2
- package/speechflow-cli/src/speechflow-node-x2x-trace.ts +2 -2
- package/speechflow-cli/src/speechflow-node-xio-device.ts +2 -2
- package/speechflow-cli/src/speechflow-node-xio-file.ts +43 -21
- package/speechflow-cli/src/speechflow-node-xio-mqtt.ts +2 -2
- package/speechflow-cli/src/speechflow-node-xio-websocket.ts +2 -2
- package/speechflow-cli/src/speechflow-utils.ts +11 -5
- package/speechflow-cli/src/speechflow.ts +13 -7
- 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/src/{speechflow-node-t2a-awspolly.ts → speechflow-node-t2a-amazon.ts}
RENAMED
|
@@ -19,10 +19,10 @@ import {
|
|
|
19
19
|
import SpeechFlowNode, { SpeechFlowChunk } from "./speechflow-node"
|
|
20
20
|
import * as utils from "./speechflow-utils"
|
|
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
|
|
|
@@ -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
|
|
|
@@ -16,9 +16,9 @@ import SpeechFlowNode, { SpeechFlowChunk } from "./speechflow-node"
|
|
|
16
16
|
import * as utils from "./speechflow-utils"
|
|
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)
|
package/speechflow-cli/src/{speechflow-node-t2t-awstranslate.ts → speechflow-node-t2t-amazon.ts}
RENAMED
|
@@ -14,10 +14,10 @@ import { TranslateClient, TranslateTextCommand } from "@aws-sdk/client-translate
|
|
|
14
14
|
import SpeechFlowNode, { SpeechFlowChunk } from "./speechflow-node"
|
|
15
15
|
import * as utils from "./speechflow-utils"
|
|
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
|
|
@@ -15,9 +15,9 @@ import SpeechFlowNode, { SpeechFlowChunk } from "./speechflow-node"
|
|
|
15
15
|
import * as utils from "./speechflow-utils"
|
|
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
|
|
@@ -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[]) {
|
|
@@ -16,9 +16,9 @@ import SpeechFlowNode, { SpeechFlowChunk } from "./speechflow-node"
|
|
|
16
16
|
import * as utils from "./speechflow-utils"
|
|
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
|
|
@@ -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 utils from "./speechflow-utils"
|
|
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 = utils.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
|
+
}
|
|
@@ -19,9 +19,9 @@ type ConfigEntry = { systemPrompt: string, chat: Array<{ role: string, content:
|
|
|
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
|
|
@@ -19,9 +19,9 @@ type ConfigEntry = { systemPrompt: string, chat: OpenAI.ChatCompletionMessagePar
|
|
|
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
|
|
@@ -24,9 +24,9 @@ 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
32
|
private queue = new utils.Queue<TextQueueElement>()
|
|
@@ -30,9 +30,9 @@ type wsPeerInfo = {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
/* SpeechFlow node for subtitle (text-to-text) "translations" */
|
|
33
|
-
export default class
|
|
33
|
+
export default class SpeechFlowNodeT2TSubtitle extends SpeechFlowNode {
|
|
34
34
|
/* declare official node name */
|
|
35
|
-
public static name = "subtitle"
|
|
35
|
+
public static name = "t2t-subtitle"
|
|
36
36
|
|
|
37
37
|
/* internal state */
|
|
38
38
|
private sequenceNo = 1
|
|
@@ -44,11 +44,11 @@ export default class SpeechFlowNodeSubtitle extends SpeechFlowNode {
|
|
|
44
44
|
|
|
45
45
|
/* declare node configuration parameters */
|
|
46
46
|
this.configure({
|
|
47
|
-
format: { type: "string",
|
|
48
|
-
words: { type: "boolean",
|
|
49
|
-
mode: { type: "string",
|
|
50
|
-
addr: { type: "string",
|
|
51
|
-
port: { type: "number",
|
|
47
|
+
format: { type: "string", pos: 0, val: "srt", match: /^(?:srt|vtt)$/ },
|
|
48
|
+
words: { type: "boolean", val: false },
|
|
49
|
+
mode: { type: "string", val: "export", match: /^(?:export|render)$/ },
|
|
50
|
+
addr: { type: "string", val: "127.0.0.1" },
|
|
51
|
+
port: { type: "number", val: 8585 }
|
|
52
52
|
})
|
|
53
53
|
|
|
54
54
|
/* declare node input/output format */
|
|
@@ -20,9 +20,9 @@ type ConfigEntry = { systemPrompt: string, chat: Array<{ role: string, content:
|
|
|
20
20
|
type Config = { [ key: string ]: ConfigEntry }
|
|
21
21
|
|
|
22
22
|
/* SpeechFlow node for Transformers text-to-text translation */
|
|
23
|
-
export default class
|
|
23
|
+
export default class SpeechFlowNodeT2TTransformers extends SpeechFlowNode {
|
|
24
24
|
/* declare official node name */
|
|
25
|
-
public static name = "transformers"
|
|
25
|
+
public static name = "t2t-transformers"
|
|
26
26
|
|
|
27
27
|
/* internal state */
|
|
28
28
|
private translator: Transformers.TranslationPipeline | null = null
|
|
@@ -12,9 +12,9 @@ import SpeechFlowNode, { SpeechFlowChunk } from "./speechflow-node"
|
|
|
12
12
|
import * as utils from "./speechflow-utils"
|
|
13
13
|
|
|
14
14
|
/* SpeechFlow node for data flow filtering (based on meta information) */
|
|
15
|
-
export default class
|
|
15
|
+
export default class SpeechFlowNodeX2XFilter extends SpeechFlowNode {
|
|
16
16
|
/* declare official node name */
|
|
17
|
-
public static name = "filter"
|
|
17
|
+
public static name = "x2x-filter"
|
|
18
18
|
|
|
19
19
|
/* cached regular expression instance */
|
|
20
20
|
private cachedRegExp = new utils.CachedRegExp()
|
|
@@ -14,9 +14,9 @@ import { Duration } from "luxon"
|
|
|
14
14
|
import SpeechFlowNode, { SpeechFlowChunk } from "./speechflow-node"
|
|
15
15
|
|
|
16
16
|
/* SpeechFlow node for data flow tracing */
|
|
17
|
-
export default class
|
|
17
|
+
export default class SpeechFlowNodeX2XTrace extends SpeechFlowNode {
|
|
18
18
|
/* declare official node name */
|
|
19
|
-
public static name = "trace"
|
|
19
|
+
public static name = "x2x-trace"
|
|
20
20
|
|
|
21
21
|
/* construct node */
|
|
22
22
|
constructor (id: string, cfg: { [ id: string ]: any }, opts: { [ id: string ]: any }, args: any[]) {
|
|
@@ -15,9 +15,9 @@ import SpeechFlowNode from "./speechflow-node"
|
|
|
15
15
|
import * as utils from "./speechflow-utils"
|
|
16
16
|
|
|
17
17
|
/* SpeechFlow node for device access */
|
|
18
|
-
export default class
|
|
18
|
+
export default class SpeechFlowNodeXIODevice extends SpeechFlowNode {
|
|
19
19
|
/* declare official node name */
|
|
20
|
-
public static name = "device"
|
|
20
|
+
public static name = "xio-device"
|
|
21
21
|
|
|
22
22
|
/* internal state */
|
|
23
23
|
private io: PortAudio.IoStreamRead
|
|
@@ -13,9 +13,9 @@ import SpeechFlowNode from "./speechflow-node"
|
|
|
13
13
|
import * as utils from "./speechflow-utils"
|
|
14
14
|
|
|
15
15
|
/* SpeechFlow node for file access */
|
|
16
|
-
export default class
|
|
16
|
+
export default class SpeechFlowNodeXIOFile extends SpeechFlowNode {
|
|
17
17
|
/* declare official node name */
|
|
18
|
-
public static name = "file"
|
|
18
|
+
public static name = "xio-file"
|
|
19
19
|
|
|
20
20
|
/* construct node */
|
|
21
21
|
constructor (id: string, cfg: { [ id: string ]: any }, opts: { [ id: string ]: any }, args: any[]) {
|
|
@@ -59,6 +59,28 @@ export default class SpeechFlowNodeFile extends SpeechFlowNode {
|
|
|
59
59
|
if (this.params.path === "")
|
|
60
60
|
throw new Error("required parameter \"path\" has to be given")
|
|
61
61
|
|
|
62
|
+
/* utility function: create a writable stream as chunker that
|
|
63
|
+
writes to process.stdout but properly handles finish events.
|
|
64
|
+
This ensures the writable side of the composed stream below
|
|
65
|
+
properly signals completion while keeping process.stdout open
|
|
66
|
+
(as it's a global stream that shouldn't be closed by individual nodes). */
|
|
67
|
+
const createStdoutChunker = () => {
|
|
68
|
+
return new Stream.Writable({
|
|
69
|
+
highWaterMark: this.params.type === "audio" ?
|
|
70
|
+
highWaterMarkAudio : highWaterMarkText,
|
|
71
|
+
write (chunk: Buffer | string, encoding, callback) {
|
|
72
|
+
const canContinue = process.stdout.write(chunk, encoding)
|
|
73
|
+
if (canContinue)
|
|
74
|
+
callback()
|
|
75
|
+
else
|
|
76
|
+
process.stdout.once("drain", callback)
|
|
77
|
+
},
|
|
78
|
+
final (callback) {
|
|
79
|
+
callback()
|
|
80
|
+
}
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
|
|
62
84
|
/* dispatch according to mode and path */
|
|
63
85
|
if (this.params.mode === "rw") {
|
|
64
86
|
if (this.params.path === "-") {
|
|
@@ -145,17 +167,13 @@ export default class SpeechFlowNodeFile extends SpeechFlowNode {
|
|
|
145
167
|
else if (this.params.mode === "w") {
|
|
146
168
|
if (this.params.path === "-") {
|
|
147
169
|
/* standard I/O */
|
|
148
|
-
|
|
149
|
-
if (this.params.type === "audio") {
|
|
170
|
+
if (this.params.type === "audio")
|
|
150
171
|
process.stdout.setEncoding()
|
|
151
|
-
|
|
152
|
-
}
|
|
153
|
-
else {
|
|
172
|
+
else
|
|
154
173
|
process.stdout.setEncoding(this.config.textEncoding)
|
|
155
|
-
|
|
156
|
-
}
|
|
174
|
+
const chunker = createStdoutChunker()
|
|
157
175
|
const wrapper = utils.createTransformStreamForWritableSide()
|
|
158
|
-
this.stream = Stream.compose(wrapper, chunker
|
|
176
|
+
this.stream = Stream.compose(wrapper, chunker)
|
|
159
177
|
}
|
|
160
178
|
else {
|
|
161
179
|
/* file I/O */
|
|
@@ -178,18 +196,22 @@ export default class SpeechFlowNodeFile extends SpeechFlowNode {
|
|
|
178
196
|
async close () {
|
|
179
197
|
/* shutdown stream */
|
|
180
198
|
if (this.stream !== null) {
|
|
181
|
-
await
|
|
182
|
-
|
|
183
|
-
this.stream.
|
|
184
|
-
if (
|
|
185
|
-
reject(err)
|
|
186
|
-
else
|
|
199
|
+
await Promise.race([
|
|
200
|
+
new Promise<void>((resolve, reject) => {
|
|
201
|
+
if (this.stream instanceof Stream.Writable || this.stream instanceof Stream.Duplex) {
|
|
202
|
+
if (this.stream.writableEnded || this.stream.destroyed)
|
|
187
203
|
resolve()
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
204
|
+
else
|
|
205
|
+
this.stream.end((err?: Error) => {
|
|
206
|
+
if (err) reject(err)
|
|
207
|
+
else resolve()
|
|
208
|
+
})
|
|
209
|
+
}
|
|
210
|
+
else
|
|
211
|
+
resolve()
|
|
212
|
+
}),
|
|
213
|
+
new Promise<void>((resolve) => setTimeout(() => resolve(), 5000))
|
|
214
|
+
])
|
|
193
215
|
if (this.params.path !== "-")
|
|
194
216
|
this.stream.destroy()
|
|
195
217
|
this.stream = null
|
|
@@ -16,9 +16,9 @@ import SpeechFlowNode, { SpeechFlowChunk } from "./speechflow-node"
|
|
|
16
16
|
import * as utils from "./speechflow-utils"
|
|
17
17
|
|
|
18
18
|
/* SpeechFlow node for MQTT networking */
|
|
19
|
-
export default class
|
|
19
|
+
export default class SpeechFlowNodeXIOMQTT extends SpeechFlowNode {
|
|
20
20
|
/* declare official node name */
|
|
21
|
-
public static name = "mqtt"
|
|
21
|
+
public static name = "xio-mqtt"
|
|
22
22
|
|
|
23
23
|
/* internal state */
|
|
24
24
|
private broker: MQTT.MqttClient | null = null
|
|
@@ -16,9 +16,9 @@ import SpeechFlowNode, { SpeechFlowChunk } from "./speechflow-node"
|
|
|
16
16
|
import * as utils from "./speechflow-utils"
|
|
17
17
|
|
|
18
18
|
/* SpeechFlow node for Websocket networking */
|
|
19
|
-
export default class
|
|
19
|
+
export default class SpeechFlowNodeXIOWebSocket extends SpeechFlowNode {
|
|
20
20
|
/* declare official node name */
|
|
21
|
-
public static name = "websocket"
|
|
21
|
+
public static name = "xio-websocket"
|
|
22
22
|
|
|
23
23
|
/* internal state */
|
|
24
24
|
private server: ws.WebSocketServer | null = null
|
|
@@ -40,7 +40,7 @@ function runFinally (onfinally?: () => void) {
|
|
|
40
40
|
if (!onfinally)
|
|
41
41
|
return
|
|
42
42
|
try { onfinally() }
|
|
43
|
-
catch (
|
|
43
|
+
catch (_error: unknown) { /* ignored */ }
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
/* helper type for ensuring T contains no Promise */
|
|
@@ -120,14 +120,14 @@ export function run<T> (
|
|
|
120
120
|
let error = ensureError(arg, description)
|
|
121
121
|
if (oncatch) {
|
|
122
122
|
try {
|
|
123
|
-
return
|
|
123
|
+
return oncatch(error)
|
|
124
124
|
}
|
|
125
125
|
catch (arg: unknown) {
|
|
126
126
|
error = ensureError(arg, description)
|
|
127
|
-
|
|
127
|
+
throw error
|
|
128
128
|
}
|
|
129
129
|
}
|
|
130
|
-
|
|
130
|
+
throw error
|
|
131
131
|
}).finally(() => {
|
|
132
132
|
/* asynchronous case (result and error branch) */
|
|
133
133
|
runFinally(onfinally)
|
|
@@ -328,6 +328,11 @@ export function createTransformStreamForReadableSide (type: "text" | "audio", ge
|
|
|
328
328
|
decodeStrings: false,
|
|
329
329
|
highWaterMark: (type === "audio" ? 19200 : 65536),
|
|
330
330
|
transform (chunk: Buffer | string, encoding, callback) {
|
|
331
|
+
if (chunk === null) {
|
|
332
|
+
this.push(null)
|
|
333
|
+
callback()
|
|
334
|
+
return
|
|
335
|
+
}
|
|
331
336
|
const timeZero = getTimeZero()
|
|
332
337
|
const start = DateTime.now().diff(timeZero)
|
|
333
338
|
let end = start
|
|
@@ -335,7 +340,8 @@ export function createTransformStreamForReadableSide (type: "text" | "audio", ge
|
|
|
335
340
|
const duration = audioBufferDuration(chunk as Buffer)
|
|
336
341
|
end = start.plus(duration * 1000)
|
|
337
342
|
}
|
|
338
|
-
const
|
|
343
|
+
const payload = ensureStreamChunk(type, chunk) as Buffer | string
|
|
344
|
+
const obj = new SpeechFlowChunk(start, end, "final", type, payload)
|
|
339
345
|
this.push(obj)
|
|
340
346
|
callback()
|
|
341
347
|
},
|
|
@@ -282,16 +282,17 @@ let debug = false
|
|
|
282
282
|
"./speechflow-node-a2a-speex.js",
|
|
283
283
|
"./speechflow-node-a2a-vad.js",
|
|
284
284
|
"./speechflow-node-a2a-wav.js",
|
|
285
|
-
"./speechflow-node-a2t-
|
|
285
|
+
"./speechflow-node-a2t-amazon.js",
|
|
286
286
|
"./speechflow-node-a2t-deepgram.js",
|
|
287
|
-
"./speechflow-node-a2t-
|
|
288
|
-
"./speechflow-node-t2a-
|
|
287
|
+
"./speechflow-node-a2t-openai.js",
|
|
288
|
+
"./speechflow-node-t2a-amazon.js",
|
|
289
289
|
"./speechflow-node-t2a-elevenlabs.js",
|
|
290
290
|
"./speechflow-node-t2a-kokoro.js",
|
|
291
|
-
"./speechflow-node-t2t-
|
|
291
|
+
"./speechflow-node-t2t-amazon.js",
|
|
292
292
|
"./speechflow-node-t2t-deepl.js",
|
|
293
293
|
"./speechflow-node-t2t-format.js",
|
|
294
294
|
"./speechflow-node-t2t-google.js",
|
|
295
|
+
"./speechflow-node-t2t-modify.js",
|
|
295
296
|
"./speechflow-node-t2t-ollama.js",
|
|
296
297
|
"./speechflow-node-t2t-openai.js",
|
|
297
298
|
"./speechflow-node-t2t-sentence.js",
|
|
@@ -441,9 +442,12 @@ let debug = false
|
|
|
441
442
|
cli!.log("error", `creation of node <${id}> failed: ${err}`)
|
|
442
443
|
process.exit(1)
|
|
443
444
|
}
|
|
444
|
-
const params = Object.keys(node!.params)
|
|
445
|
-
|
|
446
|
-
|
|
445
|
+
const params = Object.keys(node!.params).map((key) => {
|
|
446
|
+
if (key.match(/key/))
|
|
447
|
+
return `${key}: [...]`
|
|
448
|
+
else
|
|
449
|
+
return `${key}: ${JSON.stringify(node.params[key])}`
|
|
450
|
+
}).join(", ")
|
|
447
451
|
cli!.log("info", `create node <${node!.id}> (${params})`)
|
|
448
452
|
graphNodes.add(node!)
|
|
449
453
|
return node!
|
|
@@ -706,6 +710,8 @@ let debug = false
|
|
|
706
710
|
const peer = ctx.peer
|
|
707
711
|
wsPeers.delete(peer)
|
|
708
712
|
ws.removeAllListeners()
|
|
713
|
+
if (ws.readyState === WebSocket.OPEN)
|
|
714
|
+
ws.close()
|
|
709
715
|
cli!.log("info", `HAPI: WebSocket: disconnect: peer ${peer}`)
|
|
710
716
|
}
|
|
711
717
|
}
|
|
@@ -37,8 +37,8 @@
|
|
|
37
37
|
|
|
38
38
|
"@vue/eslint-config-typescript": "14.6.0",
|
|
39
39
|
"vue-eslint-parser": "10.2.0",
|
|
40
|
-
"eslint": "9.
|
|
41
|
-
"@eslint/js": "9.
|
|
40
|
+
"eslint": "9.35.0",
|
|
41
|
+
"@eslint/js": "9.35.0",
|
|
42
42
|
"neostandard": "0.12.2",
|
|
43
43
|
"eslint-plugin-import": "2.32.0",
|
|
44
44
|
"eslint-plugin-vue": "10.4.0",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"vue-tsc": "3.0.6",
|
|
61
61
|
"delay-cli": "2.0.0",
|
|
62
62
|
"cross-env": "10.0.0",
|
|
63
|
-
"serve": "14.2.
|
|
63
|
+
"serve": "14.2.5"
|
|
64
64
|
},
|
|
65
65
|
"overrides": {
|
|
66
66
|
"@liuli-util/vite-plugin-node": { "vite": ">=6.0.0" }
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|