2026-02-11 10:20:17 +00:00
#if USE_FOUNDATION_MODELS && canImport ( FoundationModels ) && canImport ( FoundationModelsMacros )
2026-02-05 21:27:11 +00:00
import Foundation
import FoundationModels
@ Generable ( description : " Plain generated text " )
public struct GeneratedText { public var text : String }
public enum AppleFM {
2026-02-06 13:29:34 +00:00
// / G l o b a l t o g g l e t o e n a b l e A p p l e F o u n d a t i o n M o d e l s f e a t u r e s a t r u n t i m e .
// / D e f a u l t s t o ` f a l s e ` s o c o d e c o m p l e t i o n / A I f e a t u r e s a r e d i s a b l e d b y d e f a u l t .
public static var isEnabled : Bool = false
private static func featureDisabledError ( ) -> NSError {
NSError ( domain : " AppleFM " , code : - 10 , userInfo : [ NSLocalizedDescriptionKey : " Foundation Models feature is disabled by default. Enable via AppleFM.isEnabled = true. " ] )
}
2026-02-05 21:27:11 +00:00
// / P e r f o r m a s i m p l e h e a l t h c h e c k b y r e q u e s t i n g a s h o r t c o m p l e t i o n u s i n g t h e s y s t e m m o d e l .
// / - R e t u r n s : A s t r i n g i n d i c a t i n g t h e m o d e l i s r e s p o n s i v e ( " p o n g " ) .
// / - T h r o w s : A n y e r r o r t h r o w n b y t h e F o u n d a t i o n M o d e l s A P I o r a v a i l a b i l i t y c h e c k s .
public static func appleFMHealthCheck ( ) async throws -> String {
if #available ( iOS 18.0 , macOS 15.0 , * ) {
2026-02-06 13:29:34 +00:00
guard isEnabled else {
throw featureDisabledError ( )
}
2026-02-05 21:27:11 +00:00
// E n s u r e t h e s y s t e m m o d e l i s a v a i l a b l e b e f o r e a t t e m p t i n g t o r e s p o n d
let model = SystemLanguageModel . default
guard case . available = model . availability else {
throw NSError ( domain : " AppleFM " , code : - 2 , userInfo : [ NSLocalizedDescriptionKey : " Apple Intelligence model unavailable: \( String ( describing : model . availability ) ) " ] )
}
let session = LanguageModelSession ( )
_ = try await session . respond ( to : " ping " )
return " pong "
} else {
throw NSError ( domain : " AppleFM " , code : - 3 , userInfo : [ NSLocalizedDescriptionKey : " Apple Intelligence requires iOS 18 / macOS 15 or later. " ] )
}
}
// / G e n e r a t e a c o m p l e t i o n f r o m t h e g i v e n p r o m p t u s i n g t h e s y s t e m l a n g u a g e m o d e l .
// / - P a r a m e t e r p r o m p t : T h e p r o m p t s t r i n g t o c o m p l e t e .
// / - R e t u r n s : T h e c o m p l e t i o n t e x t f r o m t h e m o d e l .
// / - T h r o w s : A n y e r r o r t h r o w n b y t h e F o u n d a t i o n M o d e l s A P I o r a v a i l a b i l i t y c h e c k s .
public static func appleFMComplete ( prompt : String ) async throws -> String {
if #available ( iOS 18.0 , macOS 15.0 , * ) {
2026-02-06 13:29:34 +00:00
guard isEnabled else {
throw featureDisabledError ( )
}
2026-02-05 21:27:11 +00:00
let model = SystemLanguageModel . default
guard case . available = model . availability else {
throw NSError ( domain : " AppleFM " , code : - 2 , userInfo : [ NSLocalizedDescriptionKey : " Apple Intelligence model unavailable: \( String ( describing : model . availability ) ) " ] )
}
let session = LanguageModelSession ( )
let response = try await session . respond ( to : prompt )
return response . content
} else {
throw NSError ( domain : " AppleFM " , code : - 3 , userInfo : [ NSLocalizedDescriptionKey : " Apple Intelligence requires iOS 18 / macOS 15 or later. " ] )
}
}
// / S t r e a m a c o m p l e t i o n f r o m t h e g i v e n p r o m p t , y i e l d i n g p a r t i a l u p d a t e s a s t h e m o d e l g e n e r a t e s t h e m .
// / - P a r a m e t e r p r o m p t : T h e p r o m p t s t r i n g t o c o m p l e t e .
// / - R e t u r n s : A n A s y n c S t r e a m o f i n c r e m e n t a l t e x t d e l t a s .
public static func appleFMStream ( prompt : String ) -> AsyncStream < String > {
if #available ( iOS 18.0 , macOS 15.0 , * ) {
2026-02-06 13:29:34 +00:00
guard isEnabled else {
return AsyncStream { $0 . finish ( ) }
}
2026-02-05 21:27:11 +00:00
let model = SystemLanguageModel . default
guard case . available = model . availability else {
return AsyncStream { $0 . finish ( ) }
}
return AsyncStream { continuation in
Task {
do {
let session = LanguageModelSession ( )
var last = " "
for try await partial in session . streamResponse ( to : prompt , generating : GeneratedText . self ) {
// E x t r a c t t h e f u l l c u r r e n t t e x t f r o m t h e p a r t i a l l y g e n e r a t e d c o n t e n t
let currentOptional = partial . content . text
// I f t h e m o d e l h a s n ' t p r o d u c e d a n y t e x t y e t , s k i p t h i s i t e r a t i o n
guard let current = currentOptional else { continue }
// C o m p u t e t h e d e l t a f r o m t h e l a s t f u l l c o n t e n t w e s a w u s i n g S t r i n g i n d i c e s
let lastCount = last . count
let currentCount = current . count
let prefixCount = min ( lastCount , currentCount )
let startIdx = current . index ( current . startIndex , offsetBy : prefixCount )
let delta = String ( current [ startIdx . . . ] )
if ! delta . isEmpty {
continuation . yield ( delta )
}
last = current
}
} catch {
// F a l l b a c k t o s i n g l e - s h o t c o m p l e t i o n i f s t r e a m i n g f a i l s
do {
let response = try await LanguageModelSession ( ) . respond ( to : prompt )
continuation . yield ( response . content )
} catch {
// S w a l l o w s e c o n d a r y e r r o r s
}
}
continuation . finish ( )
}
}
} else {
return AsyncStream { continuation in
continuation . finish ( )
}
}
}
}
#else
import Foundation
public enum AppleFM {
2026-02-11 10:20:17 +00:00
// / G l o b a l t o g g l e t o e n a b l e A p p l e F o u n d a t i o n M o d e l s f e a t u r e s a t r u n t i m e .
// / D e f a u l t s t o ` f a l s e ` s o c o d e c o m p l e t i o n / A I f e a t u r e s a r e d i s a b l e d b y d e f a u l t .
public static var isEnabled : Bool = false
2026-02-05 21:27:11 +00:00
// / S t u b h e a l t h c h e c k i m p l e m e n t a t i o n w h e n F o u n d a t i o n M o d e l s i s n o t a v a i l a b l e .
// / - T h r o w s : A l w a y s t h r o w s a n e r r o r i n d i c a t i n g t h e f e a t u r e i s u n a v a i l a b l e .
public static func appleFMHealthCheck ( ) async throws -> String {
throw NSError ( domain : " AppleFM " , code : - 1 , userInfo : [ NSLocalizedDescriptionKey : " Foundation Models feature is not enabled. " ] )
}
// / S t u b c o m p l e t i o n i m p l e m e n t a t i o n w h e n F o u n d a t i o n M o d e l s i s n o t a v a i l a b l e .
// / - P a r a m e t e r p r o m p t : T h e p r o m p t s t r i n g .
// / - R e t u r n s : P l a c e h o l d e r s t r i n g i n d i c a t i n g u n a v a i l a b l e f e a t u r e .
public static func appleFMComplete ( prompt : String ) async throws -> String {
return " Completion unavailable: Foundation Models feature not enabled. "
}
// / S t u b s t r e a m i n g i m p l e m e n t a t i o n w h e n F o u n d a t i o n M o d e l s i s n o t a v a i l a b l e .
public static func appleFMStream ( prompt : String ) -> AsyncStream < String > {
return AsyncStream { continuation in
continuation . finish ( )
}
}
}
#endif
2026-02-06 13:29:34 +00:00
2026-02-05 21:27:11 +00:00