forked from BasedHardware/omi
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathClientProcessingContract.swift
More file actions
147 lines (134 loc) · 5.66 KB
/
Copy pathClientProcessingContract.swift
File metadata and controls
147 lines (134 loc) · 5.66 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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
import Foundation
/// Wire contract for S4 `ClientProcessing`. Schema version is `Literal[1]` on
/// the Pydantic model (`CLIENT_PROCESSING_SCHEMA_VERSION`); a float or bool is
/// rejected server-side. Use the generated `OmiAPI` types — do not hand-wire a
/// parallel DTO (plan correction 2026-09-06: S4 already regenerated them).
enum ClientProcessingContract {
static let schemaVersion = 1
static let localRuntime = "local"
static let deterministicRuntime = "deterministic"
static let deterministicModelID = "deterministic-minimum"
static let defaultEmoji = "🧠"
static let defaultDeviceClass = "macos"
static let titleMax = 120
static let overviewMax = 4000
static let emojiMax = 8
static let sectionHeadingMax = 120
static let sectionBodyMax = 4000
static let actionDescriptionMax = 500
static let eventTitleMax = 200
static let eventDescriptionMax = 1000
static let maxSections = 12
static let maxEvents = 12
static let maxActionItems = 25
static func encode(_ projection: OmiAPI.ClientProcessing) throws -> Data {
let encoder = JSONEncoder()
encoder.outputFormatting = [.sortedKeys]
return try encoder.encode(projection)
}
static func decode(_ data: Data) throws -> OmiAPI.ClientProcessing {
try JSONDecoder().decode(OmiAPI.ClientProcessing.self, from: data)
}
static func iso8601(_ date: Date) -> String {
let formatter = ISO8601DateFormatter()
formatter.formatOptions = [.withInternetDateTime]
formatter.timeZone = TimeZone(secondsFromGMT: 0)
return formatter.string(from: date)
}
static func assemble(
draft: LocalSummaryDraft,
transcriptSha256: String,
provenance: OmiAPI.ProjectionProvenance,
fallbackTitle: String
) -> OmiAPI.ClientProcessing {
let title = clip(draft.title, max: titleMax, empty: fallbackTitle)
let overview = clip(draft.overview, max: overviewMax, empty: "")
let emoji = clip(draft.emoji ?? defaultEmoji, max: emojiMax, empty: defaultEmoji)
let category = OmiAPI.CategoryEnum(rawValue: draft.category ?? "other") ?? .other
let sections = Array(draft.sections.prefix(maxSections)).compactMap(projectedSection)
let events = Array(draft.events.prefix(maxEvents)).compactMap(projectedEvent)
let actions = Array(draft.actionItems.prefix(maxActionItems)).compactMap(projectedAction)
return OmiAPI.ClientProcessing(
actionItems: actions,
provenance: provenance,
schemaVersion: schemaVersion,
structure: OmiAPI.ProjectedStructure(
category: category == ._unknown ? .other : category,
emoji: emoji,
events: events,
overview: overview,
sections: sections,
title: title
),
transcriptSha256: transcriptSha256
)
}
static func assembleMinimum(
_ minimum: DeterministicConversationMinimum,
transcriptSha256: String,
generatedAt: Date,
deviceClass: String
) -> OmiAPI.ClientProcessing {
OmiAPI.ClientProcessing(
actionItems: [],
provenance: OmiAPI.ProjectionProvenance(
deviceClass: deviceClass,
generatedAt: iso8601(generatedAt),
modelId: deterministicModelID,
runtime: deterministicRuntime
),
schemaVersion: schemaVersion,
structure: OmiAPI.ProjectedStructure(
category: .other,
emoji: defaultEmoji,
events: [],
overview: minimum.overview,
sections: [],
title: clip(minimum.title, max: titleMax, empty: "Recording")
),
transcriptSha256: transcriptSha256
)
}
static func stored(_ projection: OmiAPI.ClientProcessing) throws -> StoredClientProjection {
let json = try encode(projection)
return StoredClientProjection(transcriptSha256: projection.transcriptSha256, json: json)
}
private static func projectedSection(_ draft: LocalSectionDraft) -> OmiAPI.ProjectedSection? {
let heading = clip(draft.heading, max: sectionHeadingMax, empty: "")
let body = clip(draft.bodyMarkdown, max: sectionBodyMax, empty: "")
guard !heading.isEmpty, !body.isEmpty else { return nil }
return OmiAPI.ProjectedSection(bodyMarkdown: body, heading: heading)
}
private static func projectedEvent(_ draft: LocalEventDraft) -> OmiAPI.ProjectedEvent? {
let title = clip(draft.title, max: eventTitleMax, empty: "")
guard !title.isEmpty, isAwareISO8601(draft.start) else { return nil }
let duration = min(max(draft.duration, 1), 1440)
return OmiAPI.ProjectedEvent(
description_: clip(draft.description, max: eventDescriptionMax, empty: ""),
duration: duration,
start: draft.start,
title: title
)
}
private static func projectedAction(_ draft: LocalActionItemDraft) -> OmiAPI.ProjectedActionItem? {
let description = clip(draft.description, max: actionDescriptionMax, empty: "")
guard !description.isEmpty else { return nil }
return OmiAPI.ProjectedActionItem(completed: draft.completed, description_: description)
}
private static func isAwareISO8601(_ raw: String) -> Bool {
let formatter = ISO8601DateFormatter()
formatter.formatOptions = [.withInternetDateTime]
if formatter.date(from: raw) != nil { return true }
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
return formatter.date(from: raw) != nil
}
/// Bound the RAW string we are about to persist. Server rejects over-cap
/// values rather than trimming them into range.
static func clip(_ raw: String, max: Int, empty: String) -> String {
let stripped = raw.trimmingCharacters(in: .whitespacesAndNewlines)
if stripped.isEmpty { return empty }
if stripped.count <= max { return stripped }
let end = stripped.index(stripped.startIndex, offsetBy: max)
return String(stripped[..<end])
}
}