diff --git a/Package.swift b/Package.swift index 88e273c..3916bf0 100644 --- a/Package.swift +++ b/Package.swift @@ -75,8 +75,10 @@ let package = Package( .macro( name: "AnyLanguageModelMacros", dependencies: [ - .product(name: "SwiftSyntaxMacros", package: "swift-syntax"), .product(name: "SwiftCompilerPlugin", package: "swift-syntax"), + .product(name: "SwiftSyntax", package: "swift-syntax"), + .product(name: "SwiftSyntaxBuilder", package: "swift-syntax"), + .product(name: "SwiftSyntaxMacros", package: "swift-syntax"), ] ), .testTarget( diff --git a/Sources/AnyLanguageModel/Models/MLXLanguageModel.swift b/Sources/AnyLanguageModel/Models/MLXLanguageModel.swift index 8f44b31..9522fbc 100644 --- a/Sources/AnyLanguageModel/Models/MLXLanguageModel.swift +++ b/Sources/AnyLanguageModel/Models/MLXLanguageModel.swift @@ -1,11 +1,11 @@ import Foundation -#if canImport(UIKit) +#if canImport(UIKit) && canImport(CoreImage) import UIKit import CoreImage #endif -#if canImport(AppKit) +#if canImport(AppKit) && canImport(CoreImage) import AppKit import CoreImage #endif @@ -357,6 +357,24 @@ import Foundation return LanguageModelSession.ResponseStream(stream: stream) } + + /// Prewarms the model + public func prewarm( + for session: LanguageModelSession, + promptPrefix: Prompt? + ) { + let modelId = self.modelId + let hub = self.hub + let directory = self.directory + + Task { + do { + _ = try await loadContext(modelId: modelId, hub: hub, directory: directory) + } catch { + // Ignore errors during prewarm + } + } + } } // MARK: - Options Mapping diff --git a/Tests/AnyLanguageModelTests/APICompatibilityAnyLanguageModelTests.swift b/Tests/AnyLanguageModelTests/APICompatibilityAnyLanguageModelTests.swift index 217f8c5..5cc07dc 100644 --- a/Tests/AnyLanguageModelTests/APICompatibilityAnyLanguageModelTests.swift +++ b/Tests/AnyLanguageModelTests/APICompatibilityAnyLanguageModelTests.swift @@ -4,13 +4,13 @@ import Testing import AnyLanguageModel private let isSystemLanguageModelAvailable: Bool = { - if #available(macOS 26.0, *) { + if #available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0, *) { return SystemLanguageModel.default.isAvailable } return false }() - @available(macOS 26.0, *) + @available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0, *) @Test("AnyLanguageModel Drop-In Compatibility", .enabled(if: isSystemLanguageModelAvailable)) func anyLanguageModelCompatibility() async throws { let model = SystemLanguageModel.default diff --git a/Tests/AnyLanguageModelTests/APICompatibilityFoundationModelsTests.swift b/Tests/AnyLanguageModelTests/APICompatibilityFoundationModelsTests.swift index fa3f3c4..89d38d8 100644 --- a/Tests/AnyLanguageModelTests/APICompatibilityFoundationModelsTests.swift +++ b/Tests/AnyLanguageModelTests/APICompatibilityFoundationModelsTests.swift @@ -4,13 +4,13 @@ import Testing import FoundationModels private let isFoundationModelsSystemLanguageModelAvailable: Bool = { - if #available(macOS 26.0, *) { + if #available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0, *) { return SystemLanguageModel.default.isAvailable } return false }() - @available(macOS 26.0, *) + @available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0, *) @Test( "FoundationModels Drop-In Compatibility", .enabled(if: isFoundationModelsSystemLanguageModelAvailable) diff --git a/Tests/AnyLanguageModelTests/SystemLanguageModelTests.swift b/Tests/AnyLanguageModelTests/SystemLanguageModelTests.swift index 8b7d12d..60dc133 100644 --- a/Tests/AnyLanguageModelTests/SystemLanguageModelTests.swift +++ b/Tests/AnyLanguageModelTests/SystemLanguageModelTests.swift @@ -3,7 +3,7 @@ import AnyLanguageModel #if canImport(FoundationModels) private let isSystemLanguageModelAvailable = { - if #available(macOS 26.0, *) { + if #available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0, *) { return SystemLanguageModel.default.isAvailable } else { return false @@ -88,7 +88,7 @@ import AnyLanguageModel .enabled(if: isSystemLanguageModelAvailable) ) struct SystemLanguageModelTests { - @available(macOS 26.0, *) + @available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0, *) @Test func basicResponse() async throws { let model: SystemLanguageModel = SystemLanguageModel() let session = LanguageModelSession(model: model) @@ -97,7 +97,7 @@ import AnyLanguageModel #expect(!response.content.isEmpty) } - @available(macOS 26.0, *) + @available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0, *) @Test func withInstructions() async throws { let model = SystemLanguageModel() let session = LanguageModelSession( @@ -109,7 +109,7 @@ import AnyLanguageModel #expect(!response.content.isEmpty) } - @available(macOS 26.0, *) + @available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0, *) @Test func withTemperature() async throws { let model: SystemLanguageModel = SystemLanguageModel() let session = LanguageModelSession(model: model) @@ -122,7 +122,7 @@ import AnyLanguageModel #expect(!response.content.isEmpty) } - @available(macOS 26.0, *) + @available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0, *) @Test func streamingString() async throws { guard isSystemLanguageModelAvailable else { return } let model: SystemLanguageModel = SystemLanguageModel() @@ -139,7 +139,7 @@ import AnyLanguageModel #expect(!snapshots.last!.rawContent.jsonString.isEmpty) } - @available(macOS 26.0, *) + @available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0, *) @Test func streamingGeneratedContent() async throws { guard isSystemLanguageModelAvailable else { return } let model: SystemLanguageModel = SystemLanguageModel() @@ -159,7 +159,7 @@ import AnyLanguageModel #expect(!snapshots.last!.rawContent.jsonString.isEmpty) } - @available(macOS 26.0, *) + @available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0, *) @Test func withTools() async throws { let weatherTool = WeatherTool() let session = LanguageModelSession(model: SystemLanguageModel.default, tools: [weatherTool]) @@ -180,7 +180,7 @@ import AnyLanguageModel #expect(content.contains("72°F")) } - @available(macOS 26.0, *) + @available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0, *) @Test func conversationContext() async throws { let model: SystemLanguageModel = SystemLanguageModel() let session = LanguageModelSession(model: model) @@ -222,7 +222,7 @@ import AnyLanguageModel // MARK: - Guided Generation Tests - @available(macOS 26.0, *) + @available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0, *) @Test func guidedGenerationSimpleStruct() async throws { let session = LanguageModelSession(model: SystemLanguageModel.default) @@ -234,7 +234,7 @@ import AnyLanguageModel #expect(!response.content.message.isEmpty) } - @available(macOS 26.0, *) + @available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0, *) @Test func guidedGenerationWithMultipleFields() async throws { let session = LanguageModelSession(model: SystemLanguageModel.default) @@ -248,7 +248,7 @@ import AnyLanguageModel #expect(!response.content.occupation.isEmpty) } - @available(macOS 26.0, *) + @available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0, *) @Test func guidedGenerationMathCalculation() async throws { let session = LanguageModelSession(model: SystemLanguageModel.default) @@ -263,7 +263,7 @@ import AnyLanguageModel #expect(combined.contains("15") || combined.contains("27") || combined.contains("42")) } - @available(macOS 26.0, *) + @available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0, *) @Test func guidedGenerationNestedStruct() async throws { let session = LanguageModelSession(model: SystemLanguageModel.default) @@ -279,7 +279,7 @@ import AnyLanguageModel #expect(response.content.rgb.blue >= 0 && response.content.rgb.blue <= 255) } - @available(macOS 26.0, *) + @available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0, *) @Test func guidedGenerationWithArray() async throws { let session = LanguageModelSession(model: SystemLanguageModel.default) @@ -295,7 +295,7 @@ import AnyLanguageModel } } - @available(macOS 26.0, *) + @available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0, *) @Test func guidedGenerationWithEnumConstraint() async throws { let session = LanguageModelSession(model: SystemLanguageModel.default) @@ -308,7 +308,7 @@ import AnyLanguageModel #expect(response.content.confidence >= 0.0 && response.content.confidence <= 1.0) } - @available(macOS 26.0, *) + @available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0, *) @Test func guidedGenerationWithInstructions() async throws { let session = LanguageModelSession( model: SystemLanguageModel.default, @@ -325,7 +325,7 @@ import AnyLanguageModel #expect(!response.content.occupation.isEmpty) } - @available(macOS 26.0, *) + @available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0, *) @Test func guidedGenerationStreaming() async throws { let session = LanguageModelSession(model: SystemLanguageModel.default)