chore: rename project to clawdbot
This commit is contained in:
311
apps/shared/ClawdbotKit/Sources/ClawdbotChatUI/ChatModels.swift
Normal file
311
apps/shared/ClawdbotKit/Sources/ClawdbotChatUI/ChatModels.swift
Normal file
@@ -0,0 +1,311 @@
|
||||
import ClawdbotKit
|
||||
import Foundation
|
||||
|
||||
// NOTE: keep this file lightweight; decode must be resilient to varying transcript formats.
|
||||
|
||||
#if canImport(AppKit)
|
||||
import AppKit
|
||||
|
||||
public typealias ClawdbotPlatformImage = NSImage
|
||||
#elseif canImport(UIKit)
|
||||
import UIKit
|
||||
|
||||
public typealias ClawdbotPlatformImage = UIImage
|
||||
#endif
|
||||
|
||||
public struct ClawdbotChatUsageCost: Codable, Hashable, Sendable {
|
||||
public let input: Double?
|
||||
public let output: Double?
|
||||
public let cacheRead: Double?
|
||||
public let cacheWrite: Double?
|
||||
public let total: Double?
|
||||
}
|
||||
|
||||
public struct ClawdbotChatUsage: Codable, Hashable, Sendable {
|
||||
public let input: Int?
|
||||
public let output: Int?
|
||||
public let cacheRead: Int?
|
||||
public let cacheWrite: Int?
|
||||
public let cost: ClawdbotChatUsageCost?
|
||||
public let total: Int?
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case input
|
||||
case output
|
||||
case cacheRead
|
||||
case cacheWrite
|
||||
case cost
|
||||
case total
|
||||
case totalTokens
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
self.input = try container.decodeIfPresent(Int.self, forKey: .input)
|
||||
self.output = try container.decodeIfPresent(Int.self, forKey: .output)
|
||||
self.cacheRead = try container.decodeIfPresent(Int.self, forKey: .cacheRead)
|
||||
self.cacheWrite = try container.decodeIfPresent(Int.self, forKey: .cacheWrite)
|
||||
self.cost = try container.decodeIfPresent(ClawdbotChatUsageCost.self, forKey: .cost)
|
||||
self.total =
|
||||
try container.decodeIfPresent(Int.self, forKey: .total) ??
|
||||
container.decodeIfPresent(Int.self, forKey: .totalTokens)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encodeIfPresent(self.input, forKey: .input)
|
||||
try container.encodeIfPresent(self.output, forKey: .output)
|
||||
try container.encodeIfPresent(self.cacheRead, forKey: .cacheRead)
|
||||
try container.encodeIfPresent(self.cacheWrite, forKey: .cacheWrite)
|
||||
try container.encodeIfPresent(self.cost, forKey: .cost)
|
||||
try container.encodeIfPresent(self.total, forKey: .total)
|
||||
}
|
||||
}
|
||||
|
||||
public struct ClawdbotChatMessageContent: Codable, Hashable, Sendable {
|
||||
public let type: String?
|
||||
public let text: String?
|
||||
public let thinking: String?
|
||||
public let thinkingSignature: String?
|
||||
public let mimeType: String?
|
||||
public let fileName: String?
|
||||
public let content: AnyCodable?
|
||||
|
||||
// Tool-call fields (when `type == "toolCall"` or similar)
|
||||
public let id: String?
|
||||
public let name: String?
|
||||
public let arguments: AnyCodable?
|
||||
|
||||
public init(
|
||||
type: String?,
|
||||
text: String?,
|
||||
thinking: String? = nil,
|
||||
thinkingSignature: String? = nil,
|
||||
mimeType: String?,
|
||||
fileName: String?,
|
||||
content: AnyCodable?,
|
||||
id: String? = nil,
|
||||
name: String? = nil,
|
||||
arguments: AnyCodable? = nil)
|
||||
{
|
||||
self.type = type
|
||||
self.text = text
|
||||
self.thinking = thinking
|
||||
self.thinkingSignature = thinkingSignature
|
||||
self.mimeType = mimeType
|
||||
self.fileName = fileName
|
||||
self.content = content
|
||||
self.id = id
|
||||
self.name = name
|
||||
self.arguments = arguments
|
||||
}
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case type
|
||||
case text
|
||||
case thinking
|
||||
case thinkingSignature
|
||||
case mimeType
|
||||
case fileName
|
||||
case content
|
||||
case id
|
||||
case name
|
||||
case arguments
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
self.type = try container.decodeIfPresent(String.self, forKey: .type)
|
||||
self.text = try container.decodeIfPresent(String.self, forKey: .text)
|
||||
self.thinking = try container.decodeIfPresent(String.self, forKey: .thinking)
|
||||
self.thinkingSignature = try container.decodeIfPresent(String.self, forKey: .thinkingSignature)
|
||||
self.mimeType = try container.decodeIfPresent(String.self, forKey: .mimeType)
|
||||
self.fileName = try container.decodeIfPresent(String.self, forKey: .fileName)
|
||||
self.id = try container.decodeIfPresent(String.self, forKey: .id)
|
||||
self.name = try container.decodeIfPresent(String.self, forKey: .name)
|
||||
self.arguments = try container.decodeIfPresent(AnyCodable.self, forKey: .arguments)
|
||||
|
||||
if let any = try container.decodeIfPresent(AnyCodable.self, forKey: .content) {
|
||||
self.content = any
|
||||
} else if let str = try container.decodeIfPresent(String.self, forKey: .content) {
|
||||
self.content = AnyCodable(str)
|
||||
} else {
|
||||
self.content = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct ClawdbotChatMessage: Codable, Identifiable, Sendable {
|
||||
public var id: UUID = .init()
|
||||
public let role: String
|
||||
public let content: [ClawdbotChatMessageContent]
|
||||
public let timestamp: Double?
|
||||
public let toolCallId: String?
|
||||
public let toolName: String?
|
||||
public let usage: ClawdbotChatUsage?
|
||||
public let stopReason: String?
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case role
|
||||
case content
|
||||
case timestamp
|
||||
case toolCallId
|
||||
case tool_call_id
|
||||
case toolName
|
||||
case tool_name
|
||||
case usage
|
||||
case stopReason
|
||||
}
|
||||
|
||||
public init(
|
||||
id: UUID = .init(),
|
||||
role: String,
|
||||
content: [ClawdbotChatMessageContent],
|
||||
timestamp: Double?,
|
||||
toolCallId: String? = nil,
|
||||
toolName: String? = nil,
|
||||
usage: ClawdbotChatUsage? = nil,
|
||||
stopReason: String? = nil)
|
||||
{
|
||||
self.id = id
|
||||
self.role = role
|
||||
self.content = content
|
||||
self.timestamp = timestamp
|
||||
self.toolCallId = toolCallId
|
||||
self.toolName = toolName
|
||||
self.usage = usage
|
||||
self.stopReason = stopReason
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
self.role = try container.decode(String.self, forKey: .role)
|
||||
self.timestamp = try container.decodeIfPresent(Double.self, forKey: .timestamp)
|
||||
self.toolCallId =
|
||||
try container.decodeIfPresent(String.self, forKey: .toolCallId) ??
|
||||
container.decodeIfPresent(String.self, forKey: .tool_call_id)
|
||||
self.toolName =
|
||||
try container.decodeIfPresent(String.self, forKey: .toolName) ??
|
||||
container.decodeIfPresent(String.self, forKey: .tool_name)
|
||||
self.usage = try container.decodeIfPresent(ClawdbotChatUsage.self, forKey: .usage)
|
||||
self.stopReason = try container.decodeIfPresent(String.self, forKey: .stopReason)
|
||||
|
||||
if let decoded = try? container.decode([ClawdbotChatMessageContent].self, forKey: .content) {
|
||||
self.content = decoded
|
||||
return
|
||||
}
|
||||
|
||||
// Some session log formats store `content` as a plain string.
|
||||
if let text = try? container.decode(String.self, forKey: .content) {
|
||||
self.content = [
|
||||
ClawdbotChatMessageContent(
|
||||
type: "text",
|
||||
text: text,
|
||||
thinking: nil,
|
||||
thinkingSignature: nil,
|
||||
mimeType: nil,
|
||||
fileName: nil,
|
||||
content: nil,
|
||||
id: nil,
|
||||
name: nil,
|
||||
arguments: nil),
|
||||
]
|
||||
return
|
||||
}
|
||||
|
||||
self.content = []
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(self.role, forKey: .role)
|
||||
try container.encodeIfPresent(self.timestamp, forKey: .timestamp)
|
||||
try container.encodeIfPresent(self.toolCallId, forKey: .toolCallId)
|
||||
try container.encodeIfPresent(self.toolName, forKey: .toolName)
|
||||
try container.encodeIfPresent(self.usage, forKey: .usage)
|
||||
try container.encodeIfPresent(self.stopReason, forKey: .stopReason)
|
||||
try container.encode(self.content, forKey: .content)
|
||||
}
|
||||
}
|
||||
|
||||
public struct ClawdbotChatHistoryPayload: Codable, Sendable {
|
||||
public let sessionKey: String
|
||||
public let sessionId: String?
|
||||
public let messages: [AnyCodable]?
|
||||
public let thinkingLevel: String?
|
||||
}
|
||||
|
||||
public struct ClawdbotChatSendResponse: Codable, Sendable {
|
||||
public let runId: String
|
||||
public let status: String
|
||||
}
|
||||
|
||||
public struct ClawdbotChatEventPayload: Codable, Sendable {
|
||||
public let runId: String?
|
||||
public let sessionKey: String?
|
||||
public let state: String?
|
||||
public let message: AnyCodable?
|
||||
public let errorMessage: String?
|
||||
}
|
||||
|
||||
public struct ClawdbotAgentEventPayload: Codable, Sendable, Identifiable {
|
||||
public var id: String { "\(self.runId)-\(self.seq ?? -1)" }
|
||||
public let runId: String
|
||||
public let seq: Int?
|
||||
public let stream: String
|
||||
public let ts: Int?
|
||||
public let data: [String: AnyCodable]
|
||||
}
|
||||
|
||||
public struct ClawdbotChatPendingToolCall: Identifiable, Hashable, Sendable {
|
||||
public var id: String { self.toolCallId }
|
||||
public let toolCallId: String
|
||||
public let name: String
|
||||
public let args: AnyCodable?
|
||||
public let startedAt: Double?
|
||||
public let isError: Bool?
|
||||
}
|
||||
|
||||
public struct ClawdbotGatewayHealthOK: Codable, Sendable {
|
||||
public let ok: Bool?
|
||||
}
|
||||
|
||||
public struct ClawdbotPendingAttachment: Identifiable {
|
||||
public let id = UUID()
|
||||
public let url: URL?
|
||||
public let data: Data
|
||||
public let fileName: String
|
||||
public let mimeType: String
|
||||
public let type: String
|
||||
public let preview: ClawdbotPlatformImage?
|
||||
|
||||
public init(
|
||||
url: URL?,
|
||||
data: Data,
|
||||
fileName: String,
|
||||
mimeType: String,
|
||||
type: String = "file",
|
||||
preview: ClawdbotPlatformImage?)
|
||||
{
|
||||
self.url = url
|
||||
self.data = data
|
||||
self.fileName = fileName
|
||||
self.mimeType = mimeType
|
||||
self.type = type
|
||||
self.preview = preview
|
||||
}
|
||||
}
|
||||
|
||||
public struct ClawdbotChatAttachmentPayload: Codable, Sendable, Hashable {
|
||||
public let type: String
|
||||
public let mimeType: String
|
||||
public let fileName: String
|
||||
public let content: String
|
||||
|
||||
public init(type: String, mimeType: String, fileName: String, content: String) {
|
||||
self.type = type
|
||||
self.mimeType = mimeType
|
||||
self.fileName = fileName
|
||||
self.content = content
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user