forked from BasedHardware/omi
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtranscript_hash.dart
More file actions
112 lines (98 loc) · 3.27 KB
/
Copy pathtranscript_hash.dart
File metadata and controls
112 lines (98 loc) · 3.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
import 'dart:convert';
import 'package:crypto/crypto.dart';
import 'package:omi/backend/schema/transcript_segment.dart';
/// Client copy of `omi:backend/utils/conversations/transcript_hash.py` encoding v5.
///
/// The digest is a wire contract. Bump [transcriptHashEncodingVersion] when
/// the framing changes. The version is not mixed into the digest bytes.
const int transcriptHashEncodingVersion = 5;
const String defaultSpeaker = 'SPEAKER_00';
const int defaultSpeakerId = 0;
const String isUserToken = 'Y';
const String notUserToken = 'N';
class CanonicalTranscriptSegment {
const CanonicalTranscriptSegment({
required this.speaker,
required this.speakerId,
required this.isUser,
required this.personId,
required this.text,
});
final String speaker;
final int speakerId;
final bool isUser;
final String? personId;
final String text;
}
String _stripOrDefault(String? raw, [String defaultValue = '']) {
if (raw == null) return defaultValue;
final stripped = raw.trim();
if (stripped.isEmpty) return defaultValue;
return stripped;
}
int derivedSpeakerId(String canonicalSpeaker) {
final parts = canonicalSpeaker.split('_');
if (parts.length < 2) return defaultSpeakerId;
return int.tryParse(parts[1]) ?? defaultSpeakerId;
}
CanonicalTranscriptSegment canonicalizeSegment({
String? speaker,
int? speakerId,
bool? isUser,
String? personId,
String? text,
}) {
final canonicalSpeaker = _stripOrDefault(speaker, defaultSpeaker);
final canonicalPerson = _stripOrDefault(personId);
return CanonicalTranscriptSegment(
speaker: canonicalSpeaker,
speakerId: speakerId ?? derivedSpeakerId(canonicalSpeaker),
isUser: isUser == true,
personId: canonicalPerson.isEmpty ? null : canonicalPerson,
text: _stripOrDefault(text),
);
}
CanonicalTranscriptSegment canonicalizeTranscriptSegment(TranscriptSegment segment) {
return canonicalizeSegment(
speaker: segment.speaker,
speakerId: segment.speakerId,
isUser: segment.isUser,
personId: segment.personId,
text: segment.text,
);
}
List<int> _frame(String value) {
final encoded = utf8.encode(value);
return [...ascii.encode('${encoded.length}\n'), ...encoded];
}
List<int> canonicalTranscriptBytes(Iterable<CanonicalTranscriptSegment> parts) {
final out = <int>[];
for (final part in parts) {
out
..addAll(_frame(part.speaker))
..addAll(_frame('${part.speakerId}'))
..addAll(_frame(part.isUser ? isUserToken : notUserToken))
..addAll(_frame(part.personId ?? ''))
..addAll(_frame(part.text));
}
return out;
}
String transcriptSha256FromCanonical(Iterable<CanonicalTranscriptSegment> parts) {
return sha256.convert(canonicalTranscriptBytes(parts)).toString();
}
String transcriptSha256(Iterable<TranscriptSegment> segments) {
return transcriptSha256FromCanonical(segments.map(canonicalizeTranscriptSegment));
}
String transcriptSha256FromMaps(Iterable<Map<String, Object?>> segments) {
return transcriptSha256FromCanonical(
segments.map(
(segment) => canonicalizeSegment(
speaker: segment['speaker'] as String?,
speakerId: segment['speaker_id'] as int?,
isUser: segment['is_user'] as bool?,
personId: segment['person_id'] as String?,
text: segment['text'] as String?,
),
),
);
}