@dxos/plugin-transcription 0.8.2-main.12df754 → 0.8.2-main.36232bc
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/browser/{TranscriptContainer-4D4BHE7G.mjs → TranscriptContainer-2VFHUHLE.mjs} +3 -3
- package/dist/lib/browser/app-graph-builder-TDP6PYV6.mjs +350 -0
- package/dist/lib/browser/app-graph-builder-TDP6PYV6.mjs.map +7 -0
- package/dist/lib/browser/{chunk-DR3NBV6Q.mjs → chunk-KSUYWWIU.mjs} +105 -78
- package/dist/lib/browser/chunk-KSUYWWIU.mjs.map +7 -0
- package/dist/lib/browser/{chunk-5BE6RTNC.mjs → chunk-LTIQFX4W.mjs} +1 -1
- package/dist/lib/browser/{chunk-5BE6RTNC.mjs.map → chunk-LTIQFX4W.mjs.map} +1 -1
- package/dist/lib/browser/{chunk-RHHJZQPM.mjs → chunk-NFVDICSJ.mjs} +133 -116
- package/dist/lib/browser/chunk-NFVDICSJ.mjs.map +7 -0
- package/dist/lib/browser/chunk-O6RY7FNK.mjs +27 -0
- package/dist/lib/browser/chunk-O6RY7FNK.mjs.map +7 -0
- package/dist/lib/browser/{chunk-5Q3R53U3.mjs → chunk-YRWBY6UK.mjs} +9 -21
- package/dist/lib/browser/chunk-YRWBY6UK.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +29 -17
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/{intent-resolver-ZQQEH27F.mjs → intent-resolver-ZWKYRFE7.mjs} +4 -4
- package/dist/lib/browser/{intent-resolver-ZQQEH27F.mjs.map → intent-resolver-ZWKYRFE7.mjs.map} +2 -2
- package/dist/lib/browser/{meeting-transcription-state-5D52VTHU.mjs → meeting-transcription-state-MNA2XROE.mjs} +2 -2
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/{react-surface-HETJ5XY6.mjs → react-surface-F32UNCKZ.mjs} +18 -7
- package/dist/lib/browser/react-surface-F32UNCKZ.mjs.map +7 -0
- package/dist/lib/browser/settings-6YAHKVKX.mjs +24 -0
- package/dist/lib/browser/settings-6YAHKVKX.mjs.map +7 -0
- package/dist/lib/browser/{transcriber-SMRLU2KY.mjs → transcriber-MSBS4VBI.mjs} +4 -4
- package/dist/lib/browser/types/index.mjs +3 -3
- package/dist/lib/node/{TranscriptContainer-ZWP6UKY6.cjs → TranscriptContainer-E2D7PKO3.cjs} +8 -8
- package/dist/lib/node/{TranscriptContainer-ZWP6UKY6.cjs.map → TranscriptContainer-E2D7PKO3.cjs.map} +2 -2
- package/dist/lib/node/app-graph-builder-W4VDMVGV.cjs +351 -0
- package/dist/lib/node/app-graph-builder-W4VDMVGV.cjs.map +7 -0
- package/dist/lib/node/{chunk-LA73IHCB.cjs → chunk-7XIIUVN3.cjs} +108 -80
- package/dist/lib/node/chunk-7XIIUVN3.cjs.map +7 -0
- package/dist/lib/node/{chunk-3QEJ5EBO.cjs → chunk-IXU57SCA.cjs} +10 -22
- package/dist/lib/node/chunk-IXU57SCA.cjs.map +7 -0
- package/dist/lib/node/{chunk-HYGDHS6X.cjs → chunk-JUGOXWES.cjs} +4 -4
- package/dist/lib/node/{chunk-HYGDHS6X.cjs.map → chunk-JUGOXWES.cjs.map} +1 -1
- package/dist/lib/node/{chunk-KO6MKCED.cjs → chunk-NBC6V3M2.cjs} +136 -118
- package/dist/lib/node/chunk-NBC6V3M2.cjs.map +7 -0
- package/dist/lib/node/{chunk-WXG4Z2EZ.cjs → chunk-TJMZ26GS.cjs} +23 -8
- package/dist/lib/node/chunk-TJMZ26GS.cjs.map +7 -0
- package/dist/lib/node/index.cjs +46 -34
- package/dist/lib/node/index.cjs.map +3 -3
- package/dist/lib/node/{intent-resolver-VY3XWYAA.cjs → intent-resolver-RM4JOCDV.cjs} +8 -8
- package/dist/lib/node/{intent-resolver-VY3XWYAA.cjs.map → intent-resolver-RM4JOCDV.cjs.map} +2 -2
- package/dist/lib/node/{meeting-transcription-state-OYWCY5XE.cjs → meeting-transcription-state-5XLTMWPA.cjs} +6 -6
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/{react-surface-M2JQIIDY.cjs → react-surface-YQHBGPOC.cjs} +20 -11
- package/dist/lib/node/react-surface-YQHBGPOC.cjs.map +7 -0
- package/dist/lib/node/settings-ZMB4XRWS.cjs +38 -0
- package/dist/lib/node/settings-ZMB4XRWS.cjs.map +7 -0
- package/dist/lib/node/{transcriber-E6AAB4SU.cjs → transcriber-55OE4X6I.cjs} +10 -10
- package/dist/lib/node/types/index.cjs +8 -8
- package/dist/lib/node/types/index.cjs.map +1 -1
- package/dist/lib/node-esm/{TranscriptContainer-3HJ752CP.mjs → TranscriptContainer-5N3BSK2S.mjs} +3 -3
- package/dist/lib/node-esm/app-graph-builder-DDDVKUHS.mjs +351 -0
- package/dist/lib/node-esm/app-graph-builder-DDDVKUHS.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-SDMGJZLN.mjs → chunk-4RMJO6QQ.mjs} +133 -116
- package/dist/lib/node-esm/chunk-4RMJO6QQ.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-YXZOLOFT.mjs → chunk-5E2MR5XK.mjs} +1 -1
- package/dist/lib/node-esm/{chunk-YXZOLOFT.mjs.map → chunk-5E2MR5XK.mjs.map} +1 -1
- package/dist/lib/node-esm/chunk-HZBSWUJH.mjs +28 -0
- package/dist/lib/node-esm/chunk-HZBSWUJH.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-54HDN33F.mjs → chunk-OS2ITZUL.mjs} +9 -21
- package/dist/lib/node-esm/chunk-OS2ITZUL.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-K3R3YK3S.mjs → chunk-UWF6LM27.mjs} +105 -78
- package/dist/lib/node-esm/chunk-UWF6LM27.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +29 -17
- package/dist/lib/node-esm/index.mjs.map +3 -3
- package/dist/lib/node-esm/{intent-resolver-FCTNJPWY.mjs → intent-resolver-HDRFXMKP.mjs} +4 -4
- package/dist/lib/node-esm/{intent-resolver-FCTNJPWY.mjs.map → intent-resolver-HDRFXMKP.mjs.map} +2 -2
- package/dist/lib/node-esm/{meeting-transcription-state-XMZJ45P4.mjs → meeting-transcription-state-3WOZ52BQ.mjs} +2 -2
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/{react-surface-PXFSIM6V.mjs → react-surface-DRDNXTFZ.mjs} +18 -7
- package/dist/lib/node-esm/react-surface-DRDNXTFZ.mjs.map +7 -0
- package/dist/lib/node-esm/settings-I35TVDUG.mjs +25 -0
- package/dist/lib/node-esm/settings-I35TVDUG.mjs.map +7 -0
- package/dist/lib/node-esm/{transcriber-REAGRCPY.mjs → transcriber-HH7EMCGH.mjs} +4 -4
- package/dist/lib/node-esm/types/index.mjs +3 -3
- package/dist/types/src/TranscriptionPlugin.d.ts.map +1 -1
- package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
- package/dist/types/src/capabilities/capabilities.d.ts +1 -1
- package/dist/types/src/capabilities/capabilities.d.ts.map +1 -1
- package/dist/types/src/capabilities/index.d.ts +1 -0
- package/dist/types/src/capabilities/index.d.ts.map +1 -1
- package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
- package/dist/types/src/capabilities/settings.d.ts +4 -0
- package/dist/types/src/capabilities/settings.d.ts.map +1 -0
- package/dist/types/src/components/Transcript/Transcript.d.ts +6 -4
- package/dist/types/src/components/Transcript/Transcript.d.ts.map +1 -1
- package/dist/types/src/components/Transcript/Transcript.stories.d.ts +8 -4
- package/dist/types/src/components/Transcript/Transcript.stories.d.ts.map +1 -1
- package/dist/types/src/components/Transcript/deprecated/OldTranscript.d.ts +27 -0
- package/dist/types/src/components/Transcript/deprecated/OldTranscript.d.ts.map +1 -0
- package/dist/types/src/components/Transcript/transcript-extension.d.ts +4 -4
- package/dist/types/src/components/Transcript/transcript-extension.d.ts.map +1 -1
- package/dist/types/src/components/TranscriptContainer.d.ts.map +1 -1
- package/dist/types/src/components/TranscriptionSettings/TranscriptionSettings.d.ts +6 -0
- package/dist/types/src/components/TranscriptionSettings/TranscriptionSettings.d.ts.map +1 -0
- package/dist/types/src/components/TranscriptionSettings/index.d.ts +2 -0
- package/dist/types/src/components/TranscriptionSettings/index.d.ts.map +1 -0
- 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/components/transcription.stories.d.ts +6 -3
- package/dist/types/src/components/transcription.stories.d.ts.map +1 -1
- package/dist/types/src/entity-extraction/entity-extraction.d.ts +33 -7
- package/dist/types/src/entity-extraction/entity-extraction.d.ts.map +1 -1
- package/dist/types/src/hooks/useQueueModelAdapter.d.ts +2 -2
- package/dist/types/src/hooks/useQueueModelAdapter.d.ts.map +1 -1
- package/dist/types/src/model/model.d.ts +23 -23
- package/dist/types/src/model/model.d.ts.map +1 -1
- package/dist/types/src/testing/index.d.ts +2 -0
- package/dist/types/src/testing/index.d.ts.map +1 -0
- package/dist/types/src/testing/testing.d.ts +39 -0
- package/dist/types/src/testing/testing.d.ts.map +1 -0
- package/dist/types/src/transcriber/transcriber.d.ts +2 -2
- package/dist/types/src/transcriber/transcriber.d.ts.map +1 -1
- package/dist/types/src/transcriber/transcription-manager.d.ts +17 -15
- package/dist/types/src/transcriber/transcription-manager.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +3 -0
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/schema.d.ts +0 -21
- package/dist/types/src/types/schema.d.ts.map +1 -1
- package/dist/types/src/types/types.d.ts +5 -1
- package/dist/types/src/types/types.d.ts.map +1 -1
- package/package.json +48 -45
- package/src/TranscriptionPlugin.tsx +29 -12
- package/src/capabilities/app-graph-builder.ts +99 -29
- package/src/capabilities/capabilities.ts +1 -1
- package/src/capabilities/index.ts +1 -0
- package/src/capabilities/intent-resolver.ts +2 -2
- package/src/capabilities/react-surface.tsx +12 -4
- package/src/capabilities/settings.ts +20 -0
- package/src/components/Transcript/Transcript.stories.tsx +71 -35
- package/src/components/Transcript/Transcript.tsx +30 -29
- package/src/components/Transcript/deprecated/{Transcript.tsx → OldTranscript.tsx} +24 -1
- package/src/components/Transcript/transcript-extension.ts +20 -17
- package/src/components/TranscriptContainer.tsx +6 -4
- package/src/components/TranscriptionSettings/TranscriptionSettings.tsx +26 -0
- package/src/components/TranscriptionSettings/index.ts +5 -0
- package/src/components/index.ts +1 -0
- package/src/components/transcription.stories.tsx +77 -29
- package/src/entity-extraction/entity-extraction.test.ts +55 -9
- package/src/entity-extraction/entity-extraction.ts +105 -55
- package/src/entity-extraction/system-prompt.tpl +38 -9
- package/src/hooks/useQueueModelAdapter.ts +44 -11
- package/src/model/model.test.ts +85 -43
- package/src/model/model.ts +59 -59
- package/src/testing/index.ts +5 -0
- package/src/testing/testing.ts +208 -0
- package/src/transcriber/transcriber.browser.test.ts +3 -3
- package/src/transcriber/transcriber.ts +16 -10
- package/src/transcriber/transcription-manager.ts +66 -57
- package/src/translations.ts +2 -0
- package/src/types/schema.ts +1 -24
- package/src/types/types.ts +11 -3
- package/dist/lib/browser/app-graph-builder-I4SSIQMW.mjs +0 -168
- package/dist/lib/browser/app-graph-builder-I4SSIQMW.mjs.map +0 -7
- package/dist/lib/browser/chunk-5Q3R53U3.mjs.map +0 -7
- package/dist/lib/browser/chunk-DR3NBV6Q.mjs.map +0 -7
- package/dist/lib/browser/chunk-GI5LZ4SV.mjs +0 -8
- package/dist/lib/browser/chunk-GI5LZ4SV.mjs.map +0 -7
- package/dist/lib/browser/chunk-RHHJZQPM.mjs.map +0 -7
- package/dist/lib/browser/react-surface-HETJ5XY6.mjs.map +0 -7
- package/dist/lib/node/app-graph-builder-WWC4WNTG.cjs +0 -177
- package/dist/lib/node/app-graph-builder-WWC4WNTG.cjs.map +0 -7
- package/dist/lib/node/chunk-3QEJ5EBO.cjs.map +0 -7
- package/dist/lib/node/chunk-KO6MKCED.cjs.map +0 -7
- package/dist/lib/node/chunk-LA73IHCB.cjs.map +0 -7
- package/dist/lib/node/chunk-WXG4Z2EZ.cjs.map +0 -7
- package/dist/lib/node/react-surface-M2JQIIDY.cjs.map +0 -7
- package/dist/lib/node-esm/app-graph-builder-PPJQAFSI.mjs +0 -169
- package/dist/lib/node-esm/app-graph-builder-PPJQAFSI.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-54HDN33F.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-K3R3YK3S.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-SDMGJZLN.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-XY22OP6H.mjs +0 -10
- package/dist/lib/node-esm/chunk-XY22OP6H.mjs.map +0 -7
- package/dist/lib/node-esm/react-surface-PXFSIM6V.mjs.map +0 -7
- package/dist/types/src/components/Transcript/deprecated/Transcript.d.ts +0 -13
- package/dist/types/src/components/Transcript/deprecated/Transcript.d.ts.map +0 -1
- package/dist/types/src/components/Transcript/testings.d.ts +0 -51
- package/dist/types/src/components/Transcript/testings.d.ts.map +0 -1
- package/dist/types/src/testing/test-data.d.ts +0 -73
- package/dist/types/src/testing/test-data.d.ts.map +0 -1
- package/src/components/Transcript/testings.ts +0 -182
- package/src/testing/test-data.ts +0 -314
- /package/dist/lib/browser/{TranscriptContainer-4D4BHE7G.mjs.map → TranscriptContainer-2VFHUHLE.mjs.map} +0 -0
- /package/dist/lib/browser/{meeting-transcription-state-5D52VTHU.mjs.map → meeting-transcription-state-MNA2XROE.mjs.map} +0 -0
- /package/dist/lib/browser/{transcriber-SMRLU2KY.mjs.map → transcriber-MSBS4VBI.mjs.map} +0 -0
- /package/dist/lib/node/{meeting-transcription-state-OYWCY5XE.cjs.map → meeting-transcription-state-5XLTMWPA.cjs.map} +0 -0
- /package/dist/lib/node/{transcriber-E6AAB4SU.cjs.map → transcriber-55OE4X6I.cjs.map} +0 -0
- /package/dist/lib/node-esm/{TranscriptContainer-3HJ752CP.mjs.map → TranscriptContainer-5N3BSK2S.mjs.map} +0 -0
- /package/dist/lib/node-esm/{meeting-transcription-state-XMZJ45P4.mjs.map → meeting-transcription-state-3WOZ52BQ.mjs.map} +0 -0
- /package/dist/lib/node-esm/{transcriber-REAGRCPY.mjs.map → transcriber-HH7EMCGH.mjs.map} +0 -0
|
@@ -18,32 +18,40 @@ import React, {
|
|
|
18
18
|
|
|
19
19
|
import { Events, IntentPlugin, SettingsPlugin } from '@dxos/app-framework';
|
|
20
20
|
import { withPluginManager } from '@dxos/app-framework/testing';
|
|
21
|
-
import {
|
|
21
|
+
import { AIServiceEdgeClient } from '@dxos/assistant';
|
|
22
|
+
import { AI_SERVICE_ENDPOINT } from '@dxos/assistant/testing';
|
|
23
|
+
import { Filter, MemoryQueue } from '@dxos/echo-db';
|
|
22
24
|
import { create } from '@dxos/echo-schema';
|
|
23
25
|
import { type DXN } from '@dxos/keys';
|
|
24
26
|
import { log } from '@dxos/log';
|
|
25
27
|
import { ClientPlugin } from '@dxos/plugin-client';
|
|
28
|
+
import { PreviewPlugin } from '@dxos/plugin-preview';
|
|
26
29
|
import { SpacePlugin } from '@dxos/plugin-space';
|
|
30
|
+
import { StorybookLayoutPlugin } from '@dxos/plugin-storybook-layout';
|
|
27
31
|
import { ThemePlugin } from '@dxos/plugin-theme';
|
|
28
|
-
import {
|
|
32
|
+
import { createQueueDxn, useQueue, useSpace } from '@dxos/react-client/echo';
|
|
29
33
|
import { IconButton, Toolbar } from '@dxos/react-ui';
|
|
30
34
|
import { ScrollContainer } from '@dxos/react-ui-components';
|
|
31
35
|
import { defaultTx } from '@dxos/react-ui-theme';
|
|
36
|
+
import { Contact, MessageType, Organization } from '@dxos/schema';
|
|
37
|
+
import { seedTestData, Testing } from '@dxos/schema/testing';
|
|
32
38
|
import { withLayout, withTheme } from '@dxos/storybook-utils';
|
|
33
39
|
|
|
34
40
|
import { renderMarkdown, Transcript } from './Transcript';
|
|
35
|
-
import { TestItem } from './Transcript/testings';
|
|
36
41
|
import { TranscriptionPlugin } from '../TranscriptionPlugin';
|
|
42
|
+
import { processTranscriptMessage } from '../entity-extraction';
|
|
37
43
|
import { useAudioFile, useAudioTrack, useQueueModelAdapter, useTranscriber } from '../hooks';
|
|
38
|
-
import { type
|
|
44
|
+
import { type SerializationModel } from '../model';
|
|
45
|
+
import { TestItem } from '../testing';
|
|
39
46
|
import { type TranscriberParams } from '../transcriber';
|
|
40
|
-
import { TranscriptBlock } from '../types';
|
|
41
47
|
|
|
42
48
|
const TranscriptionStory: FC<{
|
|
43
|
-
model:
|
|
49
|
+
model: SerializationModel<MessageType>;
|
|
44
50
|
running: boolean;
|
|
45
51
|
onRunningChange: Dispatch<SetStateAction<boolean>>;
|
|
46
52
|
}> = ({ model, running, onRunningChange }) => {
|
|
53
|
+
const space = useSpace();
|
|
54
|
+
|
|
47
55
|
return (
|
|
48
56
|
<div className='flex flex-col w-[30rem]'>
|
|
49
57
|
<Toolbar.Root>
|
|
@@ -55,30 +63,61 @@ const TranscriptionStory: FC<{
|
|
|
55
63
|
/>
|
|
56
64
|
</Toolbar.Root>
|
|
57
65
|
<ScrollContainer>
|
|
58
|
-
<Transcript model={model} attendableId='story' />
|
|
66
|
+
<Transcript space={space} model={model} attendableId='story' />
|
|
59
67
|
</ScrollContainer>
|
|
60
68
|
</div>
|
|
61
69
|
);
|
|
62
70
|
};
|
|
63
71
|
|
|
64
|
-
const
|
|
72
|
+
const aiService = new AIServiceEdgeClient({
|
|
73
|
+
endpoint: AI_SERVICE_ENDPOINT.REMOTE,
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const Microphone = ({ entityExtraction }: { entityExtraction?: boolean }) => {
|
|
65
77
|
const [running, setRunning] = useState(false);
|
|
66
78
|
|
|
67
79
|
// Audio.
|
|
68
80
|
const track = useAudioTrack(running);
|
|
69
81
|
|
|
70
82
|
// Queue.
|
|
71
|
-
const queueDxn = useMemo(() =>
|
|
72
|
-
const queue = useMemo(() => new MemoryQueue<
|
|
73
|
-
const model = useQueueModelAdapter(renderMarkdown, queue);
|
|
83
|
+
const queueDxn = useMemo(() => createQueueDxn(), []);
|
|
84
|
+
const queue = useMemo(() => new MemoryQueue<MessageType>(queueDxn), [queueDxn]);
|
|
85
|
+
const model = useQueueModelAdapter(renderMarkdown([]), queue);
|
|
86
|
+
const space = useSpace();
|
|
74
87
|
|
|
75
88
|
// Transcriber.
|
|
76
89
|
const handleSegments = useCallback<TranscriberParams['onSegments']>(
|
|
77
|
-
async (
|
|
78
|
-
const
|
|
79
|
-
|
|
90
|
+
async (blocks) => {
|
|
91
|
+
const message = create(MessageType, { sender: { name: 'You' }, created: new Date().toISOString(), blocks });
|
|
92
|
+
|
|
93
|
+
if (entityExtraction) {
|
|
94
|
+
if (!space) {
|
|
95
|
+
log.warn('no space');
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
// TODO(dmaretskyi): Move to vector search index.
|
|
99
|
+
const { objects } = await space.db
|
|
100
|
+
.query(Filter.or(Filter.schema(Contact), Filter.schema(Organization), Filter.schema(Testing.DocumentType)))
|
|
101
|
+
.run();
|
|
102
|
+
|
|
103
|
+
log.info('context', { objects });
|
|
104
|
+
|
|
105
|
+
const result = await processTranscriptMessage({
|
|
106
|
+
message,
|
|
107
|
+
aiService,
|
|
108
|
+
context: {
|
|
109
|
+
objects,
|
|
110
|
+
},
|
|
111
|
+
options: {
|
|
112
|
+
fallbackToRaw: true,
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
void queue.append([result.message]);
|
|
116
|
+
} else {
|
|
117
|
+
void queue.append([message]);
|
|
118
|
+
}
|
|
80
119
|
},
|
|
81
|
-
[queue],
|
|
120
|
+
[queue, space],
|
|
82
121
|
);
|
|
83
122
|
|
|
84
123
|
const config = useRef<TranscriberParams['config']>({
|
|
@@ -94,10 +133,8 @@ const Microphone = () => {
|
|
|
94
133
|
const [isOpen, setIsOpen] = useState(false);
|
|
95
134
|
useEffect(() => {
|
|
96
135
|
void transcriber?.open().then(() => setIsOpen(true));
|
|
97
|
-
log.info('transcriber open');
|
|
98
136
|
return () => {
|
|
99
137
|
void transcriber?.close().then(() => setIsOpen(false));
|
|
100
|
-
log.info('transcriber close');
|
|
101
138
|
};
|
|
102
139
|
}, [transcriber]);
|
|
103
140
|
|
|
@@ -105,10 +142,8 @@ const Microphone = () => {
|
|
|
105
142
|
useEffect(() => {
|
|
106
143
|
if (running && transcriber?.isOpen) {
|
|
107
144
|
transcriber?.startChunksRecording();
|
|
108
|
-
log.info('transcriber startChunksRecording');
|
|
109
145
|
} else if (!running) {
|
|
110
146
|
transcriber?.stopChunksRecording();
|
|
111
|
-
log.info('transcriber stopChunksRecording');
|
|
112
147
|
}
|
|
113
148
|
}, [transcriber, running, isOpen]);
|
|
114
149
|
|
|
@@ -141,12 +176,12 @@ const AudioFile = ({ queueDxn, audioUrl }: { queueDxn: DXN; audioUrl: string; tr
|
|
|
141
176
|
}, [audio, running]);
|
|
142
177
|
|
|
143
178
|
// Transcriber.
|
|
144
|
-
const queue = useQueue<
|
|
145
|
-
const model = useQueueModelAdapter(renderMarkdown, queue);
|
|
179
|
+
const queue = useQueue<MessageType>(queueDxn, { pollInterval: 500 });
|
|
180
|
+
const model = useQueueModelAdapter(renderMarkdown([]), queue);
|
|
146
181
|
const handleSegments = useCallback<TranscriberParams['onSegments']>(
|
|
147
|
-
async (
|
|
148
|
-
const
|
|
149
|
-
queue?.append([
|
|
182
|
+
async (blocks) => {
|
|
183
|
+
const message = create(MessageType, { sender: { name: 'You' }, created: new Date().toISOString(), blocks });
|
|
184
|
+
void queue?.append([message]);
|
|
150
185
|
},
|
|
151
186
|
[queue],
|
|
152
187
|
);
|
|
@@ -187,14 +222,19 @@ const meta: Meta<typeof AudioFile> = {
|
|
|
187
222
|
withPluginManager({
|
|
188
223
|
plugins: [
|
|
189
224
|
ThemePlugin({ tx: defaultTx }),
|
|
225
|
+
StorybookLayoutPlugin(),
|
|
190
226
|
ClientPlugin({
|
|
191
|
-
types: [TestItem],
|
|
227
|
+
types: [TestItem, Contact, Organization, Testing.DocumentType],
|
|
192
228
|
onClientInitialized: async (_, client) => {
|
|
193
229
|
await client.halo.createIdentity();
|
|
230
|
+
await client.spaces.waitUntilReady();
|
|
231
|
+
await client.spaces.default.waitUntilReady();
|
|
232
|
+
await seedTestData(client.spaces.default);
|
|
194
233
|
},
|
|
195
234
|
}),
|
|
196
235
|
SpacePlugin(),
|
|
197
236
|
SettingsPlugin(),
|
|
237
|
+
PreviewPlugin(),
|
|
198
238
|
IntentPlugin(),
|
|
199
239
|
TranscriptionPlugin(),
|
|
200
240
|
],
|
|
@@ -228,16 +268,24 @@ const meta: Meta<typeof AudioFile> = {
|
|
|
228
268
|
|
|
229
269
|
export default meta;
|
|
230
270
|
|
|
231
|
-
|
|
271
|
+
export const Default: StoryObj<typeof Microphone> = {
|
|
272
|
+
render: Microphone,
|
|
273
|
+
args: {
|
|
274
|
+
entityExtraction: false,
|
|
275
|
+
},
|
|
276
|
+
};
|
|
232
277
|
|
|
233
|
-
export const
|
|
278
|
+
export const EntityExtraction: StoryObj<typeof Microphone> = {
|
|
234
279
|
render: Microphone,
|
|
280
|
+
args: {
|
|
281
|
+
entityExtraction: true,
|
|
282
|
+
},
|
|
235
283
|
};
|
|
236
284
|
|
|
237
|
-
export const File:
|
|
285
|
+
export const File: StoryObj<typeof AudioFile> = {
|
|
238
286
|
render: AudioFile,
|
|
239
287
|
args: {
|
|
240
|
-
queueDxn:
|
|
288
|
+
queueDxn: createQueueDxn(),
|
|
241
289
|
// https://learnenglish.britishcouncil.org/general-english/audio-zone/living-london
|
|
242
290
|
transcriptUrl: 'https://dxos.network/audio-london.txt',
|
|
243
291
|
audioUrl: 'https://dxos.network/audio-london.m4a',
|
|
@@ -2,14 +2,15 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { describe, test } from 'vitest';
|
|
5
|
+
import { describe, test, expect } from 'vitest';
|
|
6
6
|
|
|
7
7
|
import { AIServiceEdgeClient } from '@dxos/assistant';
|
|
8
8
|
import { AI_SERVICE_ENDPOINT } from '@dxos/assistant/testing';
|
|
9
9
|
import { log } from '@dxos/log';
|
|
10
|
+
import { createTestData } from '@dxos/schema/testing';
|
|
11
|
+
import { range } from '@dxos/util';
|
|
10
12
|
|
|
11
|
-
import {
|
|
12
|
-
import * as TestData from '../testing/test-data';
|
|
13
|
+
import { processTranscriptMessage, postprocessText } from './entity-extraction';
|
|
13
14
|
|
|
14
15
|
const aiService = new AIServiceEdgeClient({
|
|
15
16
|
endpoint: AI_SERVICE_ENDPOINT.REMOTE,
|
|
@@ -17,16 +18,61 @@ const aiService = new AIServiceEdgeClient({
|
|
|
17
18
|
|
|
18
19
|
describe.skip('EntityExtraction', { timeout: 180_000 }, () => {
|
|
19
20
|
test('should process a transcript block', async () => {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
const { transcriptMessages, documents, contacts } = await createTestData();
|
|
22
|
+
|
|
23
|
+
log.info('context', { documents, contacts });
|
|
24
|
+
|
|
25
|
+
for (const message of transcriptMessages) {
|
|
26
|
+
log.info('input', message);
|
|
27
|
+
const { message: enhancedMessage } = await processTranscriptMessage({
|
|
28
|
+
message,
|
|
24
29
|
aiService,
|
|
25
30
|
context: {
|
|
26
|
-
objects: [...
|
|
31
|
+
objects: [...documents, ...Object.values(contacts)],
|
|
27
32
|
},
|
|
28
33
|
});
|
|
29
|
-
log.info('output',
|
|
34
|
+
log.info('output', enhancedMessage);
|
|
30
35
|
}
|
|
31
36
|
});
|
|
37
|
+
|
|
38
|
+
test.skip('computational irreducibility', async () => {
|
|
39
|
+
const { transcriptMessages, documents, contacts } = await createTestData();
|
|
40
|
+
|
|
41
|
+
log.info('context', { documents, contacts });
|
|
42
|
+
const message = transcriptMessages[0];
|
|
43
|
+
log.info('input', message);
|
|
44
|
+
|
|
45
|
+
await Promise.all(
|
|
46
|
+
range(10).map(async () => {
|
|
47
|
+
const { message: enhancedMessage, timeElapsed } = await processTranscriptMessage({
|
|
48
|
+
message,
|
|
49
|
+
aiService,
|
|
50
|
+
context: {
|
|
51
|
+
objects: [...documents, ...Object.values(contacts)],
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
log.info('output', { message: enhancedMessage.blocks[0], timeElapsed });
|
|
55
|
+
}),
|
|
56
|
+
);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe('postprocessText', () => {
|
|
61
|
+
test('should replace quotes with DXN references', () => {
|
|
62
|
+
const quotes = {
|
|
63
|
+
references: [{ quote: 'computational irreducibility', id: '01JTG9JW11XGWJZ32AW8ET93D1' }],
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
expect(postprocessText('This is a computational irreducibility test.', quotes)).toBe(
|
|
67
|
+
'This is a [computational irreducibility][dxn:echo:@:01JTG9JW11XGWJZ32AW8ET93D1] test.',
|
|
68
|
+
);
|
|
69
|
+
expect(
|
|
70
|
+
postprocessText(
|
|
71
|
+
"And what I'd like to talk today about is Steven Wolfram's concept of a computational irreducibility.",
|
|
72
|
+
quotes,
|
|
73
|
+
),
|
|
74
|
+
).toBe(
|
|
75
|
+
"And what I'd like to talk today about is Steven Wolfram's concept of a [computational irreducibility][dxn:echo:@:01JTG9JW11XGWJZ32AW8ET93D1].",
|
|
76
|
+
);
|
|
77
|
+
});
|
|
32
78
|
});
|
|
@@ -7,23 +7,38 @@ import { Schema } from 'effect';
|
|
|
7
7
|
import { createTemplate, Message, structuredOutputParser } from '@dxos/artifact';
|
|
8
8
|
import type { AIServiceClient } from '@dxos/assistant';
|
|
9
9
|
import { MixedStreamParser } from '@dxos/assistant';
|
|
10
|
-
import {
|
|
11
|
-
import { create } from '@dxos/echo-schema';
|
|
12
|
-
import
|
|
10
|
+
import { asyncTimeout } from '@dxos/async';
|
|
11
|
+
import { type BaseEchoObject, create } from '@dxos/echo-schema';
|
|
12
|
+
import { log } from '@dxos/log';
|
|
13
|
+
import { MessageType } from '@dxos/schema';
|
|
13
14
|
|
|
14
15
|
import SYSTEM_PROMPT from './system-prompt.tpl?raw';
|
|
15
|
-
import type { TranscriptBlock } from '../types';
|
|
16
16
|
|
|
17
|
-
type
|
|
18
|
-
|
|
17
|
+
type ProcessTranscriptMessageParams = {
|
|
18
|
+
message: MessageType;
|
|
19
19
|
aiService: AIServiceClient;
|
|
20
20
|
context: {
|
|
21
21
|
objects?: BaseEchoObject[];
|
|
22
22
|
};
|
|
23
|
+
|
|
24
|
+
options?: {
|
|
25
|
+
/**
|
|
26
|
+
* Timeout for the entity extraction.
|
|
27
|
+
*/
|
|
28
|
+
timeout?: number;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Fallback to raw text if the entity extraction fails.
|
|
32
|
+
* Otherwise the function will throw an error.
|
|
33
|
+
* @default false
|
|
34
|
+
*/
|
|
35
|
+
fallbackToRaw?: boolean;
|
|
36
|
+
};
|
|
23
37
|
};
|
|
24
38
|
|
|
25
|
-
type
|
|
26
|
-
|
|
39
|
+
type ProcessTranscriptMessageResult = {
|
|
40
|
+
message: MessageType;
|
|
41
|
+
timeElapsed: number;
|
|
27
42
|
};
|
|
28
43
|
|
|
29
44
|
/**
|
|
@@ -34,61 +49,96 @@ const createSystemPrompt = (): string => {
|
|
|
34
49
|
return template({});
|
|
35
50
|
};
|
|
36
51
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
): Promise<ProcessTranscriptBlockResult> => {
|
|
40
|
-
// TODO(dmaretskyi): Move context to a vector search index.
|
|
41
|
-
const systemPrompt = `
|
|
42
|
-
${createSystemPrompt()}
|
|
43
|
-
Context:
|
|
44
|
-
${JSON.stringify(params.context.objects)}
|
|
45
|
-
`;
|
|
46
|
-
|
|
47
|
-
const outputParser = structuredOutputParser(
|
|
52
|
+
const ReferencedQuotes = Schema.Struct({
|
|
53
|
+
references: Schema.Array(
|
|
48
54
|
Schema.Struct({
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}),
|
|
55
|
+
quote: Schema.String,
|
|
56
|
+
id: Schema.String,
|
|
52
57
|
}),
|
|
53
|
-
)
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
58
|
+
).annotations({
|
|
59
|
+
description:
|
|
60
|
+
'The references to the context objects that are mentioned in the transcript. quote should match the original transcript text exactly, while id is the id of the context object.',
|
|
61
|
+
}),
|
|
62
|
+
});
|
|
63
|
+
interface ReferencedQuotes extends Schema.Schema.Type<typeof ReferencedQuotes> {}
|
|
64
|
+
|
|
65
|
+
// TODO(dmaretskyi): Move context to a vector search index.
|
|
66
|
+
export const processTranscriptMessage = async (
|
|
67
|
+
params: ProcessTranscriptMessageParams,
|
|
68
|
+
): Promise<ProcessTranscriptMessageResult> => {
|
|
69
|
+
try {
|
|
70
|
+
const outputParser = structuredOutputParser(ReferencedQuotes);
|
|
71
|
+
const runParser = async (): Promise<ProcessTranscriptMessageResult> => {
|
|
72
|
+
const startTime = performance.now();
|
|
73
|
+
const result = outputParser.getResult(
|
|
74
|
+
await new MixedStreamParser().parse(
|
|
75
|
+
await params.aiService.execStream({
|
|
76
|
+
model: '@anthropic/claude-3-5-haiku-20241022',
|
|
77
|
+
systemPrompt: createSystemPrompt(),
|
|
78
|
+
history: [
|
|
79
|
+
create(Message, {
|
|
80
|
+
role: 'user',
|
|
81
|
+
content: [
|
|
82
|
+
{
|
|
83
|
+
type: 'text',
|
|
84
|
+
text: `<context>${JSON.stringify(params.context.objects)}</context>`,
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
type: 'text',
|
|
88
|
+
text: `<transcript>${JSON.stringify(params.message.blocks)}</transcript>`,
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
}),
|
|
67
92
|
],
|
|
93
|
+
tools: [outputParser.tool],
|
|
68
94
|
}),
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
})
|
|
72
|
-
),
|
|
73
|
-
);
|
|
95
|
+
),
|
|
96
|
+
);
|
|
97
|
+
log.info('entity extraction result', { refs: result.references });
|
|
74
98
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
99
|
+
return {
|
|
100
|
+
message: create(MessageType, {
|
|
101
|
+
...params.message,
|
|
102
|
+
blocks: params.message.blocks.map((block, i) =>
|
|
103
|
+
block.type !== 'transcription'
|
|
104
|
+
? block
|
|
105
|
+
: {
|
|
106
|
+
...block,
|
|
107
|
+
text: postprocessText(block.text, result),
|
|
108
|
+
},
|
|
109
|
+
),
|
|
110
|
+
}),
|
|
111
|
+
timeElapsed: performance.now() - startTime,
|
|
112
|
+
};
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
if (params.options?.timeout && params.options.timeout > 0) {
|
|
116
|
+
return await asyncTimeout(runParser(), params.options.timeout);
|
|
117
|
+
} else {
|
|
118
|
+
return await runParser();
|
|
119
|
+
}
|
|
120
|
+
} catch (error) {
|
|
121
|
+
if (params.options?.fallbackToRaw) {
|
|
122
|
+
log.warn('failed to process transcript message, falling back to raw text', { error });
|
|
123
|
+
return {
|
|
124
|
+
message: params.message,
|
|
125
|
+
timeElapsed: 0,
|
|
126
|
+
};
|
|
127
|
+
} else {
|
|
128
|
+
throw error;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
84
131
|
};
|
|
85
132
|
|
|
86
133
|
/**
|
|
87
|
-
* Finds and replaces all
|
|
134
|
+
* Finds and replaces all quotes with DXNs references.
|
|
88
135
|
*/
|
|
89
136
|
// TODO(dmaretskyi): Lookup and verifiy ids from provided context.
|
|
90
|
-
const postprocessText = (text: string) => {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
137
|
+
export const postprocessText = (text: string, quotes: ReferencedQuotes) => {
|
|
138
|
+
for (const quote of quotes.references) {
|
|
139
|
+
// Use a case-insensitive regular expression to replace the quote
|
|
140
|
+
const regex = new RegExp(quote.quote.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi');
|
|
141
|
+
text = text.replace(regex, `[${quote.quote}][dxn:echo:@:${quote.id}]`);
|
|
142
|
+
}
|
|
143
|
+
return text;
|
|
94
144
|
};
|
|
@@ -1,9 +1,38 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
You are an AI assistant specialized in processing and enhancing transcripts with contextual information.
|
|
2
|
+
Your task is to analyze a given transcript and extract references to the context objects.
|
|
3
|
+
|
|
4
|
+
The context and transcript are provided in the user message in `context` and `transcript` XML tags.
|
|
5
|
+
|
|
6
|
+
Here are your instructions:
|
|
7
|
+
|
|
8
|
+
1. Read the transcript and context information provided above.
|
|
9
|
+
|
|
10
|
+
2. Process each segment of the transcript separately.
|
|
11
|
+
|
|
12
|
+
3. For each segment:
|
|
13
|
+
a. Analyze the content and identify relevant information from the context.
|
|
14
|
+
b. Extract a number of references to the context objects that are mentioned in the segment.
|
|
15
|
+
c. Output the references in the format `{ "quote": "earnings report", "id": "01JT0JP9AX0XKGZX4MV4B69VT6" }`.
|
|
16
|
+
d. If there are no references, just output an empty array.
|
|
17
|
+
|
|
18
|
+
4. Maintain the exact order and structure of the original transcript segments.
|
|
19
|
+
|
|
20
|
+
Output Format:
|
|
21
|
+
The final output should be a JSON array of references.
|
|
22
|
+
|
|
23
|
+
Example output structure:
|
|
24
|
+
```json
|
|
25
|
+
{
|
|
26
|
+
"references": [
|
|
27
|
+
{ "quote": "earnings report", "id": "01JT0JP9AX0XKGZX4MV4B69VT6" },
|
|
28
|
+
{ "quote": "John", "id": "01JT0VM0VN3ZKN7GDBM8CV9BA7" }
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Remember:
|
|
34
|
+
- Only quote the text that is in the transcript.
|
|
35
|
+
- Analyze each segment carefully before processing it.
|
|
36
|
+
- Only reference objects that appear in the context and exactly by their id, If the object is not in the context, do not insert a reference.
|
|
37
|
+
|
|
38
|
+
Now, process the transcript and context provided below according to these instructions.
|
|
@@ -2,29 +2,62 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { useMemo, useEffect } from 'react';
|
|
5
|
+
import { useMemo, useEffect, useState } from 'react';
|
|
6
6
|
|
|
7
7
|
import { type Queue } from '@dxos/react-client/echo';
|
|
8
8
|
|
|
9
|
-
import { type
|
|
9
|
+
import { type Chunk, type ChunkRenderer, SerializationModel } from '../model';
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Model adapter for a queue.
|
|
13
13
|
*/
|
|
14
|
-
export const useQueueModelAdapter = <T extends
|
|
15
|
-
renderer:
|
|
14
|
+
export const useQueueModelAdapter = <T extends Chunk>(
|
|
15
|
+
renderer: ChunkRenderer<T>,
|
|
16
16
|
queue: Queue<T> | undefined,
|
|
17
|
-
|
|
18
|
-
):
|
|
19
|
-
const model = useMemo(() => new
|
|
17
|
+
initialChunks: T[] = [],
|
|
18
|
+
): SerializationModel<T> => {
|
|
19
|
+
const model = useMemo(() => new SerializationModel<T>(renderer, initialChunks), [queue]);
|
|
20
|
+
const [loaded, setLoaded] = useState(false);
|
|
21
|
+
|
|
22
|
+
// Set initial blocks.
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
if (!queue) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const update = () => {
|
|
29
|
+
for (const block of queue?.items ?? []) {
|
|
30
|
+
model.appendChunk(block);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
setLoaded(true);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// TODO(burdon): This is a hack to ensure the queue is loaded.
|
|
37
|
+
let i: NodeJS.Timeout | undefined;
|
|
38
|
+
if (queue.isLoading) {
|
|
39
|
+
i = setInterval(() => {
|
|
40
|
+
if (!queue?.isLoading) {
|
|
41
|
+
clearInterval(i);
|
|
42
|
+
update();
|
|
43
|
+
}
|
|
44
|
+
}, 1_000);
|
|
45
|
+
} else {
|
|
46
|
+
update();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return () => clearTimeout(i);
|
|
50
|
+
}, [model, queue]);
|
|
51
|
+
|
|
52
|
+
// TODO(burdon): Can we listen for queue events?
|
|
20
53
|
useEffect(() => {
|
|
21
|
-
if (!queue
|
|
54
|
+
if (!loaded || !queue || model.chunks.length === queue.items.length) {
|
|
22
55
|
return;
|
|
23
56
|
}
|
|
24
57
|
|
|
25
|
-
const
|
|
26
|
-
model.
|
|
27
|
-
}, [model, queue?.items.length]);
|
|
58
|
+
const chunk = queue.items[queue.items.length - 1];
|
|
59
|
+
model.appendChunk(chunk);
|
|
60
|
+
}, [model, loaded, queue?.items.length]);
|
|
28
61
|
|
|
29
62
|
return model;
|
|
30
63
|
};
|