speechflow 1.7.1 → 2.0.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.
Files changed (99) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +387 -119
  3. package/etc/claude.md +5 -5
  4. package/etc/speechflow.yaml +2 -2
  5. package/package.json +3 -3
  6. package/speechflow-cli/dst/speechflow-main-graph.d.ts +1 -0
  7. package/speechflow-cli/dst/speechflow-main-graph.js +28 -5
  8. package/speechflow-cli/dst/speechflow-main-graph.js.map +1 -1
  9. package/speechflow-cli/dst/speechflow-node-a2a-wav.js +24 -4
  10. package/speechflow-cli/dst/speechflow-node-a2a-wav.js.map +1 -1
  11. package/speechflow-cli/dst/speechflow-node-a2t-google.d.ts +17 -0
  12. package/speechflow-cli/dst/speechflow-node-a2t-google.js +320 -0
  13. package/speechflow-cli/dst/speechflow-node-a2t-google.js.map +1 -0
  14. package/speechflow-cli/dst/speechflow-node-t2a-google.d.ts +15 -0
  15. package/speechflow-cli/dst/speechflow-node-t2a-google.js +218 -0
  16. package/speechflow-cli/dst/speechflow-node-t2a-google.js.map +1 -0
  17. package/speechflow-cli/dst/speechflow-node-t2a-openai.d.ts +15 -0
  18. package/speechflow-cli/dst/speechflow-node-t2a-openai.js +195 -0
  19. package/speechflow-cli/dst/speechflow-node-t2a-openai.js.map +1 -0
  20. package/speechflow-cli/dst/speechflow-node-t2a-supertonic.d.ts +17 -0
  21. package/speechflow-cli/dst/speechflow-node-t2a-supertonic.js +608 -0
  22. package/speechflow-cli/dst/speechflow-node-t2a-supertonic.js.map +1 -0
  23. package/speechflow-cli/dst/speechflow-node-t2t-amazon.js.map +1 -1
  24. package/speechflow-cli/dst/{speechflow-node-t2t-transformers.d.ts → speechflow-node-t2t-opus.d.ts} +1 -3
  25. package/speechflow-cli/dst/speechflow-node-t2t-opus.js +159 -0
  26. package/speechflow-cli/dst/speechflow-node-t2t-opus.js.map +1 -0
  27. package/speechflow-cli/dst/speechflow-node-t2t-profanity.d.ts +11 -0
  28. package/speechflow-cli/dst/speechflow-node-t2t-profanity.js +118 -0
  29. package/speechflow-cli/dst/speechflow-node-t2t-profanity.js.map +1 -0
  30. package/speechflow-cli/dst/speechflow-node-t2t-punctuation.d.ts +13 -0
  31. package/speechflow-cli/dst/speechflow-node-t2t-punctuation.js +220 -0
  32. package/speechflow-cli/dst/speechflow-node-t2t-punctuation.js.map +1 -0
  33. package/speechflow-cli/dst/{speechflow-node-t2t-openai.d.ts → speechflow-node-t2t-spellcheck.d.ts} +2 -2
  34. package/speechflow-cli/dst/{speechflow-node-t2t-openai.js → speechflow-node-t2t-spellcheck.js} +47 -99
  35. package/speechflow-cli/dst/speechflow-node-t2t-spellcheck.js.map +1 -0
  36. package/speechflow-cli/dst/speechflow-node-t2t-subtitle.js +3 -6
  37. package/speechflow-cli/dst/speechflow-node-t2t-subtitle.js.map +1 -1
  38. package/speechflow-cli/dst/speechflow-node-t2t-summary.d.ts +16 -0
  39. package/speechflow-cli/dst/speechflow-node-t2t-summary.js +241 -0
  40. package/speechflow-cli/dst/speechflow-node-t2t-summary.js.map +1 -0
  41. package/speechflow-cli/dst/{speechflow-node-t2t-ollama.d.ts → speechflow-node-t2t-translate.d.ts} +2 -2
  42. package/speechflow-cli/dst/{speechflow-node-t2t-transformers.js → speechflow-node-t2t-translate.js} +53 -115
  43. package/speechflow-cli/dst/speechflow-node-t2t-translate.js.map +1 -0
  44. package/speechflow-cli/dst/speechflow-node-xio-exec.d.ts +12 -0
  45. package/speechflow-cli/dst/speechflow-node-xio-exec.js +223 -0
  46. package/speechflow-cli/dst/speechflow-node-xio-exec.js.map +1 -0
  47. package/speechflow-cli/dst/speechflow-node-xio-file.d.ts +1 -0
  48. package/speechflow-cli/dst/speechflow-node-xio-file.js +79 -66
  49. package/speechflow-cli/dst/speechflow-node-xio-file.js.map +1 -1
  50. package/speechflow-cli/dst/speechflow-node-xio-vban.d.ts +17 -0
  51. package/speechflow-cli/dst/speechflow-node-xio-vban.js +330 -0
  52. package/speechflow-cli/dst/speechflow-node-xio-vban.js.map +1 -0
  53. package/speechflow-cli/dst/speechflow-node-xio-webrtc.d.ts +39 -0
  54. package/speechflow-cli/dst/speechflow-node-xio-webrtc.js +500 -0
  55. package/speechflow-cli/dst/speechflow-node-xio-webrtc.js.map +1 -0
  56. package/speechflow-cli/dst/speechflow-util-audio.js +4 -5
  57. package/speechflow-cli/dst/speechflow-util-audio.js.map +1 -1
  58. package/speechflow-cli/dst/speechflow-util-error.d.ts +1 -0
  59. package/speechflow-cli/dst/speechflow-util-error.js +5 -0
  60. package/speechflow-cli/dst/speechflow-util-error.js.map +1 -1
  61. package/speechflow-cli/dst/speechflow-util-llm.d.ts +35 -0
  62. package/speechflow-cli/dst/speechflow-util-llm.js +363 -0
  63. package/speechflow-cli/dst/speechflow-util-llm.js.map +1 -0
  64. package/speechflow-cli/dst/speechflow-util.d.ts +1 -0
  65. package/speechflow-cli/dst/speechflow-util.js +1 -0
  66. package/speechflow-cli/dst/speechflow-util.js.map +1 -1
  67. package/speechflow-cli/etc/oxlint.jsonc +2 -1
  68. package/speechflow-cli/package.json +34 -17
  69. package/speechflow-cli/src/lib.d.ts +5 -0
  70. package/speechflow-cli/src/speechflow-main-graph.ts +31 -5
  71. package/speechflow-cli/src/speechflow-node-a2a-wav.ts +24 -4
  72. package/speechflow-cli/src/speechflow-node-a2t-google.ts +322 -0
  73. package/speechflow-cli/src/speechflow-node-t2a-google.ts +206 -0
  74. package/speechflow-cli/src/speechflow-node-t2a-openai.ts +179 -0
  75. package/speechflow-cli/src/speechflow-node-t2a-supertonic.ts +701 -0
  76. package/speechflow-cli/src/speechflow-node-t2t-amazon.ts +2 -1
  77. package/speechflow-cli/src/speechflow-node-t2t-opus.ts +136 -0
  78. package/speechflow-cli/src/speechflow-node-t2t-profanity.ts +93 -0
  79. package/speechflow-cli/src/speechflow-node-t2t-punctuation.ts +201 -0
  80. package/speechflow-cli/src/{speechflow-node-t2t-openai.ts → speechflow-node-t2t-spellcheck.ts} +48 -107
  81. package/speechflow-cli/src/speechflow-node-t2t-subtitle.ts +3 -6
  82. package/speechflow-cli/src/speechflow-node-t2t-summary.ts +229 -0
  83. package/speechflow-cli/src/speechflow-node-t2t-translate.ts +181 -0
  84. package/speechflow-cli/src/speechflow-node-xio-exec.ts +210 -0
  85. package/speechflow-cli/src/speechflow-node-xio-file.ts +92 -79
  86. package/speechflow-cli/src/speechflow-node-xio-vban.ts +325 -0
  87. package/speechflow-cli/src/speechflow-node-xio-webrtc.ts +533 -0
  88. package/speechflow-cli/src/speechflow-util-audio.ts +5 -5
  89. package/speechflow-cli/src/speechflow-util-error.ts +9 -0
  90. package/speechflow-cli/src/speechflow-util-llm.ts +367 -0
  91. package/speechflow-cli/src/speechflow-util.ts +1 -0
  92. package/speechflow-ui-db/package.json +9 -9
  93. package/speechflow-ui-st/package.json +9 -9
  94. package/speechflow-cli/dst/speechflow-node-t2t-ollama.js +0 -293
  95. package/speechflow-cli/dst/speechflow-node-t2t-ollama.js.map +0 -1
  96. package/speechflow-cli/dst/speechflow-node-t2t-openai.js.map +0 -1
  97. package/speechflow-cli/dst/speechflow-node-t2t-transformers.js.map +0 -1
  98. package/speechflow-cli/src/speechflow-node-t2t-ollama.ts +0 -281
  99. package/speechflow-cli/src/speechflow-node-t2t-transformers.ts +0 -247
@@ -0,0 +1,179 @@
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
+ /* external dependencies */
11
+ import OpenAI from "openai"
12
+ import { Duration } from "luxon"
13
+ import SpeexResampler from "speex-resampler"
14
+
15
+ /* internal dependencies */
16
+ import SpeechFlowNode, { SpeechFlowChunk } from "./speechflow-node"
17
+ import * as util from "./speechflow-util"
18
+
19
+ /* SpeechFlow node for OpenAI text-to-speech conversion */
20
+ export default class SpeechFlowNodeT2AOpenAI extends SpeechFlowNode {
21
+ /* declare official node name */
22
+ public static name = "t2a-openai"
23
+
24
+ /* internal state */
25
+ private openai: OpenAI | null = null
26
+ private resampler: SpeexResampler | null = null
27
+ private closing = false
28
+
29
+ /* construct node */
30
+ constructor (id: string, cfg: { [ id: string ]: any }, opts: { [ id: string ]: any }, args: any[]) {
31
+ super(id, cfg, opts, args)
32
+
33
+ /* declare node configuration parameters */
34
+ this.configure({
35
+ key: { type: "string", val: process.env.SPEECHFLOW_OPENAI_KEY },
36
+ api: { type: "string", val: "https://api.openai.com/v1", match: /^https?:\/\/.+/ },
37
+ voice: { type: "string", val: "alloy", pos: 0, match: /^(?:alloy|echo|fable|onyx|nova|shimmer)$/ },
38
+ model: { type: "string", val: "tts-1", pos: 1, match: /^(?:tts-1|tts-1-hd)$/ },
39
+ speed: { type: "number", val: 1.0, pos: 2, match: (n: number) => n >= 0.25 && n <= 4.0 }
40
+ })
41
+
42
+ /* sanity check parameters */
43
+ if (!this.params.key)
44
+ throw new Error("OpenAI API key not configured")
45
+
46
+ /* declare node input/output format */
47
+ this.input = "text"
48
+ this.output = "audio"
49
+ }
50
+
51
+ /* one-time status of node */
52
+ async status () {
53
+ return {}
54
+ }
55
+
56
+ /* open node */
57
+ async open () {
58
+ /* clear destruction flag */
59
+ this.closing = false
60
+
61
+ /* establish OpenAI API connection */
62
+ this.openai = new OpenAI({
63
+ baseURL: this.params.api,
64
+ apiKey: this.params.key,
65
+ timeout: 60000
66
+ })
67
+
68
+ /* establish resampler from OpenAI's 24Khz PCM output
69
+ to our standard audio sample rate (48KHz) */
70
+ this.resampler = new SpeexResampler(1, 24000, this.config.audioSampleRate, 7)
71
+
72
+ /* perform text-to-speech operation with OpenAI API */
73
+ const textToSpeech = async (text: string) => {
74
+ this.log("info", `OpenAI TTS: send text "${text}"`)
75
+ const response = await this.openai!.audio.speech.create({
76
+ model: this.params.model,
77
+ voice: this.params.voice,
78
+ input: text,
79
+ response_format: "pcm",
80
+ speed: this.params.speed
81
+ })
82
+
83
+ /* convert response to buffer (PCM 24kHz, 16-bit, little-endian) */
84
+ const arrayBuffer = await response.arrayBuffer()
85
+ const buffer = Buffer.from(arrayBuffer)
86
+ this.log("info", `OpenAI TTS: received audio (buffer length: ${buffer.byteLength})`)
87
+
88
+ /* resample from 24kHz to 48kHz */
89
+ const bufferResampled = this.resampler!.processChunk(buffer)
90
+ this.log("info", `OpenAI TTS: forwarding resampled audio (buffer length: ${bufferResampled.byteLength})`)
91
+ return bufferResampled
92
+ }
93
+
94
+ /* create transform stream and connect it to the OpenAI API */
95
+ const self = this
96
+ this.stream = new Stream.Transform({
97
+ writableObjectMode: true,
98
+ readableObjectMode: true,
99
+ decodeStrings: false,
100
+ highWaterMark: 1,
101
+ async transform (chunk: SpeechFlowChunk, encoding, callback) {
102
+ if (self.closing)
103
+ callback(new Error("stream already destroyed"))
104
+ else if (Buffer.isBuffer(chunk.payload))
105
+ callback(new Error("invalid chunk payload type"))
106
+ else if (chunk.payload === "") {
107
+ /* pass through empty chunks */
108
+ this.push(chunk)
109
+ callback()
110
+ }
111
+ else {
112
+ let processTimeout: ReturnType<typeof setTimeout> | null = setTimeout(() => {
113
+ processTimeout = null
114
+ callback(new Error("OpenAI TTS API timeout"))
115
+ }, 60 * 1000)
116
+ const clearProcessTimeout = () => {
117
+ if (processTimeout !== null) {
118
+ clearTimeout(processTimeout)
119
+ processTimeout = null
120
+ }
121
+ }
122
+ try {
123
+ if (self.closing) {
124
+ clearProcessTimeout()
125
+ callback(new Error("stream destroyed during processing"))
126
+ return
127
+ }
128
+ const buffer = await textToSpeech(chunk.payload as string)
129
+ if (self.closing) {
130
+ clearProcessTimeout()
131
+ callback(new Error("stream destroyed during processing"))
132
+ return
133
+ }
134
+
135
+ /* calculate actual audio duration from PCM buffer size */
136
+ const durationMs = util.audioBufferDuration(buffer,
137
+ self.config.audioSampleRate, self.config.audioBitDepth) * 1000
138
+
139
+ /* create new chunk with recalculated timestamps */
140
+ const chunkNew = chunk.clone()
141
+ chunkNew.type = "audio"
142
+ chunkNew.payload = buffer
143
+ chunkNew.timestampEnd = Duration.fromMillis(chunkNew.timestampStart.toMillis() + durationMs)
144
+ clearProcessTimeout()
145
+ this.push(chunkNew)
146
+ callback()
147
+ }
148
+ catch (error) {
149
+ clearProcessTimeout()
150
+ callback(util.ensureError(error, "OpenAI TTS processing failed"))
151
+ }
152
+ }
153
+ },
154
+ final (callback) {
155
+ callback()
156
+ }
157
+ })
158
+ }
159
+
160
+ /* close node */
161
+ async close () {
162
+ /* indicate closing */
163
+ this.closing = true
164
+
165
+ /* shutdown stream */
166
+ if (this.stream !== null) {
167
+ await util.destroyStream(this.stream)
168
+ this.stream = null
169
+ }
170
+
171
+ /* destroy resampler */
172
+ if (this.resampler !== null)
173
+ this.resampler = null
174
+
175
+ /* destroy OpenAI API */
176
+ if (this.openai !== null)
177
+ this.openai = null
178
+ }
179
+ }