ainouche-abderahmane commited on
Commit
b5f9085
·
verified ·
1 Parent(s): 37d7047

Release Mark v0.1.0: 38.7M parameter multilingual diacritics (INT8/FP16/FP32/CoreML)

Browse files
Files changed (2) hide show
  1. README.md +22 -8
  2. Sources/Mark/Mark.swift +32 -12
README.md CHANGED
@@ -145,6 +145,20 @@ Held-out evaluation report across all 37 languages:
145
 
146
  ## Install & Integration
147
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
148
  ### iOS / macOS (Swift Package Manager)
149
 
150
  Add to your `Package.swift`:
@@ -169,15 +183,15 @@ import Mark
169
  let mark = try Mark()
170
 
171
  // Yorùbá
172
- let yoruba = try await mark.restore("Omode naa n kawe daadaa ni ile-iwe.", lang: "yo")
173
  // -> "Ọmọdé náà ń kàwé dáadáa ní ilé-ìwé."
174
 
175
  // Arabic
176
- let arabic = try await mark.restore("ذهب الولد الى المدرسة", lang: "ar")
177
- // -> "ذْهَبَ الْوَلَدُ إِلَى الْمَدْرَسَةِ"
178
 
179
- // Spanish
180
- let spanish = try await mark.restore("El nino comio jamon en la manana.", lang: "es")
181
  // -> "El niño comió jamón en la mañana."
182
  ```
183
 
@@ -199,12 +213,12 @@ const yoruba = await restore("Omode naa n kawe daadaa ni ile-iwe.", "yo");
199
  // -> Ọmọdé náà ń kàwé dáadáa ní ilé-ìwé.
200
 
201
  const arabic = await restore("ذهب الولد الى المدرسة", "ar");
202
- // -> ذْهَبَ الْوَلَدُ إِلَى الْمَدْرَسَةِ
203
 
204
- // Reusable instance
205
  const mark = new Mark();
206
  await mark.init();
207
- const spanish = await mark.restore("El nino comio jamon en la manana.", "es");
208
  // -> El niño comió jamón en la mañana.
209
  ```
210
 
 
145
 
146
  ## Install & Integration
147
 
148
+ ### Python (ONNX Runtime)
149
+
150
+ ```python
151
+ from mark import restore
152
+
153
+ # Standard inference
154
+ restored = restore("Omode naa n kawe daadaa ni ile-iwe.", lang="yo")
155
+ # -> "Ọmọdé náà ń kàwé dáadáa ní ilé-ìwé."
156
+
157
+ # Calibrated anti-flicker mode for streaming keyboard input (margin >= 0.4)
158
+ stable = restore("El nino comio jamon en la manana.", lang="es", margin=0.4)
159
+ # -> "El niño comió jamón en la mañana."
160
+ ```
161
+
162
  ### iOS / macOS (Swift Package Manager)
163
 
164
  Add to your `Package.swift`:
 
183
  let mark = try Mark()
184
 
185
  // Yorùbá
186
+ let yoruba = try mark.restore("Omode naa n kawe daadaa ni ile-iwe.", lang: "yo")
187
  // -> "Ọmọdé náà ń kàwé dáadáa ní ilé-ìwé."
188
 
189
  // Arabic
190
+ let arabic = try mark.restore("ذهب الولد الى المدرسة", lang: "ar")
191
+ // -> "ذْهَبَ الْوَلَدُ إلَى الْمَدْرَسَةِ"
192
 
193
+ // Spanish (with calibrated margin: 0.4 for zero typing jitter)
194
+ let spanish = try mark.restore("El nino comio jamon en la manana.", lang: "es", margin: 0.4)
195
  // -> "El niño comió jamón en la mañana."
196
  ```
197
 
 
213
  // -> Ọmọdé náà ń kàwé dáadáa ní ilé-ìwé.
214
 
215
  const arabic = await restore("ذهب الولد الى المدرسة", "ar");
216
+ // -> ذْهَبَ الْوَلَدُ إلَى الْمَدْرَسَةِ
217
 
218
+ // Reusable instance with calibrated anti-flicker margin
219
  const mark = new Mark();
220
  await mark.init();
221
+ const spanish = await mark.restore("El nino comio jamon en la manana.", "es", { margin: 0.4 });
222
  // -> El niño comió jamón en la mañana.
223
  ```
224
 
Sources/Mark/Mark.swift CHANGED
@@ -84,7 +84,13 @@ public actor Mark {
84
  }
85
 
86
  /// Restore diacritics and tones on arbitrary input text.
87
- public func restore(_ text: String, lang: String) throws -> String {
 
 
 
 
 
 
88
  guard !text.isEmpty else { return "" }
89
 
90
  let normalizedLang = lang.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
@@ -96,7 +102,7 @@ public actor Mark {
96
  var restoredChunks: [String] = []
97
 
98
  for chunk in chunks {
99
- let restored = try restoreChunk(chunk, langId: langId)
100
  restoredChunks.append(restored)
101
  }
102
 
@@ -104,7 +110,7 @@ public actor Mark {
104
  }
105
 
106
  /// Single chunk restoration (under 512 bytes) using MLShapedArray.
107
- private func restoreChunk(_ chunk: String, langId: Int) throws -> String {
108
  let utf8Bytes = Array(chunk.utf8)
109
  guard !utf8Bytes.isEmpty else { return "" }
110
 
@@ -133,19 +139,33 @@ public actor Mark {
133
  var preds: [Int] = []
134
  preds.reserveCapacity(seqLen)
135
 
136
- // Fast contiguous scalar access for argmax
137
  for pos in 0..<seqLen {
138
  let offset = pos * numTags
139
- var maxVal = -Float.infinity
140
- var maxIdx = 0
141
- for tagIdx in 0..<numTags {
142
- let val = scalars[offset + tagIdx]
143
- if val > maxVal {
144
- maxVal = val
145
- maxIdx = tagIdx
 
 
 
 
 
 
 
 
 
 
 
 
 
 
146
  }
 
147
  }
148
- preds.append(maxIdx)
149
  }
150
 
151
  return decodePrediction(chunk: chunk, utf8Bytes: utf8Bytes, predTags: preds)
 
84
  }
85
 
86
  /// Restore diacritics and tones on arbitrary input text.
87
+ ///
88
+ /// - Parameters:
89
+ /// - text: Raw unmarked input string.
90
+ /// - lang: Target ISO 639-1 language code (e.g. "yo", "ar", "es").
91
+ /// - margin: Confidence threshold over KEEP (default: 0.0). When set to 0.4+,
92
+ /// suppresses hypothesis flicker during streaming keyboard input.
93
+ public func restore(_ text: String, lang: String, margin: Float = 0.0) throws -> String {
94
  guard !text.isEmpty else { return "" }
95
 
96
  let normalizedLang = lang.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
 
102
  var restoredChunks: [String] = []
103
 
104
  for chunk in chunks {
105
+ let restored = try restoreChunk(chunk, langId: langId, margin: margin)
106
  restoredChunks.append(restored)
107
  }
108
 
 
110
  }
111
 
112
  /// Single chunk restoration (under 512 bytes) using MLShapedArray.
113
+ private func restoreChunk(_ chunk: String, langId: Int, margin: Float = 0.0) throws -> String {
114
  let utf8Bytes = Array(chunk.utf8)
115
  guard !utf8Bytes.isEmpty else { return "" }
116
 
 
139
  var preds: [Int] = []
140
  preds.reserveCapacity(seqLen)
141
 
142
+ // Fast contiguous scalar access for calibrated margin or argmax
143
  for pos in 0..<seqLen {
144
  let offset = pos * numTags
145
+ if margin > 0.0 && numTags > 1 {
146
+ let keepVal = scalars[offset + 0]
147
+ var bestVal = -Float.infinity
148
+ var bestIdx = 0
149
+ for tagIdx in 1..<numTags {
150
+ let val = scalars[offset + tagIdx]
151
+ if val > bestVal {
152
+ bestVal = val
153
+ bestIdx = tagIdx
154
+ }
155
+ }
156
+ preds.append((bestVal - keepVal >= margin) ? bestIdx : 0)
157
+ } else {
158
+ var maxVal = -Float.infinity
159
+ var maxIdx = 0
160
+ for tagIdx in 0..<numTags {
161
+ let val = scalars[offset + tagIdx]
162
+ if val > maxVal {
163
+ maxVal = val
164
+ maxIdx = tagIdx
165
+ }
166
  }
167
+ preds.append(maxIdx)
168
  }
 
169
  }
170
 
171
  return decodePrediction(chunk: chunk, utf8Bytes: utf8Bytes, predTags: preds)