Cursor: Apply local changes for cloud agent
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
// ClipboardSemanticShadowMetricsStore.swift
|
||||
// OSGKeyboard · Shared
|
||||
//
|
||||
// Stores only bounded aggregate verifier counters. Clipboard text, identifiers,
|
||||
// model confidences, and individual predictions are intentionally never saved.
|
||||
|
||||
import Foundation
|
||||
|
||||
public struct ClipboardSemanticShadowMetrics: Codable, Equatable, Sendable {
|
||||
public var verifierRuns: Int
|
||||
public var candidateRoutes: Int
|
||||
public var verifierRoutes: Int
|
||||
public var disagreements: Int
|
||||
|
||||
public static let empty = ClipboardSemanticShadowMetrics(
|
||||
verifierRuns: 0,
|
||||
candidateRoutes: 0,
|
||||
verifierRoutes: 0,
|
||||
disagreements: 0
|
||||
)
|
||||
}
|
||||
|
||||
@MainActor
|
||||
public final class ClipboardSemanticShadowMetricsStore {
|
||||
public static let shared = ClipboardSemanticShadowMetricsStore()
|
||||
public static let maximumCounterValue = 1_000_000
|
||||
|
||||
private static let storageKey = "clipboard.semanticShadowMetrics.v1"
|
||||
private let defaults: UserDefaults?
|
||||
|
||||
public init(defaults: UserDefaults? = AppGroup.defaultsIfAvailable) {
|
||||
self.defaults = defaults
|
||||
}
|
||||
|
||||
public func metrics() -> ClipboardSemanticShadowMetrics {
|
||||
guard let data = defaults?.data(forKey: Self.storageKey),
|
||||
let value = try? JSONDecoder().decode(
|
||||
ClipboardSemanticShadowMetrics.self,
|
||||
from: data
|
||||
) else {
|
||||
return .empty
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
public func record(_ analysis: ClipboardSemanticAnalysis) {
|
||||
var next = metrics()
|
||||
if let decision = analysis.actionVerifier, decision.isShadow {
|
||||
increment(&next.verifierRuns)
|
||||
let candidateLabel = actionCandidateLabel(analysis)
|
||||
if candidateLabel != "neither" {
|
||||
increment(&next.candidateRoutes)
|
||||
}
|
||||
if decision.isRouted {
|
||||
increment(&next.verifierRoutes)
|
||||
}
|
||||
if candidateLabel != routedLabel(decision) {
|
||||
increment(&next.disagreements)
|
||||
}
|
||||
}
|
||||
if let decision = analysis.coordinationVerifier, decision.isShadow {
|
||||
increment(&next.verifierRuns)
|
||||
let candidateLabel = coordinationCandidateLabel(analysis)
|
||||
if candidateLabel != "neither" {
|
||||
increment(&next.candidateRoutes)
|
||||
}
|
||||
if decision.isRouted {
|
||||
increment(&next.verifierRoutes)
|
||||
}
|
||||
if candidateLabel != routedLabel(decision) {
|
||||
increment(&next.disagreements)
|
||||
}
|
||||
}
|
||||
guard let encoded = try? JSONEncoder().encode(next) else { return }
|
||||
defaults?.set(encoded, forKey: Self.storageKey)
|
||||
}
|
||||
|
||||
public func clear() {
|
||||
defaults?.removeObject(forKey: Self.storageKey)
|
||||
}
|
||||
|
||||
private func actionCandidateLabel(_ analysis: ClipboardSemanticAnalysis) -> String {
|
||||
if analysis.task.isDetected && analysis.complaint.isDetected {
|
||||
return "both"
|
||||
}
|
||||
if analysis.task.isDetected {
|
||||
return "taskOnly"
|
||||
}
|
||||
if analysis.complaint.isDetected {
|
||||
return "complaintOnly"
|
||||
}
|
||||
if analysis.question.isDetected {
|
||||
return "questionRequest"
|
||||
}
|
||||
return "neither"
|
||||
}
|
||||
|
||||
private func coordinationCandidateLabel(
|
||||
_ analysis: ClipboardSemanticAnalysis
|
||||
) -> String {
|
||||
let labels = [
|
||||
analysis.invitation.isDetected ? "invitation" : nil,
|
||||
analysis.scheduleNegotiation.isDetected ? "scheduleNegotiation" : nil,
|
||||
analysis.confirmationDecision.isDetected ? "confirmationDecision" : nil,
|
||||
analysis.followUpReminder.isDetected ? "followUpReminder" : nil
|
||||
].compactMap { $0 }
|
||||
return labels.count == 1 ? labels[0] : "neither"
|
||||
}
|
||||
|
||||
private func routedLabel(_ decision: ClipboardVerifierDecision) -> String {
|
||||
decision.isRouted ? decision.label : "neither"
|
||||
}
|
||||
|
||||
private func increment(_ value: inout Int) {
|
||||
value = min(value + 1, Self.maximumCounterValue)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user