speechflow 2.1.1 → 2.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 +14 -0
- package/package.json +2 -2
- package/speechflow-cli/dst/speechflow-main-api.d.ts +1 -0
- package/speechflow-cli/dst/speechflow-main-api.js +13 -0
- package/speechflow-cli/dst/speechflow-main-api.js.map +1 -1
- package/speechflow-cli/dst/speechflow-main-graph.js +10 -3
- package/speechflow-cli/dst/speechflow-main-graph.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-vad.js +1 -1
- package/speechflow-cli/dst/speechflow-node-a2a-vad.js.map +1 -1
- package/speechflow-cli/dst/speechflow-node-xio-webrtc.js +2 -2
- package/speechflow-cli/dst/speechflow-node-xio-webrtc.js.map +1 -1
- package/speechflow-cli/dst/speechflow-util-stream.d.ts +1 -1
- package/speechflow-cli/dst/speechflow-util-stream.js +15 -15
- package/speechflow-cli/dst/speechflow-util-stream.js.map +1 -1
- package/speechflow-cli/etc/oxlint.jsonc +7 -3
- package/speechflow-cli/etc/stx.conf +1 -0
- package/speechflow-cli/package.json +15 -15
- package/speechflow-cli/src/speechflow-main-api.ts +14 -0
- package/speechflow-cli/src/speechflow-main-graph.ts +10 -3
- package/speechflow-cli/src/speechflow-node-a2a-vad.ts +1 -1
- package/speechflow-cli/src/speechflow-node-xio-webrtc.ts +4 -4
- package/speechflow-cli/src/speechflow-util-stream.ts +15 -15
- package/speechflow-ui-db/dst/index.css +1 -1
- package/speechflow-ui-db/dst/index.js +15 -15
- package/speechflow-ui-db/package.json +7 -6
- package/speechflow-ui-db/src/app.vue +163 -35
- package/speechflow-ui-st/dst/index.css +1 -1
- package/speechflow-ui-st/dst/index.js +28 -28
- package/speechflow-ui-st/etc/oxlint.jsonc +2 -1
- package/speechflow-ui-st/package.json +5 -5
- package/speechflow-ui-st/src/app.vue +48 -17
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
"typopro-web": "4.2.8",
|
|
22
22
|
"@fortawesome/fontawesome-free": "7.2.0",
|
|
23
23
|
"patch-package": "8.0.1",
|
|
24
|
-
"@rse/stx": "1.1.4"
|
|
24
|
+
"@rse/stx": "1.1.4",
|
|
25
|
+
"animejs": "4.3.6"
|
|
25
26
|
},
|
|
26
27
|
"devDependencies": {
|
|
27
28
|
"vite": "7.3.1",
|
|
@@ -37,20 +38,20 @@
|
|
|
37
38
|
|
|
38
39
|
"@vue/eslint-config-typescript": "14.7.0",
|
|
39
40
|
"vue-eslint-parser": "10.4.0",
|
|
40
|
-
"eslint": "10.0.
|
|
41
|
+
"eslint": "10.0.1",
|
|
41
42
|
"@eslint/js": "10.0.1",
|
|
42
43
|
"neostandard": "0.12.2",
|
|
43
44
|
"eslint-plugin-import": "2.32.0",
|
|
44
45
|
"eslint-plugin-vue": "10.8.0",
|
|
45
46
|
|
|
46
|
-
"oxlint": "1.
|
|
47
|
-
"eslint-plugin-oxlint": "1.
|
|
47
|
+
"oxlint": "1.50.0",
|
|
48
|
+
"eslint-plugin-oxlint": "1.50.0",
|
|
48
49
|
|
|
49
50
|
"htmllint": "0.8.0",
|
|
50
51
|
"htmllint-cli": "0.0.7",
|
|
51
52
|
|
|
52
53
|
"check-dependencies": "2.0.0",
|
|
53
|
-
"nodemon": "3.1.
|
|
54
|
+
"nodemon": "3.1.14",
|
|
54
55
|
"shx": "0.4.0",
|
|
55
56
|
"stylelint": "17.3.0",
|
|
56
57
|
"stylelint-config-html": "1.1.0",
|
|
@@ -61,7 +62,7 @@
|
|
|
61
62
|
"postcss-html": "1.8.1",
|
|
62
63
|
"stylus": "0.64.0",
|
|
63
64
|
"typescript": "5.9.3",
|
|
64
|
-
"vue-tsc": "3.2.
|
|
65
|
+
"vue-tsc": "3.2.5",
|
|
65
66
|
"delay-cli": "3.0.0",
|
|
66
67
|
"cross-env": "10.1.0",
|
|
67
68
|
"serve": "14.2.5",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
class="text-col"
|
|
27
27
|
v-bind:class="{ intermediate: block.lastKind === 'intermediate' }">
|
|
28
28
|
<div v-bind:key="value"
|
|
29
|
-
v-for="value
|
|
29
|
+
v-for="value in block.value"
|
|
30
30
|
class="text-value">
|
|
31
31
|
{{ value }}
|
|
32
32
|
</div>
|
|
@@ -37,6 +37,20 @@
|
|
|
37
37
|
</div>
|
|
38
38
|
</div>
|
|
39
39
|
</div>
|
|
40
|
+
<div class="popups">
|
|
41
|
+
<div class="popup-cols">
|
|
42
|
+
<div class="popup"
|
|
43
|
+
v-for="(entry, idx) in popup"
|
|
44
|
+
v-bind:class="{ warning: entry.level === 'warning', error: entry.level === 'error' }"
|
|
45
|
+
v-bind:ref="`popup-${entry.id}`"
|
|
46
|
+
v-bind:key="entry.id">
|
|
47
|
+
<span class="time">[{{ DateTime.fromMillis(entry.time).toFormat("yyyy-MM-dd HH:mm:ss") }}] </span>
|
|
48
|
+
<span class="level">{{ entry.level.toUpperCase() }}: </span>
|
|
49
|
+
<span class="node" v-show="entry.node !== ''">{{ entry.node }}: </span>
|
|
50
|
+
<span class="message">{{ entry.message }}</span>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
40
54
|
</div>
|
|
41
55
|
</template>
|
|
42
56
|
|
|
@@ -133,6 +147,37 @@
|
|
|
133
147
|
.text-value:last-child
|
|
134
148
|
background-color: var(--color-sig-bg-3)
|
|
135
149
|
color: var(--color-sig-fg-5)
|
|
150
|
+
.popups
|
|
151
|
+
position: absolute
|
|
152
|
+
top: 0
|
|
153
|
+
left: 0
|
|
154
|
+
z-index: 100
|
|
155
|
+
width: 100%
|
|
156
|
+
height: 100%
|
|
157
|
+
display: flex
|
|
158
|
+
flex-direction: row
|
|
159
|
+
justify-content: center
|
|
160
|
+
align-items: flex-begin
|
|
161
|
+
.popup-cols
|
|
162
|
+
display: flex
|
|
163
|
+
flex-direction: column
|
|
164
|
+
justify-content: flex-begin
|
|
165
|
+
align-items: center
|
|
166
|
+
width: 30vw
|
|
167
|
+
.popup
|
|
168
|
+
width: 30vw
|
|
169
|
+
color: var(--color-std-fg-5)
|
|
170
|
+
background-color: var(--color-acc-bg-3)
|
|
171
|
+
margin-top: 0.3vw
|
|
172
|
+
padding: 0.5vw
|
|
173
|
+
border-radius: 0.5vw
|
|
174
|
+
font-size: 1.0vw
|
|
175
|
+
.level
|
|
176
|
+
font-weight: bold
|
|
177
|
+
&.warning
|
|
178
|
+
background-color: var(--color-sig-bg-3)
|
|
179
|
+
&.error
|
|
180
|
+
background-color: var(--color-sig-bg-3)
|
|
136
181
|
</style>
|
|
137
182
|
|
|
138
183
|
<script setup lang="ts">
|
|
@@ -140,6 +185,7 @@ import { defineComponent } from "vue"
|
|
|
140
185
|
import { DateTime } from "luxon"
|
|
141
186
|
import ReconnectingWebSocket from "@opensumi/reconnecting-websocket"
|
|
142
187
|
import axios from "axios"
|
|
188
|
+
import * as anime from "animejs"
|
|
143
189
|
</script>
|
|
144
190
|
|
|
145
191
|
<script lang="ts">
|
|
@@ -162,19 +208,36 @@ type Info = {
|
|
|
162
208
|
lufsBuffer?: LufsEntry[]
|
|
163
209
|
}
|
|
164
210
|
|
|
211
|
+
/* type of a popup entry */
|
|
212
|
+
type Popup = {
|
|
213
|
+
id: number,
|
|
214
|
+
time: number,
|
|
215
|
+
node: string,
|
|
216
|
+
level: string,
|
|
217
|
+
message: string,
|
|
218
|
+
removing: boolean
|
|
219
|
+
}
|
|
220
|
+
|
|
165
221
|
/* type of a websocket event message */
|
|
166
|
-
|
|
167
|
-
response:
|
|
168
|
-
|
|
222
|
+
type WebSocketEvent = {
|
|
223
|
+
response: "DASHBOARD",
|
|
224
|
+
node: string,
|
|
225
|
+
args: [ string, string, string, number | string ]
|
|
226
|
+
} | {
|
|
227
|
+
response: "HINT",
|
|
228
|
+
node: string,
|
|
229
|
+
args: [ number, string, string ]
|
|
169
230
|
}
|
|
170
231
|
|
|
171
232
|
/* the Vue component */
|
|
172
233
|
export default defineComponent({
|
|
173
234
|
name: "app",
|
|
174
235
|
data: () => ({
|
|
175
|
-
info:
|
|
176
|
-
ws:
|
|
177
|
-
lufsTimer:
|
|
236
|
+
info: [] as Info[],
|
|
237
|
+
ws: null as ReconnectingWebSocket | null,
|
|
238
|
+
lufsTimer: null as ReturnType<typeof setInterval> | null,
|
|
239
|
+
popup: [] as Popup[],
|
|
240
|
+
popupTimer: null as ReturnType<typeof setInterval> | null
|
|
178
241
|
}),
|
|
179
242
|
async mounted () {
|
|
180
243
|
/* determine API URLs */
|
|
@@ -196,6 +259,11 @@ export default defineComponent({
|
|
|
196
259
|
this.updateLufsDisplayValues()
|
|
197
260
|
}, 50)
|
|
198
261
|
|
|
262
|
+
/* start timer to remove expired popups */
|
|
263
|
+
this.popupTimer = setInterval(() => {
|
|
264
|
+
this.removeExpiredPopups()
|
|
265
|
+
}, 50)
|
|
266
|
+
|
|
199
267
|
/* connect to WebSocket API for receiving dashboard information */
|
|
200
268
|
this.ws = new ReconnectingWebSocket(urlWS.toString(), [], {
|
|
201
269
|
reconnectionDelayGrowFactor: 1.3,
|
|
@@ -209,12 +277,34 @@ export default defineComponent({
|
|
|
209
277
|
})
|
|
210
278
|
|
|
211
279
|
/* track connection open/close */
|
|
280
|
+
let initially = true
|
|
281
|
+
let popupId = 0
|
|
212
282
|
this.ws.addEventListener("open", (ev) => {
|
|
213
|
-
|
|
283
|
+
if (initially) {
|
|
284
|
+
initially = false
|
|
285
|
+
this.log("INFO", "WebSocket server connection: opened")
|
|
286
|
+
this.popup.unshift({
|
|
287
|
+
id: popupId++,
|
|
288
|
+
time: Date.now(),
|
|
289
|
+
node: "",
|
|
290
|
+
level: "info",
|
|
291
|
+
message: "server connection: opened",
|
|
292
|
+
removing: false
|
|
293
|
+
})
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
this.log("INFO", "WebSocket server connection: re-opened")
|
|
297
|
+
this.popup.unshift({
|
|
298
|
+
id: popupId++,
|
|
299
|
+
time: Date.now(),
|
|
300
|
+
node: "",
|
|
301
|
+
level: "info",
|
|
302
|
+
message: "server connection: re-opened",
|
|
303
|
+
removing: false
|
|
304
|
+
})
|
|
305
|
+
}
|
|
214
306
|
})
|
|
215
307
|
this.ws.addEventListener("close", (ev) => {
|
|
216
|
-
this.log("INFO", "WebSocket connection destroyed")
|
|
217
|
-
|
|
218
308
|
/* reset meters and clear LUFS buffers */
|
|
219
309
|
for (const block of this.info) {
|
|
220
310
|
if (block.type === "audio" && block.lufsBuffer !== undefined) {
|
|
@@ -222,6 +312,17 @@ export default defineComponent({
|
|
|
222
312
|
block.lufsBuffer.length = 0
|
|
223
313
|
}
|
|
224
314
|
}
|
|
315
|
+
|
|
316
|
+
/* drop notice */
|
|
317
|
+
this.log("INFO", "WebSocket server connection: closed")
|
|
318
|
+
this.popup.unshift({
|
|
319
|
+
id: popupId++,
|
|
320
|
+
time: Date.now(),
|
|
321
|
+
node: "",
|
|
322
|
+
level: "warning",
|
|
323
|
+
message: "server connection: closed",
|
|
324
|
+
removing: false
|
|
325
|
+
})
|
|
225
326
|
})
|
|
226
327
|
|
|
227
328
|
/* receive messages */
|
|
@@ -234,36 +335,40 @@ export default defineComponent({
|
|
|
234
335
|
this.log("ERROR", "Failed to parse WebSocket message", { error, data: ev.data })
|
|
235
336
|
return
|
|
236
337
|
}
|
|
237
|
-
if (event.response
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
block.value = arr.slice(-20)
|
|
338
|
+
if (event.response === "DASHBOARD") {
|
|
339
|
+
/* extract dashboard update parameters: [ type, id, kind, value ] */
|
|
340
|
+
const [ type, id, kind, value ] = event.args
|
|
341
|
+
for (const block of this.info) {
|
|
342
|
+
if (block.type === type && block.id === id) {
|
|
343
|
+
if (block.type === "audio" && block.lufsBuffer !== undefined) {
|
|
344
|
+
/* buffer LUFS values for temporal averaging */
|
|
345
|
+
if (kind === "final" && typeof value === "number")
|
|
346
|
+
block.lufsBuffer.push({ value, time: Date.now() })
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
if (typeof value === "string") {
|
|
350
|
+
const arr = block.value as string[]
|
|
351
|
+
if (block.lastKind === "intermediate")
|
|
352
|
+
arr[arr.length - 1] = value
|
|
353
|
+
else {
|
|
354
|
+
arr.push(value)
|
|
355
|
+
block.value = arr.slice(-20)
|
|
356
|
+
}
|
|
257
357
|
}
|
|
358
|
+
block.lastKind = kind
|
|
359
|
+
this.$nextTick(() => {
|
|
360
|
+
for (const textCol of this.$refs.textCol as HTMLDivElement[])
|
|
361
|
+
textCol.scrollTop = textCol.scrollHeight
|
|
362
|
+
})
|
|
258
363
|
}
|
|
259
|
-
block.lastKind = kind
|
|
260
|
-
this.$nextTick(() => {
|
|
261
|
-
for (const textCol of this.$refs.textCol as HTMLDivElement[])
|
|
262
|
-
textCol.scrollTop = textCol.scrollHeight
|
|
263
|
-
})
|
|
264
364
|
}
|
|
265
365
|
}
|
|
266
366
|
}
|
|
367
|
+
else if (event.response === "HINT") {
|
|
368
|
+
const node = event.node
|
|
369
|
+
const [ time, level, message ] = event.args
|
|
370
|
+
this.popup.unshift({ id: popupId++, time, node, level, message, removing: false })
|
|
371
|
+
}
|
|
267
372
|
})
|
|
268
373
|
},
|
|
269
374
|
beforeUnmount () {
|
|
@@ -271,6 +376,10 @@ export default defineComponent({
|
|
|
271
376
|
clearInterval(this.lufsTimer)
|
|
272
377
|
this.lufsTimer = null
|
|
273
378
|
}
|
|
379
|
+
if (this.popupTimer) {
|
|
380
|
+
clearInterval(this.popupTimer)
|
|
381
|
+
this.popupTimer = null
|
|
382
|
+
}
|
|
274
383
|
if (this.ws) {
|
|
275
384
|
this.ws.close()
|
|
276
385
|
this.ws = null
|
|
@@ -308,6 +417,25 @@ export default defineComponent({
|
|
|
308
417
|
else
|
|
309
418
|
block.value = -60
|
|
310
419
|
}
|
|
420
|
+
},
|
|
421
|
+
|
|
422
|
+
/* remove expired popups */
|
|
423
|
+
removeExpiredPopups () {
|
|
424
|
+
this.popup.forEach((entry) => {
|
|
425
|
+
if (entry.time < Date.now() - 8000 && !entry.removing) {
|
|
426
|
+
const el = (this.$refs[`popup-${entry.id}`] as HTMLDivElement[])[0]
|
|
427
|
+
entry.removing = true
|
|
428
|
+
anime.animate(el, {
|
|
429
|
+
opacity: [ 1, 0 ],
|
|
430
|
+
duration: 2000,
|
|
431
|
+
easing: "easeOutQuad",
|
|
432
|
+
onComplete: () => {
|
|
433
|
+
const idx = this.popup.findIndex((e) => e.id === entry.id)
|
|
434
|
+
this.popup.splice(idx, 1)
|
|
435
|
+
}
|
|
436
|
+
})
|
|
437
|
+
}
|
|
438
|
+
})
|
|
311
439
|
}
|
|
312
440
|
}
|
|
313
441
|
})
|