@@ -165,6 +165,114 @@ public struct ModelComputeOptions {
165
165
}
166
166
}
167
167
168
+ public struct ModelSupport : Codable , Equatable {
169
+ public let `default` : String
170
+ public let supported : [ String ]
171
+ /// Computed on init of ModelRepoConfig
172
+ public var disabled : [ String ] = [ ]
173
+
174
+ private enum CodingKeys : String , CodingKey {
175
+ case `default`, supported
176
+ }
177
+ }
178
+
179
+ public struct DeviceSupport : Codable {
180
+ public let identifiers : [ String ]
181
+ public var models : ModelSupport
182
+ }
183
+
184
+ public struct ModelSupportConfig : Codable {
185
+ public let repoName : String
186
+ public let repoVersion : String
187
+ public var deviceSupports : [ DeviceSupport ]
188
+ /// Computed on init
189
+ public private( set) var knownModels : [ String ]
190
+ public private( set) var defaultSupport : DeviceSupport
191
+
192
+ enum CodingKeys : String , CodingKey {
193
+ case repoName = " name "
194
+ case repoVersion = " version "
195
+ case deviceSupports = " device_support "
196
+ }
197
+
198
+ public init ( from decoder: Swift . Decoder ) throws {
199
+ let container = try decoder. container ( keyedBy: CodingKeys . self)
200
+ let repoName = try container. decode ( String . self, forKey: . repoName)
201
+ let repoVersion = try container. decode ( String . self, forKey: . repoVersion)
202
+ let deviceSupports = try container. decode ( [ DeviceSupport ] . self, forKey: . deviceSupports)
203
+
204
+ self . init ( repoName: repoName, repoVersion: repoVersion, deviceSupports: deviceSupports)
205
+ }
206
+
207
+ public init ( repoName: String , repoVersion: String , deviceSupports: [ DeviceSupport ] , includeFallback: Bool = true ) {
208
+ self . repoName = repoName
209
+ self . repoVersion = repoVersion
210
+
211
+ if includeFallback {
212
+ self . deviceSupports = Self . mergeDeviceSupport ( remote: deviceSupports, fallback: Constants . fallbackModelSupportConfig. deviceSupports)
213
+ self . knownModels = self . deviceSupports. flatMap { $0. models. supported } . orderedSet
214
+ } else {
215
+ self . deviceSupports = deviceSupports
216
+ self . knownModels = deviceSupports. flatMap { $0. models. supported } . orderedSet
217
+ }
218
+
219
+ // Add default device support with all models supported for unknown devices
220
+ self . defaultSupport = DeviceSupport (
221
+ identifiers: [ ] ,
222
+ models: ModelSupport (
223
+ default: " openai_whisper-base " ,
224
+ supported: self . knownModels
225
+ )
226
+ )
227
+
228
+ computeDisabledModels ( )
229
+ }
230
+
231
+ @available ( macOS 13 , iOS 16 , watchOS 10 , visionOS 1 , * )
232
+ public func modelSupport( for deviceIdentifier: String = WhisperKit . deviceName ( ) ) -> ModelSupport {
233
+ for support in deviceSupports {
234
+ if support. identifiers. contains ( where: { deviceIdentifier. hasPrefix ( $0) } ) {
235
+ return support. models
236
+ }
237
+ }
238
+
239
+ Logging . info ( " No device support found for \( deviceIdentifier) , using default " )
240
+ return defaultSupport. models
241
+ }
242
+
243
+ private mutating func computeDisabledModels( ) {
244
+ for i in 0 ..< deviceSupports. count {
245
+ let disabledModels = Set ( knownModels) . subtracting ( deviceSupports [ i] . models. supported)
246
+ self . deviceSupports [ i] . models. disabled = Array ( disabledModels)
247
+ }
248
+ }
249
+
250
+ private static func mergeDeviceSupport( remote: [ DeviceSupport ] , fallback: [ DeviceSupport ] ) -> [ DeviceSupport ] {
251
+ var mergedSupports : [ DeviceSupport ] = [ ]
252
+ let remoteIdentifiers = Set ( remote. flatMap { $0. identifiers } )
253
+
254
+ // Add remote device supports, merging with fallback if identifiers overlap
255
+ for remoteSupport in remote {
256
+ if let fallbackSupport = fallback. first ( where: { $0. identifiers. contains ( where: remoteSupport. identifiers. contains) } ) {
257
+ let mergedModels = ModelSupport (
258
+ default: remoteSupport. models. default,
259
+ supported: ( remoteSupport. models. supported + fallbackSupport. models. supported) . orderedSet
260
+ )
261
+ mergedSupports. append ( DeviceSupport ( identifiers: remoteSupport. identifiers, models: mergedModels) )
262
+ } else {
263
+ mergedSupports. append ( remoteSupport)
264
+ }
265
+ }
266
+
267
+ // Add fallback device supports that don't overlap with remote
268
+ for fallbackSupport in fallback where !fallbackSupport. identifiers. contains ( where: remoteIdentifiers. contains) {
269
+ mergedSupports. append ( fallbackSupport)
270
+ }
271
+
272
+ return mergedSupports
273
+ }
274
+ }
275
+
168
276
// MARK: - Chunking
169
277
170
278
public struct AudioChunk {
@@ -1346,4 +1454,141 @@ public enum Constants {
1346
1454
public static let defaultLanguageCode : String = " en "
1347
1455
1348
1456
public static let defaultAudioReadFrameSize : AVAudioFrameCount = 1_323_000 // 30s of audio at commonly found 44.1khz sample rate
1457
+
1458
+ public static let fallbackModelSupportConfig : ModelSupportConfig = {
1459
+ var config = ModelSupportConfig (
1460
+ repoName: " whisperkit-coreml-fallback " ,
1461
+ repoVersion: " 0.2 " ,
1462
+ deviceSupports: [
1463
+ DeviceSupport (
1464
+ identifiers: [ " iPhone11 " , " iPhone12 " , " Watch7 " , " Watch8 " ] ,
1465
+ models: ModelSupport (
1466
+ default: " openai_whisper-tiny " ,
1467
+ supported: [
1468
+ " openai_whisper-base " ,
1469
+ " openai_whisper-base.en " ,
1470
+ " openai_whisper-tiny " ,
1471
+ " openai_whisper-tiny.en " ,
1472
+ ]
1473
+ )
1474
+ ) ,
1475
+ DeviceSupport (
1476
+ identifiers: [ " iPhone13 " , " iPad13,18 " , " iPad13,1 " ] ,
1477
+ models: ModelSupport (
1478
+ default: " openai_whisper-base " ,
1479
+ supported: [
1480
+ " openai_whisper-tiny " ,
1481
+ " openai_whisper-tiny.en " ,
1482
+ " openai_whisper-base " ,
1483
+ " openai_whisper-base.en " ,
1484
+ " openai_whisper-small " ,
1485
+ " openai_whisper-small.en " ,
1486
+ ]
1487
+ )
1488
+ ) ,
1489
+ DeviceSupport (
1490
+ identifiers: [ " iPhone14 " , " iPhone15 " , " iPhone16 " , " iPhone17 " , " iPad14,1 " , " iPad14,2 " ] ,
1491
+ models: ModelSupport (
1492
+ default: " openai_whisper-base " ,
1493
+ supported: [
1494
+ " openai_whisper-tiny " ,
1495
+ " openai_whisper-tiny.en " ,
1496
+ " openai_whisper-base " ,
1497
+ " openai_whisper-base.en " ,
1498
+ " openai_whisper-small " ,
1499
+ " openai_whisper-small.en " ,
1500
+ " openai_whisper-large-v2_949MB " ,
1501
+ " openai_whisper-large-v2_turbo_955MB " ,
1502
+ " openai_whisper-large-v3_947MB " ,
1503
+ " openai_whisper-large-v3_turbo_954MB " ,
1504
+ " distil-whisper_distil-large-v3_594MB " ,
1505
+ " distil-whisper_distil-large-v3_turbo_600MB " ,
1506
+ " openai_whisper-large-v3-v20240930_626MB " ,
1507
+ " openai_whisper-large-v3-v20240930_turbo_632MB " ,
1508
+ ]
1509
+ )
1510
+ ) ,
1511
+ DeviceSupport (
1512
+ identifiers: [
1513
+ " Mac13 " ,
1514
+ " iMac21 " ,
1515
+ " MacBookAir10,1 " ,
1516
+ " MacBookPro17 " ,
1517
+ " MacBookPro18 " ,
1518
+ " Macmini9 " ,
1519
+ " iPad13,16 " ,
1520
+ " iPad13,4 " ,
1521
+ " iPad13,8 " ,
1522
+ ] ,
1523
+ models: ModelSupport (
1524
+ default: " openai_whisper-large-v3-v20240930 " ,
1525
+ supported: [
1526
+ " openai_whisper-tiny " ,
1527
+ " openai_whisper-tiny.en " ,
1528
+ " openai_whisper-base " ,
1529
+ " openai_whisper-base.en " ,
1530
+ " openai_whisper-small " ,
1531
+ " openai_whisper-small.en " ,
1532
+ " openai_whisper-large-v2 " ,
1533
+ " openai_whisper-large-v2_949MB " ,
1534
+ " openai_whisper-large-v3 " ,
1535
+ " openai_whisper-large-v3_947MB " ,
1536
+ " distil-whisper_distil-large-v3 " ,
1537
+ " distil-whisper_distil-large-v3_594MB " ,
1538
+ " openai_whisper-large-v3-v20240930 " ,
1539
+ " openai_whisper-large-v3-v20240930_626MB " ,
1540
+ ]
1541
+ )
1542
+ ) ,
1543
+ DeviceSupport (
1544
+ identifiers: [
1545
+ " Mac14 " ,
1546
+ " Mac15 " ,
1547
+ " Mac16 " ,
1548
+ " iPad14,3 " ,
1549
+ " iPad14,4 " ,
1550
+ " iPad14,5 " ,
1551
+ " iPad14,6 " ,
1552
+ " iPad14,8 " ,
1553
+ " iPad14,9 " ,
1554
+ " iPad14,10 " ,
1555
+ " iPad14,11 " ,
1556
+ " iPad16 " ,
1557
+ ] ,
1558
+ models: ModelSupport (
1559
+ default: " openai_whisper-large-v3-v20240930 " ,
1560
+ supported: [
1561
+ " openai_whisper-tiny " ,
1562
+ " openai_whisper-tiny.en " ,
1563
+ " openai_whisper-base " ,
1564
+ " openai_whisper-base.en " ,
1565
+ " openai_whisper-small " ,
1566
+ " openai_whisper-small.en " ,
1567
+ " openai_whisper-large-v2 " ,
1568
+ " openai_whisper-large-v2_949MB " ,
1569
+ " openai_whisper-large-v2_turbo " ,
1570
+ " openai_whisper-large-v2_turbo_955MB " ,
1571
+ " openai_whisper-large-v3 " ,
1572
+ " openai_whisper-large-v3_947MB " ,
1573
+ " openai_whisper-large-v3_turbo " ,
1574
+ " openai_whisper-large-v3_turbo_954MB " ,
1575
+ " distil-whisper_distil-large-v3 " ,
1576
+ " distil-whisper_distil-large-v3_594MB " ,
1577
+ " distil-whisper_distil-large-v3_turbo " ,
1578
+ " distil-whisper_distil-large-v3_turbo_600MB " ,
1579
+ " openai_whisper-large-v3-v20240930 " ,
1580
+ " openai_whisper-large-v3-v20240930_turbo " ,
1581
+ " openai_whisper-large-v3-v20240930_626MB " ,
1582
+ " openai_whisper-large-v3-v20240930_turbo_632MB " ,
1583
+ ]
1584
+ )
1585
+ ) ,
1586
+ ] ,
1587
+ includeFallback: false
1588
+ )
1589
+
1590
+ return config
1591
+ } ( )
1592
+
1593
+ public static let knownModels : [ String ] = fallbackModelSupportConfig. deviceSupports. flatMap { $0. models. supported } . orderedSet
1349
1594
}
0 commit comments