speechflow 1.0.0 → 1.2.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 +19 -0
- package/README.md +46 -11
- package/dst/speechflow-node-a2a-gender.d.ts +17 -0
- package/dst/speechflow-node-a2a-gender.js +272 -0
- package/dst/speechflow-node-a2a-gender.js.map +1 -0
- package/dst/speechflow-node-a2a-meter.js +7 -3
- package/dst/speechflow-node-a2a-meter.js.map +1 -1
- package/dst/speechflow-node-a2a-mute.js +1 -0
- package/dst/speechflow-node-a2a-mute.js.map +1 -1
- package/dst/speechflow-node-a2a-vad.js +47 -63
- package/dst/speechflow-node-a2a-vad.js.map +1 -1
- package/dst/speechflow-node-a2a-wav.js +145 -122
- package/dst/speechflow-node-a2a-wav.js.map +1 -1
- package/dst/speechflow-node-a2t-deepgram.d.ts +3 -0
- package/dst/speechflow-node-a2t-deepgram.js +29 -4
- package/dst/speechflow-node-a2t-deepgram.js.map +1 -1
- package/dst/speechflow-node-t2a-elevenlabs.d.ts +3 -0
- package/dst/speechflow-node-t2a-elevenlabs.js +18 -6
- package/dst/speechflow-node-t2a-elevenlabs.js.map +1 -1
- package/dst/speechflow-node-t2a-kokoro.js.map +1 -1
- package/dst/speechflow-node-t2t-deepl.d.ts +3 -0
- package/dst/speechflow-node-t2t-deepl.js +8 -1
- package/dst/speechflow-node-t2t-deepl.js.map +1 -1
- package/dst/speechflow-node-t2t-format.js.map +1 -1
- package/dst/speechflow-node-t2t-ollama.js.map +1 -1
- package/dst/speechflow-node-t2t-openai.js +1 -1
- package/dst/speechflow-node-t2t-openai.js.map +1 -1
- package/dst/speechflow-node-t2t-subtitle.js.map +1 -1
- package/dst/speechflow-node-t2t-transformers.js.map +1 -1
- package/dst/speechflow-node-x2x-filter.d.ts +11 -0
- package/dst/speechflow-node-x2x-filter.js +113 -0
- package/dst/speechflow-node-x2x-filter.js.map +1 -0
- package/dst/speechflow-node-x2x-trace.js +25 -11
- package/dst/speechflow-node-x2x-trace.js.map +1 -1
- package/dst/speechflow-node-xio-device.js +17 -6
- package/dst/speechflow-node-xio-device.js.map +1 -1
- package/dst/speechflow-node-xio-file.js +61 -28
- package/dst/speechflow-node-xio-file.js.map +1 -1
- package/dst/speechflow-node-xio-mqtt.js +7 -5
- package/dst/speechflow-node-xio-mqtt.js.map +1 -1
- package/dst/speechflow-node-xio-websocket.js +5 -5
- package/dst/speechflow-node-xio-websocket.js.map +1 -1
- package/dst/speechflow-node.d.ts +5 -1
- package/dst/speechflow-node.js +9 -2
- package/dst/speechflow-node.js.map +1 -1
- package/dst/speechflow-utils.d.ts +14 -1
- package/dst/speechflow-utils.js +110 -2
- package/dst/speechflow-utils.js.map +1 -1
- package/dst/speechflow.js +73 -14
- package/dst/speechflow.js.map +1 -1
- package/etc/speechflow.yaml +53 -26
- package/package.json +12 -10
- package/src/speechflow-node-a2a-gender.ts +272 -0
- package/src/speechflow-node-a2a-meter.ts +8 -4
- package/src/speechflow-node-a2a-mute.ts +1 -0
- package/src/speechflow-node-a2a-vad.ts +58 -68
- package/src/speechflow-node-a2a-wav.ts +128 -91
- package/src/speechflow-node-a2t-deepgram.ts +32 -5
- package/src/speechflow-node-t2a-elevenlabs.ts +21 -8
- package/src/speechflow-node-t2a-kokoro.ts +3 -3
- package/src/speechflow-node-t2t-deepl.ts +11 -3
- package/src/speechflow-node-t2t-format.ts +2 -2
- package/src/speechflow-node-t2t-ollama.ts +2 -2
- package/src/speechflow-node-t2t-openai.ts +3 -3
- package/src/speechflow-node-t2t-subtitle.ts +1 -1
- package/src/speechflow-node-t2t-transformers.ts +2 -2
- package/src/speechflow-node-x2x-filter.ts +122 -0
- package/src/speechflow-node-x2x-trace.ts +29 -12
- package/src/speechflow-node-xio-device.ts +24 -9
- package/src/speechflow-node-xio-file.ts +76 -36
- package/src/speechflow-node-xio-mqtt.ts +11 -9
- package/src/speechflow-node-xio-websocket.ts +7 -7
- package/src/speechflow-node.ts +11 -2
- package/src/speechflow-utils.ts +81 -2
- package/src/speechflow.ts +96 -35
package/src/speechflow.ts
CHANGED
|
@@ -5,31 +5,33 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
/* standard dependencies */
|
|
8
|
-
import path
|
|
9
|
-
import Stream
|
|
10
|
-
import { EventEmitter }
|
|
11
|
-
import http
|
|
12
|
-
import * as HAPI
|
|
13
|
-
import WebSocket
|
|
14
|
-
import HAPIWebSocket
|
|
15
|
-
import HAPIHeader
|
|
8
|
+
import path from "node:path"
|
|
9
|
+
import Stream from "node:stream"
|
|
10
|
+
import { EventEmitter } from "node:events"
|
|
11
|
+
import http from "node:http"
|
|
12
|
+
import * as HAPI from "@hapi/hapi"
|
|
13
|
+
import WebSocket from "ws"
|
|
14
|
+
import HAPIWebSocket from "hapi-plugin-websocket"
|
|
15
|
+
import HAPIHeader from "hapi-plugin-header"
|
|
16
16
|
|
|
17
17
|
/* external dependencies */
|
|
18
|
-
import { DateTime }
|
|
19
|
-
import CLIio
|
|
20
|
-
import yargs
|
|
21
|
-
import { hideBin }
|
|
22
|
-
import jsYAML
|
|
23
|
-
import FlowLink
|
|
24
|
-
import objectPath
|
|
25
|
-
import installedPackages
|
|
26
|
-
import dotenvx
|
|
27
|
-
import syspath
|
|
28
|
-
import * as arktype
|
|
18
|
+
import { DateTime } from "luxon"
|
|
19
|
+
import CLIio from "cli-io"
|
|
20
|
+
import yargs from "yargs"
|
|
21
|
+
import { hideBin } from "yargs/helpers"
|
|
22
|
+
import jsYAML from "js-yaml"
|
|
23
|
+
import FlowLink from "flowlink"
|
|
24
|
+
import objectPath from "object-path"
|
|
25
|
+
import installedPackages from "installed-packages"
|
|
26
|
+
import dotenvx from "@dotenvx/dotenvx"
|
|
27
|
+
import syspath from "syspath"
|
|
28
|
+
import * as arktype from "arktype"
|
|
29
|
+
import Table from "cli-table3"
|
|
30
|
+
import chalk from "chalk"
|
|
29
31
|
|
|
30
32
|
/* internal dependencies */
|
|
31
|
-
import SpeechFlowNode
|
|
32
|
-
import pkg
|
|
33
|
+
import SpeechFlowNode from "./speechflow-node"
|
|
34
|
+
import pkg from "../package.json"
|
|
33
35
|
|
|
34
36
|
/* central CLI context */
|
|
35
37
|
let cli: CLIio | null = null
|
|
@@ -63,11 +65,13 @@ type wsPeerInfo = {
|
|
|
63
65
|
"[-a|--address <ip-address>] " +
|
|
64
66
|
"[-p|--port <tcp-port>] " +
|
|
65
67
|
"[-C|--cache <directory>] " +
|
|
68
|
+
"[-S|--status] " +
|
|
66
69
|
"[-e|--expression <expression>] " +
|
|
67
70
|
"[-f|--file <file>] " +
|
|
68
71
|
"[-c|--config <id>@<yaml-config-file>] " +
|
|
69
72
|
"[<argument> [...]]"
|
|
70
73
|
)
|
|
74
|
+
.version(false)
|
|
71
75
|
.option("V", {
|
|
72
76
|
alias: "version",
|
|
73
77
|
type: "boolean",
|
|
@@ -112,6 +116,14 @@ type wsPeerInfo = {
|
|
|
112
116
|
default: path.join(dataDir, "cache"),
|
|
113
117
|
describe: "directory for cached files (primarily AI model files)"
|
|
114
118
|
})
|
|
119
|
+
.option("S", {
|
|
120
|
+
alias: "status",
|
|
121
|
+
type: "boolean",
|
|
122
|
+
array: false,
|
|
123
|
+
coerce,
|
|
124
|
+
default: false,
|
|
125
|
+
describe: "show one-time status of nodes"
|
|
126
|
+
})
|
|
115
127
|
.option("e", {
|
|
116
128
|
alias: "expression",
|
|
117
129
|
type: "string",
|
|
@@ -142,7 +154,6 @@ type wsPeerInfo = {
|
|
|
142
154
|
.help("h", "show usage help")
|
|
143
155
|
.alias("h", "help")
|
|
144
156
|
.showHelpOnFail(true)
|
|
145
|
-
.version(false)
|
|
146
157
|
.strict()
|
|
147
158
|
.demand(0)
|
|
148
159
|
.parse(hideBin(process.argv))
|
|
@@ -223,6 +234,7 @@ type wsPeerInfo = {
|
|
|
223
234
|
"./speechflow-node-a2a-wav.js",
|
|
224
235
|
"./speechflow-node-a2a-mute.js",
|
|
225
236
|
"./speechflow-node-a2a-meter.js",
|
|
237
|
+
"./speechflow-node-a2a-gender.js",
|
|
226
238
|
"./speechflow-node-a2a-vad.js",
|
|
227
239
|
"./speechflow-node-a2t-deepgram.js",
|
|
228
240
|
"./speechflow-node-t2a-elevenlabs.js",
|
|
@@ -233,6 +245,7 @@ type wsPeerInfo = {
|
|
|
233
245
|
"./speechflow-node-t2t-transformers.js",
|
|
234
246
|
"./speechflow-node-t2t-subtitle.js",
|
|
235
247
|
"./speechflow-node-t2t-format.js",
|
|
248
|
+
"./speechflow-node-x2x-filter.js",
|
|
236
249
|
"./speechflow-node-x2x-trace.js",
|
|
237
250
|
"./speechflow-node-xio-device.js",
|
|
238
251
|
"./speechflow-node-xio-file.js",
|
|
@@ -268,6 +281,45 @@ type wsPeerInfo = {
|
|
|
268
281
|
}
|
|
269
282
|
}
|
|
270
283
|
|
|
284
|
+
/* static configuration */
|
|
285
|
+
const cfg = {
|
|
286
|
+
audioChannels: 1,
|
|
287
|
+
audioBitDepth: 16,
|
|
288
|
+
audioLittleEndian: true,
|
|
289
|
+
audioSampleRate: 48000,
|
|
290
|
+
textEncoding: "utf8",
|
|
291
|
+
cacheDir: args.C
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/* handle one-time status query of nodes */
|
|
295
|
+
if (args.S) {
|
|
296
|
+
const table = new Table({
|
|
297
|
+
head: [
|
|
298
|
+
chalk.reset.bold("NODE"),
|
|
299
|
+
chalk.reset.bold("PROPERTY"),
|
|
300
|
+
chalk.reset.bold("VALUE")
|
|
301
|
+
],
|
|
302
|
+
colWidths: [ 15, 15, 50 - (2 * 2 + 2 * 3) ],
|
|
303
|
+
style: { "padding-left": 1, "padding-right": 1, border: [ "grey" ], compact: true },
|
|
304
|
+
chars: { "left-mid": "", mid: "", "mid-mid": "", "right-mid": "" }
|
|
305
|
+
})
|
|
306
|
+
for (const name of Object.keys(nodes)) {
|
|
307
|
+
cli!.log("info", `gathering status of node <${name}>`)
|
|
308
|
+
const node = new nodes[name](name, cfg, {}, [])
|
|
309
|
+
const status = await node.status()
|
|
310
|
+
if (Object.keys(status).length > 0) {
|
|
311
|
+
let first = true
|
|
312
|
+
for (const key of Object.keys(status)) {
|
|
313
|
+
table.push([ first ? chalk.bold(name) : "", key, chalk.blue(status[key]) ])
|
|
314
|
+
first = false
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
const output = table.toString()
|
|
319
|
+
process.stdout.write(output + "\n")
|
|
320
|
+
process.exit(0)
|
|
321
|
+
}
|
|
322
|
+
|
|
271
323
|
/* graph processing: PASS 1: parse DSL and create and connect nodes */
|
|
272
324
|
const flowlink = new FlowLink<SpeechFlowNode>({
|
|
273
325
|
trace: (msg: string) => {
|
|
@@ -277,14 +329,6 @@ type wsPeerInfo = {
|
|
|
277
329
|
const variables = { argv: args._, env: process.env }
|
|
278
330
|
const graphNodes = new Set<SpeechFlowNode>()
|
|
279
331
|
const nodeNums = new Map<typeof SpeechFlowNode, number>()
|
|
280
|
-
const cfg = {
|
|
281
|
-
audioChannels: 1,
|
|
282
|
-
audioBitDepth: 16,
|
|
283
|
-
audioLittleEndian: true,
|
|
284
|
-
audioSampleRate: 48000,
|
|
285
|
-
textEncoding: "utf8",
|
|
286
|
-
cacheDir: args.C
|
|
287
|
-
}
|
|
288
332
|
let ast: unknown
|
|
289
333
|
try {
|
|
290
334
|
ast = flowlink.compile(config)
|
|
@@ -429,9 +473,22 @@ type wsPeerInfo = {
|
|
|
429
473
|
throw new Error(`stream of node "${node.id}" still not initialized`)
|
|
430
474
|
cli!.log("info", `observe stream of node "${node.id}" for finish event`)
|
|
431
475
|
activeNodes.add(node)
|
|
476
|
+
node.stream.on("end", () => {
|
|
477
|
+
if (activeNodes.has(node))
|
|
478
|
+
activeNodes.delete(node)
|
|
479
|
+
cli!.log("info", `readable stream of node "${node.id}" ended (${activeNodes.size} nodes remaining active)`)
|
|
480
|
+
if (activeNodes.size === 0) {
|
|
481
|
+
const timeFinished = DateTime.now()
|
|
482
|
+
const duration = timeFinished.diff(timeZero)
|
|
483
|
+
cli!.log("info", "everything finished -- stream processing in SpeechFlow graph stops " +
|
|
484
|
+
`(total duration: ${duration.toFormat("hh:mm:ss.SSS")})`)
|
|
485
|
+
finishEvents.emit("finished")
|
|
486
|
+
}
|
|
487
|
+
})
|
|
432
488
|
node.stream.on("finish", () => {
|
|
433
|
-
activeNodes.
|
|
434
|
-
|
|
489
|
+
if (activeNodes.has(node))
|
|
490
|
+
activeNodes.delete(node)
|
|
491
|
+
cli!.log("info", `writable stream of node "${node.id}" finished (${activeNodes.size} nodes remaining active)`)
|
|
435
492
|
if (activeNodes.size === 0) {
|
|
436
493
|
const timeFinished = DateTime.now()
|
|
437
494
|
const duration = timeFinished.diff(timeZero)
|
|
@@ -654,10 +711,14 @@ type wsPeerInfo = {
|
|
|
654
711
|
}
|
|
655
712
|
|
|
656
713
|
/* terminate process */
|
|
657
|
-
if (signal === "finished")
|
|
714
|
+
if (signal === "finished") {
|
|
715
|
+
cli!.log("info", "terminate process (exit code 0)")
|
|
658
716
|
process.exit(0)
|
|
659
|
-
|
|
717
|
+
}
|
|
718
|
+
else {
|
|
719
|
+
cli!.log("info", "terminate process (exit code 1)")
|
|
660
720
|
process.exit(1)
|
|
721
|
+
}
|
|
661
722
|
}
|
|
662
723
|
finishEvents.on("finished", () => {
|
|
663
724
|
shutdown("finished")
|
|
@@ -678,7 +739,7 @@ type wsPeerInfo = {
|
|
|
678
739
|
if (cli !== null)
|
|
679
740
|
cli.log("error", err.message)
|
|
680
741
|
else
|
|
681
|
-
process.stderr.write(`${pkg.name}: ERROR: ${err.message}\n`)
|
|
742
|
+
process.stderr.write(`${pkg.name}: ${chalk.red("ERROR")}: ${err.message} ${err.stack}\n`)
|
|
682
743
|
process.exit(1)
|
|
683
744
|
})
|
|
684
745
|
|