@@ -20,12 +20,6 @@ public class AppleLLMImpl: NSObject {
20
20
21
21
private var streamTasks : [ String : Task < Void , Never > ] = [ : ]
22
22
23
- // MARK: - Constants
24
-
25
- private static let supportedStringFormats : Set < String > = [
26
- " date-time " , " time " , " date " , " duration " , " email " , " hostname " , " ipv4 " , " ipv6 " , " uuid "
27
- ]
28
-
29
23
@objc
30
24
public func isAvailable( ) -> Bool {
31
25
#if canImport(FoundationModels)
@@ -42,7 +36,7 @@ public class AppleLLMImpl: NSObject {
42
36
@objc
43
37
public func generateText(
44
38
_ messages: [ [ String : Any ] ] ,
45
- options: [ String : Any ] ? ,
39
+ options: [ String : Any ] ,
46
40
resolve: @escaping ( Any ? ) -> Void ,
47
41
reject: @escaping ( String , String , Error ? ) -> Void ,
48
42
toolInvoker: @escaping ToolInvoker
@@ -59,7 +53,7 @@ public class AppleLLMImpl: NSObject {
59
53
}
60
54
Task {
61
55
do {
62
- let tools = try self . createTools ( from: options ?? [ : ] , toolInvoker: toolInvoker)
56
+ let tools = try self . createTools ( from: options, toolInvoker: toolInvoker)
63
57
let ( transcript, userPrompt) = try self . createTranscriptAndPrompt ( from: messages, tools: tools)
64
58
65
59
let session = LanguageModelSession . init (
@@ -69,13 +63,14 @@ public class AppleLLMImpl: NSObject {
69
63
transcript: transcript
70
64
)
71
65
72
- let generationOptions = try self . createGenerationOptions ( from: options ?? [ : ] )
66
+ let generationOptions = try self . createGenerationOptions ( from: options)
73
67
74
- if let schemaObj = options ? [ " schema " ] {
75
- let generationSchema = try AppleLLMSchemaParser . createGenerationSchema ( from: schemaObj)
68
+ if let schemaObj = options [ " schema " ] , !( schemaObj is NSNull ) ,
69
+ let schema = schemaObj as? [ String : Any ] {
70
+ let generationSchema = try AppleLLMSchemaParser . createGenerationSchema ( from: schema)
76
71
let response = try await session. respond ( to: userPrompt, schema: generationSchema, includeSchemaInPrompt: true , options: generationOptions)
77
72
78
- resolve ( try response. rawValue ( ) )
73
+ resolve ( try response. content . toDictionary ( using : schema ) )
79
74
} else {
80
75
let response = try await session. respond ( to: userPrompt, options: generationOptions)
81
76
resolve ( response. content)
@@ -97,7 +92,7 @@ public class AppleLLMImpl: NSObject {
97
92
@objc
98
93
public func generateStream(
99
94
_ messages: [ [ String : Any ] ] ,
100
- options: [ String : Any ] ? ,
95
+ options: [ String : Any ] ,
101
96
onUpdate: @escaping ( String , String ) -> Void ,
102
97
onComplete: @escaping ( String ) -> Void ,
103
98
onError: @escaping ( String , String ) -> Void
@@ -121,9 +116,9 @@ public class AppleLLMImpl: NSObject {
121
116
transcript: transcript
122
117
)
123
118
124
- let generationOptions = try self . createGenerationOptions ( from: options ?? [ : ] )
119
+ let generationOptions = try self . createGenerationOptions ( from: options)
125
120
126
- if let schemaOption = options ? [ " schema " ] {
121
+ if let schemaOption = options [ " schema " ] as? [ String : Any ] {
127
122
let generationSchema = try AppleLLMSchemaParser . createGenerationSchema ( from: schemaOption)
128
123
let responseStream = session. streamResponse (
129
124
to: userPrompt,
@@ -175,30 +170,14 @@ public class AppleLLMImpl: NSObject {
175
170
}
176
171
}
177
172
178
- @objc
179
- public func isModelAvailable(
180
- _ modelId: String ,
181
- resolve: @escaping ( Any ? ) -> Void ,
182
- reject: @escaping ( String , String , Error ? ) -> Void
183
- ) {
184
- #if canImport(FoundationModels)
185
- if #available( iOS 26 , * ) {
186
- resolve ( SystemLanguageModel . default. availability == . available)
187
- } else {
188
- resolve ( false )
189
- }
190
- #else
191
- resolve ( false )
192
- #endif
193
- }
194
-
195
173
// MARK: - Private Methods
196
174
#if canImport(FoundationModels)
197
175
198
176
@available ( iOS 26 , * )
199
177
private func createTools( from options: [ String : Any ] , toolInvoker: @escaping ToolInvoker ) throws -> [ any Tool ] {
200
- guard let toolsDict = options [ " tools " ] as? [ String : [ String : Any ] ] else {
201
- throw AppleLLMError . invalidSchema ( " Tools must be an object with tool definitions " )
178
+ guard let toolsObj = options [ " tools " ] , !( toolsObj is NSNull ) ,
179
+ let toolsDict = toolsObj as? [ String : [ String : Any ] ] else {
180
+ return [ ]
202
181
}
203
182
204
183
var tools : [ any Tool ] = [ ]
@@ -345,16 +324,7 @@ public class AppleLLMImpl: NSObject {
345
324
346
325
@available ( iOS 26 , * )
347
326
struct AppleLLMSchemaParser {
348
-
349
- // MARK: - Constants
350
- private static let supportedStringFormats : Set < String > = [
351
- " date-time " , " time " , " date " , " duration " , " email " , " hostname " , " ipv4 " , " ipv6 " , " uuid "
352
- ]
353
-
354
- static func createGenerationSchema( from schemaObj: Any ) throws -> GenerationSchema {
355
- guard let schemaDict = schemaObj as? [ String : Any ] else {
356
- throw AppleLLMError . invalidSchema ( " Schema must be an object " )
357
- }
327
+ static func createGenerationSchema( from schemaDict: [ String : Any ] ) throws -> GenerationSchema {
358
328
let dynamicSchemas = try parseDynamicSchema ( from: schemaDict)
359
329
return try GenerationSchema ( root: dynamicSchemas, dependencies: [ ] )
360
330
}
@@ -529,49 +499,8 @@ public class AppleLLMImpl: NSObject {
529
499
530
500
#endif
531
501
}
532
-
533
- #if canImport(FoundationModels)
534
502
535
- @available ( iOS 26 , * )
536
- extension LanguageModelSession . Response < GeneratedContent > {
537
- enum RawValueExtractionError : Error {
538
- case noTranscriptEntries
539
- case notAResponseEntry
540
- case noSegments
541
- case notAStructuredSegment
542
- case rawValueNotFound
543
- }
544
-
545
- func rawValue( ) throws -> String {
546
- guard let lastEntry = transcriptEntries. last else {
547
- throw RawValueExtractionError . noTranscriptEntries
548
- }
549
-
550
- guard case let . response( res) = lastEntry else {
551
- throw RawValueExtractionError . notAResponseEntry
552
- }
553
-
554
- guard let lastSegment = res. segments. last else {
555
- throw RawValueExtractionError . noSegments
556
- }
557
-
558
- if case let . text( textSegment) = lastSegment {
559
- return textSegment. content
560
- }
561
-
562
- guard case let . structure( structureSegment) = lastSegment else {
563
- throw RawValueExtractionError . notAStructuredSegment
564
- }
565
-
566
- for child in Mirror ( reflecting: structureSegment) . children {
567
- if child. label == " rawValue " , let rawValue = child. value as? String {
568
- return rawValue
569
- }
570
- }
571
-
572
- throw RawValueExtractionError . rawValueNotFound
573
- }
574
- }
503
+ #if canImport(FoundationModels)
575
504
576
505
@available ( iOS 26 , * )
577
506
extension GeneratedContent {
0 commit comments