Files
hyungi_document_server/clients/ds-app/Sources/AppFeature/AI/AIService.swift
T

60 lines
2.5 KiB
Swift

import Foundation
import AIFabric
/// Renderable failure (the UI never sees a raw AIRoutingError that would break the
/// "visible error, not silent fallback" contract).
public enum AIServiceError: Error, Sendable {
case explicitUnavailable(AIProviderID)
case notConfigured(AIProviderID)
case noneAvailable(AITask)
case providerFailed(String)
case unknown(String)
}
public enum AIResult: Sendable {
case success(AICompletionResponse)
case failure(AIServiceError)
}
/// The single app-facing facade over the S2 fabric. Views call intent methods; AIService is the ONLY
/// place AICompletionRequest is built. An actor (not @Observable): routing is async work that should
/// serialize and not hop to MainActor.
public actor AIService {
private let router: AIRouter
public init(router: AIRouter) { self.router = router }
private func run(_ request: AICompletionRequest) async -> AIResult {
do {
return .success(try await router.route(request))
} catch let e as AIRoutingError {
switch e {
case .explicitProviderUnavailable(let id): return .failure(.explicitUnavailable(id))
case .providerNotConfigured(let id): return .failure(.notConfigured(id))
case .noProviderAvailable(let t): return .failure(.noneAvailable(t))
}
} catch let e as AIProviderError {
return .failure(.providerFailed("\(e)"))
} catch {
return .failure(.unknown("\(error)"))
}
}
// Intent methods UI gesture -> AITask. (vision is deferred: the frozen request is text-only.)
public func summarize(text: String) async -> AIResult {
await run(AICompletionRequest(task: .quickSummarize, prompt: "다음 문서를 요약", context: text))
}
public func memoAssist(content: String) async -> AIResult {
await run(AICompletionRequest(task: .memoAssist, prompt: "제목과 태그 제안", context: content))
}
public func askSelection(selection: String, question: String) async -> AIResult {
await run(AICompletionRequest(task: .askSelection, prompt: question, context: selection))
}
public func corpusAsk(question: String, explicit: AIProviderID? = nil) async -> AIResult {
await run(AICompletionRequest(task: .corpusAsk, prompt: question, context: nil, explicitProvider: explicit))
}
public func classify(documentText: String) async -> AIResult {
await run(AICompletionRequest(task: .classify, prompt: "도메인/카테고리 분류 제안", context: documentText))
}
}