@dxos/plugin-transcription 0.8.4-main.abd8ff62ef → 0.8.4-main.bc2380dfbc
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/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 +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/TranscriptionPlugin.workerd.mjs +21 -0
- package/dist/lib/neutral/TranscriptionPlugin.workerd.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/{browser/chunk-NQQK7UZQ.mjs → neutral/chunk-2O3ONPRZ.mjs} +94 -40
- package/dist/lib/neutral/chunk-2O3ONPRZ.mjs.map +7 -0
- package/dist/lib/{browser/chunk-L4HW35L6.mjs → neutral/chunk-5FXU36PN.mjs} +19 -10
- package/dist/lib/neutral/chunk-5FXU36PN.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/{browser/index.mjs → neutral/chunk-ILSA3DU7.mjs} +32 -70
- package/dist/lib/neutral/chunk-ILSA3DU7.mjs.map +7 -0
- package/dist/lib/neutral/chunk-Y5EQAFFR.mjs +8 -0
- package/dist/lib/neutral/chunk-Y5EQAFFR.mjs.map +7 -0
- package/dist/lib/{browser/blueprints/index.mjs → neutral/chunk-ZR4BH4QQ.mjs} +4 -5
- package/dist/lib/neutral/chunk-ZR4BH4QQ.mjs.map +7 -0
- package/dist/lib/{browser → neutral}/components/index.mjs +83 -18
- 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/{browser/create-ULR5DPJZ.mjs → neutral/create-RRWRIUSR.mjs} +4 -4
- package/dist/lib/neutral/create-RRWRIUSR.mjs.map +7 -0
- package/dist/lib/{browser → neutral}/hooks/index.mjs +5 -3
- 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/{browser/open-SFMPLZVW.mjs → neutral/open-CVMX5KXZ.mjs} +5 -5
- package/dist/lib/neutral/open-CVMX5KXZ.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/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/{browser/summarize-BDFX4APN.mjs → neutral/summarize-LA7TRW7A.mjs} +5 -5
- package/dist/lib/neutral/summarize-LA7TRW7A.mjs.map +7 -0
- package/dist/lib/neutral/text-content-F4ASMITB.mjs +30 -0
- package/dist/lib/neutral/text-content-F4ASMITB.mjs.map +7 -0
- package/dist/lib/neutral/transcriber-ABHATBVH.mjs +59 -0
- package/dist/lib/neutral/transcriber-ABHATBVH.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.map +1 -1
- package/dist/types/src/TranscriptionPlugin.node.d.ts.map +1 -1
- 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/capabilities/index.d.ts +5 -1
- package/dist/types/src/capabilities/index.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/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 +0 -5
- package/dist/types/src/components/Transcription/Transcription.d.ts.map +1 -1
- package/dist/types/src/components/Transcription/Transcription.stories.d.ts +13 -16
- package/dist/types/src/components/Transcription/Transcription.stories.d.ts.map +1 -1
- package/dist/types/src/components/index.d.ts +1 -0
- 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 +1 -1
- package/dist/types/src/containers/index.d.ts.map +1 -1
- 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/useFeedModelAdapter.d.ts +12 -0
- package/dist/types/src/hooks/useFeedModelAdapter.d.ts.map +1 -0
- 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/index.d.ts +3 -4
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/normalization/message-normalizer.d.ts +7 -5
- package/dist/types/src/normalization/message-normalizer.d.ts.map +1 -1
- package/dist/types/src/normalization/normalization.d.ts +180 -1
- package/dist/types/src/normalization/normalization.d.ts.map +1 -1
- package/dist/types/src/operations/create.d.ts +2 -2
- package/dist/types/src/operations/create.d.ts.map +1 -1
- package/dist/types/src/operations/index.d.ts +0 -1
- package/dist/types/src/operations/index.d.ts.map +1 -1
- package/dist/types/src/operations/open.d.ts +2 -2
- package/dist/types/src/operations/open.d.ts.map +1 -1
- package/dist/types/src/operations/summarize.d.ts +2 -2
- package/dist/types/src/operations/summarize.d.ts.map +1 -1
- 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 +5 -3
- package/dist/types/src/stories/FileTranscription.stories.d.ts.map +1 -1
- package/dist/types/src/stories/KeyWordRecognition.stories.d.ts.map +1 -1
- package/dist/types/src/stories/{MicrophoneTranscription.stories.d.ts → LiveTranscription.stories.d.ts} +4 -4
- package/dist/types/src/stories/LiveTranscription.stories.d.ts.map +1 -0
- package/dist/types/src/stories/TranscriptionStory.d.ts +11 -13
- package/dist/types/src/stories/TranscriptionStory.d.ts.map +1 -1
- package/dist/types/src/stories/common.d.ts +46 -0
- package/dist/types/src/stories/common.d.ts.map +1 -0
- package/dist/types/src/testing/testing.d.ts +5 -6
- package/dist/types/src/testing/testing.d.ts.map +1 -1
- package/dist/types/src/transcriber/transcriber.d.ts +4 -4
- package/dist/types/src/transcriber/transcriber.d.ts.map +1 -1
- package/dist/types/src/transcriber/transcription-manager.d.ts +6 -4
- package/dist/types/src/transcriber/transcription-manager.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +2 -0
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/{operations/definitions.d.ts → types/TranscriptOperation.d.ts} +1 -1
- 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/tsconfig.tsbuildinfo +1 -1
- package/package.json +125 -94
- package/src/TranscriptionPlugin.node.ts +3 -31
- package/src/TranscriptionPlugin.test.ts +3 -3
- package/src/TranscriptionPlugin.tsx +3 -31
- package/src/TranscriptionPlugin.workerd.ts +18 -0
- package/src/blueprints/transcription-blueprint.ts +1 -1
- package/src/capabilities/index.ts +1 -0
- package/src/capabilities/react-surface.tsx +2 -2
- package/src/capabilities/text-content.ts +33 -0
- package/src/capabilities/transcriber.ts +1 -1
- package/src/components/Oracle/Oracle.stories.tsx +147 -0
- package/src/components/Oracle/Oracle.tsx +141 -0
- package/src/components/Oracle/index.ts +5 -0
- package/src/components/Transcription/Transcription.stories.tsx +116 -150
- package/src/components/Transcription/Transcription.tsx +7 -13
- package/src/components/index.ts +1 -0
- 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 +1 -1
- package/src/enrichment/conversation.test.ts +162 -0
- package/src/enrichment/conversation.txt +23 -0
- package/src/enrichment/operations.ts +268 -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/useFeedModelAdapter.ts +46 -0
- package/src/hooks/useTranscriptionRecording.ts +87 -0
- package/src/index.ts +3 -9
- package/src/normalization/message-normalizer.ts +25 -13
- package/src/normalization/normalization.test.ts +3 -37
- package/src/normalization/normalization.ts +6 -7
- package/src/operations/create.ts +2 -2
- package/src/operations/index.ts +0 -2
- package/src/operations/open.ts +2 -2
- package/src/operations/summarize.ts +3 -3
- package/src/plugin.ts +11 -0
- package/src/stories/FileTranscription.stories.tsx +49 -175
- package/src/stories/KeyWordRecognition.stories.tsx +7 -9
- package/src/stories/LiveTranscription.stories.tsx +153 -0
- package/src/stories/TranscriptionStory.tsx +32 -24
- package/src/stories/common.ts +233 -0
- package/src/testing/testing.ts +20 -27
- package/src/transcriber/transcriber.test.ts +9 -9
- package/src/transcriber/transcriber.ts +18 -14
- package/src/transcriber/transcription-manager.ts +14 -9
- package/src/translations.ts +3 -0
- package/src/{operations/definitions.ts → types/TranscriptOperation.ts} +2 -0
- package/src/types/TranscriptionCapabilities.ts +39 -0
- package/src/types/index.ts +2 -2
- package/dist/lib/browser/blueprints/index.mjs.map +0 -7
- package/dist/lib/browser/chunk-3L623EVL.mjs +0 -17
- package/dist/lib/browser/chunk-3L623EVL.mjs.map +0 -7
- package/dist/lib/browser/chunk-L4HW35L6.mjs.map +0 -7
- package/dist/lib/browser/chunk-NQQK7UZQ.mjs.map +0 -7
- package/dist/lib/browser/components/index.mjs.map +0 -7
- package/dist/lib/browser/create-ULR5DPJZ.mjs.map +0 -7
- package/dist/lib/browser/index.mjs.map +0 -7
- package/dist/lib/browser/meta.json +0 -1
- package/dist/lib/browser/open-SFMPLZVW.mjs.map +0 -7
- package/dist/lib/browser/operations/index.mjs +0 -13
- package/dist/lib/browser/operations/index.mjs.map +0 -7
- package/dist/lib/browser/summarize-BDFX4APN.mjs.map +0 -7
- package/dist/lib/browser/types/index.mjs +0 -10
- package/dist/lib/node-esm/blueprints/index.mjs +0 -32
- package/dist/lib/node-esm/blueprints/index.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-7UIZWCD4.mjs +0 -112
- package/dist/lib/node-esm/chunk-7UIZWCD4.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-C3AE2NYJ.mjs +0 -181
- package/dist/lib/node-esm/chunk-C3AE2NYJ.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +0 -11
- package/dist/lib/node-esm/chunk-KO3IOD2O.mjs +0 -19
- package/dist/lib/node-esm/chunk-KO3IOD2O.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-MQK4R6GT.mjs +0 -245
- package/dist/lib/node-esm/chunk-MQK4R6GT.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-O533NBMK.mjs +0 -37
- package/dist/lib/node-esm/chunk-O533NBMK.mjs.map +0 -7
- package/dist/lib/node-esm/components/index.mjs +0 -208
- package/dist/lib/node-esm/components/index.mjs.map +0 -7
- package/dist/lib/node-esm/create-A5QHNWRF.mjs +0 -22
- package/dist/lib/node-esm/create-A5QHNWRF.mjs.map +0 -7
- package/dist/lib/node-esm/hooks/index.mjs +0 -18
- package/dist/lib/node-esm/index.mjs +0 -474
- package/dist/lib/node-esm/index.mjs.map +0 -7
- package/dist/lib/node-esm/meta.json +0 -1
- package/dist/lib/node-esm/open-OI24QNTO.mjs +0 -28
- package/dist/lib/node-esm/open-OI24QNTO.mjs.map +0 -7
- package/dist/lib/node-esm/operations/index.mjs +0 -14
- package/dist/lib/node-esm/operations/index.mjs.map +0 -7
- package/dist/lib/node-esm/summarize-3UNN24I2.mjs +0 -79
- package/dist/lib/node-esm/summarize-3UNN24I2.mjs.map +0 -7
- package/dist/lib/node-esm/types/index.mjs +0 -11
- package/dist/types/src/containers/TranscriptionContainer/TranscriptionContainer.d.ts +0 -6
- package/dist/types/src/containers/TranscriptionContainer/TranscriptionContainer.d.ts.map +0 -1
- package/dist/types/src/containers/TranscriptionContainer/index.d.ts +0 -2
- package/dist/types/src/containers/TranscriptionContainer/index.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/operations/definitions.d.ts.map +0 -1
- package/dist/types/src/stories/MicrophoneTranscription.stories.d.ts.map +0 -1
- package/dist/types/src/stories/useIsSpeaking.d.ts +0 -3
- package/dist/types/src/stories/useIsSpeaking.d.ts.map +0 -1
- package/dist/types/src/types/capabilities.d.ts +0 -19
- package/dist/types/src/types/capabilities.d.ts.map +0 -1
- package/dist/types/src/types/defs.d.ts +0 -5
- package/dist/types/src/types/defs.d.ts.map +0 -1
- package/src/containers/TranscriptionContainer/TranscriptionContainer.tsx +0 -35
- package/src/containers/TranscriptionContainer/index.ts +0 -5
- package/src/hooks/useQueueModelAdapter.ts +0 -66
- package/src/stories/MicrophoneTranscription.stories.tsx +0 -264
- package/src/stories/useIsSpeaking.tsx +0 -40
- package/src/types/capabilities.ts +0 -39
- package/src/types/defs.ts +0 -9
- /package/dist/lib/{browser/hooks → neutral/blueprints}/index.mjs.map +0 -0
- /package/dist/lib/{browser → neutral}/chunk-J5LGTIGS.mjs +0 -0
- /package/dist/lib/{browser → neutral}/chunk-J5LGTIGS.mjs.map +0 -0
- /package/dist/lib/{browser → neutral}/chunk-KAEXRX3Y.mjs +0 -0
- /package/dist/lib/{browser → neutral}/chunk-KAEXRX3Y.mjs.map +0 -0
- /package/dist/lib/{browser → neutral}/chunk-MLRJWAQH.mjs +0 -0
- /package/dist/lib/{browser → neutral}/chunk-MLRJWAQH.mjs.map +0 -0
- /package/dist/lib/{browser/types → neutral/hooks}/index.mjs.map +0 -0
- /package/dist/lib/{node-esm/hooks → neutral}/index.mjs.map +0 -0
- /package/dist/lib/{node-esm/chunk-HSLMI22Q.mjs.map → neutral/meta.mjs.map} +0 -0
- /package/dist/lib/{node-esm/types → neutral/operations}/index.mjs.map +0 -0
|
@@ -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';
|
|
@@ -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
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
7
|
+
|
|
8
|
+
import { createFeedServiceLayer } from '@dxos/client/echo';
|
|
9
|
+
import { Feed, Obj } from '@dxos/echo';
|
|
10
|
+
import { runAndForwardErrors } from '@dxos/effect';
|
|
11
|
+
import { log } from '@dxos/log';
|
|
12
|
+
import { getSpace } from '@dxos/react-client/echo';
|
|
13
|
+
import { useIdentity } from '@dxos/react-client/halo';
|
|
14
|
+
import { Message, type Transcript } from '@dxos/types';
|
|
15
|
+
|
|
16
|
+
import { useAudioTrack } from './useAudioTrack';
|
|
17
|
+
import { useTranscriber } from './useTranscriber';
|
|
18
|
+
|
|
19
|
+
export type TranscriptionRecording = {
|
|
20
|
+
recording: boolean;
|
|
21
|
+
toggleRecording: () => void;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Drives the recording lifecycle for a transcript: opens an audio track, feeds it through the
|
|
26
|
+
* transcriber, and appends transcribed message batches to the transcript's feed. Returns the
|
|
27
|
+
* recording flag and a toggle callback for the caller to wire to UI.
|
|
28
|
+
*/
|
|
29
|
+
export const useTranscriptionRecording = (transcript: Transcript.Transcript): TranscriptionRecording => {
|
|
30
|
+
const space = getSpace(transcript);
|
|
31
|
+
const identity = useIdentity();
|
|
32
|
+
const feed = transcript.feed.target;
|
|
33
|
+
|
|
34
|
+
const [recording, setRecording] = useState(false);
|
|
35
|
+
const track = useAudioTrack(recording);
|
|
36
|
+
|
|
37
|
+
// Append a transcribed message batch to the transcript's feed.
|
|
38
|
+
const handleSegments = useCallback(
|
|
39
|
+
async (blocks: Message.Message['blocks']) => {
|
|
40
|
+
if (!space || !feed || blocks.length === 0) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const sender = identity ? { identityDid: identity.did, name: identity.profile?.displayName } : {};
|
|
44
|
+
const message = Obj.make(Message.Message, {
|
|
45
|
+
sender,
|
|
46
|
+
created: new Date().toISOString(),
|
|
47
|
+
blocks,
|
|
48
|
+
});
|
|
49
|
+
const feedServiceLayer = createFeedServiceLayer(space.queues);
|
|
50
|
+
await Feed.append(feed, [message]).pipe(Effect.provide(feedServiceLayer), runAndForwardErrors);
|
|
51
|
+
},
|
|
52
|
+
[space, feed, identity],
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
// Drive the transcriber lifecycle off the recording flag + audio track.
|
|
56
|
+
const transcriber = useTranscriber({ audioStreamTrack: track, onSegments: handleSegments });
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
if (!transcriber) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
let cancelled = false;
|
|
62
|
+
// If `open()` rejects (e.g. mic permission denied, device init failure) we must
|
|
63
|
+
// clear `recording` — otherwise the toolbar button reports "stop" forever while
|
|
64
|
+
// nothing is actually being captured.
|
|
65
|
+
void transcriber.open().then(
|
|
66
|
+
() => {
|
|
67
|
+
if (!cancelled && recording) {
|
|
68
|
+
transcriber.startChunksRecording();
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
(err) => {
|
|
72
|
+
if (!cancelled) {
|
|
73
|
+
log.catch(err);
|
|
74
|
+
setRecording(false);
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
);
|
|
78
|
+
return () => {
|
|
79
|
+
cancelled = true;
|
|
80
|
+
transcriber.stopChunksRecording();
|
|
81
|
+
};
|
|
82
|
+
}, [transcriber, recording]);
|
|
83
|
+
|
|
84
|
+
const toggleRecording = useCallback(() => setRecording((value) => !value), []);
|
|
85
|
+
|
|
86
|
+
return { recording, toggleRecording };
|
|
87
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -2,16 +2,10 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { Plugin } from '@dxos/app-framework';
|
|
6
|
-
|
|
7
|
-
import { meta } from './meta';
|
|
8
|
-
|
|
9
|
-
export const TranscriptionPlugin = Plugin.lazy(meta, () => import('#plugin'));
|
|
10
|
-
|
|
11
|
-
export { TranscriptionCapabilities } from './types';
|
|
12
|
-
|
|
13
5
|
// TODO(wittjosiah): Hooks should not be exported from the plugin package at all.
|
|
14
6
|
// Either refactor callers to not need them or factor them out to a shared package.
|
|
15
|
-
export * from './
|
|
7
|
+
export * from './blueprints';
|
|
16
8
|
export * from './hooks';
|
|
9
|
+
export * from './meta';
|
|
17
10
|
export * from './transcriber';
|
|
11
|
+
export * from './types';
|
|
@@ -2,12 +2,16 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
import type * as Runtime from 'effect/Runtime';
|
|
7
|
+
|
|
5
8
|
import { DeferredTask, asyncTimeout } from '@dxos/async';
|
|
6
9
|
import { LifecycleState, Resource } from '@dxos/context';
|
|
7
|
-
import {
|
|
10
|
+
import { Feed, Filter } from '@dxos/echo';
|
|
11
|
+
import { runAndForwardErrors } from '@dxos/effect';
|
|
8
12
|
import { type FunctionExecutor } from '@dxos/functions-runtime';
|
|
9
13
|
import { log } from '@dxos/log';
|
|
10
|
-
import {
|
|
14
|
+
import { Message } from '@dxos/types';
|
|
11
15
|
|
|
12
16
|
import { type MessageWithRangeId, type NormalizationOutput, sentenceNormalization } from './normalization';
|
|
13
17
|
import { getActorId } from './utils';
|
|
@@ -17,7 +21,8 @@ const MAX_RANGE_ID_COUNT = 10;
|
|
|
17
21
|
|
|
18
22
|
export type SegmentsNormalizerProps = {
|
|
19
23
|
functionExecutor: FunctionExecutor;
|
|
20
|
-
|
|
24
|
+
feed: Feed.Feed;
|
|
25
|
+
feedRuntime: Runtime.Runtime<Feed.FeedService>;
|
|
21
26
|
startingCursor: QueueCursor;
|
|
22
27
|
};
|
|
23
28
|
|
|
@@ -30,28 +35,35 @@ export type QueueCursor = {
|
|
|
30
35
|
export class MessageNormalizer extends Resource {
|
|
31
36
|
private readonly _functionExecutor: FunctionExecutor;
|
|
32
37
|
|
|
33
|
-
private
|
|
38
|
+
private _feed: Feed.Feed;
|
|
39
|
+
private _feedRuntime: Runtime.Runtime<Feed.FeedService>;
|
|
34
40
|
private _cursor: QueueCursor;
|
|
35
41
|
private _messagesToProcess: MessageWithRangeId[] = [];
|
|
36
42
|
private _normalizationTask?: DeferredTask;
|
|
37
43
|
private _lastProcessedMessageIds?: string[];
|
|
38
44
|
|
|
39
|
-
constructor({ functionExecutor,
|
|
45
|
+
constructor({ functionExecutor, feed, feedRuntime, startingCursor }: SegmentsNormalizerProps) {
|
|
40
46
|
super();
|
|
41
47
|
this._functionExecutor = functionExecutor;
|
|
42
|
-
this.
|
|
48
|
+
this._feed = feed;
|
|
49
|
+
this._feedRuntime = feedRuntime;
|
|
43
50
|
this._cursor = startingCursor;
|
|
44
51
|
}
|
|
45
52
|
|
|
46
53
|
protected override async _open(): Promise<void> {
|
|
47
54
|
this._normalizationTask = new DeferredTask(this._ctx, () => this._processMessages());
|
|
48
55
|
|
|
56
|
+
const queryResult = await Feed.query(this._feed, Filter.type(Message.Message)).pipe(
|
|
57
|
+
Effect.provide(this._feedRuntime),
|
|
58
|
+
runAndForwardErrors,
|
|
59
|
+
);
|
|
60
|
+
|
|
49
61
|
const updateMessages = () => {
|
|
50
62
|
if (this._lifecycleState !== LifecycleState.OPEN) {
|
|
51
63
|
return;
|
|
52
64
|
}
|
|
53
65
|
|
|
54
|
-
this._messagesToProcess =
|
|
66
|
+
this._messagesToProcess = (queryResult.results as Message.Message[]).filter((message) => {
|
|
55
67
|
const actorId = getActorId(message.sender);
|
|
56
68
|
return actorId === this._cursor.actorId && message.created >= this._cursor.timestamp;
|
|
57
69
|
});
|
|
@@ -62,8 +74,8 @@ export class MessageNormalizer extends Resource {
|
|
|
62
74
|
// Initial update.
|
|
63
75
|
updateMessages();
|
|
64
76
|
|
|
65
|
-
// Subscribe to
|
|
66
|
-
const unsubscribe =
|
|
77
|
+
// Subscribe to feed changes.
|
|
78
|
+
const unsubscribe = queryResult.subscribe(updateMessages);
|
|
67
79
|
this._ctx.onDispose(unsubscribe);
|
|
68
80
|
}
|
|
69
81
|
|
|
@@ -87,7 +99,7 @@ export class MessageNormalizer extends Resource {
|
|
|
87
99
|
PROCESSING_TIMEOUT,
|
|
88
100
|
);
|
|
89
101
|
|
|
90
|
-
this._writeMessages(response.sentences);
|
|
102
|
+
await this._writeMessages(response.sentences);
|
|
91
103
|
this._lastProcessedMessageIds.push(...response.sentences.map((sentence) => sentence.id));
|
|
92
104
|
if (response.sentences.length === 1 && response.sentences.at(-1)!.rangeId!.length > MAX_RANGE_ID_COUNT) {
|
|
93
105
|
log.warn('Sentence is too long', { messages });
|
|
@@ -96,14 +108,14 @@ export class MessageNormalizer extends Resource {
|
|
|
96
108
|
} catch {
|
|
97
109
|
log.error('Failed to normalize segments', { messages });
|
|
98
110
|
// If processing failed, emit the segments as is.
|
|
99
|
-
this._writeMessages(messages);
|
|
111
|
+
await this._writeMessages(messages);
|
|
100
112
|
}
|
|
101
113
|
}
|
|
102
114
|
|
|
103
|
-
private _writeMessages(messages: MessageWithRangeId[]): void {
|
|
115
|
+
private async _writeMessages(messages: MessageWithRangeId[]): Promise<void> {
|
|
104
116
|
log.info('writing messages', { messages });
|
|
105
117
|
const lastMessage = messages[messages.length - 1];
|
|
106
118
|
this._cursor.timestamp = lastMessage.created;
|
|
107
|
-
|
|
119
|
+
await Feed.append(this._feed, messages).pipe(Effect.provide(this._feedRuntime), runAndForwardErrors);
|
|
108
120
|
}
|
|
109
121
|
}
|
|
@@ -4,18 +4,12 @@
|
|
|
4
4
|
|
|
5
5
|
import { describe, test } from 'vitest';
|
|
6
6
|
|
|
7
|
-
import { scheduleTaskInterval } from '@dxos/async';
|
|
8
|
-
import { Context } from '@dxos/context';
|
|
9
7
|
import { Obj } from '@dxos/echo';
|
|
10
|
-
import { MemoryQueue } from '@dxos/echo-db';
|
|
11
|
-
import { createQueueDXN } from '@dxos/echo/internal';
|
|
12
8
|
import { FunctionExecutor, ServiceContainer } from '@dxos/functions-runtime';
|
|
13
9
|
import { log } from '@dxos/log';
|
|
14
10
|
import { type Actor, Message } from '@dxos/types';
|
|
15
11
|
|
|
16
|
-
import { MessageNormalizer } from './message-normalizer';
|
|
17
12
|
import { type MessageWithRangeId, sentenceNormalization } from './normalization';
|
|
18
|
-
import { getActorId } from './utils';
|
|
19
13
|
|
|
20
14
|
const sender: Actor.Actor = {
|
|
21
15
|
identityDid: 'did:key:123',
|
|
@@ -104,35 +98,7 @@ describe.skip('SentenceNormalization', () => {
|
|
|
104
98
|
throw new Error('test');
|
|
105
99
|
});
|
|
106
100
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
const queue = new MemoryQueue<Message.Message>(createQueueDXN());
|
|
111
|
-
const ctx = new Context();
|
|
112
|
-
let idx = 0;
|
|
113
|
-
scheduleTaskInterval(
|
|
114
|
-
ctx,
|
|
115
|
-
async () => {
|
|
116
|
-
if (idx >= messages.length) {
|
|
117
|
-
void ctx.dispose();
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
await queue.append([messages[idx]]);
|
|
121
|
-
idx++;
|
|
122
|
-
},
|
|
123
|
-
2_000,
|
|
124
|
-
);
|
|
125
|
-
|
|
126
|
-
// Create normalizer.
|
|
127
|
-
const normalizer = new MessageNormalizer({
|
|
128
|
-
functionExecutor: getExecutor(),
|
|
129
|
-
queue,
|
|
130
|
-
startingCursor: { actorId: getActorId(sender), timestamp: '0' },
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
// Start normalizer.
|
|
134
|
-
await normalizer.open();
|
|
135
|
-
|
|
136
|
-
await new Promise(() => {});
|
|
137
|
-
});
|
|
101
|
+
// TODO(burdon): Restore a Feed-based version once `MessageNormalizer` has a
|
|
102
|
+
// space-backed test fixture available; the previous `MemoryQueue`-based
|
|
103
|
+
// test was incompatible with the `Feed.Feed` + `FeedService` refactor.
|
|
138
104
|
});
|
|
@@ -9,12 +9,11 @@ import { Operation } from '@dxos/compute';
|
|
|
9
9
|
import { log } from '@dxos/log';
|
|
10
10
|
import { trim } from '@dxos/util';
|
|
11
11
|
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
} from '../operations/definitions';
|
|
12
|
+
import { TranscriptOperation } from '../types';
|
|
13
|
+
|
|
14
|
+
type MessageWithRangeIdType = TranscriptOperation.MessageWithRangeIdType;
|
|
15
|
+
type SentenceNormalizationInputType = TranscriptOperation.SentenceNormalizationInputType;
|
|
16
|
+
const { SentenceNormalization, SentenceNormalizationOutput } = TranscriptOperation;
|
|
18
17
|
|
|
19
18
|
export type MessageWithRangeId = MessageWithRangeIdType;
|
|
20
19
|
|
|
@@ -35,7 +34,7 @@ export const sentenceNormalization = SentenceNormalization.pipe(
|
|
|
35
34
|
// TODO(dmaretskyi): runStructured was removed from AiRequest. Reimplement using new API.
|
|
36
35
|
return yield* Effect.die(
|
|
37
36
|
new Error(
|
|
38
|
-
'Sentence normalization needs to be reimplemented - runStructured was removed from AiRequest. Use AiRequest.run or LanguageModel.generateObject.',
|
|
37
|
+
'Sentence normalization needs to be reimplemented - runStructured was removed from AiRequest.Request. Use AiRequest.run or LanguageModel.generateObject.',
|
|
39
38
|
),
|
|
40
39
|
);
|
|
41
40
|
}),
|
package/src/operations/create.ts
CHANGED
|
@@ -8,9 +8,9 @@ import { Operation } from '@dxos/compute';
|
|
|
8
8
|
import { Feed, Ref } from '@dxos/echo';
|
|
9
9
|
import { Transcript } from '@dxos/types';
|
|
10
10
|
|
|
11
|
-
import {
|
|
11
|
+
import { TranscriptOperation } from '../types';
|
|
12
12
|
|
|
13
|
-
const handler: Operation.WithHandler<typeof Create> = Create.pipe(
|
|
13
|
+
const handler: Operation.WithHandler<typeof TranscriptOperation.Create> = TranscriptOperation.Create.pipe(
|
|
14
14
|
Operation.withHandler(
|
|
15
15
|
Effect.fn(function* ({ space }) {
|
|
16
16
|
const feed = space.db.add(Feed.make());
|
package/src/operations/index.ts
CHANGED
package/src/operations/open.ts
CHANGED
|
@@ -8,10 +8,10 @@ import { Operation } from '@dxos/compute';
|
|
|
8
8
|
import { Database, Feed, Filter } from '@dxos/echo';
|
|
9
9
|
import { Message } from '@dxos/types';
|
|
10
10
|
|
|
11
|
+
import { TranscriptOperation } from '../types';
|
|
11
12
|
import { renderByline } from '../util';
|
|
12
|
-
import { Open } from './definitions';
|
|
13
13
|
|
|
14
|
-
const handler: Operation.WithHandler<typeof Open> = Open.pipe(
|
|
14
|
+
const handler: Operation.WithHandler<typeof TranscriptOperation.Open> = TranscriptOperation.Open.pipe(
|
|
15
15
|
Operation.withHandler(
|
|
16
16
|
Effect.fn(function* ({ transcript }) {
|
|
17
17
|
const transcriptObj = yield* Database.load(transcript);
|
|
@@ -14,16 +14,16 @@ import { Trace, Operation, OperationRegistry } from '@dxos/compute';
|
|
|
14
14
|
import { Database } from '@dxos/echo';
|
|
15
15
|
import { trim } from '@dxos/util';
|
|
16
16
|
|
|
17
|
-
import {
|
|
17
|
+
import { TranscriptOperation } from '../types';
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* Summarize a transcript of a meeting.
|
|
21
21
|
*/
|
|
22
|
-
const handler: Operation.WithHandler<typeof Summarize> = Summarize.pipe(
|
|
22
|
+
const handler: Operation.WithHandler<typeof TranscriptOperation.Summarize> = TranscriptOperation.Summarize.pipe(
|
|
23
23
|
Operation.withHandler(
|
|
24
24
|
Effect.fnUntraced(
|
|
25
25
|
function* ({ transcript, notes }) {
|
|
26
|
-
const result = yield* new AiRequest({
|
|
26
|
+
const result = yield* new AiRequest.Request({
|
|
27
27
|
observer: GenerationObserver.fromPrinter(new ConsolePrinter({ tag: 'summarize' })),
|
|
28
28
|
}).run({
|
|
29
29
|
prompt: `Transcript: ${transcript}\n\nNotes: ${notes}`,
|
package/src/plugin.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Plugin } from '@dxos/app-framework';
|
|
6
|
+
|
|
7
|
+
import { meta } from './meta';
|
|
8
|
+
|
|
9
|
+
export const TranscriptionPlugin = Plugin.lazy(meta, () => import('#plugin'));
|
|
10
|
+
|
|
11
|
+
export { TranscriptionOperationHandlerSet } from './operations';
|