Settings: add heartbeat controls
This commit is contained in:
@@ -13,6 +13,8 @@ struct ConfigSettings: View {
|
|||||||
@AppStorage(modelCatalogPathKey) private var modelCatalogPath: String = ModelCatalogLoader.defaultPath
|
@AppStorage(modelCatalogPathKey) private var modelCatalogPath: String = ModelCatalogLoader.defaultPath
|
||||||
@AppStorage(modelCatalogReloadKey) private var modelCatalogReloadBump: Int = 0
|
@AppStorage(modelCatalogReloadKey) private var modelCatalogReloadBump: Int = 0
|
||||||
@State private var allowAutosave = false
|
@State private var allowAutosave = false
|
||||||
|
@State private var heartbeatMinutes: Int?
|
||||||
|
@State private var heartbeatBody: String = "HEARTBEAT"
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(alignment: .leading, spacing: 14) {
|
VStack(alignment: .leading, spacing: 14) {
|
||||||
@@ -63,6 +65,34 @@ struct ConfigSettings: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LabeledContent("Heartbeat") {
|
||||||
|
VStack(alignment: .leading, spacing: 8) {
|
||||||
|
HStack(spacing: 8) {
|
||||||
|
Stepper(
|
||||||
|
value: Binding(
|
||||||
|
get: { self.heartbeatMinutes ?? 10 },
|
||||||
|
set: { self.heartbeatMinutes = $0; self.autosaveConfig() }),
|
||||||
|
in: 0...720)
|
||||||
|
{
|
||||||
|
Text("Every \(self.heartbeatMinutes ?? 10) min")
|
||||||
|
}
|
||||||
|
.help("Set to 0 to disable automatic heartbeats")
|
||||||
|
}
|
||||||
|
|
||||||
|
TextField("HEARTBEAT", text: self.$heartbeatBody)
|
||||||
|
.textFieldStyle(.roundedBorder)
|
||||||
|
.frame(width: 320)
|
||||||
|
.onChange(of: self.heartbeatBody) { _, _ in
|
||||||
|
self.autosaveConfig()
|
||||||
|
}
|
||||||
|
.help("Message body sent on each heartbeat")
|
||||||
|
|
||||||
|
Text("Heartbeats keep Pi sessions warm; 0 minutes disables them.")
|
||||||
|
.font(.footnote)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LabeledContent("Session store") {
|
LabeledContent("Session store") {
|
||||||
TextField("Path", text: self.$configStorePath)
|
TextField("Path", text: self.$configStorePath)
|
||||||
.textFieldStyle(.roundedBorder)
|
.textFieldStyle(.roundedBorder)
|
||||||
@@ -114,6 +144,9 @@ struct ConfigSettings: View {
|
|||||||
|
|
||||||
let session = reply["session"] as? [String: Any]
|
let session = reply["session"] as? [String: Any]
|
||||||
let agent = reply["agent"] as? [String: Any]
|
let agent = reply["agent"] as? [String: Any]
|
||||||
|
let heartbeatMinutes = reply["heartbeatMinutes"] as? Int
|
||||||
|
let heartbeatBody = reply["heartbeatBody"] as? String
|
||||||
|
|
||||||
self.configStorePath = (session?["store"] as? String) ?? SessionLoader.defaultStorePath
|
self.configStorePath = (session?["store"] as? String) ?? SessionLoader.defaultStorePath
|
||||||
let loadedModel = (agent?["model"] as? String) ?? ""
|
let loadedModel = (agent?["model"] as? String) ?? ""
|
||||||
if !loadedModel.isEmpty {
|
if !loadedModel.isEmpty {
|
||||||
@@ -123,6 +156,9 @@ struct ConfigSettings: View {
|
|||||||
self.configModel = SessionLoader.fallbackModel
|
self.configModel = SessionLoader.fallbackModel
|
||||||
self.customModel = SessionLoader.fallbackModel
|
self.customModel = SessionLoader.fallbackModel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let heartbeatMinutes { self.heartbeatMinutes = heartbeatMinutes }
|
||||||
|
if let heartbeatBody, !heartbeatBody.isEmpty { self.heartbeatBody = heartbeatBody }
|
||||||
}
|
}
|
||||||
|
|
||||||
private func autosaveConfig() {
|
private func autosaveConfig() {
|
||||||
@@ -137,6 +173,7 @@ struct ConfigSettings: View {
|
|||||||
|
|
||||||
var session: [String: Any] = [:]
|
var session: [String: Any] = [:]
|
||||||
var agent: [String: Any] = [:]
|
var agent: [String: Any] = [:]
|
||||||
|
var reply: [String: Any] = [:]
|
||||||
|
|
||||||
let trimmedStore = self.configStorePath.trimmingCharacters(in: .whitespacesAndNewlines)
|
let trimmedStore = self.configStorePath.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
if !trimmedStore.isEmpty { session["store"] = trimmedStore }
|
if !trimmedStore.isEmpty { session["store"] = trimmedStore }
|
||||||
@@ -146,10 +183,18 @@ struct ConfigSettings: View {
|
|||||||
let trimmedModel = chosenModel
|
let trimmedModel = chosenModel
|
||||||
if !trimmedModel.isEmpty { agent["model"] = trimmedModel }
|
if !trimmedModel.isEmpty { agent["model"] = trimmedModel }
|
||||||
|
|
||||||
let reply: [String: Any] = [
|
reply["session"] = session
|
||||||
"session": session,
|
reply["agent"] = agent
|
||||||
"agent": agent,
|
|
||||||
]
|
if let heartbeatMinutes {
|
||||||
|
reply["heartbeatMinutes"] = heartbeatMinutes
|
||||||
|
}
|
||||||
|
|
||||||
|
let trimmedBody = self.heartbeatBody.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
|
if !trimmedBody.isEmpty {
|
||||||
|
reply["heartbeatBody"] = trimmedBody
|
||||||
|
}
|
||||||
|
|
||||||
let inbound: [String: Any] = ["reply": reply]
|
let inbound: [String: Any] = ["reply": reply]
|
||||||
let root: [String: Any] = ["inbound": inbound]
|
let root: [String: Any] = ["inbound": inbound]
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user