Implements a Swift gRPC server that exposes Apple's Foundation Models (Apple Intelligence) over the network for LAN access. Features: - Complete: Unary RPC for prompt/response - StreamComplete: Server streaming RPC for token-by-token responses - Health: Check model availability - Optional API key authentication via gRPC metadata - Configurable host/port via CLI args or environment variables Requires macOS 26 (Tahoe) with Apple Intelligence enabled. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
98 lines
3.1 KiB
Swift
98 lines
3.1 KiB
Swift
import Foundation
|
|
import FoundationModels
|
|
|
|
/// Errors that can occur when using Apple Intelligence
|
|
enum AppleIntelligenceError: Error, CustomStringConvertible {
|
|
case modelNotAvailable
|
|
case generationFailed(String)
|
|
case sessionCreationFailed
|
|
|
|
var description: String {
|
|
switch self {
|
|
case .modelNotAvailable:
|
|
return "Apple Intelligence model is not available on this device"
|
|
case .generationFailed(let reason):
|
|
return "Generation failed: \(reason)"
|
|
case .sessionCreationFailed:
|
|
return "Failed to create language model session"
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Service wrapper for Apple Intelligence Foundation Models
|
|
actor AppleIntelligenceService {
|
|
/// The language model session
|
|
private var session: LanguageModelSession?
|
|
|
|
/// Whether the model is available
|
|
private(set) var isAvailable: Bool = false
|
|
|
|
/// Initialize and check model availability
|
|
init() async {
|
|
await checkAvailability()
|
|
}
|
|
|
|
/// Check if Apple Intelligence is available
|
|
private func checkAvailability() async {
|
|
let availability = SystemLanguageModel.default.availability
|
|
switch availability {
|
|
case .available:
|
|
isAvailable = true
|
|
session = LanguageModelSession()
|
|
case .unavailable:
|
|
isAvailable = false
|
|
@unknown default:
|
|
isAvailable = false
|
|
}
|
|
}
|
|
|
|
/// Get the current model status as a string
|
|
func getModelStatus() -> String {
|
|
let availability = SystemLanguageModel.default.availability
|
|
switch availability {
|
|
case .available:
|
|
return "available"
|
|
case .unavailable(let reason):
|
|
return "unavailable: \(reason)"
|
|
@unknown default:
|
|
return "unknown"
|
|
}
|
|
}
|
|
|
|
/// Generate a completion for the given prompt (non-streaming)
|
|
func complete(prompt: String, temperature: Float?, maxTokens: Int?) async throws -> String {
|
|
guard isAvailable, let session = session else {
|
|
throw AppleIntelligenceError.modelNotAvailable
|
|
}
|
|
|
|
let response = try await session.respond(to: prompt)
|
|
return response.content
|
|
}
|
|
|
|
/// Generate a streaming completion for the given prompt
|
|
func streamComplete(
|
|
prompt: String,
|
|
temperature: Float?,
|
|
maxTokens: Int?
|
|
) -> AsyncThrowingStream<String, Error> {
|
|
AsyncThrowingStream { continuation in
|
|
Task {
|
|
guard self.isAvailable, let session = self.session else {
|
|
continuation.finish(throwing: AppleIntelligenceError.modelNotAvailable)
|
|
return
|
|
}
|
|
|
|
do {
|
|
let stream = session.streamResponse(to: prompt)
|
|
for try await partialResponse in stream {
|
|
continuation.yield(partialResponse.content)
|
|
}
|
|
continuation.finish()
|
|
} catch {
|
|
continuation.finish(throwing: AppleIntelligenceError.generationFailed(error.localizedDescription))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|