@dxos/plugin-transcription 0.8.4-main.fd6878d → 0.9.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/LICENSE +102 -5
- package/PLUGIN.mdl +401 -0
- package/README.md +1 -1
- package/dist/lib/neutral/TranscriptionArticle-L6XANBH5.mjs +54 -0
- package/dist/lib/neutral/TranscriptionArticle-L6XANBH5.mjs.map +7 -0
- package/dist/lib/neutral/TranscriptionPlugin.mjs +46 -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/TranscriptionPlugin.workerd.mjs +21 -0
- package/dist/lib/neutral/TranscriptionPlugin.workerd.mjs.map +7 -0
- package/dist/lib/neutral/blueprint-definition-DA4QFRYJ.mjs +15 -0
- package/dist/lib/neutral/blueprint-definition-DA4QFRYJ.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-FHNI26QW.mjs +45 -0
- package/dist/lib/neutral/chunk-FHNI26QW.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/neutral/chunk-KMT3BMMQ.mjs +124 -0
- package/dist/lib/neutral/chunk-KMT3BMMQ.mjs.map +7 -0
- package/dist/lib/neutral/chunk-KXJZEYS7.mjs +8 -0
- package/dist/lib/neutral/chunk-KXJZEYS7.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-TO77KAQK.mjs +298 -0
- package/dist/lib/neutral/chunk-TO77KAQK.mjs.map +7 -0
- package/dist/lib/{browser/chunk-KJIQQHJB.mjs → neutral/chunk-TX3NQISZ.mjs} +115 -186
- package/dist/lib/neutral/chunk-TX3NQISZ.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 +269 -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-RVR7VC33.mjs +21 -0
- package/dist/lib/neutral/create-RVR7VC33.mjs.map +7 -0
- package/dist/lib/neutral/hooks/index.mjs +19 -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-A5KXKRH4.mjs +27 -0
- package/dist/lib/neutral/open-A5KXKRH4.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-YMZRO242.mjs +26 -0
- package/dist/lib/neutral/react-surface-YMZRO242.mjs.map +7 -0
- package/dist/lib/neutral/summarize-5XPPMVTE.mjs +77 -0
- package/dist/lib/neutral/summarize-5XPPMVTE.mjs.map +7 -0
- package/dist/lib/neutral/text-content-KBB37HSM.mjs +30 -0
- package/dist/lib/neutral/text-content-KBB37HSM.mjs.map +7 -0
- package/dist/lib/neutral/transcriber-OGSF3XO5.mjs +59 -0
- package/dist/lib/neutral/transcriber-OGSF3XO5.mjs.map +7 -0
- package/dist/lib/neutral/translations.mjs +24 -0
- package/dist/lib/neutral/translations.mjs.map +7 -0
- package/dist/lib/neutral/types/index.mjs +10 -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/TranscriptionPlugin.workerd.d.ts +4 -0
- package/dist/types/src/TranscriptionPlugin.workerd.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 +6 -0
- package/dist/types/src/capabilities/blueprint-definition.d.ts.map +1 -0
- package/dist/types/src/capabilities/index.d.ts +11 -4
- 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/Oracle/Oracle.d.ts +24 -0
- package/dist/types/src/components/Oracle/Oracle.d.ts.map +1 -0
- package/dist/types/src/components/Oracle/Oracle.stories.d.ts +32 -0
- package/dist/types/src/components/Oracle/Oracle.stories.d.ts.map +1 -0
- package/dist/types/src/components/Oracle/index.d.ts +2 -0
- package/dist/types/src/components/Oracle/index.d.ts.map +1 -0
- package/dist/types/src/components/Transcription/Transcription.d.ts +11 -0
- package/dist/types/src/components/Transcription/Transcription.d.ts.map +1 -0
- package/dist/types/src/components/Transcription/Transcription.stories.d.ts +37 -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 +2 -3
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/containers/TranscriptionArticle/TranscriptionArticle.d.ts +6 -0
- package/dist/types/src/containers/TranscriptionArticle/TranscriptionArticle.d.ts.map +1 -0
- package/dist/types/src/containers/TranscriptionArticle/TranscriptionArticle.stories.d.ts +28 -0
- package/dist/types/src/containers/TranscriptionArticle/TranscriptionArticle.stories.d.ts.map +1 -0
- package/dist/types/src/containers/TranscriptionArticle/index.d.ts +2 -0
- package/dist/types/src/containers/TranscriptionArticle/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/enrichment/conversation.test.d.ts +2 -0
- package/dist/types/src/enrichment/conversation.test.d.ts.map +1 -0
- package/dist/types/src/enrichment/operations.d.ts +200 -0
- package/dist/types/src/enrichment/operations.d.ts.map +1 -0
- package/dist/types/src/enrichment/parser.d.ts +26 -0
- package/dist/types/src/enrichment/parser.d.ts.map +1 -0
- package/dist/types/src/enrichment/stream-simulator.d.ts +38 -0
- package/dist/types/src/enrichment/stream-simulator.d.ts.map +1 -0
- package/dist/types/src/hooks/index.d.ts +2 -1
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- 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/useFeedModelAdapter.d.ts +12 -0
- package/dist/types/src/hooks/useFeedModelAdapter.d.ts.map +1 -0
- 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/useTranscriptionRecording.d.ts +12 -0
- package/dist/types/src/hooks/useTranscriptionRecording.d.ts.map +1 -0
- 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 +9 -7
- package/dist/types/src/normalization/message-normalizer.d.ts.map +1 -0
- package/dist/types/src/normalization/normalization.d.ts +834 -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/stories/FileTranscription.stories.d.ts +20 -0
- package/dist/types/src/stories/FileTranscription.stories.d.ts.map +1 -0
- package/dist/types/src/stories/KeyWordRecognition.stories.d.ts +14 -0
- package/dist/types/src/stories/KeyWordRecognition.stories.d.ts.map +1 -0
- package/dist/types/src/stories/LiveTranscription.stories.d.ts +20 -0
- package/dist/types/src/stories/LiveTranscription.stories.d.ts.map +1 -0
- package/dist/types/src/stories/TranscriptionStory.d.ts +21 -0
- package/dist/types/src/stories/TranscriptionStory.d.ts.map +1 -0
- package/dist/types/src/stories/common.d.ts +248 -0
- package/dist/types/src/stories/common.d.ts.map +1 -0
- package/dist/types/src/testing/testing.d.ts +14 -15
- 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 +61 -6
- 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 +17 -16
- package/dist/types/src/transcriber/transcription-manager.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +9 -8
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/TranscriptOperation.d.ts +1670 -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/index.d.ts +2 -2
- 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 +153 -85
- package/src/TranscriptionPlugin.node.ts +20 -0
- package/src/TranscriptionPlugin.test.ts +31 -0
- package/src/TranscriptionPlugin.tsx +28 -61
- package/src/TranscriptionPlugin.workerd.ts +18 -0
- package/src/blueprints/index.ts +5 -0
- package/src/blueprints/transcription-blueprint.ts +29 -0
- package/src/capabilities/blueprint-definition.ts +19 -0
- package/src/capabilities/index.ts +16 -6
- 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 +54 -41
- package/src/components/Oracle/Oracle.stories.tsx +147 -0
- package/src/components/Oracle/Oracle.tsx +142 -0
- package/src/components/Oracle/index.ts +5 -0
- package/src/components/Transcription/Transcription.stories.tsx +213 -0
- package/src/components/Transcription/Transcription.tsx +55 -0
- package/src/components/{Transcript → Transcription}/index.ts +1 -1
- package/src/components/{Transcript/transcript-extension.ts → Transcription/transcription-extension.ts} +13 -46
- package/src/components/index.ts +2 -6
- package/src/containers/TranscriptionArticle/TranscriptionArticle.stories.tsx +61 -0
- package/src/containers/TranscriptionArticle/TranscriptionArticle.tsx +62 -0
- package/src/containers/TranscriptionArticle/index.ts +5 -0
- package/src/containers/index.ts +7 -0
- package/src/enrichment/conversation.test.ts +162 -0
- package/src/enrichment/conversation.txt +23 -0
- package/src/enrichment/operations.ts +271 -0
- package/src/enrichment/parser.ts +101 -0
- package/src/enrichment/stream-simulator.ts +125 -0
- package/src/hooks/index.ts +2 -1
- package/src/hooks/useAudioFile.ts +49 -53
- package/src/hooks/useAudioTrack.ts +1 -1
- package/src/hooks/useFeedModelAdapter.ts +46 -0
- package/src/hooks/useTranscriber.ts +15 -7
- package/src/hooks/useTranscriptionRecording.ts +87 -0
- package/src/hooks/useVoiceInput.ts +3 -4
- package/src/index.ts +4 -5
- package/src/meta.ts +32 -7
- package/src/model/model.test.ts +22 -16
- package/src/model/model.ts +3 -3
- package/src/{segments-normalization → normalization}/message-normalizer.ts +37 -24
- package/src/{segments-normalization → normalization}/normalization.test.ts +9 -48
- 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 +102 -0
- package/src/plugin.ts +11 -0
- package/src/stories/FileTranscription.stories.tsx +143 -0
- package/src/{components/stories → stories}/KeyWordRecognition.stories.tsx +50 -46
- package/src/stories/LiveTranscription.stories.tsx +153 -0
- package/src/stories/TranscriptionStory.tsx +101 -0
- package/src/stories/common.ts +233 -0
- package/src/testing/testing.ts +53 -65
- 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 +105 -60
- package/src/transcriber/transcription-manager.ts +49 -59
- package/src/translations.ts +10 -7
- package/src/types/TranscriptOperation.ts +106 -0
- package/src/types/TranscriptionCapabilities.ts +39 -0
- package/src/types/index.ts +2 -2
- package/src/util/index.ts +1 -0
- package/src/util/render-byline.ts +33 -0
- package/src/vite-env.d.ts +10 -0
- package/dist/lib/browser/TranscriptContainer-FH7VAWYA.mjs +0 -11
- package/dist/lib/browser/chunk-5WH45KBY.mjs +0 -19
- package/dist/lib/browser/chunk-5WH45KBY.mjs.map +0 -7
- package/dist/lib/browser/chunk-BJWYA2BU.mjs +0 -782
- package/dist/lib/browser/chunk-BJWYA2BU.mjs.map +0 -7
- package/dist/lib/browser/chunk-KJIQQHJB.mjs.map +0 -7
- package/dist/lib/browser/chunk-KWOOVS5G.mjs +0 -55
- package/dist/lib/browser/chunk-KWOOVS5G.mjs.map +0 -7
- package/dist/lib/browser/chunk-SREIAQSP.mjs +0 -16
- package/dist/lib/browser/chunk-SREIAQSP.mjs.map +0 -7
- package/dist/lib/browser/chunk-UKUSJTQW.mjs +0 -8
- package/dist/lib/browser/chunk-UKUSJTQW.mjs.map +0 -7
- package/dist/lib/browser/index.mjs +0 -125
- package/dist/lib/browser/index.mjs.map +0 -7
- package/dist/lib/browser/intent-resolver-AOVQ7JI4.mjs +0 -30
- package/dist/lib/browser/intent-resolver-AOVQ7JI4.mjs.map +0 -7
- package/dist/lib/browser/meta.json +0 -1
- package/dist/lib/browser/react-surface-4MEJMKYQ.mjs +0 -34
- package/dist/lib/browser/react-surface-4MEJMKYQ.mjs.map +0 -7
- package/dist/lib/browser/transcriber-V77CN7XX.mjs +0 -56
- package/dist/lib/browser/transcriber-V77CN7XX.mjs.map +0 -7
- package/dist/lib/browser/types/index.mjs +0 -14
- package/dist/lib/node-esm/TranscriptContainer-GEUOOYHX.mjs +0 -12
- package/dist/lib/node-esm/chunk-A33GNR44.mjs +0 -507
- package/dist/lib/node-esm/chunk-A33GNR44.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-IKQMDRY6.mjs +0 -56
- package/dist/lib/node-esm/chunk-IKQMDRY6.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-PHP7MCAY.mjs +0 -10
- package/dist/lib/node-esm/chunk-PHP7MCAY.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-TM2ASOZA.mjs +0 -17
- package/dist/lib/node-esm/chunk-TM2ASOZA.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-XMUCZBP3.mjs +0 -21
- package/dist/lib/node-esm/chunk-XMUCZBP3.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-Y7PCJ34K.mjs +0 -783
- package/dist/lib/node-esm/chunk-Y7PCJ34K.mjs.map +0 -7
- package/dist/lib/node-esm/index.mjs +0 -126
- package/dist/lib/node-esm/index.mjs.map +0 -7
- package/dist/lib/node-esm/intent-resolver-P4XPIKQF.mjs +0 -31
- package/dist/lib/node-esm/intent-resolver-P4XPIKQF.mjs.map +0 -7
- package/dist/lib/node-esm/meta.json +0 -1
- package/dist/lib/node-esm/react-surface-EZUPNHUC.mjs +0 -35
- package/dist/lib/node-esm/react-surface-EZUPNHUC.mjs.map +0 -7
- package/dist/lib/node-esm/transcriber-3SX3DNLV.mjs +0 -57
- package/dist/lib/node-esm/transcriber-3SX3DNLV.mjs.map +0 -7
- package/dist/lib/node-esm/types/index.mjs +0 -15
- 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 -24
- 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 -19
- 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 +0 -17
- package/dist/types/src/components/stories/FileTranscription.stories.d.ts.map +0 -1
- package/dist/types/src/components/stories/KeyWordRecognition.stories.d.ts +0 -10
- package/dist/types/src/components/stories/KeyWordRecognition.stories.d.ts.map +0 -1
- package/dist/types/src/components/stories/MicrophoneTranscription.stories.d.ts +0 -19
- package/dist/types/src/components/stories/MicrophoneTranscription.stories.d.ts.map +0 -1
- package/dist/types/src/components/stories/TranscriptionStory.d.ts +0 -11
- package/dist/types/src/components/stories/TranscriptionStory.d.ts.map +0 -1
- package/dist/types/src/components/stories/useIsSpeaking.d.ts +0 -3
- package/dist/types/src/components/stories/useIsSpeaking.d.ts.map +0 -1
- package/dist/types/src/hooks/useQueueModelAdapter.d.ts +0 -8
- package/dist/types/src/hooks/useQueueModelAdapter.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 -422
- 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/schema.d.ts +0 -41
- package/dist/types/src/types/schema.d.ts.map +0 -1
- package/dist/types/src/types/types.d.ts +0 -31
- 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 -24
- package/src/components/Transcript/Transcript.stories.tsx +0 -243
- package/src/components/Transcript/Transcript.tsx +0 -101
- package/src/components/Transcript/deprecated/OldTranscript.tsx +0 -260
- package/src/components/TranscriptContainer.tsx +0 -36
- package/src/components/stories/FileTranscription.stories.tsx +0 -246
- package/src/components/stories/MicrophoneTranscription.stories.tsx +0 -283
- package/src/components/stories/TranscriptionStory.tsx +0 -41
- package/src/components/stories/useIsSpeaking.tsx +0 -40
- package/src/hooks/useQueueModelAdapter.ts +0 -66
- package/src/segments-normalization/normalization.ts +0 -115
- package/src/types/schema.ts +0 -41
- package/src/types/types.ts +0 -33
- package/dist/lib/{browser/types → neutral/blueprints}/index.mjs.map +0 -0
- package/dist/lib/{browser/TranscriptContainer-FH7VAWYA.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-GEUOOYHX.mjs.map → neutral/index.mjs.map} +0 -0
- package/dist/types/src/{segments-normalization → normalization}/normalization.test.d.ts +0 -0
- package/src/{segments-normalization → normalization}/index.ts +1 -1
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
// Operation definitions for the transcript enrichment pipeline (see ENRICHMENT.md).
|
|
6
|
+
//
|
|
7
|
+
// **Stub status.** The handlers below return deterministic dummy values rather than calling
|
|
8
|
+
// an LLM; they exist so the end-to-end test harness (`conversation.test.ts`) can exercise
|
|
9
|
+
// the schemas, the operation wiring, and the streaming/orchestration code paths without
|
|
10
|
+
// requiring an Anthropic key on first run. The real handlers will replace these stubs in
|
|
11
|
+
// a follow-up PR (calling `LanguageModel.generateObject(...)`) while keeping the same
|
|
12
|
+
// inputs / outputs.
|
|
13
|
+
|
|
14
|
+
import * as Effect from 'effect/Effect';
|
|
15
|
+
import * as Schema from 'effect/Schema';
|
|
16
|
+
|
|
17
|
+
import { Operation } from '@dxos/compute';
|
|
18
|
+
import { DXN } from '@dxos/keys';
|
|
19
|
+
|
|
20
|
+
import { meta } from '#meta';
|
|
21
|
+
|
|
22
|
+
const NAMESPACE = `${meta.id}.enrichment`;
|
|
23
|
+
|
|
24
|
+
//
|
|
25
|
+
// Shared sub-schemas.
|
|
26
|
+
//
|
|
27
|
+
|
|
28
|
+
/** A noun / proper noun surfaced for follow-up actions (create object, research, link…). */
|
|
29
|
+
export const CandidateMention = Schema.Struct({
|
|
30
|
+
text: Schema.String.annotations({ description: 'Surface form as it appears in the transcript.' }),
|
|
31
|
+
kind: Schema.Literal('noun', 'proper-noun').annotations({
|
|
32
|
+
description: 'Whether this is a common noun (e.g. "team") or a proper noun (e.g. "Munich").',
|
|
33
|
+
}),
|
|
34
|
+
start: Schema.Number.annotations({ description: 'Character offset within the corrected text.' }),
|
|
35
|
+
end: Schema.Number.annotations({ description: 'Character offset within the corrected text.' }),
|
|
36
|
+
suggested: Schema.optional(
|
|
37
|
+
Schema.Struct({
|
|
38
|
+
typename: Schema.String.annotations({
|
|
39
|
+
description: 'Optional hint for the ECHO type the user might create (e.g. "org.dxos.type.organization").',
|
|
40
|
+
}),
|
|
41
|
+
}),
|
|
42
|
+
),
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
/** Per-block enrichment output produced by Pass A. */
|
|
46
|
+
export const EnrichmentBlock = Schema.Struct({
|
|
47
|
+
/** Index into the input window; lets the caller match enrichment to the originating block. */
|
|
48
|
+
index: Schema.Number,
|
|
49
|
+
/** Corrected text — punctuation, capitalization, words split across batch boundaries reassembled. */
|
|
50
|
+
corrected: Schema.String,
|
|
51
|
+
/**
|
|
52
|
+
* IDs of ECHO objects the LLM is confident the block mentions. Strings rather than `Ref<T>`
|
|
53
|
+
* to keep the schema serialisable; the consumer resolves them via `Database.resolve(...)`.
|
|
54
|
+
*/
|
|
55
|
+
referenceIds: Schema.Array(Schema.String),
|
|
56
|
+
/** Candidate nouns / proper nouns not already covered by `referenceIds`. */
|
|
57
|
+
candidates: Schema.Array(CandidateMention),
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
//
|
|
61
|
+
// Pass A — enrichment (per Whisper batch / sliding window).
|
|
62
|
+
//
|
|
63
|
+
|
|
64
|
+
export const EnrichmentInput = Schema.Struct({
|
|
65
|
+
/** Sliding-window blocks. Each carries the speaker that produced it. */
|
|
66
|
+
window: Schema.Array(
|
|
67
|
+
Schema.Struct({
|
|
68
|
+
speaker: Schema.String,
|
|
69
|
+
text: Schema.String,
|
|
70
|
+
started: Schema.String,
|
|
71
|
+
}),
|
|
72
|
+
),
|
|
73
|
+
/** ECHO objects available in the space that the LLM may pick from when linking references. */
|
|
74
|
+
knownEntities: Schema.optional(
|
|
75
|
+
Schema.Array(
|
|
76
|
+
Schema.Struct({
|
|
77
|
+
id: Schema.String,
|
|
78
|
+
typename: Schema.String,
|
|
79
|
+
name: Schema.String,
|
|
80
|
+
}),
|
|
81
|
+
),
|
|
82
|
+
),
|
|
83
|
+
});
|
|
84
|
+
export type EnrichmentInputType = Schema.Schema.Type<typeof EnrichmentInput>;
|
|
85
|
+
|
|
86
|
+
export const EnrichmentOutput = Schema.Struct({
|
|
87
|
+
blocks: Schema.Array(EnrichmentBlock),
|
|
88
|
+
});
|
|
89
|
+
export type EnrichmentOutputType = Schema.Schema.Type<typeof EnrichmentOutput>;
|
|
90
|
+
|
|
91
|
+
export const EnrichTranscript = Operation.make({
|
|
92
|
+
meta: {
|
|
93
|
+
key: DXN.make(`${NAMESPACE}.enrich`),
|
|
94
|
+
name: 'Enrich Transcript',
|
|
95
|
+
description: 'Correct Whisper output and surface entity references and candidates over a sliding window.',
|
|
96
|
+
icon: 'ph--microphone--regular',
|
|
97
|
+
},
|
|
98
|
+
input: EnrichmentInput,
|
|
99
|
+
output: EnrichmentOutput,
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Deterministic stub: echoes the input text as `corrected`, links every block whose text contains
|
|
104
|
+
* a `knownEntities[i].name` substring to that entity's id, and surfaces every capitalised word
|
|
105
|
+
* not already linked as a `proper-noun` candidate. Sentence-end punctuation is added if missing.
|
|
106
|
+
*/
|
|
107
|
+
const enrichHandler: Operation.WithHandler<typeof EnrichTranscript> = EnrichTranscript.pipe(
|
|
108
|
+
Operation.withHandler(
|
|
109
|
+
Effect.fnUntraced(function* ({ window, knownEntities }) {
|
|
110
|
+
const known = knownEntities ?? [];
|
|
111
|
+
const blocks = window.map((entry, index) => {
|
|
112
|
+
const corrected = ensureTerminalPunctuation(entry.text);
|
|
113
|
+
const referenceIds: string[] = [];
|
|
114
|
+
const covered: Array<{ start: number; end: number }> = [];
|
|
115
|
+
for (const entity of known) {
|
|
116
|
+
const haystack = corrected.toLowerCase();
|
|
117
|
+
const needle = entity.name.toLowerCase();
|
|
118
|
+
const idx = haystack.indexOf(needle);
|
|
119
|
+
if (idx >= 0) {
|
|
120
|
+
referenceIds.push(entity.id);
|
|
121
|
+
covered.push({ start: idx, end: idx + entity.name.length });
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const candidates = [...findCapitalisedWords(corrected)].filter(
|
|
126
|
+
({ start, end }) => !covered.some((c) => c.start <= start && end <= c.end),
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
return { index, corrected, referenceIds, candidates };
|
|
130
|
+
});
|
|
131
|
+
return { blocks };
|
|
132
|
+
}),
|
|
133
|
+
),
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
//
|
|
137
|
+
// Pass B — summary (end-of-utterance + periodic).
|
|
138
|
+
//
|
|
139
|
+
|
|
140
|
+
export const SummaryInput = Schema.Struct({
|
|
141
|
+
/** The cumulative summary as of the last invocation (empty string on first call). */
|
|
142
|
+
previousSummary: Schema.String,
|
|
143
|
+
/** New corrected utterances appended since the previous summary. */
|
|
144
|
+
newUtterances: Schema.Array(
|
|
145
|
+
Schema.Struct({
|
|
146
|
+
speaker: Schema.String,
|
|
147
|
+
text: Schema.String,
|
|
148
|
+
}),
|
|
149
|
+
),
|
|
150
|
+
/** Map of speaker label → human-readable display name. Drives "I" resolution. */
|
|
151
|
+
speakerNames: Schema.optional(Schema.Record({ key: Schema.String, value: Schema.String })),
|
|
152
|
+
/** IDs and surface forms of references emitted by recent Pass A invocations. */
|
|
153
|
+
recentReferences: Schema.optional(
|
|
154
|
+
Schema.Array(
|
|
155
|
+
Schema.Struct({
|
|
156
|
+
id: Schema.String,
|
|
157
|
+
name: Schema.String,
|
|
158
|
+
}),
|
|
159
|
+
),
|
|
160
|
+
),
|
|
161
|
+
});
|
|
162
|
+
export type SummaryInputType = Schema.Schema.Type<typeof SummaryInput>;
|
|
163
|
+
|
|
164
|
+
export const ResolvedReferent = Schema.Struct({
|
|
165
|
+
surface: Schema.String.annotations({ description: 'The deictic / anaphoric form, e.g. "I", "there".' }),
|
|
166
|
+
referent: Schema.String.annotations({ description: 'Canonical name or description the surface resolves to.' }),
|
|
167
|
+
refId: Schema.optional(Schema.String).annotations({
|
|
168
|
+
description: 'ECHO object id when the referent maps to a known entity.',
|
|
169
|
+
}),
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
export const SummaryOutput = Schema.Struct({
|
|
173
|
+
summary: Schema.String,
|
|
174
|
+
resolvedReferents: Schema.Array(ResolvedReferent),
|
|
175
|
+
});
|
|
176
|
+
export type SummaryOutputType = Schema.Schema.Type<typeof SummaryOutput>;
|
|
177
|
+
|
|
178
|
+
export const SummarizeConversation = Operation.make({
|
|
179
|
+
meta: {
|
|
180
|
+
key: DXN.make(`${NAMESPACE}.summarize`),
|
|
181
|
+
name: 'Summarize Conversation',
|
|
182
|
+
description: 'Maintain a cumulative summary with referent resolution over a transcript stream.',
|
|
183
|
+
icon: 'ph--text-align-left--regular',
|
|
184
|
+
},
|
|
185
|
+
input: SummaryInput,
|
|
186
|
+
output: SummaryOutput,
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Deterministic stub: appends each new utterance to the previous summary on its own line,
|
|
191
|
+
* prefixed with the speaker's display name (resolving the speaker label via `speakerNames`).
|
|
192
|
+
* If any utterance contains "I" as a standalone word, emits a `resolvedReferents` entry binding
|
|
193
|
+
* it to the speaker's display name — this is the minimal demonstration of the Pass-C contract.
|
|
194
|
+
*/
|
|
195
|
+
const summarizeHandler: Operation.WithHandler<typeof SummarizeConversation> = SummarizeConversation.pipe(
|
|
196
|
+
Operation.withHandler(
|
|
197
|
+
Effect.fnUntraced(function* ({ previousSummary, newUtterances, speakerNames, recentReferences }) {
|
|
198
|
+
const names = speakerNames ?? {};
|
|
199
|
+
const lines = newUtterances.map((u) => `${names[u.speaker] ?? u.speaker}: ${u.text}`);
|
|
200
|
+
const summary = [previousSummary.trim(), ...lines].filter(Boolean).join('\n');
|
|
201
|
+
|
|
202
|
+
const resolvedReferents = newUtterances
|
|
203
|
+
.filter((u) => /\bI\b/.test(u.text))
|
|
204
|
+
.map((u) => {
|
|
205
|
+
const displayName = names[u.speaker] ?? u.speaker;
|
|
206
|
+
const ref = (recentReferences ?? []).find((r) => r.name === displayName);
|
|
207
|
+
return { surface: 'I', referent: displayName, refId: ref?.id };
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
return { summary, resolvedReferents };
|
|
211
|
+
}),
|
|
212
|
+
),
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
//
|
|
216
|
+
// Exports.
|
|
217
|
+
//
|
|
218
|
+
|
|
219
|
+
export const EnrichmentHandlers = [enrichHandler, summarizeHandler];
|
|
220
|
+
|
|
221
|
+
//
|
|
222
|
+
// Helpers.
|
|
223
|
+
//
|
|
224
|
+
|
|
225
|
+
const ensureTerminalPunctuation = (text: string): string => {
|
|
226
|
+
const trimmed = text.trim();
|
|
227
|
+
if (trimmed.length === 0) {
|
|
228
|
+
return trimmed;
|
|
229
|
+
}
|
|
230
|
+
return /[.!?]$/.test(trimmed) ? trimmed : `${trimmed}.`;
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
function* findCapitalisedWords(
|
|
234
|
+
text: string,
|
|
235
|
+
): Generator<{ text: string; kind: 'proper-noun'; start: number; end: number }> {
|
|
236
|
+
const re = /\b[A-Z][a-zA-Z]+(?:\s+[A-Z][a-zA-Z]+)*\b/g;
|
|
237
|
+
let match: RegExpExecArray | null;
|
|
238
|
+
while ((match = re.exec(text)) !== null) {
|
|
239
|
+
// Skip pure sentence-initial single words ("Yes", "I", "But", "They") to keep candidates focused on
|
|
240
|
+
// proper nouns. Heuristic for the stub: drop common stop-words.
|
|
241
|
+
const word = match[0];
|
|
242
|
+
if (STOP_WORDS.has(word)) {
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
yield { text: word, kind: 'proper-noun', start: match.index, end: match.index + word.length };
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const STOP_WORDS = new Set([
|
|
250
|
+
'I',
|
|
251
|
+
'Yes',
|
|
252
|
+
'No',
|
|
253
|
+
'But',
|
|
254
|
+
'And',
|
|
255
|
+
'Or',
|
|
256
|
+
'So',
|
|
257
|
+
'They',
|
|
258
|
+
'We',
|
|
259
|
+
'He',
|
|
260
|
+
'She',
|
|
261
|
+
'It',
|
|
262
|
+
'The',
|
|
263
|
+
'A',
|
|
264
|
+
'An',
|
|
265
|
+
'Is',
|
|
266
|
+
'Are',
|
|
267
|
+
'Was',
|
|
268
|
+
'Were',
|
|
269
|
+
'Be',
|
|
270
|
+
'Been',
|
|
271
|
+
]);
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
// Parser for the human-authored conversation fixture (see `conversation.txt`).
|
|
6
|
+
// Captures both the seed entities (Persons, Organizations, …) and the speaker-tagged
|
|
7
|
+
// dialogue used by `stream-simulator.ts` to feed the enrichment pipeline.
|
|
8
|
+
|
|
9
|
+
export type ConversationEntity = {
|
|
10
|
+
/** Type name as written in the fixture, e.g. `Person`, `Organization`. */
|
|
11
|
+
kind: string;
|
|
12
|
+
/** Free-form field values; consumers map these to the relevant ECHO schema. */
|
|
13
|
+
fields: Record<string, string>;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type ConversationUtterance = {
|
|
17
|
+
/** Speaker label as written in the fixture (e.g. `A`, `B`, `Rich`). */
|
|
18
|
+
speaker: string;
|
|
19
|
+
/** Raw utterance text. */
|
|
20
|
+
text: string;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type ParsedConversation = {
|
|
24
|
+
entities: ConversationEntity[];
|
|
25
|
+
utterances: ConversationUtterance[];
|
|
26
|
+
/** Distinct speaker labels in first-seen order. */
|
|
27
|
+
speakers: string[];
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Parse the on-disk conversation format into structured data.
|
|
32
|
+
*
|
|
33
|
+
* Tolerates leading whitespace inside blocks and ignores blank / `#` comment lines.
|
|
34
|
+
* Throws if the input doesn't contain a `Conversation:` block — `Entities:` is optional.
|
|
35
|
+
*/
|
|
36
|
+
export const parseConversation = (input: string): ParsedConversation => {
|
|
37
|
+
const lines = input.split('\n').map((line) => line.replace(/\s+$/, ''));
|
|
38
|
+
|
|
39
|
+
const entities: ConversationEntity[] = [];
|
|
40
|
+
const utterances: ConversationUtterance[] = [];
|
|
41
|
+
const speakerOrder = new Set<string>();
|
|
42
|
+
|
|
43
|
+
type Section = 'none' | 'entities' | 'conversation';
|
|
44
|
+
let section: Section = 'none';
|
|
45
|
+
let currentEntity: ConversationEntity | undefined;
|
|
46
|
+
|
|
47
|
+
for (const raw of lines) {
|
|
48
|
+
const line = raw.trim();
|
|
49
|
+
if (line.length === 0 || line.startsWith('#')) {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Section headers (must be left-aligned to switch sections).
|
|
54
|
+
if (raw.startsWith('Entities:') || raw.startsWith('Entitites:')) {
|
|
55
|
+
// tolerate the typo present in earlier fixtures
|
|
56
|
+
section = 'entities';
|
|
57
|
+
currentEntity = undefined;
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (raw.startsWith('Conversation:')) {
|
|
61
|
+
section = 'conversation';
|
|
62
|
+
currentEntity = undefined;
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (section === 'entities') {
|
|
67
|
+
// `<TypeName>:` starts a new entity; nested `<field>: <value>` lines fill it.
|
|
68
|
+
const typeMatch = line.match(/^([A-Za-z][A-Za-z0-9_-]*):$/);
|
|
69
|
+
if (typeMatch) {
|
|
70
|
+
currentEntity = { kind: typeMatch[1], fields: {} };
|
|
71
|
+
entities.push(currentEntity);
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
const fieldMatch = line.match(/^([A-Za-z][A-Za-z0-9_-]*):\s*(.+)$/);
|
|
75
|
+
if (fieldMatch && currentEntity) {
|
|
76
|
+
currentEntity.fields[fieldMatch[1]] = fieldMatch[2];
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
// Silently skip unrecognised lines so authors can add free-form annotations.
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (section === 'conversation') {
|
|
84
|
+
const utteranceMatch = line.match(/^([^:]+):\s*(.+)$/);
|
|
85
|
+
if (utteranceMatch) {
|
|
86
|
+
const speaker = utteranceMatch[1].trim();
|
|
87
|
+
const text = utteranceMatch[2].trim();
|
|
88
|
+
if (text.length > 0) {
|
|
89
|
+
utterances.push({ speaker, text });
|
|
90
|
+
speakerOrder.add(speaker);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (utterances.length === 0) {
|
|
97
|
+
throw new Error('parseConversation: no utterances found');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return { entities, utterances, speakers: Array.from(speakerOrder) };
|
|
101
|
+
};
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
// Turns a parsed conversation into a chronologically-ordered sequence of Whisper-shaped
|
|
6
|
+
// transcript batches, one per (speaker, utterance) pair. Each batch carries a virtual
|
|
7
|
+
// timestamp (ms since the conversation start) and the speaker label so consumers can fan
|
|
8
|
+
// out per-stream — analogous to two microphones being transcribed in parallel.
|
|
9
|
+
//
|
|
10
|
+
// The simulator does NOT do any wall-clock waiting. Tests drive virtual time via
|
|
11
|
+
// `vi.useFakeTimers()` or step through the batches directly; production code wouldn't use
|
|
12
|
+
// this module at all.
|
|
13
|
+
|
|
14
|
+
import { type ContentBlock } from '@dxos/types';
|
|
15
|
+
|
|
16
|
+
import type { ConversationUtterance, ParsedConversation } from './parser';
|
|
17
|
+
|
|
18
|
+
export type StreamSimulatorOptions = {
|
|
19
|
+
/**
|
|
20
|
+
* Average speaking rate, words per second. Used to assign a virtual duration to each
|
|
21
|
+
* utterance so consecutive utterances land at realistic offsets.
|
|
22
|
+
* @default 2.5 (≈150 wpm — typical conversational pace)
|
|
23
|
+
*/
|
|
24
|
+
wordsPerSecond?: number;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Gap between consecutive utterances in seconds. Models a brief pause for turn-taking
|
|
28
|
+
* even when speakers don't actually overlap.
|
|
29
|
+
* @default 0.5
|
|
30
|
+
*/
|
|
31
|
+
gapSeconds?: number;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Whisper batches typically arrive in chunks of N seconds rather than per-utterance.
|
|
35
|
+
* When set, utterances shorter than `batchSeconds` are merged into the same batch as
|
|
36
|
+
* the following utterance from the same speaker. `0` disables merging — every utterance
|
|
37
|
+
* is its own batch.
|
|
38
|
+
* @default 0
|
|
39
|
+
*/
|
|
40
|
+
batchSeconds?: number;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export type SimulatedBatch = {
|
|
44
|
+
/** Speaker label from the fixture (`A`, `B`, …). Maps to a virtual `identityDid`. */
|
|
45
|
+
speaker: string;
|
|
46
|
+
/** ms offset from the conversation start when this batch is delivered. */
|
|
47
|
+
offsetMs: number;
|
|
48
|
+
/** Whisper-shaped transcript blocks (raw text, no enrichment). */
|
|
49
|
+
blocks: ContentBlock.Transcript[];
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Build the chronological batch schedule for a parsed conversation. The result is a
|
|
54
|
+
* pure data structure — consumers iterate and drive virtual time themselves.
|
|
55
|
+
*/
|
|
56
|
+
export const simulateStream = (
|
|
57
|
+
conversation: ParsedConversation,
|
|
58
|
+
options: StreamSimulatorOptions = {},
|
|
59
|
+
): SimulatedBatch[] => {
|
|
60
|
+
const wordsPerSecond = options.wordsPerSecond ?? 2.5;
|
|
61
|
+
const gapSeconds = options.gapSeconds ?? 0.5;
|
|
62
|
+
const batchSeconds = options.batchSeconds ?? 0;
|
|
63
|
+
|
|
64
|
+
// Guard against divide-by-zero / NaN / negative inputs — `wordsPerSecond` divides into a
|
|
65
|
+
// duration below, and the other knobs feed into offsets that must be finite.
|
|
66
|
+
if (!Number.isFinite(wordsPerSecond) || wordsPerSecond <= 0) {
|
|
67
|
+
throw new Error(`simulateStream: wordsPerSecond must be a finite number > 0 (got ${wordsPerSecond}).`);
|
|
68
|
+
}
|
|
69
|
+
if (!Number.isFinite(gapSeconds) || gapSeconds < 0) {
|
|
70
|
+
throw new Error(`simulateStream: gapSeconds must be a finite number >= 0 (got ${gapSeconds}).`);
|
|
71
|
+
}
|
|
72
|
+
if (!Number.isFinite(batchSeconds) || batchSeconds < 0) {
|
|
73
|
+
throw new Error(`simulateStream: batchSeconds must be a finite number >= 0 (got ${batchSeconds}).`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const conversationStart = Date.UTC(2024, 0, 1, 12, 0, 0); // deterministic, ignored by callers
|
|
77
|
+
let cursorMs = 0;
|
|
78
|
+
const batches: SimulatedBatch[] = [];
|
|
79
|
+
|
|
80
|
+
// Optionally merge same-speaker consecutive utterances until they reach `batchSeconds`.
|
|
81
|
+
const grouped: { speaker: string; utterances: ConversationUtterance[] }[] = [];
|
|
82
|
+
if (batchSeconds === 0) {
|
|
83
|
+
for (const u of conversation.utterances) {
|
|
84
|
+
grouped.push({ speaker: u.speaker, utterances: [u] });
|
|
85
|
+
}
|
|
86
|
+
} else {
|
|
87
|
+
let pending: { speaker: string; utterances: ConversationUtterance[]; words: number } | undefined;
|
|
88
|
+
for (const u of conversation.utterances) {
|
|
89
|
+
const words = utteranceWordCount(u.text);
|
|
90
|
+
if (pending && pending.speaker === u.speaker && pending.words / wordsPerSecond < batchSeconds) {
|
|
91
|
+
pending.utterances.push(u);
|
|
92
|
+
pending.words += words;
|
|
93
|
+
} else {
|
|
94
|
+
if (pending) {
|
|
95
|
+
grouped.push({ speaker: pending.speaker, utterances: pending.utterances });
|
|
96
|
+
}
|
|
97
|
+
pending = { speaker: u.speaker, utterances: [u], words };
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (pending) {
|
|
101
|
+
grouped.push({ speaker: pending.speaker, utterances: pending.utterances });
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
for (const group of grouped) {
|
|
106
|
+
const startMs = cursorMs;
|
|
107
|
+
const blocks: ContentBlock.Transcript[] = [];
|
|
108
|
+
let segmentCursorMs = cursorMs;
|
|
109
|
+
for (const u of group.utterances) {
|
|
110
|
+
const durationMs = Math.max(500, (utteranceWordCount(u.text) / wordsPerSecond) * 1_000);
|
|
111
|
+
blocks.push({
|
|
112
|
+
_tag: 'transcript',
|
|
113
|
+
started: new Date(conversationStart + segmentCursorMs).toISOString(),
|
|
114
|
+
text: u.text,
|
|
115
|
+
});
|
|
116
|
+
segmentCursorMs += durationMs;
|
|
117
|
+
}
|
|
118
|
+
batches.push({ speaker: group.speaker, offsetMs: startMs, blocks });
|
|
119
|
+
cursorMs = segmentCursorMs + gapSeconds * 1_000;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return batches;
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const utteranceWordCount = (text: string): number => text.trim().split(/\s+/).filter(Boolean).length;
|
package/src/hooks/index.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
export * from './useAudioFile';
|
|
6
6
|
export * from './useAudioTrack';
|
|
7
|
-
export * from './
|
|
7
|
+
export * from './useFeedModelAdapter';
|
|
8
8
|
export * from './useTranscriber';
|
|
9
|
+
export * from './useTranscriptionRecording';
|
|
9
10
|
export * from './useVoiceInput';
|
|
@@ -20,67 +20,63 @@ export const useAudioFile = (audioUrl: string, constraints?: MediaTrackConstrain
|
|
|
20
20
|
const ctx = new Context();
|
|
21
21
|
|
|
22
22
|
scheduleTask(ctx, async () => {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
URL.
|
|
28
|
-
|
|
23
|
+
// TODO(wittjosiah): Fetch to external url fails in headless storybook test.
|
|
24
|
+
try {
|
|
25
|
+
const response = await fetch(audioUrl);
|
|
26
|
+
const blob = await response.blob();
|
|
27
|
+
const objectUrl = URL.createObjectURL(blob);
|
|
28
|
+
ctx.onDispose(() => {
|
|
29
|
+
URL.revokeObjectURL(objectUrl);
|
|
30
|
+
});
|
|
29
31
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
+
const audio = new Audio();
|
|
33
|
+
audio.src = objectUrl;
|
|
32
34
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
log.info('starting...');
|
|
46
|
-
try {
|
|
47
|
-
// Try to play the audio
|
|
48
|
-
await audio.play();
|
|
35
|
+
await new Promise<void>((resolve, reject) => {
|
|
36
|
+
audio.addEventListener(
|
|
37
|
+
'error',
|
|
38
|
+
(err) => {
|
|
39
|
+
log.error('error', { err });
|
|
40
|
+
reject(err);
|
|
41
|
+
},
|
|
42
|
+
{ once: true },
|
|
43
|
+
);
|
|
44
|
+
audio.addEventListener(
|
|
45
|
+
'canplay',
|
|
46
|
+
async () => {
|
|
49
47
|
resolve();
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
},
|
|
56
|
-
{ once: true },
|
|
57
|
-
);
|
|
58
|
-
audio.load();
|
|
59
|
-
});
|
|
48
|
+
},
|
|
49
|
+
{ once: true },
|
|
50
|
+
);
|
|
51
|
+
audio.load();
|
|
52
|
+
});
|
|
60
53
|
|
|
61
|
-
|
|
54
|
+
const audioCtx = new AudioContext();
|
|
62
55
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
56
|
+
// Resume AudioContext if it's suspended.
|
|
57
|
+
if (audioCtx.state === 'suspended') {
|
|
58
|
+
await audioCtx.resume();
|
|
59
|
+
}
|
|
67
60
|
|
|
68
|
-
|
|
69
|
-
|
|
61
|
+
const destination = audioCtx.createMediaStreamDestination();
|
|
62
|
+
destination.channelCount = 1;
|
|
70
63
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
64
|
+
const source = audioCtx.createMediaElementSource(audio);
|
|
65
|
+
source.connect(destination);
|
|
66
|
+
const track = destination.stream.getAudioTracks()[0];
|
|
67
|
+
if (constraints) {
|
|
68
|
+
await track.applyConstraints(constraints).catch((err) => log.catch(err));
|
|
69
|
+
}
|
|
77
70
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
71
|
+
// Also connect to speakers so audio is audible.
|
|
72
|
+
setStream({
|
|
73
|
+
audio,
|
|
74
|
+
stream: destination.stream,
|
|
75
|
+
track: destination.stream.getAudioTracks()[0],
|
|
76
|
+
});
|
|
77
|
+
} catch (err) {
|
|
78
|
+
log.catch(err);
|
|
79
|
+
}
|
|
84
80
|
});
|
|
85
81
|
|
|
86
82
|
return () => {
|
|
@@ -7,7 +7,7 @@ import { useEffect, useState } from 'react';
|
|
|
7
7
|
/**
|
|
8
8
|
*
|
|
9
9
|
*/
|
|
10
|
-
// TODO(burdon): Reconcile with react-ui-
|
|
10
|
+
// TODO(burdon): Reconcile with react-ui-experimental and plugin-calls.
|
|
11
11
|
export const useAudioTrack = (active?: boolean, constraints?: MediaTrackConstraints): MediaStreamTrack | undefined => {
|
|
12
12
|
const [track, setTrack] = useState<MediaStreamTrack>();
|
|
13
13
|
useEffect(() => {
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { useEffect, useMemo } from 'react';
|
|
6
|
+
|
|
7
|
+
import type { Obj } from '@dxos/echo';
|
|
8
|
+
|
|
9
|
+
import { type ChunkRenderer, SerializationModel } from '../model';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Feeds an array of objects (typically from `useQuery(db, Query.from(feed))`) into a
|
|
13
|
+
* `SerializationModel`, appending each new tail item as the array grows.
|
|
14
|
+
*
|
|
15
|
+
* @param renderer - chunk renderer.
|
|
16
|
+
* @param objects - current snapshot of feed items.
|
|
17
|
+
* @param initialChunks - chunks to seed the model with on first render.
|
|
18
|
+
*/
|
|
19
|
+
export const useFeedModelAdapter = <T extends Obj.Unknown>(
|
|
20
|
+
renderer: ChunkRenderer<T>,
|
|
21
|
+
objects: readonly T[],
|
|
22
|
+
initialChunks: T[] = [],
|
|
23
|
+
): SerializationModel<T> => {
|
|
24
|
+
const model = useMemo(() => new SerializationModel<T>(renderer, initialChunks), [renderer]);
|
|
25
|
+
|
|
26
|
+
// Seed the model with the current snapshot on first render and whenever the
|
|
27
|
+
// identity of `objects` changes (e.g. feed swapped).
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
for (const chunk of objects) {
|
|
30
|
+
model.appendChunk(chunk);
|
|
31
|
+
}
|
|
32
|
+
}, [model, objects]);
|
|
33
|
+
|
|
34
|
+
// Append new tail items as the array grows.
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
if (model.chunks.length === objects.length) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const chunk = objects.at(-1);
|
|
40
|
+
if (chunk) {
|
|
41
|
+
model.appendChunk(chunk);
|
|
42
|
+
}
|
|
43
|
+
}, [model, objects.length]);
|
|
44
|
+
|
|
45
|
+
return model;
|
|
46
|
+
};
|