Download apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatWorkingProgress.swift from SaylorTwift/openclaw: direct link, hf CLI and curl.
- Browser
- Download file 10.5 kB
-
https://huggingface.co/SaylorTwift/openclaw/resolve/main/apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatWorkingProgress.swift
- Command line
-
hf download hf://SaylorTwift/openclaw/apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatWorkingProgress.swift
-
curl -L -o ChatWorkingProgress.swift https://huggingface.co/SaylorTwift/openclaw/resolve/main/apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatWorkingProgress.swift
10.5 kB
| import Foundation | |
| enum ChatWorkingClawStance: CaseIterable, Equatable, Sendable { | |
| case standard | |
| case southpaw | |
| case flurry | |
| case spin | |
| case shadowbox | |
| case backflip | |
| case zen | |
| case drummer | |
| case peekaboo | |
| case nodOff | |
| case curious | |
| case omNom | |
| case fakeOut | |
| static let weightedStances: [(stance: Self, weight: Double)] = [ | |
| (.standard, 55), | |
| (.southpaw, 18), | |
| (.flurry, 5), | |
| (.spin, 4), | |
| (.shadowbox, 3), | |
| (.backflip, 2), | |
| (.zen, 2), | |
| (.drummer, 2), | |
| (.peekaboo, 2), | |
| (.nodOff, 2), | |
| (.curious, 2), | |
| (.omNom, 2), | |
| (.fakeOut, 1), | |
| ] | |
| static func seeded(_ key: String, salt: UInt32) -> Self { | |
| let totalWeight = self.weightedStances.reduce(0) { $0 + $1.weight } | |
| var roll = Double((fnv1a(key) ^ salt) % 1000) / 1000 * totalWeight | |
| for entry in self.weightedStances { | |
| roll -= entry.weight | |
| if roll <= 0 { | |
| return entry.stance | |
| } | |
| } | |
| return .standard | |
| } | |
| } | |
| enum ChatWorkingPhrase { | |
| static let showAfterMilliseconds = 30000 | |
| static let rotateEveryMilliseconds = 45000 | |
| /// Keep source phrases localizable; the post-merge native locale refresh | |
| /// owns the generated catalog entries. | |
| static let resources: [String] = [ | |
| String(localized: "Shelling"), | |
| String(localized: "Scuttling"), | |
| String(localized: "Clawing"), | |
| String(localized: "Pinching"), | |
| String(localized: "Molting"), | |
| String(localized: "Bubbling"), | |
| String(localized: "Tiding"), | |
| String(localized: "Reefing"), | |
| String(localized: "Cracking"), | |
| String(localized: "Sifting"), | |
| String(localized: "Brining"), | |
| String(localized: "Nautiling"), | |
| String(localized: "Krilling"), | |
| String(localized: "Barnacling"), | |
| String(localized: "Lobstering"), | |
| String(localized: "Tidepooling"), | |
| String(localized: "Pearling"), | |
| String(localized: "Snapping"), | |
| String(localized: "Surfacing"), | |
| ] | |
| /// A constant-time stride walk keeps adjacent phrases distinct. The prime | |
| /// list length makes every non-zero stride visit all phrases before repeating. | |
| static func index(seed: String, elapsedMilliseconds: Int) -> Int? { | |
| guard elapsedMilliseconds >= self.showAfterMilliseconds else { return nil } | |
| let bucket = (elapsedMilliseconds - self.showAfterMilliseconds) / self.rotateEveryMilliseconds | |
| return self.index(seed: seed, bucket: bucket) | |
| } | |
| static func index(seed: String, bucket: Int) -> Int { | |
| let count = self.resources.count | |
| let offset = Int(fnv1a("\(seed):offset") % UInt32(count)) | |
| let stride = 1 + Int(fnv1a("\(seed):stride") % UInt32(count - 1)) | |
| return (offset + bucket * stride) % count | |
| } | |
| } | |
| enum ChatWorkingDurationFormatter { | |
| private static let compactUnits = [ | |
| (suffix: "d", seconds: 86400), | |
| (suffix: "h", seconds: 3600), | |
| (suffix: "m", seconds: 60), | |
| (suffix: "s", seconds: 1), | |
| ] | |
| static func compact(milliseconds: Double) -> String { | |
| let totalSeconds = self.totalSeconds(milliseconds: milliseconds) | |
| var remainder = totalSeconds | |
| var parts: [String] = [] | |
| for unit in self.compactUnits { | |
| let value = remainder / unit.seconds | |
| remainder %= unit.seconds | |
| if value > 0 { | |
| parts.append("\(value)\(unit.suffix)") | |
| } | |
| if parts.count == 2 { | |
| break | |
| } | |
| } | |
| return parts.joined(separator: " ") | |
| } | |
| static func full(milliseconds: Double, locale: Locale = .current) -> String { | |
| let formatter = DateComponentsFormatter() | |
| formatter.allowedUnits = [.day, .hour, .minute, .second] | |
| formatter.unitsStyle = .full | |
| formatter.maximumUnitCount = 2 | |
| formatter.zeroFormattingBehavior = .dropAll | |
| var calendar = Calendar(identifier: .gregorian) | |
| calendar.locale = locale | |
| formatter.calendar = calendar | |
| return formatter.string(from: TimeInterval(self.totalSeconds(milliseconds: milliseconds))) ?? | |
| String(localized: "1 second", locale: locale) | |
| } | |
| private static func totalSeconds(milliseconds: Double) -> Int { | |
| let finiteMilliseconds = milliseconds.isFinite ? milliseconds : 1000 | |
| let roundedSeconds = (max(1000, finiteMilliseconds) / 1000).rounded() | |
| return max(1, Int(min(roundedSeconds, Double(Int.max / 2)))) | |
| } | |
| } | |
| enum ChatWorkingIdentity { | |
| static func resolve( | |
| sessionKey: String, | |
| pendingRunIDs: Set<String>, | |
| localUserMessageIDsByRunID: [String: UUID], | |
| fallbackGeneration: UInt64) -> String | |
| { | |
| if let messageID = pendingRunIDs.compactMap({ localUserMessageIDsByRunID[$0]?.uuidString }).min() { | |
| return "\(sessionKey):user:\(messageID)" | |
| } | |
| if let runID = pendingRunIDs.min() { | |
| return "\(sessionKey):run:\(runID)" | |
| } | |
| return "\(sessionKey):generation:\(fallbackGeneration)" | |
| } | |
| } | |
| struct ChatTurnRecap: Equatable, Sendable { | |
| let runtimeMs: Double | |
| let outputTokens: Int? | |
| } | |
| enum ChatTurnRecapText { | |
| static func done(runtimeMs: Double, locale: Locale = .current) -> String { | |
| String( | |
| format: String(localized: "Done in %@", locale: locale), | |
| locale: locale, | |
| ChatWorkingDurationFormatter.full(milliseconds: runtimeMs, locale: locale)) | |
| } | |
| static func tokens(_ outputTokens: Int?, locale: Locale = .current) -> String? { | |
| guard let outputTokens else { return nil } | |
| // Mirrors the web and American-English source contract: exactly one | |
| // uses the singular key; zero and every other count use the generic key. | |
| if outputTokens == 1 { | |
| return String(localized: "1 token", locale: locale) | |
| } | |
| return String( | |
| format: String(localized: "%@ tokens", locale: locale), | |
| locale: locale, | |
| outputTokens.formatted(.number.locale(locale))) | |
| } | |
| } | |
| struct ChatTurnRecapSessionRow: Equatable, Sendable { | |
| let status: String? | |
| let endedAt: Double? | |
| let runtimeMs: Double? | |
| let outputTokens: Int? | |
| init( | |
| status: String? = nil, | |
| endedAt: Double? = nil, | |
| runtimeMs: Double? = nil, | |
| outputTokens: Int? = nil) | |
| { | |
| self.status = status | |
| self.endedAt = endedAt | |
| self.runtimeMs = runtimeMs | |
| self.outputTokens = outputTokens | |
| } | |
| init(_ entry: OpenClawChatSessionEntry) { | |
| self.init( | |
| status: entry.status, | |
| endedAt: entry.endedAt, | |
| runtimeMs: entry.runtimeMs, | |
| outputTokens: entry.outputTokens) | |
| } | |
| } | |
| /// Watches the turn whose working indicator was visible. Most anomalous | |
| /// interleavings fail quiet; without a row run ID, an unrelated completion | |
| /// inside the settle window remains an accepted cosmetic attribution risk. | |
| struct ChatTurnRecapResolver { | |
| private struct Watch { | |
| var watching: Bool | |
| var baselineKnown: Bool | |
| var baselineEndedAt: Double? | |
| var absorbedTerminal: Bool | |
| var settleStartedAt: Date? | |
| var settled: ChatTurnRecap? | |
| } | |
| private static let settleWindow: TimeInterval = 30 | |
| private var watches: [String: Watch] = [:] | |
| mutating func resolve( | |
| sessionKey: String, | |
| indicatorVisible: Bool, | |
| row: ChatTurnRecapSessionRow?, | |
| now: Date = Date()) -> ChatTurnRecap? | |
| { | |
| var watch = self.watches[sessionKey] | |
| let rowEndedAt = row?.endedAt | |
| if indicatorVisible { | |
| if watch == nil || watch?.watching == false { | |
| watch = Watch( | |
| watching: true, | |
| baselineKnown: row != nil, | |
| baselineEndedAt: rowEndedAt, | |
| absorbedTerminal: false, | |
| settleStartedAt: nil, | |
| settled: nil) | |
| } else if watch?.baselineKnown == false { | |
| if row != nil { | |
| watch?.baselineKnown = true | |
| watch?.baselineEndedAt = rowEndedAt | |
| } | |
| } else if let rowEndedAt, rowEndedAt != watch?.baselineEndedAt { | |
| watch?.baselineEndedAt = rowEndedAt | |
| watch?.absorbedTerminal = true | |
| } | |
| self.watches[sessionKey] = watch | |
| return nil | |
| } | |
| guard var watch else { return nil } | |
| watch.watching = false | |
| if let settled = watch.settled { | |
| self.watches[sessionKey] = watch | |
| return settled | |
| } | |
| if watch.absorbedTerminal || !watch.baselineKnown { | |
| // Without an attributable baseline, later terminal rows could belong | |
| // to background work, so consume this watch unresolved. | |
| self.watches[sessionKey] = nil | |
| return nil | |
| } | |
| if let settleStartedAt = watch.settleStartedAt { | |
| if now.timeIntervalSince(settleStartedAt) > Self.settleWindow { | |
| self.watches[sessionKey] = nil | |
| return nil | |
| } | |
| } else { | |
| watch.settleStartedAt = now | |
| } | |
| let isStale: Bool = if let rowEndedAt, let baselineEndedAt = watch.baselineEndedAt { | |
| rowEndedAt <= baselineEndedAt | |
| } else { | |
| rowEndedAt == nil | |
| } | |
| if isStale { | |
| // Terminal stamps are monotonic. Equality and regression both mean | |
| // the watched run's terminal patch has not arrived yet. | |
| self.watches[sessionKey] = watch | |
| return nil | |
| } | |
| // Any fresh terminal concludes the watch. Only clean done rows with a | |
| // finite runtime produce a recap; waiting longer could attach another run. | |
| self.watches[sessionKey] = nil | |
| guard row?.status == "done", let runtimeMs = row?.runtimeMs, runtimeMs.isFinite else { | |
| return nil | |
| } | |
| let settled = ChatTurnRecap(runtimeMs: runtimeMs, outputTokens: row?.outputTokens) | |
| watch.settled = settled | |
| self.watches[sessionKey] = watch | |
| return settled | |
| } | |
| } | |
| private func fnv1a(_ key: String) -> UInt32 { | |
| var hash: UInt32 = 0x811C_9DC5 | |
| for codeUnit in key.utf16 { | |
| hash ^= UInt32(codeUnit) | |
| hash = hash &* 0x0100_0193 | |
| } | |
| return hash | |
| } | |