Save a default model per user, set from Settings

This commit is contained in:
Ian Gulliver
2026-10-18 00:57:51 -07:00
parent 17f09ecb7c
commit 10c6c84f74
10 changed files with 395 additions and 257 deletions
+2 -1
View File
@@ -291,7 +291,8 @@ struct ComposerView: View {
let offered = live.map { s in models.filter { vendorOf($0) == s.vendor.rawValue } } ?? models
let options = !current.isEmpty && !offered.contains(current) ? [current] + offered : offered
let modelKey = "message:\(live?.id ?? key)"
return ModelState(live: live, current: current, options: options, modelKey: modelKey, selected: pick ?? modelChoices[modelKey] ?? (current.isEmpty ? options[0] : current))
let fresh = live == nil ? store.defaultModel : options[0]
return ModelState(live: live, current: current, options: options, modelKey: modelKey, selected: pick ?? modelChoices[modelKey] ?? (current.isEmpty ? fresh : current))
}
var body: some View {
+41
View File
@@ -25,6 +25,11 @@ struct SettingsView: View {
heading("Notifications")
DeviceNotifications()
PrefsBox()
heading("Default model")
Text("New tasks you start from byre run on this model.")
.foregroundStyle(Palette.muted)
.fixedSize(horizontal: false, vertical: true)
DefaultModelMenu()
heading("What your lead has learned")
Text("Your lead notes your priorities and preferences as it works with you, and starts every session with them.")
.foregroundStyle(Palette.muted)
@@ -132,6 +137,42 @@ private struct PrefsBox: View {
}
}
private struct DefaultModelMenu: View {
@Environment(Store.self) private var store
@Environment(Overlays.self) private var overlays
var body: some View {
Menu {
ForEach(models, id: \.self) { m in
Button(m) {
Task {
do {
store.me = try await store.api.client.updateDefaultModel(body: .json(.init(model: m))).ok.body.json
} catch {
overlays.showAlert(errorText(error))
}
}
}
}
} label: {
HStack(spacing: 8) {
Text(store.defaultModel)
Image(systemName: "chevron.up.chevron.down")
.font(.system(size: 9, weight: .semibold))
}
.foregroundStyle(Palette.text)
.modifier(FieldStyle())
.contentShape(Rectangle())
}
.menuStyle(.button)
.buttonStyle(.plain)
.menuIndicator(.hidden)
.fixedSize()
.help("Model for new tasks")
.padding(.top, 8)
}
}
private struct LeadNotes: View {
@Environment(Store.self) private var store
@Environment(Overlays.self) private var overlays
+7 -3
View File
@@ -92,7 +92,7 @@ final class Store {
todoBusy[key] = discuss ? "discuss" : "start"
Task {
do {
_ = try await api.client.startTodoItem(path: .init(projectId: projectId), body: .json(.init(title: title, model: models[0], discuss: discuss))).created
_ = try await api.client.startTodoItem(path: .init(projectId: projectId), body: .json(.init(title: title, model: defaultModel, discuss: discuss))).created
} catch {
overlays.showAlert(errorText(error))
}
@@ -108,7 +108,7 @@ final class Store {
todoBusy[key] = "start"
Task {
do {
_ = try await api.client.createTask(path: .init(projectId: projectId), body: .json(.init(title: title, prompt: brief, model: models[0]))).created
_ = try await api.client.createTask(path: .init(projectId: projectId), body: .json(.init(title: title, prompt: brief, model: defaultModel))).created
} catch {
overlays.showAlert(errorText(error))
}
@@ -126,7 +126,7 @@ final class Store {
let live = lead != nil && lead!.state != .completed
Task {
do {
_ = try await api.client.messageLead(body: .json(.init(text: "Let's discuss your suggestion \"\(title)\" before starting it:\n\n\(brief)", model: live ? nil : models[0]))).accepted
_ = try await api.client.messageLead(body: .json(.init(text: "Let's discuss your suggestion \"\(title)\" before starting it:\n\n\(brief)", model: live ? nil : defaultModel))).accepted
} catch {
overlays.showAlert(errorText(error))
}
@@ -208,6 +208,10 @@ final class Store {
focusTab(.reviews)
}
var defaultModel: String {
me!.defaultModel
}
func loadMe() async throws {
me = try await api.client.getMe().ok.body.json
}
+3 -3
View File
@@ -234,7 +234,7 @@ final class SwitcherModel {
case .newTask(let p, let title):
create(p.id, .init(title: title))
case .startTask(let p, let title):
create(p.id, .init(title: title, prompt: title, model: models[0]))
create(p.id, .init(title: title, prompt: title, model: store.defaultModel))
case .startWorkflow(let p, let title):
pickWorkflow(p, title)
case .workflow(let p, let title, let w):
@@ -442,7 +442,7 @@ private struct SwitcherRowView: View {
.lineLimit(1)
.truncationMode(.tail)
.frame(maxWidth: .infinity, alignment: .leading)
state(models[0])
state(store.defaultModel)
.help("Model for the first message")
case .startTask(let p, let title):
Text(created([("Start in ", nil), (p.name, Palette.muted), (": \(title)", nil)]))
@@ -450,7 +450,7 @@ private struct SwitcherRowView: View {
.lineLimit(1)
.truncationMode(.tail)
.frame(maxWidth: .infinity, alignment: .leading)
state(models[0])
state(store.defaultModel)
.help("The title is the agent's first message")
case .startWorkflow(let p, let title):
Text(created([("Start workflow… in ", nil), (p.name, Palette.muted), (": \(title)", nil)]))