speechflow 2.3.0 → 2.4.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 (113) hide show
  1. package/.ase/service.log +10272 -0
  2. package/.claude/CLAUDE.md +1 -0
  3. package/AGENTS.md +1 -1
  4. package/CHANGELOG.md +38 -0
  5. package/README.md +10 -37
  6. package/package.json +7 -7
  7. package/speechflow-cli/dst/speechflow-main-graph.js +14 -5
  8. package/speechflow-cli/dst/speechflow-main-graph.js.map +1 -1
  9. package/speechflow-cli/dst/speechflow-node-a2a-compressor-wt.js +10 -9
  10. package/speechflow-cli/dst/speechflow-node-a2a-compressor-wt.js.map +1 -1
  11. package/speechflow-cli/dst/speechflow-node-a2a-compressor.js +17 -2
  12. package/speechflow-cli/dst/speechflow-node-a2a-compressor.js.map +1 -1
  13. package/speechflow-cli/dst/speechflow-node-a2a-expander-wt.js +7 -8
  14. package/speechflow-cli/dst/speechflow-node-a2a-expander-wt.js.map +1 -1
  15. package/speechflow-cli/dst/speechflow-node-a2a-ffmpeg.js +5 -5
  16. package/speechflow-cli/dst/speechflow-node-a2a-ffmpeg.js.map +1 -1
  17. package/speechflow-cli/dst/speechflow-node-a2a-filler.js +5 -0
  18. package/speechflow-cli/dst/speechflow-node-a2a-filler.js.map +1 -1
  19. package/speechflow-cli/dst/speechflow-node-a2a-gender.js +1 -4
  20. package/speechflow-cli/dst/speechflow-node-a2a-gender.js.map +1 -1
  21. package/speechflow-cli/dst/speechflow-node-a2a-meter.js +1 -1
  22. package/speechflow-cli/dst/speechflow-node-a2a-meter.js.map +1 -1
  23. package/speechflow-cli/dst/speechflow-node-a2t-amazon.js +8 -3
  24. package/speechflow-cli/dst/speechflow-node-a2t-amazon.js.map +1 -1
  25. package/speechflow-cli/dst/speechflow-node-a2t-deepgram.d.ts +7 -0
  26. package/speechflow-cli/dst/speechflow-node-a2t-deepgram.js +145 -62
  27. package/speechflow-cli/dst/speechflow-node-a2t-deepgram.js.map +1 -1
  28. package/speechflow-cli/dst/speechflow-node-a2t-google.d.ts +1 -0
  29. package/speechflow-cli/dst/speechflow-node-a2t-google.js +16 -5
  30. package/speechflow-cli/dst/speechflow-node-a2t-google.js.map +1 -1
  31. package/speechflow-cli/dst/speechflow-node-a2t-openai.js +10 -4
  32. package/speechflow-cli/dst/speechflow-node-a2t-openai.js.map +1 -1
  33. package/speechflow-cli/dst/speechflow-node-t2a-kokoro.js +3 -1
  34. package/speechflow-cli/dst/speechflow-node-t2a-kokoro.js.map +1 -1
  35. package/speechflow-cli/dst/speechflow-node-t2a-supertonic.js +1 -0
  36. package/speechflow-cli/dst/speechflow-node-t2a-supertonic.js.map +1 -1
  37. package/speechflow-cli/dst/speechflow-node-t2t-opus.js +9 -1
  38. package/speechflow-cli/dst/speechflow-node-t2t-opus.js.map +1 -1
  39. package/speechflow-cli/dst/{speechflow-node-t2t-spellcheck.d.ts → speechflow-node-t2t-proofread.d.ts} +1 -1
  40. package/speechflow-cli/dst/{speechflow-node-t2t-spellcheck.js → speechflow-node-t2t-proofread.js} +38 -54
  41. package/speechflow-cli/dst/speechflow-node-t2t-proofread.js.map +1 -0
  42. package/speechflow-cli/dst/speechflow-node-t2t-sentence.d.ts +3 -0
  43. package/speechflow-cli/dst/speechflow-node-t2t-sentence.js +195 -59
  44. package/speechflow-cli/dst/speechflow-node-t2t-sentence.js.map +1 -1
  45. package/speechflow-cli/dst/speechflow-node-t2t-summary.js +1 -1
  46. package/speechflow-cli/dst/speechflow-node-t2t-summary.js.map +1 -1
  47. package/speechflow-cli/dst/speechflow-node-t2t-translate.js +1 -1
  48. package/speechflow-cli/dst/speechflow-node-t2t-translate.js.map +1 -1
  49. package/speechflow-cli/dst/speechflow-node-xio-device.js +4 -1
  50. package/speechflow-cli/dst/speechflow-node-xio-device.js.map +1 -1
  51. package/speechflow-cli/dst/speechflow-node-xio-exec.js +8 -5
  52. package/speechflow-cli/dst/speechflow-node-xio-exec.js.map +1 -1
  53. package/speechflow-cli/dst/speechflow-node-xio-webrtc.d.ts +1 -0
  54. package/speechflow-cli/dst/speechflow-node-xio-webrtc.js +6 -1
  55. package/speechflow-cli/dst/speechflow-node-xio-webrtc.js.map +1 -1
  56. package/speechflow-cli/dst/speechflow-node-xio-websocket.js +14 -4
  57. package/speechflow-cli/dst/speechflow-node-xio-websocket.js.map +1 -1
  58. package/speechflow-cli/dst/speechflow-util-audio.js +11 -9
  59. package/speechflow-cli/dst/speechflow-util-audio.js.map +1 -1
  60. package/speechflow-cli/dst/speechflow-util-llm.d.ts +1 -0
  61. package/speechflow-cli/dst/speechflow-util-llm.js +18 -4
  62. package/speechflow-cli/dst/speechflow-util-llm.js.map +1 -1
  63. package/speechflow-cli/dst/speechflow-util-misc.js +28 -14
  64. package/speechflow-cli/dst/speechflow-util-misc.js.map +1 -1
  65. package/speechflow-cli/dst/speechflow-util-queue.d.ts +4 -0
  66. package/speechflow-cli/dst/speechflow-util-queue.js +41 -0
  67. package/speechflow-cli/dst/speechflow-util-queue.js.map +1 -1
  68. package/speechflow-cli/dst/speechflow.js.map +1 -1
  69. package/speechflow-cli/etc/oxlint.jsonc +9 -2
  70. package/speechflow-cli/package.d/{@typescript-eslint+typescript-estree+8.57.2.patch → @typescript-eslint+typescript-estree+8.59.3.patch} +1 -1
  71. package/speechflow-cli/package.json +30 -30
  72. package/speechflow-cli/src/speechflow-main-graph.ts +14 -5
  73. package/speechflow-cli/src/speechflow-node-a2a-compressor-wt.ts +12 -9
  74. package/speechflow-cli/src/speechflow-node-a2a-compressor.ts +23 -7
  75. package/speechflow-cli/src/speechflow-node-a2a-expander-wt.ts +7 -8
  76. package/speechflow-cli/src/speechflow-node-a2a-ffmpeg.ts +6 -6
  77. package/speechflow-cli/src/speechflow-node-a2a-filler.ts +6 -0
  78. package/speechflow-cli/src/speechflow-node-a2a-gender.ts +3 -6
  79. package/speechflow-cli/src/speechflow-node-a2a-meter.ts +1 -1
  80. package/speechflow-cli/src/speechflow-node-a2t-amazon.ts +12 -3
  81. package/speechflow-cli/src/speechflow-node-a2t-deepgram.ts +156 -66
  82. package/speechflow-cli/src/speechflow-node-a2t-google.ts +18 -5
  83. package/speechflow-cli/src/speechflow-node-a2t-openai.ts +10 -4
  84. package/speechflow-cli/src/speechflow-node-t2a-kokoro.ts +3 -1
  85. package/speechflow-cli/src/speechflow-node-t2a-supertonic.ts +1 -0
  86. package/speechflow-cli/src/speechflow-node-t2t-opus.ts +9 -1
  87. package/speechflow-cli/src/{speechflow-node-t2t-spellcheck.ts → speechflow-node-t2t-proofread.ts} +46 -59
  88. package/speechflow-cli/src/speechflow-node-t2t-sentence.ts +214 -61
  89. package/speechflow-cli/src/speechflow-node-t2t-summary.ts +1 -1
  90. package/speechflow-cli/src/speechflow-node-t2t-translate.ts +1 -1
  91. package/speechflow-cli/src/speechflow-node-xio-device.ts +4 -1
  92. package/speechflow-cli/src/speechflow-node-xio-exec.ts +9 -6
  93. package/speechflow-cli/src/speechflow-node-xio-webrtc.ts +7 -1
  94. package/speechflow-cli/src/speechflow-node-xio-websocket.ts +14 -4
  95. package/speechflow-cli/src/speechflow-util-audio.ts +12 -10
  96. package/speechflow-cli/src/speechflow-util-llm.ts +20 -4
  97. package/speechflow-cli/src/speechflow-util-misc.ts +33 -16
  98. package/speechflow-cli/src/speechflow-util-queue.ts +43 -0
  99. package/speechflow-cli/src/speechflow.ts +1 -0
  100. package/speechflow-ui-db/dst/index.js +19 -16
  101. package/speechflow-ui-db/etc/oxlint.jsonc +0 -1
  102. package/speechflow-ui-db/package.d/{@typescript-eslint+typescript-estree+8.57.2.patch → @typescript-eslint+typescript-estree+8.59.3.patch} +1 -1
  103. package/speechflow-ui-db/package.json +15 -15
  104. package/speechflow-ui-st/dst/index.js +33 -33
  105. package/speechflow-ui-st/etc/oxlint.jsonc +2 -2
  106. package/speechflow-ui-st/package.d/{@typescript-eslint+typescript-estree+8.57.2.patch → @typescript-eslint+typescript-estree+8.59.3.patch} +1 -1
  107. package/speechflow-ui-st/package.json +15 -15
  108. package/.claude/settings.local.json +0 -3
  109. package/speechflow-cli/dst/speechflow-node-t2t-punctuation.d.ts +0 -13
  110. package/speechflow-cli/dst/speechflow-node-t2t-punctuation.js +0 -219
  111. package/speechflow-cli/dst/speechflow-node-t2t-punctuation.js.map +0 -1
  112. package/speechflow-cli/dst/speechflow-node-t2t-spellcheck.js.map +0 -1
  113. package/speechflow-cli/src/speechflow-node-t2t-punctuation.ts +0 -200
@@ -7,6 +7,9 @@
7
7
  /* standard dependencies */
8
8
  import EventEmitter from "node:events"
9
9
 
10
+ /* internal dependencies */
11
+ import * as util from "./speechflow-util-misc"
12
+
10
13
  /* external dependencies */
11
14
  import OpenAI from "openai"
12
15
  import Anthropic from "@anthropic-ai/sdk"
@@ -28,6 +31,7 @@ export type LLMConfig = {
28
31
  temperature?: number
29
32
  maxTokens?: number
30
33
  cacheDir?: string
34
+ thinking?: boolean
31
35
  }
32
36
  export type LLMCompleteOptions = {
33
37
  system?: string
@@ -61,6 +65,7 @@ export class LLM extends EventEmitter {
61
65
  temperature: 0.7,
62
66
  maxTokens: 1024,
63
67
  cacheDir: "",
68
+ thinking: false,
64
69
  ...config
65
70
  } as Required<LLMConfig>
66
71
 
@@ -252,7 +257,8 @@ export class LLM extends EventEmitter {
252
257
  max_tokens: this.config.maxTokens,
253
258
  temperature: this.config.temperature,
254
259
  system: systemMessage?.content,
255
- messages: chatMessages as Anthropic.MessageParam[]
260
+ messages: chatMessages as Anthropic.MessageParam[],
261
+ ...(!this.config.thinking ? { thinking: { type: "disabled" } } : {})
256
262
  }).catch((err) => {
257
263
  throw new Error(`failed to perform Anthropic chat completion: ${err}`, { cause: err })
258
264
  })
@@ -281,7 +287,8 @@ export class LLM extends EventEmitter {
281
287
  config: {
282
288
  maxOutputTokens: this.config.maxTokens,
283
289
  temperature: this.config.temperature,
284
- ...(systemInstruction ? { systemInstruction } : {})
290
+ ...(systemInstruction ? { systemInstruction } : {}),
291
+ ...(!this.config.thinking ? { thinkingConfig: { thinkingBudget: 0 } } : {})
285
292
  }
286
293
  }).catch((err) => {
287
294
  throw new Error(`failed to perform Google chat completion: ${err}`, { cause: err })
@@ -300,6 +307,7 @@ export class LLM extends EventEmitter {
300
307
  model: this.config.model,
301
308
  messages,
302
309
  keep_alive: "10m",
310
+ think: this.config.thinking,
303
311
  options: {
304
312
  num_predict: this.config.maxTokens,
305
313
  temperature: this.config.temperature
@@ -353,8 +361,16 @@ export class LLM extends EventEmitter {
353
361
  this.ollama?.abort()
354
362
  this.ollama = null
355
363
  }
356
- else if (this.config.provider === "transformers") {
357
- this.transformer?.dispose()
364
+ else if (this.config.provider === "transformers" && this.transformer !== null) {
365
+ const ac = new AbortController()
366
+ await Promise.race([
367
+ this.transformer.dispose(),
368
+ util.timeout(5000, "transformer dispose timeout", ac.signal)
369
+ ]).finally(() => {
370
+ ac.abort()
371
+ }).catch((error) => {
372
+ this.log("warning", `error during transformer cleanup: ${error}`)
373
+ })
358
374
  this.transformer = null
359
375
  }
360
376
  this.initialized = false
@@ -46,35 +46,52 @@ export const deepClone = (value: any): any => {
46
46
  }
47
47
 
48
48
  /* sleep: wait a duration of time and then resolve */
49
- export function sleep (durationMs: number, signal?: AbortSignal) {
49
+ export function sleep (durationMs: number, signal?: AbortSignal): Promise<void> {
50
50
  return new Promise<void>((resolve) => {
51
- const ac = new AbortController()
52
- const timer = setTimeout(() => {
53
- ac.abort()
51
+ const ac: AbortController | undefined =
52
+ signal !== undefined ? new AbortController() : undefined
53
+ let timer: ReturnType<typeof setTimeout> | null = setTimeout(() => {
54
+ timer = null
55
+ if (signal !== undefined)
56
+ ac!.abort()
54
57
  resolve()
55
58
  }, durationMs)
56
59
  timer.unref()
57
- if (signal !== undefined)
58
- signal.addEventListener("abort", () => {
59
- clearTimeout(timer)
60
+ if (signal !== undefined) {
61
+ if (signal.aborted)
60
62
  resolve()
61
- }, { once: true, signal: ac.signal })
63
+ else
64
+ signal.addEventListener("abort", () => {
65
+ if (timer !== null)
66
+ clearTimeout(timer)
67
+ resolve()
68
+ }, { once: true, signal: ac!.signal })
69
+ }
62
70
  })
63
71
  }
64
72
 
65
73
  /* timeout: wait a duration of time and then reject */
66
- export function timeout (durationMs: number, info = "timeout", signal?: AbortSignal) {
74
+ export function timeout (durationMs: number, info = "timeout", signal?: AbortSignal): Promise<never> {
67
75
  return new Promise<never>((resolve, reject) => {
68
- const ac = new AbortController()
69
- const timer = setTimeout(() => {
70
- ac.abort()
76
+ const ac: AbortController | undefined =
77
+ signal !== undefined ? new AbortController() : undefined
78
+ let timer: ReturnType<typeof setTimeout> | null = setTimeout(() => {
79
+ timer = null
80
+ if (signal !== undefined)
81
+ ac!.abort()
71
82
  reject(new Error(info))
72
83
  }, durationMs)
73
84
  timer.unref()
74
- if (signal !== undefined)
75
- signal.addEventListener("abort", () => {
76
- clearTimeout(timer)
85
+ if (signal !== undefined) {
86
+ if (signal.aborted)
77
87
  resolve(undefined as never)
78
- }, { once: true, signal: ac.signal })
88
+ else
89
+ signal.addEventListener("abort", () => {
90
+ if (timer !== null)
91
+ clearTimeout(timer)
92
+ resolve(undefined as never)
93
+ }, { once: true, signal: ac!.signal })
94
+ }
79
95
  })
80
96
  }
97
+
@@ -48,6 +48,15 @@ export class QueuePointer<T extends QueueElement> extends EventEmitter {
48
48
  silent (silence: boolean) {
49
49
  this.silence = silence
50
50
  }
51
+ silently<R> (fn: () => R): R {
52
+ this.silence = true
53
+ try {
54
+ return fn()
55
+ }
56
+ finally {
57
+ this.silence = false
58
+ }
59
+ }
51
60
 
52
61
  /* notify about operation */
53
62
  notify (event: string, info: any) {
@@ -171,12 +180,14 @@ export class QueuePointer<T extends QueueElement> extends EventEmitter {
171
180
  }
172
181
  insert (element: T) {
173
182
  this.queue.elements.splice(this.index, 0, element)
183
+ this.queue.adjustPointers(this, this.index, "insert")
174
184
  this.queue.notify("write", { start: this.index, end: this.index, op: "insert" })
175
185
  }
176
186
  delete () {
177
187
  if (this.index >= this.queue.elements.length)
178
188
  throw new Error("cannot delete after last element")
179
189
  this.queue.elements.splice(this.index, 1)
190
+ this.queue.adjustPointers(this, this.index, "delete")
180
191
  this.queue.notify("write", { start: this.index, end: this.index, op: "delete" })
181
192
  }
182
193
  }
@@ -193,6 +204,15 @@ export class Queue<T extends QueueElement> extends EventEmitter {
193
204
  silent (silence: boolean) {
194
205
  this.silence = silence
195
206
  }
207
+ silently<R> (fn: () => R): R {
208
+ this.silence = true
209
+ try {
210
+ return fn()
211
+ }
212
+ finally {
213
+ this.silence = false
214
+ }
215
+ }
196
216
  notify (event: string, info: any) {
197
217
  if (!this.silence)
198
218
  this.emit(event, info)
@@ -207,6 +227,24 @@ export class Queue<T extends QueueElement> extends EventEmitter {
207
227
  throw new Error("pointer does not exist")
208
228
  this.pointers.delete(name)
209
229
  }
230
+
231
+ /* adjust all sibling pointer positions (after insert/delete splice)
232
+ NOTICE: for insert, pointers AT the index are shifted forward to preserve
233
+ their pointer-to-element binding; for delete, pointers AT the index are
234
+ intentionally NOT adjusted, causing them to silently advance to the next
235
+ element (which shifted into the deleted position). */
236
+ adjustPointers (exclude: QueuePointer<T>, index: number, op: "insert" | "delete"): void {
237
+ for (const pointer of this.pointers.values()) {
238
+ if (pointer === exclude)
239
+ continue
240
+ const pos = pointer.position()
241
+ if (op === "insert" && pos >= index)
242
+ pointer.position(pos + 1)
243
+ else if (op === "delete" && pos > index)
244
+ pointer.position(pos - 1)
245
+ }
246
+ }
247
+
210
248
  trim (): void {
211
249
  /* determine minimum pointer position */
212
250
  let min = this.elements.length
@@ -227,6 +265,11 @@ export class Queue<T extends QueueElement> extends EventEmitter {
227
265
  this.notify("write", { start: 0, end: min, op: "trim" })
228
266
  }
229
267
  }
268
+ clear (): void {
269
+ this.elements.length = 0
270
+ for (const pointer of this.pointers.values())
271
+ pointer.position(0)
272
+ }
230
273
  }
231
274
 
232
275
  /* meta store */
@@ -7,5 +7,6 @@
7
7
 
8
8
  /* pass-through control to the main module */
9
9
  import Main from "./speechflow-main"
10
+
10
11
  Main.main()
11
12