swift-apple-intelligence-grpc/Sources/AppleIntelligenceGRPC/Services/AppleIntelligenceService.swift
Mathias Beaulieu-Duncan 47feeedf9d Add Apple Intelligence gRPC server
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>
2025-12-30 02:54:12 -05:00

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))
}
}
}
}
}