@dxos/plugin-transcription 0.8.2-main.12df754 → 0.8.2-main.30e4dbb
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-3LU3XBEB.mjs} +3 -3
- package/dist/lib/browser/app-graph-builder-YXELPN6F.mjs +349 -0
- package/dist/lib/browser/app-graph-builder-YXELPN6F.mjs.map +7 -0
- package/dist/lib/browser/{chunk-DR3NBV6Q.mjs → chunk-GWY2Q2NE.mjs} +105 -78
- package/dist/lib/browser/chunk-GWY2Q2NE.mjs.map +7 -0
- package/dist/lib/browser/{chunk-RHHJZQPM.mjs → chunk-HNIKXVCG.mjs} +135 -117
- package/dist/lib/browser/chunk-HNIKXVCG.mjs.map +7 -0
- package/dist/lib/browser/chunk-HRDAILQX.mjs +27 -0
- package/dist/lib/browser/chunk-HRDAILQX.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-5Q3R53U3.mjs → chunk-TGOKX32G.mjs} +21 -31
- package/dist/lib/browser/chunk-TGOKX32G.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-BBB3DWV4.mjs} +4 -4
- package/dist/lib/browser/{intent-resolver-ZQQEH27F.mjs.map → intent-resolver-BBB3DWV4.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-R277DACJ.mjs} +18 -7
- package/dist/lib/browser/react-surface-R277DACJ.mjs.map +7 -0
- package/dist/lib/browser/settings-I226DZ27.mjs +24 -0
- package/dist/lib/browser/settings-I226DZ27.mjs.map +7 -0
- package/dist/lib/browser/{transcriber-SMRLU2KY.mjs → transcriber-QRSS6X6S.mjs} +4 -4
- package/dist/lib/browser/types/index.mjs +3 -3
- package/dist/lib/node/{TranscriptContainer-ZWP6UKY6.cjs → TranscriptContainer-DICOMZLM.cjs} +8 -8
- package/dist/lib/node/{TranscriptContainer-ZWP6UKY6.cjs.map → TranscriptContainer-DICOMZLM.cjs.map} +2 -2
- package/dist/lib/node/app-graph-builder-I6LYVIYY.cjs +350 -0
- package/dist/lib/node/app-graph-builder-I6LYVIYY.cjs.map +7 -0
- package/dist/lib/node/{chunk-3QEJ5EBO.cjs → chunk-34WFY7M7.cjs} +22 -32
- package/dist/lib/node/chunk-34WFY7M7.cjs.map +7 -0
- package/dist/lib/node/{chunk-KO6MKCED.cjs → chunk-4JOJCZPJ.cjs} +138 -119
- package/dist/lib/node/chunk-4JOJCZPJ.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-WXG4Z2EZ.cjs → chunk-K553L7CZ.cjs} +23 -8
- package/dist/lib/node/chunk-K553L7CZ.cjs.map +7 -0
- package/dist/lib/node/{chunk-LA73IHCB.cjs → chunk-RK4BEN5E.cjs} +108 -80
- package/dist/lib/node/chunk-RK4BEN5E.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-7XY3WITN.cjs} +8 -8
- package/dist/lib/node/{intent-resolver-VY3XWYAA.cjs.map → intent-resolver-7XY3WITN.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-HUSWAORU.cjs} +20 -11
- package/dist/lib/node/react-surface-HUSWAORU.cjs.map +7 -0
- package/dist/lib/node/settings-NDMU3EWQ.cjs +38 -0
- package/dist/lib/node/settings-NDMU3EWQ.cjs.map +7 -0
- package/dist/lib/node/{transcriber-E6AAB4SU.cjs → transcriber-UZJMG44C.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-K4E4P5T6.mjs} +3 -3
- package/dist/lib/node-esm/app-graph-builder-6FY2UQOH.mjs +350 -0
- package/dist/lib/node-esm/app-graph-builder-6FY2UQOH.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-3I7AC3D3.mjs +28 -0
- package/dist/lib/node-esm/chunk-3I7AC3D3.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-SDMGJZLN.mjs → chunk-6C34FO4C.mjs} +135 -117
- package/dist/lib/node-esm/chunk-6C34FO4C.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-K3R3YK3S.mjs → chunk-CKANLRS4.mjs} +105 -78
- package/dist/lib/node-esm/chunk-CKANLRS4.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-54HDN33F.mjs → chunk-KUQ3ZTRX.mjs} +21 -31
- package/dist/lib/node-esm/chunk-KUQ3ZTRX.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-ACGAPLDZ.mjs} +4 -4
- package/dist/lib/node-esm/{intent-resolver-FCTNJPWY.mjs.map → intent-resolver-ACGAPLDZ.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-G2OY4MWL.mjs} +18 -7
- package/dist/lib/node-esm/react-surface-G2OY4MWL.mjs.map +7 -0
- package/dist/lib/node-esm/settings-O7X2UQQG.mjs +25 -0
- package/dist/lib/node-esm/settings-O7X2UQQG.mjs.map +7 -0
- package/dist/lib/node-esm/{transcriber-REAGRCPY.mjs → transcriber-NVVFH7IF.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/intent-resolver.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/useAudioFile.d.ts.map +1 -1
- package/dist/types/src/hooks/useAudioTrack.d.ts.map +1 -1
- package/dist/types/src/hooks/useQueueModelAdapter.d.ts +2 -2
- package/dist/types/src/hooks/useQueueModelAdapter.d.ts.map +1 -1
- package/dist/types/src/hooks/useTranscriber.d.ts.map +1 -1
- package/dist/types/src/hooks/useVoiceInput.d.ts.map +1 -1
- package/dist/types/src/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 +40 -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 -18
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/schema.d.ts +26 -40
- package/dist/types/src/types/schema.d.ts.map +1 -1
- package/dist/types/src/types/types.d.ts +11 -7
- package/dist/types/src/types/types.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/monitor-audio-level.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +51 -46
- 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 +72 -36
- package/src/components/Transcript/Transcript.tsx +37 -30
- package/src/components/Transcript/deprecated/{Transcript.tsx → OldTranscript.tsx} +30 -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 +84 -34
- package/src/entity-extraction/entity-extraction.test.ts +75 -10
- package/src/entity-extraction/entity-extraction.ts +110 -55
- package/src/entity-extraction/system-prompt.tpl +38 -9
- package/src/hooks/useQueueModelAdapter.ts +44 -11
- package/src/model/model.test.ts +90 -44
- package/src/model/model.ts +59 -59
- package/src/testing/index.ts +5 -0
- package/src/testing/testing.ts +221 -0
- package/src/transcriber/transcriber.browser.test.ts +3 -3
- package/src/transcriber/transcriber.ts +19 -10
- package/src/transcriber/transcription-manager.ts +66 -57
- package/src/translations.ts +2 -0
- package/src/types/schema.ts +10 -31
- package/src/types/types.ts +17 -7
- 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-3LU3XBEB.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-QRSS6X6S.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-UZJMG44C.cjs.map} +0 -0
- /package/dist/lib/node-esm/{TranscriptContainer-3HJ752CP.mjs.map → TranscriptContainer-K4E4P5T6.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-NVVFH7IF.mjs.map} +0 -0
package/src/model/model.ts
CHANGED
|
@@ -9,17 +9,17 @@ import { Event } from '@dxos/async';
|
|
|
9
9
|
import { invariant } from '@dxos/invariant';
|
|
10
10
|
import { log } from '@dxos/log';
|
|
11
11
|
|
|
12
|
-
export type
|
|
12
|
+
export type Chunk = { id: string };
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Covert block to a set of markdown lines.
|
|
16
16
|
*/
|
|
17
|
-
export type
|
|
17
|
+
export type ChunkRenderer<T extends Chunk> = (chunk: T, index: number, debug?: boolean) => string[];
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* Abstract representation of the document model.
|
|
21
21
|
*/
|
|
22
|
-
export interface
|
|
22
|
+
export interface ChunkDocument {
|
|
23
23
|
lineCount(): number;
|
|
24
24
|
replaceLines(from: number, remove: number, insert: string[]): void;
|
|
25
25
|
}
|
|
@@ -27,7 +27,7 @@ export interface BlockDocument {
|
|
|
27
27
|
/**
|
|
28
28
|
* Codemirror document adapter.
|
|
29
29
|
*/
|
|
30
|
-
export class DocumentAdapter implements
|
|
30
|
+
export class DocumentAdapter implements ChunkDocument {
|
|
31
31
|
constructor(private readonly _view: EditorView) {}
|
|
32
32
|
|
|
33
33
|
/** The document must always have at least one line. */
|
|
@@ -54,16 +54,16 @@ export class DocumentAdapter implements BlockDocument {
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
type
|
|
57
|
+
type ChunkChange<T extends Chunk> =
|
|
58
58
|
| {
|
|
59
59
|
type: 'append';
|
|
60
60
|
index: number;
|
|
61
|
-
|
|
61
|
+
chunk: T;
|
|
62
62
|
}
|
|
63
63
|
| {
|
|
64
64
|
type: 'update';
|
|
65
65
|
index: number;
|
|
66
|
-
|
|
66
|
+
chunk: T;
|
|
67
67
|
}
|
|
68
68
|
| {
|
|
69
69
|
type: 'delete';
|
|
@@ -72,98 +72,98 @@ type BlockChange<T extends Block> =
|
|
|
72
72
|
};
|
|
73
73
|
|
|
74
74
|
/**
|
|
75
|
-
* Each
|
|
76
|
-
*
|
|
75
|
+
* Each Chunk can be converted into a set of lines.
|
|
76
|
+
* Chunks can be added, updated or deleted from the Queue.
|
|
77
77
|
* A Document is an immutable projection that represents a flat list of lines corresponding to the Queue.
|
|
78
78
|
*
|
|
79
79
|
* Ideally we would implement a custom virtual Text model for the View, but this currently isn't possible in Codemirror.
|
|
80
|
-
* Instead this model tracks changes and syncs them with the
|
|
80
|
+
* Instead this model tracks changes and syncs them with the ChunkDocument.
|
|
81
81
|
*/
|
|
82
|
-
export class
|
|
83
|
-
/** Ordered set of
|
|
84
|
-
private readonly
|
|
82
|
+
export class SerializationModel<T extends Chunk> {
|
|
83
|
+
/** Ordered set of chunks. */
|
|
84
|
+
private readonly _chunks: T[] = [];
|
|
85
85
|
|
|
86
|
-
/** Map of
|
|
87
|
-
private readonly
|
|
86
|
+
/** Map of chunk IDs to their current line counts. */
|
|
87
|
+
private readonly _chunkLineCounts = new Map<string, number>();
|
|
88
88
|
|
|
89
|
-
/** Map of line numbers to
|
|
90
|
-
private readonly
|
|
89
|
+
/** Map of line numbers to chunk IDs. */
|
|
90
|
+
private readonly _lineToChunk = new Map<number, T>();
|
|
91
91
|
|
|
92
|
-
/** Track
|
|
93
|
-
private readonly _changes: Array<
|
|
92
|
+
/** Track chunk changes since last sync. */
|
|
93
|
+
private readonly _changes: Array<ChunkChange<T>> = [];
|
|
94
94
|
|
|
95
95
|
/** Emits when the document is updated. */
|
|
96
96
|
public readonly update = new Event<void>();
|
|
97
97
|
|
|
98
98
|
constructor(
|
|
99
|
-
private readonly _renderer:
|
|
100
|
-
|
|
99
|
+
private readonly _renderer: ChunkRenderer<T>,
|
|
100
|
+
initialChunks: T[] = [],
|
|
101
101
|
) {
|
|
102
|
-
|
|
103
|
-
this.
|
|
102
|
+
initialChunks.forEach((chunk) => {
|
|
103
|
+
this.appendChunk(chunk);
|
|
104
104
|
});
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
toJSON() {
|
|
108
108
|
return {
|
|
109
|
-
|
|
109
|
+
chunks: this._chunks.length,
|
|
110
110
|
changes: this._changes.length,
|
|
111
111
|
};
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
get doc() {
|
|
115
|
-
return Text.of([...this.
|
|
115
|
+
return Text.of([...this._chunks.flatMap((chunk, index) => this._renderer(chunk, index)), '']);
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
get
|
|
119
|
-
return this.
|
|
118
|
+
get chunks() {
|
|
119
|
+
return this._chunks;
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
/**
|
|
123
123
|
* Get the block ID at the given line number.
|
|
124
124
|
*/
|
|
125
|
-
|
|
126
|
-
return this.
|
|
125
|
+
getChunkAtLine(line: number): T | undefined {
|
|
126
|
+
return this._lineToChunk.get(line);
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
reset(): this {
|
|
130
|
-
this.
|
|
131
|
-
this.
|
|
132
|
-
this.
|
|
130
|
+
this._chunks.length = 0;
|
|
131
|
+
this._chunkLineCounts.clear();
|
|
132
|
+
this._lineToChunk.clear();
|
|
133
133
|
this.update.emit();
|
|
134
134
|
return this;
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
-
|
|
138
|
-
const index = this.
|
|
139
|
-
this.
|
|
140
|
-
this._changes.push({ type: 'append', index,
|
|
137
|
+
appendChunk(chunk: T): this {
|
|
138
|
+
const index = this._chunks.length;
|
|
139
|
+
this._chunks.push(chunk);
|
|
140
|
+
this._changes.push({ type: 'append', index, chunk });
|
|
141
141
|
this.update.emit();
|
|
142
142
|
return this;
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
-
|
|
146
|
-
const index = this.
|
|
145
|
+
updateChunk(chunk: T): this {
|
|
146
|
+
const index = this._chunks.findIndex((c) => c.id === chunk.id);
|
|
147
147
|
invariant(index !== -1);
|
|
148
|
-
this.
|
|
149
|
-
this._changes.push({ type: 'update', index,
|
|
148
|
+
this._chunks[index] = chunk;
|
|
149
|
+
this._changes.push({ type: 'update', index, chunk });
|
|
150
150
|
this.update.emit();
|
|
151
151
|
return this;
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
deleteBlock(id: string): this {
|
|
155
|
-
const index = this.
|
|
155
|
+
const index = this._chunks.findIndex((c) => c.id === id);
|
|
156
156
|
invariant(index !== -1);
|
|
157
|
-
this.
|
|
157
|
+
this._chunks.splice(index, 1);
|
|
158
158
|
this._changes.push({ type: 'delete', index, id });
|
|
159
159
|
this.update.emit();
|
|
160
160
|
return this;
|
|
161
161
|
}
|
|
162
162
|
|
|
163
163
|
/**
|
|
164
|
-
* Syncs the tracked changes with the
|
|
164
|
+
* Syncs the tracked changes with the ChunkDocument.
|
|
165
165
|
*/
|
|
166
|
-
sync(document:
|
|
166
|
+
sync(document: ChunkDocument): this {
|
|
167
167
|
log('sync', { changes: this._changes });
|
|
168
168
|
|
|
169
169
|
// Process each change in order.
|
|
@@ -171,7 +171,7 @@ export class BlockModel<T extends Block> {
|
|
|
171
171
|
switch (change.type) {
|
|
172
172
|
case 'append': {
|
|
173
173
|
// For appends, convert block to lines and add at the end.
|
|
174
|
-
const lines = this._renderer(change.
|
|
174
|
+
const lines = this._renderer(change.chunk, change.index);
|
|
175
175
|
const lastLine = document.lineCount();
|
|
176
176
|
document.replaceLines(lastLine + 1, 0, lines);
|
|
177
177
|
break;
|
|
@@ -181,10 +181,10 @@ export class BlockModel<T extends Block> {
|
|
|
181
181
|
// For updates, find the block's line range and replace with new lines.
|
|
182
182
|
let lineStart = 1;
|
|
183
183
|
for (let i = 0; i < change.index; i++) {
|
|
184
|
-
lineStart += this.
|
|
184
|
+
lineStart += this._chunkLineCounts.get(this._chunks[i].id)!;
|
|
185
185
|
}
|
|
186
|
-
const previousLineCount = this.
|
|
187
|
-
const newLines = this._renderer(change.
|
|
186
|
+
const previousLineCount = this._chunkLineCounts.get(change.chunk.id)!;
|
|
187
|
+
const newLines = this._renderer(change.chunk, change.index);
|
|
188
188
|
document.replaceLines(lineStart, previousLineCount, newLines);
|
|
189
189
|
break;
|
|
190
190
|
}
|
|
@@ -193,9 +193,9 @@ export class BlockModel<T extends Block> {
|
|
|
193
193
|
// For deletes, find the block's line range and remove those lines.
|
|
194
194
|
let lineStart = 1;
|
|
195
195
|
for (let i = 0; i < change.index; i++) {
|
|
196
|
-
lineStart += this.
|
|
196
|
+
lineStart += this._chunkLineCounts.get(this._chunks[i].id)!;
|
|
197
197
|
}
|
|
198
|
-
const previousLineCount = this.
|
|
198
|
+
const previousLineCount = this._chunkLineCounts.get(change.id)!;
|
|
199
199
|
document.replaceLines(lineStart, previousLineCount, []);
|
|
200
200
|
break;
|
|
201
201
|
}
|
|
@@ -203,22 +203,22 @@ export class BlockModel<T extends Block> {
|
|
|
203
203
|
}
|
|
204
204
|
|
|
205
205
|
// Clear the line-to-block mapping.
|
|
206
|
-
this.
|
|
206
|
+
this._lineToChunk.clear();
|
|
207
207
|
|
|
208
208
|
// Update all block line counts and line-to-block mapping after processing changes.
|
|
209
209
|
let currentLine = 1;
|
|
210
|
-
for (const
|
|
211
|
-
const lineCount = this._renderer(
|
|
212
|
-
this.
|
|
213
|
-
this.
|
|
210
|
+
for (const [index, chunk] of this._chunks.entries()) {
|
|
211
|
+
const lineCount = this._renderer(chunk, index).length;
|
|
212
|
+
this._chunkLineCounts.set(chunk.id, lineCount);
|
|
213
|
+
this._lineToChunk.set(currentLine, chunk);
|
|
214
214
|
currentLine += lineCount;
|
|
215
215
|
}
|
|
216
216
|
|
|
217
217
|
// Clean up deleted blocks.
|
|
218
|
-
const
|
|
219
|
-
for (const id of this.
|
|
220
|
-
if (!
|
|
221
|
-
this.
|
|
218
|
+
const chunkIds = new Set(this._chunks.map((chunk) => chunk.id));
|
|
219
|
+
for (const id of this._chunkLineCounts.keys()) {
|
|
220
|
+
if (!chunkIds.has(id)) {
|
|
221
|
+
this._chunkLineCounts.delete(id);
|
|
222
222
|
}
|
|
223
223
|
}
|
|
224
224
|
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Schema, SchemaAST } from 'effect';
|
|
6
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
7
|
+
|
|
8
|
+
import { AIServiceEdgeClient } from '@dxos/assistant';
|
|
9
|
+
import { AI_SERVICE_ENDPOINT } from '@dxos/assistant/testing';
|
|
10
|
+
import { scheduleTaskInterval } from '@dxos/async';
|
|
11
|
+
import { createQueueDxn, Filter, type Queue } from '@dxos/client/echo';
|
|
12
|
+
import { Context } from '@dxos/context';
|
|
13
|
+
import { Type } from '@dxos/echo';
|
|
14
|
+
import { create, ObjectId } from '@dxos/echo-schema';
|
|
15
|
+
import { IdentityDid } from '@dxos/keys';
|
|
16
|
+
import { log } from '@dxos/log';
|
|
17
|
+
import { faker } from '@dxos/random';
|
|
18
|
+
import { live, makeRef, useQueue, type Space } from '@dxos/react-client/echo';
|
|
19
|
+
import { DataType } from '@dxos/schema';
|
|
20
|
+
import { Testing, seedTestData } from '@dxos/schema/testing';
|
|
21
|
+
|
|
22
|
+
import { processTranscriptMessage } from '../entity-extraction';
|
|
23
|
+
|
|
24
|
+
// TODO(burdon): Reconcile with plugin-markdown. Move to @dxos/schema/testing.
|
|
25
|
+
|
|
26
|
+
export const TestItem = Schema.Struct({
|
|
27
|
+
title: Schema.String.annotations({
|
|
28
|
+
[SchemaAST.TitleAnnotationId]: 'Title',
|
|
29
|
+
[SchemaAST.DescriptionAnnotationId]: 'Product title',
|
|
30
|
+
}),
|
|
31
|
+
description: Schema.String.annotations({
|
|
32
|
+
[SchemaAST.TitleAnnotationId]: 'Description',
|
|
33
|
+
[SchemaAST.DescriptionAnnotationId]: 'Product description',
|
|
34
|
+
}),
|
|
35
|
+
}).pipe(
|
|
36
|
+
Type.def({
|
|
37
|
+
typename: 'dxos.org/type/Test',
|
|
38
|
+
version: '0.1.0',
|
|
39
|
+
}),
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
// TODO(wittjosiah): Make builder generic and reuse for all message types.
|
|
43
|
+
abstract class AbstractMessageBuilder {
|
|
44
|
+
abstract createMessage(numSegments?: number): Promise<DataType.Message>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Generator of transcript messages.
|
|
49
|
+
*/
|
|
50
|
+
export class MessageBuilder extends AbstractMessageBuilder {
|
|
51
|
+
static readonly singleton = new MessageBuilder();
|
|
52
|
+
|
|
53
|
+
users = Array.from({ length: 5 }, () => ({
|
|
54
|
+
identityDid: IdentityDid.random().toString(),
|
|
55
|
+
name: faker.person.fullName(),
|
|
56
|
+
email: faker.internet.email(),
|
|
57
|
+
}));
|
|
58
|
+
|
|
59
|
+
start = new Date(Date.now() - 24 * 60 * 60 * 10_000);
|
|
60
|
+
|
|
61
|
+
constructor(private readonly _space?: Space) {
|
|
62
|
+
super();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
override async createMessage(numSegments = 1): Promise<DataType.Message> {
|
|
66
|
+
return {
|
|
67
|
+
id: ObjectId.random().toString(),
|
|
68
|
+
created: this.next().toISOString(),
|
|
69
|
+
sender: faker.helpers.arrayElement(this.users),
|
|
70
|
+
blocks: Array.from({ length: numSegments }).map(() => this.createBlock()),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
createBlock(): DataType.MessageBlock.Transcription {
|
|
75
|
+
let text = faker.lorem.paragraph();
|
|
76
|
+
if (this._space) {
|
|
77
|
+
const label = faker.commerce.productName();
|
|
78
|
+
const obj = this._space.db.add(live(TestItem, { title: label, description: faker.lorem.paragraph() }));
|
|
79
|
+
const dxn = makeRef(obj).dxn.toString();
|
|
80
|
+
const words = text.split(' ');
|
|
81
|
+
words.splice(Math.floor(Math.random() * words.length), 0, `[${label}][${dxn}]`);
|
|
82
|
+
text = words.join(' ');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
type: 'transcription',
|
|
87
|
+
started: this.next().toISOString(),
|
|
88
|
+
text,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
next() {
|
|
93
|
+
this.start = new Date(this.start.getTime() + Math.random() * 10_000);
|
|
94
|
+
return this.start;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// TODO(burdon): Reconcile with BlockBuilder.
|
|
99
|
+
class EntityExtractionMessageBuilder extends AbstractMessageBuilder {
|
|
100
|
+
aiService = new AIServiceEdgeClient({
|
|
101
|
+
endpoint: AI_SERVICE_ENDPOINT.REMOTE,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
space: Space | undefined;
|
|
105
|
+
currentMessage: number = 0;
|
|
106
|
+
transcriptMessages: DataType.Message[] = [];
|
|
107
|
+
|
|
108
|
+
async connect(space: Space) {
|
|
109
|
+
this.space = space;
|
|
110
|
+
const { transcriptMessages } = await seedTestData(space);
|
|
111
|
+
this.transcriptMessages = transcriptMessages;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
override async createMessage(): Promise<DataType.Message> {
|
|
115
|
+
if (!this.space) {
|
|
116
|
+
throw new Error('Space not connected');
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const { objects } = await this.space.db
|
|
120
|
+
.query(
|
|
121
|
+
Filter.or(
|
|
122
|
+
Filter.schema(DataType.Person),
|
|
123
|
+
Filter.schema(DataType.Organization),
|
|
124
|
+
Filter.schema(Testing.DocumentType),
|
|
125
|
+
),
|
|
126
|
+
)
|
|
127
|
+
.run();
|
|
128
|
+
|
|
129
|
+
log.info('context', { objects });
|
|
130
|
+
const message = this.transcriptMessages[this.currentMessage];
|
|
131
|
+
this.currentMessage++;
|
|
132
|
+
this.currentMessage = this.currentMessage % this.transcriptMessages.length;
|
|
133
|
+
|
|
134
|
+
const { message: enhancedMessage } = await processTranscriptMessage({
|
|
135
|
+
message,
|
|
136
|
+
aiService: this.aiService,
|
|
137
|
+
context: {
|
|
138
|
+
objects,
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
return enhancedMessage;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
type UseTestTranscriptionQueue = (
|
|
147
|
+
space: Space | undefined,
|
|
148
|
+
queueId?: ObjectId,
|
|
149
|
+
running?: boolean,
|
|
150
|
+
interval?: number,
|
|
151
|
+
) => Queue<DataType.Message> | undefined;
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Test transcriptionqueue.
|
|
155
|
+
*/
|
|
156
|
+
export const useTestTranscriptionQueue: UseTestTranscriptionQueue = (
|
|
157
|
+
space: Space | undefined,
|
|
158
|
+
queueId?: ObjectId,
|
|
159
|
+
running = true,
|
|
160
|
+
interval = 1_000,
|
|
161
|
+
) => {
|
|
162
|
+
const queueDxn = useMemo(() => (space ? createQueueDxn(space.id, queueId) : undefined), [space, queueId]);
|
|
163
|
+
const queue = useQueue<DataType.Message>(queueDxn);
|
|
164
|
+
const builder = useMemo(() => new MessageBuilder(space), [space]);
|
|
165
|
+
|
|
166
|
+
useEffect(() => {
|
|
167
|
+
if (!queue || !running) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const i = setInterval(() => {
|
|
172
|
+
void builder.createMessage(Math.ceil(Math.random() * 3)).then((message) => {
|
|
173
|
+
queue.append([create(DataType.Message, message)]);
|
|
174
|
+
});
|
|
175
|
+
}, interval);
|
|
176
|
+
return () => clearInterval(i);
|
|
177
|
+
}, [queue, running, interval]);
|
|
178
|
+
|
|
179
|
+
return queue;
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Test transcription queue.
|
|
184
|
+
*/
|
|
185
|
+
// TODO(burdon): Reconcile with useTestTranscriptionQueue.
|
|
186
|
+
export const useTestTranscriptionQueueWithEntityExtraction: UseTestTranscriptionQueue = (
|
|
187
|
+
space: Space | undefined,
|
|
188
|
+
queueId?: ObjectId,
|
|
189
|
+
running = true,
|
|
190
|
+
interval = 1_000,
|
|
191
|
+
) => {
|
|
192
|
+
const queueDxn = useMemo(() => (space ? createQueueDxn(space.id, queueId) : undefined), [space, queueId]);
|
|
193
|
+
const queue = useQueue<DataType.Message>(queueDxn);
|
|
194
|
+
const [builder] = useState(() => new EntityExtractionMessageBuilder());
|
|
195
|
+
|
|
196
|
+
useEffect(() => {
|
|
197
|
+
if (!queue || !running) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (space) {
|
|
202
|
+
void builder.connect(space);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const ctx = new Context();
|
|
206
|
+
scheduleTaskInterval(
|
|
207
|
+
ctx,
|
|
208
|
+
async () => {
|
|
209
|
+
const message = await builder.createMessage();
|
|
210
|
+
queue.append([create(DataType.Message, message)]);
|
|
211
|
+
},
|
|
212
|
+
interval,
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
return () => {
|
|
216
|
+
void ctx.dispose();
|
|
217
|
+
};
|
|
218
|
+
}, [space, queue, running, interval]);
|
|
219
|
+
|
|
220
|
+
return queue;
|
|
221
|
+
};
|
|
@@ -9,11 +9,11 @@ import { WaveFile } from 'wavefile';
|
|
|
9
9
|
|
|
10
10
|
import { Trigger } from '@dxos/async';
|
|
11
11
|
import { log } from '@dxos/log';
|
|
12
|
+
import { type DataType } from '@dxos/schema';
|
|
12
13
|
import { openAndClose } from '@dxos/test-utils';
|
|
13
14
|
import { trace, TRACE_PROCESSOR } from '@dxos/tracing';
|
|
14
15
|
|
|
15
16
|
import { type AudioChunk, type AudioRecorder, Transcriber } from '../transcriber';
|
|
16
|
-
import { type TranscriptSegment } from '../types';
|
|
17
17
|
import { mergeFloat64Arrays } from '../util';
|
|
18
18
|
|
|
19
19
|
// TODO(burdon): ReferenceError: Worker is not defined (Can only run with web workers).
|
|
@@ -93,7 +93,7 @@ describe.skip('Transcriber', () => {
|
|
|
93
93
|
});
|
|
94
94
|
|
|
95
95
|
test.skip('transcription of audio recording', { timeout: 10_000 }, async () => {
|
|
96
|
-
const trigger = new Trigger<
|
|
96
|
+
const trigger = new Trigger<DataType.MessageBlock.Transcription[]>({ autoReset: true });
|
|
97
97
|
const recorder = new MockAudioRecorder({
|
|
98
98
|
buffer: await readFile('test.wav'),
|
|
99
99
|
chunkDuration: 3_000,
|
|
@@ -127,7 +127,7 @@ describe.skip('Transcriber', () => {
|
|
|
127
127
|
});
|
|
128
128
|
|
|
129
129
|
test.skip('transcription of audio recording with overlapping chunks', { timeout: 20_000 }, async () => {
|
|
130
|
-
const trigger = new Trigger<
|
|
130
|
+
const trigger = new Trigger<DataType.MessageBlock.Transcription[]>({ autoReset: true });
|
|
131
131
|
const recorder = new MockAudioRecorder({
|
|
132
132
|
buffer: await readFile('test.wav'),
|
|
133
133
|
chunkDuration: 3_000,
|
|
@@ -7,10 +7,11 @@ import { WaveFile } from 'wavefile';
|
|
|
7
7
|
import { DeferredTask, Trigger } from '@dxos/async';
|
|
8
8
|
import { type Context, LifecycleState, Resource } from '@dxos/context';
|
|
9
9
|
import { log } from '@dxos/log';
|
|
10
|
+
import { type DataType } from '@dxos/schema';
|
|
10
11
|
import { trace } from '@dxos/tracing';
|
|
11
12
|
|
|
12
13
|
import { type AudioRecorder, type AudioChunk } from './audio-recorder';
|
|
13
|
-
import { TRANSCRIPTION_URL
|
|
14
|
+
import { TRANSCRIPTION_URL } from '../types';
|
|
14
15
|
import { mergeFloat64Arrays } from '../util';
|
|
15
16
|
|
|
16
17
|
type WhisperWord = {
|
|
@@ -64,7 +65,7 @@ export type TranscribeConfig = {
|
|
|
64
65
|
export type TranscriberParams = {
|
|
65
66
|
config: TranscribeConfig;
|
|
66
67
|
recorder: AudioRecorder;
|
|
67
|
-
onSegments: (segments:
|
|
68
|
+
onSegments: (segments: DataType.MessageBlock.Transcription[]) => Promise<void>;
|
|
68
69
|
};
|
|
69
70
|
|
|
70
71
|
/**
|
|
@@ -93,6 +94,7 @@ export class Transcriber extends Resource {
|
|
|
93
94
|
}
|
|
94
95
|
|
|
95
96
|
protected override async _open(ctx: Context) {
|
|
97
|
+
log.info('opening');
|
|
96
98
|
this._recorder.setOnChunk((chunk) => this._saveAudioChunk(chunk));
|
|
97
99
|
await this._recorder.start();
|
|
98
100
|
this._transcribeTask = new DeferredTask(ctx, async () => this._transcribe());
|
|
@@ -100,15 +102,18 @@ export class Transcriber extends Resource {
|
|
|
100
102
|
}
|
|
101
103
|
|
|
102
104
|
protected override async _close() {
|
|
105
|
+
log.info('closing');
|
|
103
106
|
this._recording = false;
|
|
104
107
|
this._transcribeTask = undefined;
|
|
105
108
|
await this._recorder.stop();
|
|
106
109
|
}
|
|
107
110
|
|
|
108
111
|
startChunksRecording() {
|
|
112
|
+
log.info('starting');
|
|
109
113
|
if (this._lifecycleState !== LifecycleState.OPEN) {
|
|
110
114
|
return;
|
|
111
115
|
}
|
|
116
|
+
|
|
112
117
|
this._recording = true;
|
|
113
118
|
}
|
|
114
119
|
|
|
@@ -116,8 +121,10 @@ export class Transcriber extends Resource {
|
|
|
116
121
|
if (this._lifecycleState !== LifecycleState.OPEN || !this._recording) {
|
|
117
122
|
return;
|
|
118
123
|
}
|
|
124
|
+
|
|
119
125
|
this._recording = false;
|
|
120
126
|
this._transcribeTask?.schedule();
|
|
127
|
+
log.info('stopped');
|
|
121
128
|
}
|
|
122
129
|
|
|
123
130
|
private _saveAudioChunk(chunk: AudioChunk) {
|
|
@@ -194,24 +201,25 @@ export class Transcriber extends Resource {
|
|
|
194
201
|
|
|
195
202
|
if (!response.ok) {
|
|
196
203
|
this._audioChunks = [];
|
|
197
|
-
throw new Error(
|
|
204
|
+
throw new Error(`Transcription failed: ${response.statusText}`);
|
|
198
205
|
}
|
|
199
206
|
|
|
200
|
-
const {
|
|
201
|
-
segments,
|
|
202
|
-
}: {
|
|
207
|
+
const { segments } = (await response.json()) as {
|
|
203
208
|
segments: WhisperSegment[];
|
|
204
|
-
}
|
|
209
|
+
};
|
|
205
210
|
|
|
206
211
|
log.info('transcription response', {
|
|
207
|
-
segments,
|
|
212
|
+
segments: segments.length,
|
|
208
213
|
string: segments.map((segments) => segments.text).join(' '),
|
|
209
214
|
});
|
|
210
215
|
|
|
211
216
|
return segments;
|
|
212
217
|
}
|
|
213
218
|
|
|
214
|
-
private _alignSegments(
|
|
219
|
+
private _alignSegments(
|
|
220
|
+
segments: WhisperSegment[],
|
|
221
|
+
originalChunks: AudioChunk[],
|
|
222
|
+
): DataType.MessageBlock.Transcription[] {
|
|
215
223
|
// Absolute zero for all relative timestamps in the segments.
|
|
216
224
|
const zeroTimestamp = originalChunks.at(0)!.timestamp;
|
|
217
225
|
|
|
@@ -240,8 +248,9 @@ export class Transcriber extends Resource {
|
|
|
240
248
|
|
|
241
249
|
// Add absolute timestamp to each segment.
|
|
242
250
|
return filteredSegments.map((segment) => ({
|
|
251
|
+
type: 'transcription',
|
|
243
252
|
started: new Date(zeroTimestamp + segment.start * 1_000).toISOString(),
|
|
244
|
-
text: segment.text,
|
|
253
|
+
text: segment.text.trim(),
|
|
245
254
|
}));
|
|
246
255
|
}
|
|
247
256
|
}
|