Files

144 lines
5.1 KiB
Swift

// AIProvider.swift S2 ( LLM )
//
// (S3) "AI " .
// S3 + MockAIProvider , S2 provider
// (OnDevice / LocalMLX / RemoteDS / Specialized) .
//
// Foundation 0 . (FoundationModels / MLX / URLSession)
// Providers/ S2 .
import Foundation
// MARK: - Provider ( = 1:1)
public enum AIProviderID: String, Codable, Sendable, CaseIterable {
/// · (Apple Foundation Models). ··.
case onDevice
/// LLM (Gemma 4 26B, MLX :8801 / llm-router :8890). .
case localMLX
/// DS RAG (`GET /search/ask`). .
case remoteDS
/// GPU (rerank / embed / vision / OCR). .
case specialized
}
// MARK: - ( AI )
public enum AITask: String, Codable, Sendable {
/// / .
case quickSummarize
/// · · .
case memoAssist
/// / ( , ).
case askSelection
/// RAG ( DS).
case corpusAsk
/// (/ ).
case classify
/// / PDF ().
case vision
}
public enum AIConfidence: String, Codable, Sendable {
case high, medium, low
}
public enum AIFinishReason: String, Codable, Sendable {
case completed //
case refused // ( )
case timeout //
case unavailable // provider (503 )
case noEvidence // (corpusAsk)
}
// MARK: - /
public struct AICompletionRequest: Sendable {
public var task: AITask
public var prompt: String
/// · (corpusAsk nil ).
public var context: String?
public var systemPrompt: String?
public var maxTokens: Int?
/// provider. ** fallback **( opt-in).
public var explicitProvider: AIProviderID?
public init(
task: AITask,
prompt: String,
context: String? = nil,
systemPrompt: String? = nil,
maxTokens: Int? = nil,
explicitProvider: AIProviderID? = nil
) {
self.task = task
self.prompt = prompt
self.context = context
self.systemPrompt = systemPrompt
self.maxTokens = maxTokens
self.explicitProvider = explicitProvider
}
}
/// corpusAsk DS `Citation`(CONTRACT.md §4) .
public struct AICitation: Codable, Sendable, Identifiable {
public var id: Int { n }
public var n: Int
public var docId: Int
public var title: String?
public var sectionTitle: String?
public var spanText: String
public init(n: Int, docId: Int, title: String?, sectionTitle: String?, spanText: String) {
self.n = n
self.docId = docId
self.title = title
self.sectionTitle = sectionTitle
self.spanText = spanText
}
}
public struct AICompletionResponse: Sendable {
public var text: String
public var providerUsed: AIProviderID
public var finishReason: AIFinishReason
public var citations: [AICitation]
public var confidence: AIConfidence?
public var latencyMs: Double?
/// rule-based fallback 1 (silent ).
public var routingNote: String?
public init(
text: String,
providerUsed: AIProviderID,
finishReason: AIFinishReason = .completed,
citations: [AICitation] = [],
confidence: AIConfidence? = nil,
latencyMs: Double? = nil,
routingNote: String? = nil
) {
self.text = text
self.providerUsed = providerUsed
self.finishReason = finishReason
self.citations = citations
self.confidence = confidence
self.latencyMs = latencyMs
self.routingNote = routingNote
}
}
// MARK: - (S2 , S3 )
public protocol AIProvider: Sendable {
var id: AIProviderID { get }
/// ( Apple Intelligence ? ? ).
var isAvailable: Bool { get async }
func complete(_ request: AICompletionRequest) async throws -> AICompletionResponse
}
public enum AIProviderError: Error, Sendable {
case notImplemented(AIProviderID)
case unavailable(AIProviderID)
case backendError(AIProviderID, status: Int, reason: String?)
}