@dxos/plugin-transcription 0.8.4-main.dedc0f3 → 0.8.4-main.dfabb4ec29
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/dist/lib/neutral/TranscriptionContainer-XNGXNGPX.mjs +35 -0
- package/dist/lib/neutral/TranscriptionContainer-XNGXNGPX.mjs.map +7 -0
- package/dist/lib/neutral/TranscriptionPlugin.mjs +34 -0
- package/dist/lib/neutral/TranscriptionPlugin.mjs.map +7 -0
- package/dist/lib/neutral/TranscriptionPlugin.node.mjs +25 -0
- package/dist/lib/neutral/TranscriptionPlugin.node.mjs.map +7 -0
- package/dist/lib/neutral/blueprint-definition-HOXDG5QP.mjs +15 -0
- package/dist/lib/neutral/blueprint-definition-HOXDG5QP.mjs.map +7 -0
- package/dist/lib/neutral/blueprints/index.mjs +8 -0
- package/dist/lib/neutral/capabilities/index.mjs +17 -0
- package/dist/lib/neutral/capabilities/index.mjs.map +7 -0
- package/dist/lib/neutral/chunk-GL4RS533.mjs +22 -0
- package/dist/lib/neutral/chunk-GL4RS533.mjs.map +7 -0
- package/dist/lib/neutral/chunk-J5LGTIGS.mjs +10 -0
- package/dist/lib/neutral/chunk-KAEXRX3Y.mjs +35 -0
- package/dist/lib/neutral/chunk-KAEXRX3Y.mjs.map +7 -0
- package/dist/lib/{browser/chunk-BA64VLLR.mjs → neutral/chunk-LS3R7EQI.mjs} +129 -185
- package/dist/lib/neutral/chunk-LS3R7EQI.mjs.map +7 -0
- package/dist/lib/neutral/chunk-MLRJWAQH.mjs +179 -0
- package/dist/lib/neutral/chunk-MLRJWAQH.mjs.map +7 -0
- package/dist/lib/neutral/chunk-NNR3EYR2.mjs +124 -0
- package/dist/lib/neutral/chunk-NNR3EYR2.mjs.map +7 -0
- package/dist/lib/neutral/chunk-NQQK7UZQ.mjs +244 -0
- package/dist/lib/neutral/chunk-NQQK7UZQ.mjs.map +7 -0
- package/dist/lib/neutral/chunk-WVEZMGBT.mjs +8 -0
- package/dist/lib/neutral/chunk-WVEZMGBT.mjs.map +7 -0
- package/dist/lib/neutral/chunk-ZR4BH4QQ.mjs +30 -0
- package/dist/lib/neutral/chunk-ZR4BH4QQ.mjs.map +7 -0
- package/dist/lib/neutral/components/index.mjs +207 -0
- package/dist/lib/neutral/components/index.mjs.map +7 -0
- package/dist/lib/neutral/containers/index.mjs +9 -0
- package/dist/lib/neutral/containers/index.mjs.map +7 -0
- package/dist/lib/neutral/create-2D6S4IMQ.mjs +21 -0
- package/dist/lib/neutral/create-2D6S4IMQ.mjs.map +7 -0
- package/dist/lib/neutral/hooks/index.mjs +17 -0
- package/dist/lib/neutral/index.mjs +44 -0
- package/dist/lib/neutral/meta.json +1 -0
- package/dist/lib/neutral/meta.mjs +8 -0
- package/dist/lib/neutral/meta.mjs.map +7 -0
- package/dist/lib/neutral/open-2QB6IIET.mjs +27 -0
- package/dist/lib/neutral/open-2QB6IIET.mjs.map +7 -0
- package/dist/lib/neutral/operation-handler-NXMOAXFP.mjs +13 -0
- package/dist/lib/neutral/operation-handler-NXMOAXFP.mjs.map +7 -0
- package/dist/lib/neutral/operations/index.mjs +8 -0
- package/dist/lib/neutral/operations/index.mjs.map +7 -0
- package/dist/lib/neutral/plugin.mjs +16 -0
- package/dist/lib/neutral/plugin.mjs.map +7 -0
- package/dist/lib/neutral/react-surface-QF4MUMWG.mjs +26 -0
- package/dist/lib/neutral/react-surface-QF4MUMWG.mjs.map +7 -0
- package/dist/lib/neutral/summarize-CJPVKWYZ.mjs +78 -0
- package/dist/lib/neutral/summarize-CJPVKWYZ.mjs.map +7 -0
- package/dist/lib/neutral/text-content-LHSUG2D2.mjs +30 -0
- package/dist/lib/neutral/text-content-LHSUG2D2.mjs.map +7 -0
- package/dist/lib/{node-esm/transcriber-QPS55YP2.mjs → neutral/transcriber-M3R6HPTZ.mjs} +20 -18
- package/dist/lib/neutral/transcriber-M3R6HPTZ.mjs.map +7 -0
- package/dist/lib/neutral/translations.mjs +22 -0
- package/dist/lib/neutral/translations.mjs.map +7 -0
- package/dist/lib/neutral/types/index.mjs +12 -0
- package/dist/lib/neutral/types/index.mjs.map +7 -0
- package/dist/types/src/TranscriptionPlugin.d.ts +3 -1
- package/dist/types/src/TranscriptionPlugin.d.ts.map +1 -1
- package/dist/types/src/TranscriptionPlugin.node.d.ts +4 -0
- package/dist/types/src/TranscriptionPlugin.node.d.ts.map +1 -0
- package/dist/types/src/TranscriptionPlugin.test.d.ts +2 -0
- package/dist/types/src/TranscriptionPlugin.test.d.ts.map +1 -0
- package/dist/types/src/blueprints/index.d.ts +2 -0
- package/dist/types/src/blueprints/index.d.ts.map +1 -0
- package/dist/types/src/blueprints/transcription-blueprint.d.ts +4 -0
- package/dist/types/src/blueprints/transcription-blueprint.d.ts.map +1 -0
- package/dist/types/src/capabilities/blueprint-definition.d.ts +5 -4
- package/dist/types/src/capabilities/blueprint-definition.d.ts.map +1 -1
- package/dist/types/src/capabilities/index.d.ts +10 -5
- package/dist/types/src/capabilities/index.d.ts.map +1 -1
- package/dist/types/src/capabilities/operation-handler.d.ts +6 -0
- package/dist/types/src/capabilities/operation-handler.d.ts.map +1 -0
- package/dist/types/src/capabilities/react-surface.d.ts +3 -2
- package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
- package/dist/types/src/capabilities/text-content.d.ts +8 -0
- package/dist/types/src/capabilities/text-content.d.ts.map +1 -0
- package/dist/types/src/capabilities/transcriber.d.ts +4 -3
- package/dist/types/src/capabilities/transcriber.d.ts.map +1 -1
- package/dist/types/src/components/Transcription/Transcription.d.ts +18 -0
- package/dist/types/src/components/Transcription/Transcription.d.ts.map +1 -0
- package/dist/types/src/components/Transcription/Transcription.stories.d.ts +40 -0
- package/dist/types/src/components/Transcription/Transcription.stories.d.ts.map +1 -0
- package/dist/types/src/components/Transcription/index.d.ts +2 -0
- package/dist/types/src/components/Transcription/index.d.ts.map +1 -0
- package/dist/types/src/components/Transcription/transcription-extension.d.ts +15 -0
- package/dist/types/src/components/Transcription/transcription-extension.d.ts.map +1 -0
- package/dist/types/src/components/index.d.ts +1 -3
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/containers/TranscriptionContainer/TranscriptionContainer.d.ts +6 -0
- package/dist/types/src/containers/TranscriptionContainer/TranscriptionContainer.d.ts.map +1 -0
- package/dist/types/src/containers/TranscriptionContainer/index.d.ts +2 -0
- package/dist/types/src/containers/TranscriptionContainer/index.d.ts.map +1 -0
- package/dist/types/src/containers/index.d.ts +3 -0
- package/dist/types/src/containers/index.d.ts.map +1 -0
- package/dist/types/src/hooks/useAudioFile.d.ts.map +1 -1
- package/dist/types/src/hooks/useAudioTrack.d.ts.map +1 -1
- package/dist/types/src/hooks/useQueueModelAdapter.d.ts +1 -1
- package/dist/types/src/hooks/useQueueModelAdapter.d.ts.map +1 -1
- package/dist/types/src/hooks/useTranscriber.d.ts +2 -2
- package/dist/types/src/hooks/useTranscriber.d.ts.map +1 -1
- package/dist/types/src/hooks/useVoiceInput.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +2 -3
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/meta.d.ts +2 -3
- package/dist/types/src/meta.d.ts.map +1 -1
- package/dist/types/src/model/model.d.ts +2 -2
- package/dist/types/src/model/model.d.ts.map +1 -1
- package/dist/types/src/{segments-normalization → normalization}/index.d.ts +1 -1
- package/dist/types/src/normalization/index.d.ts.map +1 -0
- package/dist/types/src/{segments-normalization → normalization}/message-normalizer.d.ts +5 -5
- package/dist/types/src/normalization/message-normalizer.d.ts.map +1 -0
- package/dist/types/src/normalization/normalization.d.ts +543 -0
- package/dist/types/src/normalization/normalization.d.ts.map +1 -0
- package/dist/types/src/normalization/normalization.test.d.ts.map +1 -0
- package/dist/types/src/normalization/utils.d.ts +3 -0
- package/dist/types/src/normalization/utils.d.ts.map +1 -0
- package/dist/types/src/operations/create.d.ts +5 -0
- package/dist/types/src/operations/create.d.ts.map +1 -0
- package/dist/types/src/operations/index.d.ts +3 -0
- package/dist/types/src/operations/index.d.ts.map +1 -0
- package/dist/types/src/operations/open.d.ts +5 -0
- package/dist/types/src/operations/open.d.ts.map +1 -0
- package/dist/types/src/operations/summarize.d.ts +8 -0
- package/dist/types/src/operations/summarize.d.ts.map +1 -0
- package/dist/types/src/plugin.d.ts +4 -0
- package/dist/types/src/plugin.d.ts.map +1 -0
- package/dist/types/src/{components/stories → stories}/FileTranscription.stories.d.ts +3 -4
- package/dist/types/src/stories/FileTranscription.stories.d.ts.map +1 -0
- package/dist/types/src/{components/stories → stories}/KeyWordRecognition.stories.d.ts +2 -3
- package/dist/types/src/stories/KeyWordRecognition.stories.d.ts.map +1 -0
- package/dist/types/src/{components/stories → stories}/MicrophoneTranscription.stories.d.ts +5 -6
- package/dist/types/src/stories/MicrophoneTranscription.stories.d.ts.map +1 -0
- package/dist/types/src/stories/TranscriptionStory.d.ts +23 -0
- package/dist/types/src/stories/TranscriptionStory.d.ts.map +1 -0
- package/dist/types/src/stories/useIsSpeaking.d.ts.map +1 -0
- package/dist/types/src/testing/testing.d.ts +10 -10
- package/dist/types/src/testing/testing.d.ts.map +1 -1
- package/dist/types/src/transcriber/index.d.ts +1 -1
- package/dist/types/src/transcriber/index.d.ts.map +1 -1
- package/dist/types/src/transcriber/media-stream-recorder.d.ts +2 -2
- package/dist/types/src/transcriber/media-stream-recorder.d.ts.map +1 -1
- package/dist/types/src/transcriber/transcriber.d.ts +60 -5
- package/dist/types/src/transcriber/transcriber.d.ts.map +1 -1
- package/dist/types/src/transcriber/transcriber.test.d.ts +2 -0
- package/dist/types/src/transcriber/transcriber.test.d.ts.map +1 -0
- package/dist/types/src/transcriber/transcription-manager.d.ts +9 -6
- package/dist/types/src/transcriber/transcription-manager.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +7 -8
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/TranscriptOperation.d.ts +917 -0
- package/dist/types/src/types/TranscriptOperation.d.ts.map +1 -0
- package/dist/types/src/types/TranscriptionCapabilities.d.ts +17 -0
- package/dist/types/src/types/TranscriptionCapabilities.d.ts.map +1 -0
- package/dist/types/src/types/{types.d.ts → defs.d.ts} +1 -1
- package/dist/types/src/types/defs.d.ts.map +1 -0
- package/dist/types/src/types/index.d.ts +3 -3
- package/dist/types/src/types/index.d.ts.map +1 -1
- package/dist/types/src/util/array.d.ts.map +1 -1
- package/dist/types/src/util/get-time-str.d.ts.map +1 -1
- package/dist/types/src/util/index.d.ts +1 -0
- package/dist/types/src/util/index.d.ts.map +1 -1
- package/dist/types/src/util/monitor-audio-level.d.ts.map +1 -1
- package/dist/types/src/util/render-byline.d.ts +4 -0
- package/dist/types/src/util/render-byline.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +148 -82
- package/src/TranscriptionPlugin.node.ts +20 -0
- package/src/TranscriptionPlugin.test.ts +31 -0
- package/src/TranscriptionPlugin.tsx +22 -66
- package/src/blueprints/index.ts +5 -0
- package/src/blueprints/transcription-blueprint.ts +29 -0
- package/src/capabilities/blueprint-definition.ts +12 -24
- package/src/capabilities/index.ts +11 -7
- package/src/capabilities/operation-handler.ts +16 -0
- package/src/capabilities/react-surface.tsx +23 -14
- package/src/capabilities/text-content.ts +33 -0
- package/src/capabilities/transcriber.ts +53 -42
- package/src/components/{Transcript/Transcript.stories.tsx → Transcription/Transcription.stories.tsx} +62 -59
- package/src/components/Transcription/Transcription.tsx +64 -0
- package/src/components/{Transcript → Transcription}/index.ts +1 -1
- package/src/components/{Transcript/transcript-extension.ts → Transcription/transcription-extension.ts} +13 -9
- package/src/components/index.ts +1 -6
- package/src/containers/TranscriptionContainer/TranscriptionContainer.tsx +35 -0
- package/src/containers/TranscriptionContainer/index.ts +5 -0
- package/src/containers/index.ts +7 -0
- package/src/hooks/useAudioFile.ts +50 -45
- package/src/hooks/useQueueModelAdapter.ts +1 -1
- package/src/hooks/useTranscriber.ts +12 -4
- package/src/hooks/useVoiceInput.ts +2 -3
- package/src/index.ts +4 -5
- package/src/meta.ts +9 -6
- package/src/model/model.test.ts +20 -14
- package/src/model/model.ts +3 -3
- package/src/{segments-normalization → normalization}/message-normalizer.ts +19 -18
- package/src/{segments-normalization → normalization}/normalization.test.ts +7 -12
- package/src/normalization/normalization.ts +76 -0
- package/src/{segments-normalization → normalization}/utils.ts +2 -2
- package/src/operations/create.ts +24 -0
- package/src/operations/index.ts +11 -0
- package/src/operations/open.ts +28 -0
- package/src/operations/summarize.ts +101 -0
- package/src/plugin.ts +11 -0
- package/src/{components/stories → stories}/FileTranscription.stories.tsx +56 -43
- package/src/{components/stories → stories}/KeyWordRecognition.stories.tsx +36 -36
- package/src/{components/stories → stories}/MicrophoneTranscription.stories.tsx +60 -81
- package/src/stories/TranscriptionStory.tsx +93 -0
- package/src/testing/testing.ts +37 -37
- package/src/transcriber/index.ts +1 -1
- package/src/transcriber/media-stream-recorder.ts +3 -3
- package/src/transcriber/transcriber.browser.test.ts +4 -5
- package/src/transcriber/transcriber.test.ts +104 -0
- package/src/transcriber/transcriber.ts +99 -58
- package/src/transcriber/transcription-manager.ts +21 -15
- package/src/translations.ts +7 -7
- package/src/types/TranscriptOperation.ts +103 -0
- package/src/types/TranscriptionCapabilities.ts +39 -0
- package/src/types/index.ts +4 -3
- package/src/util/index.ts +1 -0
- package/src/util/render-byline.ts +33 -0
- package/dist/lib/browser/TranscriptContainer-4RBK74JP.mjs +0 -11
- package/dist/lib/browser/blueprint-definition-TJABB43G.mjs +0 -142
- package/dist/lib/browser/blueprint-definition-TJABB43G.mjs.map +0 -7
- package/dist/lib/browser/chunk-3X7HP44M.mjs +0 -26
- package/dist/lib/browser/chunk-3X7HP44M.mjs.map +0 -7
- package/dist/lib/browser/chunk-AGZYQGSP.mjs +0 -8
- package/dist/lib/browser/chunk-AGZYQGSP.mjs.map +0 -7
- package/dist/lib/browser/chunk-BA64VLLR.mjs.map +0 -7
- package/dist/lib/browser/chunk-HXVGV7T6.mjs +0 -63
- package/dist/lib/browser/chunk-HXVGV7T6.mjs.map +0 -7
- package/dist/lib/browser/chunk-TNEQFHIU.mjs +0 -16
- package/dist/lib/browser/chunk-TNEQFHIU.mjs.map +0 -7
- package/dist/lib/browser/chunk-VVXRHJCH.mjs +0 -746
- package/dist/lib/browser/chunk-VVXRHJCH.mjs.map +0 -7
- package/dist/lib/browser/index.mjs +0 -131
- package/dist/lib/browser/index.mjs.map +0 -7
- package/dist/lib/browser/intent-resolver-T3EGLS3L.mjs +0 -25
- package/dist/lib/browser/intent-resolver-T3EGLS3L.mjs.map +0 -7
- package/dist/lib/browser/meta.json +0 -1
- package/dist/lib/browser/react-surface-4Y3YISAD.mjs +0 -34
- package/dist/lib/browser/react-surface-4Y3YISAD.mjs.map +0 -7
- package/dist/lib/browser/transcriber-JOOAQVYV.mjs +0 -56
- package/dist/lib/browser/transcriber-JOOAQVYV.mjs.map +0 -7
- package/dist/lib/browser/types/index.mjs +0 -12
- package/dist/lib/node-esm/TranscriptContainer-EGAAYVOP.mjs +0 -12
- package/dist/lib/node-esm/blueprint-definition-R4UF7YJZ.mjs +0 -143
- package/dist/lib/node-esm/blueprint-definition-R4UF7YJZ.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-3QAIZGMW.mjs +0 -17
- package/dist/lib/node-esm/chunk-3QAIZGMW.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-CF5Z6FAB.mjs +0 -10
- package/dist/lib/node-esm/chunk-CF5Z6FAB.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-J5GQ3KGX.mjs +0 -487
- package/dist/lib/node-esm/chunk-J5GQ3KGX.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-QARUDLHN.mjs +0 -27
- package/dist/lib/node-esm/chunk-QARUDLHN.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-SFQNMFHX.mjs +0 -747
- package/dist/lib/node-esm/chunk-SFQNMFHX.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-W72HVQMD.mjs +0 -64
- package/dist/lib/node-esm/chunk-W72HVQMD.mjs.map +0 -7
- package/dist/lib/node-esm/index.mjs +0 -132
- package/dist/lib/node-esm/index.mjs.map +0 -7
- package/dist/lib/node-esm/intent-resolver-VDHCUAIO.mjs +0 -26
- package/dist/lib/node-esm/intent-resolver-VDHCUAIO.mjs.map +0 -7
- package/dist/lib/node-esm/meta.json +0 -1
- package/dist/lib/node-esm/react-surface-ZIXDGJAN.mjs +0 -35
- package/dist/lib/node-esm/react-surface-ZIXDGJAN.mjs.map +0 -7
- package/dist/lib/node-esm/transcriber-QPS55YP2.mjs.map +0 -7
- package/dist/lib/node-esm/types/index.mjs +0 -13
- package/dist/types/src/capabilities/capabilities.d.ts +0 -17
- package/dist/types/src/capabilities/capabilities.d.ts.map +0 -1
- package/dist/types/src/capabilities/intent-resolver.d.ts +0 -4
- package/dist/types/src/capabilities/intent-resolver.d.ts.map +0 -1
- package/dist/types/src/components/Transcript/Transcript.d.ts +0 -20
- package/dist/types/src/components/Transcript/Transcript.d.ts.map +0 -1
- package/dist/types/src/components/Transcript/Transcript.stories.d.ts +0 -39
- package/dist/types/src/components/Transcript/Transcript.stories.d.ts.map +0 -1
- package/dist/types/src/components/Transcript/deprecated/OldTranscript.d.ts +0 -28
- package/dist/types/src/components/Transcript/deprecated/OldTranscript.d.ts.map +0 -1
- package/dist/types/src/components/Transcript/index.d.ts +0 -2
- package/dist/types/src/components/Transcript/index.d.ts.map +0 -1
- package/dist/types/src/components/Transcript/transcript-extension.d.ts +0 -15
- package/dist/types/src/components/Transcript/transcript-extension.d.ts.map +0 -1
- package/dist/types/src/components/TranscriptContainer.d.ts +0 -9
- package/dist/types/src/components/TranscriptContainer.d.ts.map +0 -1
- package/dist/types/src/components/stories/FileTranscription.stories.d.ts.map +0 -1
- package/dist/types/src/components/stories/KeyWordRecognition.stories.d.ts.map +0 -1
- package/dist/types/src/components/stories/MicrophoneTranscription.stories.d.ts.map +0 -1
- package/dist/types/src/components/stories/TranscriptionStory.d.ts +0 -12
- package/dist/types/src/components/stories/TranscriptionStory.d.ts.map +0 -1
- package/dist/types/src/components/stories/useIsSpeaking.d.ts.map +0 -1
- package/dist/types/src/functions/index.d.ts +0 -3
- package/dist/types/src/functions/index.d.ts.map +0 -1
- package/dist/types/src/functions/open.d.ts +0 -7
- package/dist/types/src/functions/open.d.ts.map +0 -1
- package/dist/types/src/functions/summarize.d.ts +0 -11
- package/dist/types/src/functions/summarize.d.ts.map +0 -1
- package/dist/types/src/segments-normalization/index.d.ts.map +0 -1
- package/dist/types/src/segments-normalization/message-normalizer.d.ts.map +0 -1
- package/dist/types/src/segments-normalization/normalization.d.ts +0 -461
- package/dist/types/src/segments-normalization/normalization.d.ts.map +0 -1
- package/dist/types/src/segments-normalization/normalization.test.d.ts.map +0 -1
- package/dist/types/src/segments-normalization/utils.d.ts +0 -3
- package/dist/types/src/segments-normalization/utils.d.ts.map +0 -1
- package/dist/types/src/types/Transcript.d.ts +0 -30
- package/dist/types/src/types/Transcript.d.ts.map +0 -1
- package/dist/types/src/types/TranscriptAction.d.ts +0 -23
- package/dist/types/src/types/TranscriptAction.d.ts.map +0 -1
- package/dist/types/src/types/types.d.ts.map +0 -1
- package/src/capabilities/capabilities.ts +0 -33
- package/src/capabilities/intent-resolver.ts +0 -18
- package/src/components/Transcript/Transcript.tsx +0 -93
- package/src/components/Transcript/deprecated/OldTranscript.tsx +0 -260
- package/src/components/TranscriptContainer.tsx +0 -36
- package/src/components/stories/TranscriptionStory.tsx +0 -45
- package/src/functions/index.ts +0 -6
- package/src/functions/open.ts +0 -37
- package/src/functions/summarize.ts +0 -97
- package/src/segments-normalization/normalization.ts +0 -115
- package/src/types/Transcript.ts +0 -44
- package/src/types/TranscriptAction.ts +0 -24
- package/dist/lib/{browser/types → neutral/blueprints}/index.mjs.map +0 -0
- package/dist/lib/{browser/TranscriptContainer-4RBK74JP.mjs.map → neutral/chunk-J5LGTIGS.mjs.map} +0 -0
- package/dist/lib/{node-esm/types → neutral/hooks}/index.mjs.map +0 -0
- package/dist/lib/{node-esm/TranscriptContainer-EGAAYVOP.mjs.map → neutral/index.mjs.map} +0 -0
- package/dist/types/src/{segments-normalization → normalization}/normalization.test.d.ts +0 -0
- package/dist/types/src/{components/stories → stories}/useIsSpeaking.d.ts +0 -0
- package/src/{segments-normalization → normalization}/index.ts +1 -1
- /package/src/{components/stories → stories}/useIsSpeaking.tsx +0 -0
- /package/src/types/{types.ts → defs.ts} +0 -0
package/src/testing/testing.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import * as Schema from 'effect/Schema';
|
|
6
6
|
import { useEffect, useMemo, useState } from 'react';
|
|
7
7
|
|
|
8
8
|
import { extractionAnthropicFunction, processTranscriptMessage } from '@dxos/assistant/extraction';
|
|
@@ -10,14 +10,14 @@ import { scheduleTaskInterval } from '@dxos/async';
|
|
|
10
10
|
import { Filter, type Queue } from '@dxos/client/echo';
|
|
11
11
|
import { Context } from '@dxos/context';
|
|
12
12
|
import { type Key, Obj, Ref, Type } from '@dxos/echo';
|
|
13
|
-
import { createQueueDXN } from '@dxos/echo
|
|
14
|
-
import { FunctionExecutor, ServiceContainer } from '@dxos/functions';
|
|
13
|
+
import { createQueueDXN } from '@dxos/echo/internal';
|
|
15
14
|
import { IdentityDid } from '@dxos/keys';
|
|
16
15
|
import { log } from '@dxos/log';
|
|
17
|
-
import {
|
|
16
|
+
import { random } from '@dxos/random';
|
|
18
17
|
import { type Space, useQueue } from '@dxos/react-client/echo';
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
18
|
+
import { TestSchema } from '@dxos/schema/testing';
|
|
19
|
+
import { type ContentBlock, Message, Organization, Person } from '@dxos/types';
|
|
20
|
+
import { seedTestData } from '@dxos/types/testing';
|
|
21
21
|
|
|
22
22
|
// TODO(burdon): Reconcile with plugin-markdown. Move to @dxos/schema/testing.
|
|
23
23
|
export const TestItem = Schema.Struct({
|
|
@@ -30,15 +30,15 @@ export const TestItem = Schema.Struct({
|
|
|
30
30
|
description: 'Product description',
|
|
31
31
|
}),
|
|
32
32
|
}).pipe(
|
|
33
|
-
Type.
|
|
34
|
-
typename: 'dxos.
|
|
33
|
+
Type.object({
|
|
34
|
+
typename: 'org.dxos.type.test',
|
|
35
35
|
version: '0.1.0',
|
|
36
36
|
}),
|
|
37
37
|
);
|
|
38
38
|
|
|
39
39
|
// TODO(wittjosiah): Make builder generic and reuse for all message types.
|
|
40
40
|
abstract class AbstractMessageBuilder {
|
|
41
|
-
abstract createMessage(numSegments?: number): Promise<
|
|
41
|
+
abstract createMessage(numSegments?: number): Promise<Message.Message>;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
/**
|
|
@@ -49,8 +49,8 @@ export class MessageBuilder extends AbstractMessageBuilder {
|
|
|
49
49
|
|
|
50
50
|
users = Array.from({ length: 5 }, () => ({
|
|
51
51
|
identityDid: IdentityDid.random().toString(),
|
|
52
|
-
name:
|
|
53
|
-
email:
|
|
52
|
+
name: random.person.fullName(),
|
|
53
|
+
email: random.internet.email(),
|
|
54
54
|
}));
|
|
55
55
|
|
|
56
56
|
start = new Date(Date.now() - 24 * 60 * 60 * 10_000);
|
|
@@ -59,22 +59,27 @@ export class MessageBuilder extends AbstractMessageBuilder {
|
|
|
59
59
|
super();
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
override async createMessage(numSegments = 1): Promise<
|
|
63
|
-
return Obj.make(
|
|
62
|
+
override async createMessage(numSegments = 1): Promise<Message.Message> {
|
|
63
|
+
return Obj.make(Message.Message, {
|
|
64
64
|
created: this.next().toISOString(),
|
|
65
|
-
sender:
|
|
65
|
+
sender: random.helpers.arrayElement(this.users),
|
|
66
66
|
blocks: Array.from({ length: numSegments }).map(() => this.createBlock()),
|
|
67
67
|
});
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
createBlock():
|
|
71
|
-
let text =
|
|
70
|
+
createBlock(): ContentBlock.Transcript {
|
|
71
|
+
let text = random.lorem.paragraph();
|
|
72
72
|
if (this._space) {
|
|
73
|
-
const label =
|
|
74
|
-
const obj = this._space.db.add(
|
|
73
|
+
const label = random.commerce.productName();
|
|
74
|
+
const obj = this._space.db.add(
|
|
75
|
+
Obj.make(TestItem, {
|
|
76
|
+
title: label,
|
|
77
|
+
description: random.lorem.paragraph(),
|
|
78
|
+
}),
|
|
79
|
+
);
|
|
75
80
|
const dxn = Ref.make(obj).dxn.toString();
|
|
76
81
|
const words = text.split(' ');
|
|
77
|
-
words.splice(Math.floor(Math.random() * words.length), 0, `[${label}]
|
|
82
|
+
words.splice(Math.floor(Math.random() * words.length), 0, `[${label}](${dxn})`);
|
|
78
83
|
text = words.join(' ');
|
|
79
84
|
}
|
|
80
85
|
|
|
@@ -93,17 +98,9 @@ export class MessageBuilder extends AbstractMessageBuilder {
|
|
|
93
98
|
|
|
94
99
|
// TODO(burdon): Reconcile with BlockBuilder.
|
|
95
100
|
class EntityExtractionMessageBuilder extends AbstractMessageBuilder {
|
|
96
|
-
executor = new FunctionExecutor(
|
|
97
|
-
new ServiceContainer().setServices({
|
|
98
|
-
// ai: {
|
|
99
|
-
// client: this.AiService,
|
|
100
|
-
// },
|
|
101
|
-
}),
|
|
102
|
-
);
|
|
103
|
-
|
|
104
101
|
space: Space | undefined;
|
|
105
102
|
currentMessage: number = 0;
|
|
106
|
-
transcriptMessages:
|
|
103
|
+
transcriptMessages: Message.Message[] = [];
|
|
107
104
|
|
|
108
105
|
async connect(space: Space): Promise<void> {
|
|
109
106
|
this.space = space;
|
|
@@ -111,14 +108,18 @@ class EntityExtractionMessageBuilder extends AbstractMessageBuilder {
|
|
|
111
108
|
this.transcriptMessages = transcriptMessages;
|
|
112
109
|
}
|
|
113
110
|
|
|
114
|
-
override async createMessage(): Promise<
|
|
111
|
+
override async createMessage(): Promise<Message.Message> {
|
|
115
112
|
if (!this.space) {
|
|
116
113
|
throw new Error('Space not connected');
|
|
117
114
|
}
|
|
118
115
|
|
|
119
|
-
const
|
|
116
|
+
const objects = await this.space.db
|
|
120
117
|
.query(
|
|
121
|
-
Filter.or(
|
|
118
|
+
Filter.or(
|
|
119
|
+
Filter.type(Person.Person),
|
|
120
|
+
Filter.type(Organization.Organization),
|
|
121
|
+
Filter.type(TestSchema.DocumentType),
|
|
122
|
+
),
|
|
122
123
|
)
|
|
123
124
|
.run();
|
|
124
125
|
|
|
@@ -129,7 +130,6 @@ class EntityExtractionMessageBuilder extends AbstractMessageBuilder {
|
|
|
129
130
|
|
|
130
131
|
const { message: enhancedMessage } = await processTranscriptMessage({
|
|
131
132
|
input: { message },
|
|
132
|
-
executor: this.executor,
|
|
133
133
|
function: extractionAnthropicFunction,
|
|
134
134
|
});
|
|
135
135
|
|
|
@@ -142,7 +142,7 @@ type UseTestTranscriptionQueue = (
|
|
|
142
142
|
queueId?: Key.ObjectId,
|
|
143
143
|
running?: boolean,
|
|
144
144
|
interval?: number,
|
|
145
|
-
) => Queue<
|
|
145
|
+
) => Queue<Message.Message> | undefined;
|
|
146
146
|
|
|
147
147
|
/**
|
|
148
148
|
* Test transcriptionqueue.
|
|
@@ -155,7 +155,7 @@ export const useTestTranscriptionQueue: UseTestTranscriptionQueue = (
|
|
|
155
155
|
) => {
|
|
156
156
|
// TODO(dmaretskyi): Use space.queues.create() instead.
|
|
157
157
|
const queueDxn = useMemo(() => (space ? createQueueDXN(space.id, queueId) : undefined), [space, queueId]);
|
|
158
|
-
const queue = useQueue<
|
|
158
|
+
const queue = useQueue<Message.Message>(queueDxn);
|
|
159
159
|
const builder = useMemo(() => new MessageBuilder(space), [space]);
|
|
160
160
|
|
|
161
161
|
useEffect(() => {
|
|
@@ -165,7 +165,7 @@ export const useTestTranscriptionQueue: UseTestTranscriptionQueue = (
|
|
|
165
165
|
|
|
166
166
|
const i = setInterval(() => {
|
|
167
167
|
void builder.createMessage(Math.ceil(Math.random() * 3)).then(async (message) => {
|
|
168
|
-
await queue.append([Obj.make(
|
|
168
|
+
await queue.append([Obj.make(Message.Message, message)]);
|
|
169
169
|
});
|
|
170
170
|
}, interval);
|
|
171
171
|
return () => clearInterval(i);
|
|
@@ -186,7 +186,7 @@ export const useTestTranscriptionQueueWithEntityExtraction: UseTestTranscription
|
|
|
186
186
|
) => {
|
|
187
187
|
// TODO(dmaretskyi): Use space.queues.create() instead.
|
|
188
188
|
const queueDxn = useMemo(() => (space ? createQueueDXN(space.id, queueId) : undefined), [space, queueId]);
|
|
189
|
-
const queue = useQueue<
|
|
189
|
+
const queue = useQueue<Message.Message>(queueDxn);
|
|
190
190
|
const [builder] = useState(() => new EntityExtractionMessageBuilder());
|
|
191
191
|
|
|
192
192
|
useEffect(() => {
|
|
@@ -203,7 +203,7 @@ export const useTestTranscriptionQueueWithEntityExtraction: UseTestTranscription
|
|
|
203
203
|
ctx,
|
|
204
204
|
async () => {
|
|
205
205
|
const message = await builder.createMessage();
|
|
206
|
-
void queue.append([Obj.make(
|
|
206
|
+
void queue.append([Obj.make(Message.Message, message)]);
|
|
207
207
|
},
|
|
208
208
|
interval,
|
|
209
209
|
);
|
package/src/transcriber/index.ts
CHANGED
|
@@ -19,7 +19,7 @@ const initializeExtendableMediaRecorder = async () => {
|
|
|
19
19
|
await register(await connect());
|
|
20
20
|
};
|
|
21
21
|
|
|
22
|
-
export type
|
|
22
|
+
export type MediaStreamRecorderProps = {
|
|
23
23
|
mediaStreamTrack: MediaStreamTrack;
|
|
24
24
|
config: {
|
|
25
25
|
interval: number;
|
|
@@ -37,12 +37,12 @@ export class MediaStreamRecorder implements AudioRecorder {
|
|
|
37
37
|
/**
|
|
38
38
|
* Default MediaRecorder implementation do not support wav encoding.
|
|
39
39
|
*/
|
|
40
|
-
private readonly _config:
|
|
40
|
+
private readonly _config: MediaStreamRecorderProps['config'];
|
|
41
41
|
private _mediaRecorder?: IMediaRecorder = undefined;
|
|
42
42
|
private _header?: Uint8Array = undefined;
|
|
43
43
|
private _onChunk?: (chunk: AudioChunk) => void;
|
|
44
44
|
|
|
45
|
-
constructor({ mediaStreamTrack, config }:
|
|
45
|
+
constructor({ mediaStreamTrack, config }: MediaStreamRecorderProps) {
|
|
46
46
|
this._mediaStreamTrack = mediaStreamTrack;
|
|
47
47
|
this._config = config;
|
|
48
48
|
}
|
|
@@ -4,15 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
import fs from 'node:fs';
|
|
6
6
|
import path from 'node:path';
|
|
7
|
-
|
|
8
7
|
import { describe, expect, test } from 'vitest';
|
|
9
8
|
import { WaveFile } from 'wavefile';
|
|
10
9
|
|
|
11
10
|
import { Trigger } from '@dxos/async';
|
|
12
11
|
import { log } from '@dxos/log';
|
|
13
|
-
import { type DataType } from '@dxos/schema';
|
|
14
12
|
import { openAndClose } from '@dxos/test-utils';
|
|
15
13
|
import { TRACE_PROCESSOR, trace } from '@dxos/tracing';
|
|
14
|
+
import { type ContentBlock } from '@dxos/types';
|
|
16
15
|
|
|
17
16
|
import { type AudioChunk, type AudioRecorder, Transcriber } from '../transcriber';
|
|
18
17
|
import { mergeFloat64Arrays } from '../util';
|
|
@@ -94,7 +93,7 @@ describe.skip('Transcriber', () => {
|
|
|
94
93
|
});
|
|
95
94
|
|
|
96
95
|
test.skip('transcription of audio recording', { timeout: 10_000 }, async () => {
|
|
97
|
-
const trigger = new Trigger<
|
|
96
|
+
const trigger = new Trigger<ContentBlock.Transcript[]>({ autoReset: true });
|
|
98
97
|
const recorder = new MockAudioRecorder({
|
|
99
98
|
buffer: await readFile('test.wav'),
|
|
100
99
|
chunkDuration: 3_000,
|
|
@@ -128,7 +127,7 @@ describe.skip('Transcriber', () => {
|
|
|
128
127
|
});
|
|
129
128
|
|
|
130
129
|
test.skip('transcription of audio recording with overlapping chunks', { timeout: 20_000 }, async () => {
|
|
131
|
-
const trigger = new Trigger<
|
|
130
|
+
const trigger = new Trigger<ContentBlock.Transcript[]>({ autoReset: true });
|
|
132
131
|
const recorder = new MockAudioRecorder({
|
|
133
132
|
buffer: await readFile('test.wav'),
|
|
134
133
|
chunkDuration: 3_000,
|
|
@@ -213,7 +212,7 @@ class MockAudioRecorder implements AudioRecorder {
|
|
|
213
212
|
|
|
214
213
|
@trace.span()
|
|
215
214
|
async start(): Promise<void> {
|
|
216
|
-
log.info('start mock audio recorder', {
|
|
215
|
+
log.info('start mock audio recorder', { wavProps: this.wav.fmt });
|
|
217
216
|
const now = Date.now();
|
|
218
217
|
const samples = this.wav.getSamples();
|
|
219
218
|
const sampleRate = this.wavConfig.sampleRate;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { describe, test } from 'vitest';
|
|
6
|
+
|
|
7
|
+
import { type AudioChunk } from './audio-recorder';
|
|
8
|
+
import { alignWhisperSegments, type WhisperSegment } from './transcriber';
|
|
9
|
+
|
|
10
|
+
const T0 = Date.parse('2026-01-01T00:00:00.000Z');
|
|
11
|
+
|
|
12
|
+
const chunk = (offsetMs = 0, samples = 16): AudioChunk => ({
|
|
13
|
+
timestamp: T0 + offsetMs,
|
|
14
|
+
data: new Float64Array(samples),
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const segment = (overrides: Partial<WhisperSegment>): WhisperSegment => ({
|
|
18
|
+
text: '',
|
|
19
|
+
start: 0,
|
|
20
|
+
end: 0,
|
|
21
|
+
no_speech_prob: 0,
|
|
22
|
+
words: [],
|
|
23
|
+
...overrides,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
describe('alignWhisperSegments', () => {
|
|
27
|
+
test('returns empty when no chunks were transcribed', ({ expect }) => {
|
|
28
|
+
const result = alignWhisperSegments(
|
|
29
|
+
[segment({ text: 'x', end: 1, words: [{ word: 'x', start: 0, end: 1 }] })],
|
|
30
|
+
[],
|
|
31
|
+
0,
|
|
32
|
+
);
|
|
33
|
+
expect(result.transcripts).toEqual([]);
|
|
34
|
+
expect(result.lastUsedTimestamp).toBeUndefined();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('absolute-timestamps and deduplicates segments against lastUsedTimestamp', ({ expect }) => {
|
|
38
|
+
const segments: WhisperSegment[] = [
|
|
39
|
+
segment({
|
|
40
|
+
text: 'hello world',
|
|
41
|
+
start: 0,
|
|
42
|
+
end: 1,
|
|
43
|
+
words: [
|
|
44
|
+
{ word: 'hello ', start: 0, end: 0.5 },
|
|
45
|
+
{ word: 'world', start: 0.5, end: 1 },
|
|
46
|
+
],
|
|
47
|
+
}),
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
const first = alignWhisperSegments(segments, [chunk(0)], 0);
|
|
51
|
+
expect(first.transcripts).toHaveLength(1);
|
|
52
|
+
expect(first.transcripts[0]._tag).toBe('transcript');
|
|
53
|
+
expect(first.transcripts[0].text).toBe('hello world');
|
|
54
|
+
expect(new Date(first.transcripts[0].started).getTime()).toBe(T0);
|
|
55
|
+
expect(first.lastUsedTimestamp).toBe(T0 + 1_000);
|
|
56
|
+
|
|
57
|
+
// Same segments, same chunk start: every word's absolute start is < lastUsedTimestamp -> filtered out.
|
|
58
|
+
const second = alignWhisperSegments(segments, [chunk(0)], first.lastUsedTimestamp!);
|
|
59
|
+
expect(second.transcripts).toEqual([]);
|
|
60
|
+
expect(second.lastUsedTimestamp).toBeUndefined();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('keeps later words when earlier words fall before lastUsedTimestamp', ({ expect }) => {
|
|
64
|
+
const segments: WhisperSegment[] = [
|
|
65
|
+
segment({
|
|
66
|
+
text: 'old new',
|
|
67
|
+
start: 0,
|
|
68
|
+
end: 2,
|
|
69
|
+
words: [
|
|
70
|
+
{ word: 'old ', start: 0, end: 1 },
|
|
71
|
+
{ word: 'new', start: 1.5, end: 2 },
|
|
72
|
+
],
|
|
73
|
+
}),
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
// First word starts at T0, second at T0+1500 — only the second survives a cursor at T0+1000.
|
|
77
|
+
const result = alignWhisperSegments(segments, [chunk(0)], T0 + 1_000);
|
|
78
|
+
expect(result.transcripts).toHaveLength(1);
|
|
79
|
+
expect(result.transcripts[0].text).toBe('new');
|
|
80
|
+
expect(new Date(result.transcripts[0].started).getTime()).toBe(T0 + 1_500);
|
|
81
|
+
expect(result.lastUsedTimestamp).toBe(T0 + 2_000);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test('drops segments that end before lastUsedTimestamp', ({ expect }) => {
|
|
85
|
+
const segments: WhisperSegment[] = [
|
|
86
|
+
segment({
|
|
87
|
+
text: 'stale',
|
|
88
|
+
start: 0,
|
|
89
|
+
end: 0.5,
|
|
90
|
+
words: [{ word: 'stale', start: 0, end: 0.5 }],
|
|
91
|
+
}),
|
|
92
|
+
segment({
|
|
93
|
+
text: 'fresh',
|
|
94
|
+
start: 1,
|
|
95
|
+
end: 2,
|
|
96
|
+
words: [{ word: 'fresh', start: 1.5, end: 2 }],
|
|
97
|
+
}),
|
|
98
|
+
];
|
|
99
|
+
|
|
100
|
+
const result = alignWhisperSegments(segments, [chunk(0)], T0 + 1_000);
|
|
101
|
+
expect(result.transcripts).toHaveLength(1);
|
|
102
|
+
expect(result.transcripts[0].text).toBe('fresh');
|
|
103
|
+
});
|
|
104
|
+
});
|
|
@@ -7,15 +7,15 @@ import { WaveFile } from 'wavefile';
|
|
|
7
7
|
import { DeferredTask, Trigger } from '@dxos/async';
|
|
8
8
|
import { type Context, LifecycleState, Resource } from '@dxos/context';
|
|
9
9
|
import { log } from '@dxos/log';
|
|
10
|
-
import { type DataType } from '@dxos/schema';
|
|
11
10
|
import { trace } from '@dxos/tracing';
|
|
11
|
+
import { type ContentBlock } from '@dxos/types';
|
|
12
12
|
|
|
13
|
-
import { TRANSCRIPTION_URL } from '
|
|
14
|
-
import { mergeFloat64Arrays } from '../util';
|
|
13
|
+
import { TRANSCRIPTION_URL } from '#types';
|
|
15
14
|
|
|
15
|
+
import { mergeFloat64Arrays } from '../util';
|
|
16
16
|
import { type AudioChunk, type AudioRecorder } from './audio-recorder';
|
|
17
17
|
|
|
18
|
-
type WhisperWord = {
|
|
18
|
+
export type WhisperWord = {
|
|
19
19
|
word: string;
|
|
20
20
|
|
|
21
21
|
/**
|
|
@@ -29,7 +29,7 @@ type WhisperWord = {
|
|
|
29
29
|
end: number;
|
|
30
30
|
};
|
|
31
31
|
|
|
32
|
-
type WhisperSegment = {
|
|
32
|
+
export type WhisperSegment = {
|
|
33
33
|
text: string;
|
|
34
34
|
|
|
35
35
|
/**
|
|
@@ -61,16 +61,32 @@ export type TranscribeConfig = {
|
|
|
61
61
|
* This is needed to catch the beginning of the user's speech.
|
|
62
62
|
*/
|
|
63
63
|
prefixBufferChunksAmount: number;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Override the transcription endpoint base URL.
|
|
67
|
+
* Defaults to the DXOS calls service.
|
|
68
|
+
*/
|
|
69
|
+
endpoint?: string;
|
|
64
70
|
};
|
|
65
71
|
|
|
66
|
-
|
|
72
|
+
/**
|
|
73
|
+
* Function that converts a base64-encoded WAV payload into Whisper segments.
|
|
74
|
+
* Allows callers to swap in alternative providers or mock the transport.
|
|
75
|
+
*/
|
|
76
|
+
export type TranscribeFn = (audio: string) => Promise<WhisperSegment[]>;
|
|
77
|
+
|
|
78
|
+
export type TranscriberProps = {
|
|
67
79
|
config: TranscribeConfig;
|
|
68
80
|
recorder: AudioRecorder;
|
|
69
81
|
/**
|
|
70
82
|
* Callback to handle the transcribed segments, after all segment transformers are applied.
|
|
71
83
|
* @param segments - The transcribed segments.
|
|
72
84
|
*/
|
|
73
|
-
onSegments: (segments:
|
|
85
|
+
onSegments: (segments: ContentBlock.Transcript[]) => Promise<void>;
|
|
86
|
+
/**
|
|
87
|
+
* Optional override of the transcription transport. When provided, supersedes `config.endpoint`.
|
|
88
|
+
*/
|
|
89
|
+
transcribe?: TranscribeFn;
|
|
74
90
|
};
|
|
75
91
|
|
|
76
92
|
/**
|
|
@@ -86,16 +102,18 @@ export class Transcriber extends Resource {
|
|
|
86
102
|
|
|
87
103
|
private readonly _config: TranscribeConfig;
|
|
88
104
|
private readonly _recorder: AudioRecorder;
|
|
89
|
-
private readonly _onSegments:
|
|
105
|
+
private readonly _onSegments: TranscriberProps['onSegments'];
|
|
106
|
+
private readonly _transcribeFn?: TranscribeFn;
|
|
90
107
|
|
|
91
108
|
private _recording = false;
|
|
92
109
|
private _transcribeTask?: DeferredTask = undefined;
|
|
93
110
|
|
|
94
|
-
constructor({ config, recorder, onSegments }:
|
|
111
|
+
constructor({ config, recorder, onSegments, transcribe }: TranscriberProps) {
|
|
95
112
|
super();
|
|
96
113
|
this._config = config;
|
|
97
114
|
this._recorder = recorder;
|
|
98
115
|
this._onSegments = onSegments;
|
|
116
|
+
this._transcribeFn = transcribe;
|
|
99
117
|
}
|
|
100
118
|
|
|
101
119
|
protected override async _open(ctx: Context): Promise<void> {
|
|
@@ -192,67 +210,90 @@ export class Transcriber extends Resource {
|
|
|
192
210
|
@trace.span({ showInBrowserTimeline: true })
|
|
193
211
|
private async _fetchTranscription(audio: string): Promise<WhisperSegment[]> {
|
|
194
212
|
if (audio.length === 0) {
|
|
195
|
-
this._audioChunks = [];
|
|
196
213
|
throw new Error('No audio to send for transcribing');
|
|
197
214
|
}
|
|
198
215
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
216
|
+
let segments: unknown;
|
|
217
|
+
if (this._transcribeFn) {
|
|
218
|
+
segments = await this._transcribeFn(audio);
|
|
219
|
+
} else {
|
|
220
|
+
const endpoint = this._config.endpoint ?? TRANSCRIPTION_URL;
|
|
221
|
+
const response = await fetch(`${endpoint}/transcribe`, {
|
|
222
|
+
method: 'POST',
|
|
223
|
+
body: JSON.stringify({ audio }),
|
|
224
|
+
headers: {
|
|
225
|
+
'Content-Type': 'application/json',
|
|
226
|
+
},
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
if (!response.ok) {
|
|
230
|
+
throw new Error(`Transcription failed: ${response.statusText}`);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
({ segments } = (await response.json()) as { segments?: unknown });
|
|
210
234
|
}
|
|
211
235
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
}
|
|
236
|
+
if (!Array.isArray(segments)) {
|
|
237
|
+
throw new Error('Transcription response payload is invalid');
|
|
238
|
+
}
|
|
215
239
|
|
|
216
|
-
log.info('transcription response', {
|
|
217
|
-
segments: segments.length,
|
|
218
|
-
string: segments.map((segments) => segments.text).join(' '),
|
|
219
|
-
});
|
|
240
|
+
log.info('transcription response', { segments: segments.length });
|
|
220
241
|
|
|
221
|
-
return segments;
|
|
242
|
+
return segments as WhisperSegment[];
|
|
222
243
|
}
|
|
223
244
|
|
|
224
|
-
private _alignSegments(segments: WhisperSegment[], originalChunks: AudioChunk[]):
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
const filteredSegments = segments
|
|
229
|
-
// Use segments that end after the last used timestamp.
|
|
230
|
-
// Segments could overlap, so we need to filter words that starts after the last used timestamp.
|
|
231
|
-
.filter((segment) => zeroTimestamp + segment.end * 1_000 >= this._lastUsedTimestamp)
|
|
232
|
-
// Use words that starts after the last used timestamp.
|
|
233
|
-
.map((segment) => {
|
|
234
|
-
const words = segment.words.filter((word) => zeroTimestamp + word.start * 1_000 >= this._lastUsedTimestamp);
|
|
235
|
-
return {
|
|
236
|
-
...segment,
|
|
237
|
-
words,
|
|
238
|
-
text: words.map((word) => word.word).join(''),
|
|
239
|
-
start: words.at(0)?.start ?? segment.end,
|
|
240
|
-
};
|
|
241
|
-
})
|
|
242
|
-
.filter((segment) => segment.words.length > 0);
|
|
243
|
-
|
|
244
|
-
if (filteredSegments.length === 0) {
|
|
245
|
-
return [];
|
|
245
|
+
private _alignSegments(segments: WhisperSegment[], originalChunks: AudioChunk[]): ContentBlock.Transcript[] {
|
|
246
|
+
const result = alignWhisperSegments(segments, originalChunks, this._lastUsedTimestamp);
|
|
247
|
+
if (result.lastUsedTimestamp !== undefined) {
|
|
248
|
+
this._lastUsedTimestamp = result.lastUsedTimestamp;
|
|
246
249
|
}
|
|
250
|
+
return result.transcripts;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Pure helper that filters and absolute-timestamps Whisper segments against a chunk timeline.
|
|
256
|
+
* Exported for testing.
|
|
257
|
+
*
|
|
258
|
+
* @param segments - Raw segments returned by the Whisper service for `originalChunks`.
|
|
259
|
+
* @param originalChunks - Chunks fed into the transcription request (used for the zero timestamp).
|
|
260
|
+
* @param lastUsedTimestamp - Most recent absolute timestamp already emitted; segments whose end
|
|
261
|
+
* falls strictly before this point are deduped out, as are individual words whose start does.
|
|
262
|
+
*/
|
|
263
|
+
export const alignWhisperSegments = (
|
|
264
|
+
segments: WhisperSegment[],
|
|
265
|
+
originalChunks: AudioChunk[],
|
|
266
|
+
lastUsedTimestamp: number,
|
|
267
|
+
): { transcripts: ContentBlock.Transcript[]; lastUsedTimestamp?: number } => {
|
|
268
|
+
if (originalChunks.length === 0) {
|
|
269
|
+
return { transcripts: [] };
|
|
270
|
+
}
|
|
247
271
|
|
|
248
|
-
|
|
249
|
-
|
|
272
|
+
const zeroTimestamp = originalChunks[0].timestamp;
|
|
273
|
+
|
|
274
|
+
const filteredSegments = segments
|
|
275
|
+
.filter((segment) => zeroTimestamp + segment.end * 1_000 >= lastUsedTimestamp)
|
|
276
|
+
.map((segment) => {
|
|
277
|
+
const words = segment.words.filter((word) => zeroTimestamp + word.start * 1_000 >= lastUsedTimestamp);
|
|
278
|
+
return {
|
|
279
|
+
...segment,
|
|
280
|
+
words,
|
|
281
|
+
text: words.map((word) => word.word).join(''),
|
|
282
|
+
start: words.at(0)?.start ?? segment.end,
|
|
283
|
+
};
|
|
284
|
+
})
|
|
285
|
+
.filter((segment) => segment.words.length > 0);
|
|
286
|
+
|
|
287
|
+
if (filteredSegments.length === 0) {
|
|
288
|
+
return { transcripts: [] };
|
|
289
|
+
}
|
|
250
290
|
|
|
251
|
-
|
|
252
|
-
|
|
291
|
+
return {
|
|
292
|
+
transcripts: filteredSegments.map((segment) => ({
|
|
253
293
|
_tag: 'transcript',
|
|
254
294
|
started: new Date(zeroTimestamp + segment.start * 1_000).toISOString(),
|
|
255
295
|
text: segment.text.trim(),
|
|
256
|
-
}))
|
|
257
|
-
|
|
258
|
-
}
|
|
296
|
+
})),
|
|
297
|
+
lastUsedTimestamp: zeroTimestamp + filteredSegments.at(-1)!.end * 1_000,
|
|
298
|
+
};
|
|
299
|
+
};
|