Overview
Implement the Apple Intelligence provider using the macOS 26 FoundationModels framework. This is the primary/default provider that requires zero external dependencies.
Dependencies
Files to Create/Modify
| File |
Action |
Description |
Sources/SortAI/Core/LLM/AppleIntelligenceProvider.swift |
Create |
Main provider implementation |
Sources/SortAI/Core/LLM/GenerableTypes.swift |
Create |
@generable struct definitions |
Tests/SortAITests/AppleIntelligenceTests.swift |
Create |
Unit tests |
Implementation Details
1. @generable Types for Structured Output
import FoundationModels
@Generable
struct FileCategoryResponse {
@Guide(description: "The category path like 'Documents / Financial / Reports'")
var categoryPath: String
@Guide(description: "Confidence from 0.0 to 1.0")
var confidence: Double
@Guide(description: "Brief explanation for the categorization")
var rationale: String
@Guide(description: "Relevant keywords extracted from the file")
var keywords: [String]
}
@Generable
struct EntityExtractionResponse {
@Guide(description: "List of extracted entities with types")
var entities: [EntityItem]
}
@Generable
struct EntityItem {
@Guide(description: "The entity text")
var text: String
@Guide(description: "Entity type: person, organization, location, date, keyword")
var type: String
}
2. AppleIntelligenceProvider
import Foundation
import FoundationModels
@available(macOS 26.0, *)
actor AppleIntelligenceProvider: LLMCategorizationProvider {
let identifier = "apple-intelligence"
let priority = 1 // Highest priority (default)
private var session: LanguageModelSession?
private let escalationThreshold: Double
var supportsModelSelection: Bool { false }
var supportsTemperature: Bool { false }
var supportsCustomPrompts: Bool { false }
init(escalationThreshold: Double = 0.5) {
self.escalationThreshold = escalationThreshold
}
func isAvailable() async -> Bool {
return LanguageModelSession.isSupported
}
func categorize(signature: FileSignature) async throws -> CategorizationResult {
let session = try await getOrCreateSession()
let prompt = buildCategorizationPrompt(signature: signature)
let response: FileCategoryResponse = try await session.respond(
to: prompt,
generating: FileCategoryResponse.self
)
return CategorizationResult(
categoryPath: CategoryPath(path: response.categoryPath),
confidence: response.confidence,
rationale: response.rationale,
extractedKeywords: response.keywords,
provider: identifier,
escalationThreshold: escalationThreshold
)
}
private func getOrCreateSession() async throws -> LanguageModelSession {
if let session = session { return session }
let newSession = LanguageModelSession()
self.session = newSession
return newSession
}
private func buildCategorizationPrompt(signature: FileSignature) -> String {
var prompt = """
Categorize this file based on its characteristics:
Filename: \(signature.url.lastPathComponent)
Type: \(signature.kind.rawValue)
Size: \(signature.size) bytes
"""
if let content = signature.textContent {
prompt += "\n\nContent preview:\n\(content.prefix(1500))"
}
if !signature.keywords.isEmpty {
prompt += "\n\nExtracted keywords: \(signature.keywords.joined(separator: ", "))"
}
prompt += """
Suggest the most appropriate category path (e.g., "Documents / Work / Reports")
and explain your reasoning.
"""
return prompt
}
}
3. Fallback for Pre-macOS 26
/// Stub provider for systems without Apple Intelligence
final class AppleIntelligenceUnavailableProvider: LLMCategorizationProvider, Sendable {
let identifier = "apple-intelligence"
let priority = 1
var supportsModelSelection: Bool { false }
var supportsTemperature: Bool { false }
var supportsCustomPrompts: Bool { false }
func isAvailable() async -> Bool { false }
func categorize(signature: FileSignature) async throws -> CategorizationResult {
throw CategorizationError.providerUnavailable("Apple Intelligence requires macOS 26+")
}
}
Acceptance Criteria
Testing
@available(macOS 26.0, *)
func testAppleIntelligenceAvailable() async {
let provider = AppleIntelligenceProvider()
let available = await provider.isAvailable()
XCTAssertTrue(available, "Apple Intelligence should be available on macOS 26+")
}
@available(macOS 26.0, *)
func testAppleIntelligenceCategorization() async throws {
let provider = AppleIntelligenceProvider()
let signature = FileSignature.mock(
filename: "quarterly_report_2025.pdf",
content: "Q4 2025 Financial Summary..."
)
let result = try await provider.categorize(signature: signature)
XCTAssertEqual(result.provider, "apple-intelligence")
XCTAssertGreaterThan(result.confidence, 0.0)
XCTAssertFalse(result.categoryPath.path.isEmpty)
}
Performance Expectations
Based on prototype testing:
- Simple categorization: 1.5-1.9s
- Entity extraction: 0.5-0.9s
- Streaming responses: 1.5s initial
Estimated Size
~300 lines of code
Risk Assessment
Medium - New macOS 26 API. Mitigation: comprehensive @available guards and fallback stub.
Overview
Implement the Apple Intelligence provider using the macOS 26 FoundationModels framework. This is the primary/default provider that requires zero external dependencies.
Dependencies
Files to Create/Modify
Sources/SortAI/Core/LLM/AppleIntelligenceProvider.swiftSources/SortAI/Core/LLM/GenerableTypes.swiftTests/SortAITests/AppleIntelligenceTests.swiftImplementation Details
1. @generable Types for Structured Output
2. AppleIntelligenceProvider
3. Fallback for Pre-macOS 26
Acceptance Criteria
LLMCategorizationProviderprotocol@Generablefor type-safe structured outputTesting
Performance Expectations
Based on prototype testing:
Estimated Size
~300 lines of code
Risk Assessment
Medium - New macOS 26 API. Mitigation: comprehensive
@availableguards and fallback stub.