import Foundation import FoundationModels @available(iOS 26.0, *) @Generable(description: "A factual statement grounded in one or more supplied passage identifiers.") private struct RDAIModelStatement { var text: String var passageIDs: [String] } @available(iOS 26.0, *) @Generable(description: "A citation-grounded book summary.") private struct RDAIModelSummary { var overview: String var statements: [RDAIModelStatement] } @available(iOS 26.0, *) @Generable(description: "A citation-grounded answer to a question about supplied passages.") private struct RDAIModelAnswer { var answer: String var statements: [RDAIModelStatement] } /// Optional on-device provider. The Core service remains fully functional on /// older systems by falling back to evidence-only retrieval. @available(iOS 26.0, *) public actor RDAIAppleFoundationModelsProvider: RDAIGenerativeProvider { public let identifier = "apple.foundation-models" public init() {} public func availability(for capability: RDAICapability, locale: Locale?) async -> RDAICapabilityAvailability { switch SystemLanguageModel.default.availability { case .available: return .available case .unavailable(.deviceNotEligible): return .unavailable(reason: .deviceNotEligible) case .unavailable(.appleIntelligenceNotEnabled): return .unavailable(reason: .appleIntelligenceNotEnabled) case .unavailable(.modelNotReady): return .unavailable(reason: .modelNotReady) @unknown default: return .unavailable(reason: .unknown("system-model-unavailable")) } } public func generate(_ request: RDAIGenerationRequest) async throws -> RDAIGeneratedArtifact { guard case .available = await availability(for: capability(for: request.task), locale: request.locale) else { throw RDAIError.modelUnavailable(.modelNotReady) } let context = request.passages.enumerated().map { "[\($0.element.id)] \($0.element.text)" }.joined(separator: "\n\n") let task: String switch request.task { case .summary: task = "Summarize the passages in the user's language. Do not add facts." case .answer: task = "Answer the question using only the passages. If evidence is insufficient, say so. Question: \(request.userText ?? "")" case .characters, .relationships: throw RDAIError.insufficientEvidence } let session = LanguageModelSession(instructions: "You are a book reading assistant. Use only supplied passages and never reveal unread content.") switch request.task { case .summary: let response = try await session.respond(to: "\(task) Each statement must include its supplied passage IDs.\n\nPassages:\n\(context)", generating: RDAIModelSummary.self) return .summary(overview: response.content.overview, statements: response.content.statements.map(statement)) case .answer: let response = try await session.respond(to: "\(task) Each statement must include its supplied passage IDs.\n\nPassages:\n\(context)", generating: RDAIModelAnswer.self) return .answer(text: response.content.answer, statements: response.content.statements.map(statement)) case .characters, .relationships: throw RDAIError.insufficientEvidence } } private func capability(for task: RDAIGenerationTask) -> RDAICapability { switch task { case .summary: return .summarization case .answer: return .questionAnswering case .characters, .relationships: return .characterRelationships } } private func statement(_ value: RDAIModelStatement) -> RDAISourcedStatement { RDAISourcedStatement(text: value.text, citationIdentifiers: value.passageIDs) } }