315 lines
8.3 KiB
Swift
315 lines
8.3 KiB
Swift
import Foundation
|
|
|
|
public enum Provider: String, Codable, CaseIterable, Hashable, Sendable {
|
|
case openai
|
|
case anthropic
|
|
case xai
|
|
|
|
public var displayName: String {
|
|
switch self {
|
|
case .openai: return "OpenAI"
|
|
case .anthropic: return "Anthropic"
|
|
case .xai: return "xAI"
|
|
}
|
|
}
|
|
}
|
|
|
|
public enum MessageRole: String, Codable, Hashable, Sendable {
|
|
case system
|
|
case user
|
|
case assistant
|
|
case tool
|
|
}
|
|
|
|
public struct ChatSummary: Codable, Identifiable, Hashable, Sendable {
|
|
public var id: String
|
|
public var title: String?
|
|
public var createdAt: Date
|
|
public var updatedAt: Date
|
|
public var initiatedProvider: Provider?
|
|
public var initiatedModel: String?
|
|
public var lastUsedProvider: Provider?
|
|
public var lastUsedModel: String?
|
|
}
|
|
|
|
public struct SearchSummary: Codable, Identifiable, Hashable, Sendable {
|
|
public var id: String
|
|
public var title: String?
|
|
public var query: String?
|
|
public var createdAt: Date
|
|
public var updatedAt: Date
|
|
}
|
|
|
|
public struct Message: Codable, Identifiable, Hashable, Sendable {
|
|
public var id: String
|
|
public var createdAt: Date
|
|
public var role: MessageRole
|
|
public var content: String
|
|
public var name: String?
|
|
}
|
|
|
|
public struct ChatDetail: Codable, Identifiable, Hashable, Sendable {
|
|
public var id: String
|
|
public var title: String?
|
|
public var createdAt: Date
|
|
public var updatedAt: Date
|
|
public var initiatedProvider: Provider?
|
|
public var initiatedModel: String?
|
|
public var lastUsedProvider: Provider?
|
|
public var lastUsedModel: String?
|
|
public var messages: [Message]
|
|
}
|
|
|
|
public struct SearchResultItem: Codable, Identifiable, Hashable, Sendable {
|
|
public var id: String
|
|
public var createdAt: Date
|
|
public var rank: Int
|
|
public var title: String?
|
|
public var url: String
|
|
public var publishedDate: String?
|
|
public var author: String?
|
|
public var text: String?
|
|
public var highlights: [String]?
|
|
public var highlightScores: [Double]?
|
|
public var score: Double?
|
|
public var favicon: String?
|
|
public var image: String?
|
|
}
|
|
|
|
public struct SearchCitation: Codable, Hashable, Sendable {
|
|
public var id: String?
|
|
public var url: String?
|
|
public var title: String?
|
|
public var publishedDate: String?
|
|
public var author: String?
|
|
public var text: String?
|
|
}
|
|
|
|
public struct SearchDetail: Codable, Identifiable, Hashable, Sendable {
|
|
public var id: String
|
|
public var title: String?
|
|
public var query: String?
|
|
public var createdAt: Date
|
|
public var updatedAt: Date
|
|
public var requestId: String?
|
|
public var latencyMs: Int?
|
|
public var error: String?
|
|
public var answerText: String?
|
|
public var answerRequestId: String?
|
|
public var answerCitations: [SearchCitation]?
|
|
public var answerError: String?
|
|
public var results: [SearchResultItem]
|
|
}
|
|
|
|
public struct SearchRunRequest: Codable, Sendable {
|
|
public var query: String?
|
|
public var title: String?
|
|
public var type: String?
|
|
public var numResults: Int?
|
|
public var includeDomains: [String]?
|
|
public var excludeDomains: [String]?
|
|
|
|
public init(
|
|
query: String? = nil,
|
|
title: String? = nil,
|
|
type: String? = nil,
|
|
numResults: Int? = nil,
|
|
includeDomains: [String]? = nil,
|
|
excludeDomains: [String]? = nil
|
|
) {
|
|
self.query = query
|
|
self.title = title
|
|
self.type = type
|
|
self.numResults = numResults
|
|
self.includeDomains = includeDomains
|
|
self.excludeDomains = excludeDomains
|
|
}
|
|
}
|
|
|
|
public struct CompletionRequestMessage: Codable, Sendable {
|
|
public var role: MessageRole
|
|
public var content: String
|
|
public var name: String?
|
|
|
|
public init(role: MessageRole, content: String, name: String? = nil) {
|
|
self.role = role
|
|
self.content = content
|
|
self.name = name
|
|
}
|
|
}
|
|
|
|
public struct CompletionStreamMeta: Codable, Sendable {
|
|
public var chatId: String
|
|
public var callId: String
|
|
public var provider: Provider
|
|
public var model: String
|
|
}
|
|
|
|
public struct CompletionStreamDelta: Codable, Sendable {
|
|
public var text: String
|
|
}
|
|
|
|
public struct CompletionStreamDone: Codable, Sendable {
|
|
public var text: String
|
|
}
|
|
|
|
public struct StreamErrorPayload: Codable, Sendable {
|
|
public var message: String
|
|
}
|
|
|
|
public enum CompletionStreamEvent: Sendable {
|
|
case meta(CompletionStreamMeta)
|
|
case delta(CompletionStreamDelta)
|
|
case done(CompletionStreamDone)
|
|
case error(StreamErrorPayload)
|
|
case ignored
|
|
}
|
|
|
|
public struct SearchResultsPayload: Codable, Sendable {
|
|
public var requestId: String?
|
|
public var results: [SearchResultItem]
|
|
}
|
|
|
|
public struct SearchErrorPayload: Codable, Sendable {
|
|
public var error: String
|
|
}
|
|
|
|
public struct SearchAnswerPayload: Codable, Sendable {
|
|
public var answerText: String?
|
|
public var answerRequestId: String?
|
|
public var answerCitations: [SearchCitation]?
|
|
}
|
|
|
|
public struct SearchDonePayload: Codable, Sendable {
|
|
public var search: SearchDetail
|
|
}
|
|
|
|
public enum SearchStreamEvent: Sendable {
|
|
case searchResults(SearchResultsPayload)
|
|
case searchError(SearchErrorPayload)
|
|
case answer(SearchAnswerPayload)
|
|
case answerError(SearchErrorPayload)
|
|
case done(SearchDonePayload)
|
|
case error(StreamErrorPayload)
|
|
case ignored
|
|
}
|
|
|
|
public struct ProviderModelInfo: Codable, Hashable, Sendable {
|
|
public var models: [String]
|
|
public var loadedAt: Date?
|
|
public var error: String?
|
|
}
|
|
|
|
public struct ModelCatalogResponse: Codable, Hashable, Sendable {
|
|
public var providers: [Provider: ProviderModelInfo]
|
|
|
|
enum CodingKeys: String, CodingKey {
|
|
case providers
|
|
}
|
|
|
|
public init(providers: [Provider: ProviderModelInfo]) {
|
|
self.providers = providers
|
|
}
|
|
|
|
public init(from decoder: Decoder) throws {
|
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
let rawProviders = try container.decode([String: ProviderModelInfo].self, forKey: .providers)
|
|
var mapped: [Provider: ProviderModelInfo] = [:]
|
|
for (key, value) in rawProviders {
|
|
if let provider = Provider(rawValue: key) {
|
|
mapped[provider] = value
|
|
}
|
|
}
|
|
self.providers = mapped
|
|
}
|
|
|
|
public func encode(to encoder: Encoder) throws {
|
|
var container = encoder.container(keyedBy: CodingKeys.self)
|
|
let raw = Dictionary(uniqueKeysWithValues: providers.map { ($0.key.rawValue, $0.value) })
|
|
try container.encode(raw, forKey: .providers)
|
|
}
|
|
}
|
|
|
|
struct AuthSession: Codable {
|
|
var authenticated: Bool
|
|
var mode: String
|
|
}
|
|
|
|
struct ChatListResponse: Codable {
|
|
var chats: [ChatSummary]
|
|
}
|
|
|
|
struct SearchListResponse: Codable {
|
|
var searches: [SearchSummary]
|
|
}
|
|
|
|
struct ChatDetailResponse: Codable {
|
|
var chat: ChatDetail
|
|
}
|
|
|
|
struct SearchDetailResponse: Codable {
|
|
var search: SearchDetail
|
|
}
|
|
|
|
struct ChatCreateResponse: Codable {
|
|
var chat: ChatSummary
|
|
}
|
|
|
|
struct SearchCreateResponse: Codable {
|
|
var search: SearchSummary
|
|
}
|
|
|
|
struct DeleteResponse: Codable {
|
|
var deleted: Bool
|
|
}
|
|
|
|
struct SuggestTitleBody: Codable {
|
|
var chatId: String
|
|
var content: String
|
|
}
|
|
|
|
enum APIError: LocalizedError {
|
|
case invalidBaseURL
|
|
case httpError(statusCode: Int, message: String)
|
|
case networkError(message: String)
|
|
case decodingError(message: String)
|
|
case invalidResponse
|
|
case noResponseStream
|
|
|
|
var errorDescription: String? {
|
|
switch self {
|
|
case .invalidBaseURL:
|
|
return "Invalid API URL"
|
|
case let .httpError(_, message):
|
|
return message
|
|
case let .networkError(message):
|
|
return message
|
|
case let .decodingError(message):
|
|
return message
|
|
case .invalidResponse:
|
|
return "Unexpected server response"
|
|
case .noResponseStream:
|
|
return "No response stream"
|
|
}
|
|
}
|
|
}
|
|
|
|
extension DateFormatter {
|
|
static let sybilDisplayDate: DateFormatter = {
|
|
let formatter = DateFormatter()
|
|
formatter.locale = .autoupdatingCurrent
|
|
formatter.dateStyle = .medium
|
|
formatter.timeStyle = .short
|
|
return formatter
|
|
}()
|
|
}
|
|
|
|
extension Date {
|
|
var sybilRelativeLabel: String {
|
|
let formatter = RelativeDateTimeFormatter()
|
|
formatter.locale = .autoupdatingCurrent
|
|
formatter.unitsStyle = .abbreviated
|
|
return formatter.localizedString(for: self, relativeTo: Date())
|
|
}
|
|
}
|