Initial commit
This commit is contained in:
125
oAI/Models/Message.swift
Normal file
125
oAI/Models/Message.swift
Normal file
@@ -0,0 +1,125 @@
|
||||
//
|
||||
// Message.swift
|
||||
// oAI
|
||||
//
|
||||
// Core message model for chat conversations
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
enum MessageRole: String, Codable {
|
||||
case user
|
||||
case assistant
|
||||
case system
|
||||
}
|
||||
|
||||
struct Message: Identifiable, Codable, Equatable {
|
||||
let id: UUID
|
||||
let role: MessageRole
|
||||
var content: String
|
||||
var tokens: Int?
|
||||
var cost: Double?
|
||||
let timestamp: Date
|
||||
let attachments: [FileAttachment]?
|
||||
|
||||
// Streaming state (not persisted)
|
||||
var isStreaming: Bool = false
|
||||
|
||||
// Generated images from image-output models (base64-decoded PNG/JPEG data)
|
||||
var generatedImages: [Data]? = nil
|
||||
|
||||
init(
|
||||
id: UUID = UUID(),
|
||||
role: MessageRole,
|
||||
content: String,
|
||||
tokens: Int? = nil,
|
||||
cost: Double? = nil,
|
||||
timestamp: Date = Date(),
|
||||
attachments: [FileAttachment]? = nil,
|
||||
isStreaming: Bool = false,
|
||||
generatedImages: [Data]? = nil
|
||||
) {
|
||||
self.id = id
|
||||
self.role = role
|
||||
self.content = content
|
||||
self.tokens = tokens
|
||||
self.cost = cost
|
||||
self.timestamp = timestamp
|
||||
self.attachments = attachments
|
||||
self.isStreaming = isStreaming
|
||||
self.generatedImages = generatedImages
|
||||
}
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case id, role, content, tokens, cost, timestamp, attachments
|
||||
}
|
||||
|
||||
static func == (lhs: Message, rhs: Message) -> Bool {
|
||||
lhs.id == rhs.id &&
|
||||
lhs.content == rhs.content &&
|
||||
lhs.tokens == rhs.tokens &&
|
||||
lhs.cost == rhs.cost &&
|
||||
lhs.isStreaming == rhs.isStreaming &&
|
||||
lhs.generatedImages == rhs.generatedImages
|
||||
}
|
||||
}
|
||||
|
||||
struct FileAttachment: Codable, Equatable {
|
||||
let path: String
|
||||
let type: AttachmentType
|
||||
let data: Data? // file contents: raw bytes for images/PDFs, UTF-8 for text
|
||||
|
||||
enum AttachmentType: String, Codable {
|
||||
case image
|
||||
case pdf
|
||||
case text
|
||||
}
|
||||
|
||||
/// Detect attachment type from file extension
|
||||
static func typeFromExtension(_ path: String) -> AttachmentType {
|
||||
let ext = (path as NSString).pathExtension.lowercased()
|
||||
switch ext {
|
||||
case "png", "jpg", "jpeg", "gif", "webp", "bmp", "svg":
|
||||
return .image
|
||||
case "pdf":
|
||||
return .pdf
|
||||
default:
|
||||
return .text
|
||||
}
|
||||
}
|
||||
|
||||
/// MIME type string for the file (used in base64 data URLs)
|
||||
var mimeType: String {
|
||||
let ext = (path as NSString).pathExtension.lowercased()
|
||||
switch ext {
|
||||
case "png": return "image/png"
|
||||
case "jpg", "jpeg": return "image/jpeg"
|
||||
case "gif": return "image/gif"
|
||||
case "webp": return "image/webp"
|
||||
case "bmp": return "image/bmp"
|
||||
case "svg": return "image/svg+xml"
|
||||
case "pdf": return "application/pdf"
|
||||
default: return "text/plain"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Display Helpers
|
||||
|
||||
extension MessageRole {
|
||||
var displayName: String {
|
||||
switch self {
|
||||
case .user: return "You"
|
||||
case .assistant: return "Assistant"
|
||||
case .system: return "System"
|
||||
}
|
||||
}
|
||||
|
||||
var iconName: String {
|
||||
switch self {
|
||||
case .user: return "person.circle.fill"
|
||||
case .assistant: return "cpu"
|
||||
case .system: return "info.circle.fill"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user