feat(macos): load models from gateway

This commit is contained in:
Peter Steinberger
2025-12-20 23:24:09 +01:00
parent e3015bbfb7
commit 8c18dd40a3
4 changed files with 46 additions and 7 deletions

View File

@@ -17,6 +17,7 @@ struct ConfigSettings: View {
@State private var models: [ModelChoice] = [] @State private var models: [ModelChoice] = []
@State private var modelsLoading = false @State private var modelsLoading = false
@State private var modelError: String? @State private var modelError: String?
@State private var modelsSourceLabel: String?
@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
@@ -142,6 +143,12 @@ struct ConfigSettings: View {
.font(.footnote) .font(.footnote)
.foregroundStyle(.secondary) .foregroundStyle(.secondary)
} }
if let modelsSourceLabel {
Text("Model catalog: \(modelsSourceLabel)")
.font(.footnote)
.foregroundStyle(.secondary)
}
} }
private var anthropicAuthHelpText: String { private var anthropicAuthHelpText: String {
@@ -410,20 +417,44 @@ struct ConfigSettings: View {
guard !self.modelsLoading else { return } guard !self.modelsLoading else { return }
self.modelsLoading = true self.modelsLoading = true
self.modelError = nil self.modelError = nil
self.modelsSourceLabel = nil
do { do {
let loaded = try await ModelCatalogLoader.load(from: self.modelCatalogPath) let res: ModelsListResult =
self.models = loaded try await GatewayConnection.shared
if !self.configModel.isEmpty, !loaded.contains(where: { $0.id == self.configModel }) { .requestDecoded(
method: .modelsList,
timeoutMs: 15000)
self.models = res.models
self.modelsSourceLabel = "gateway"
if !self.configModel.isEmpty,
!res.models.contains(where: { $0.id == self.configModel })
{
self.customModel = self.configModel self.customModel = self.configModel
self.configModel = "__custom__" self.configModel = "__custom__"
} }
} catch { } catch {
self.modelError = error.localizedDescription do {
self.models = [] let loaded = try await ModelCatalogLoader.load(from: self.modelCatalogPath)
self.models = loaded
self.modelsSourceLabel = "local fallback"
if !self.configModel.isEmpty,
!loaded.contains(where: { $0.id == self.configModel })
{
self.customModel = self.configModel
self.configModel = "__custom__"
}
} catch {
self.modelError = error.localizedDescription
self.models = []
}
} }
self.modelsLoading = false self.modelsLoading = false
} }
private struct ModelsListResult: Decodable {
let models: [ModelChoice]
}
private var selectedContextLabel: String? { private var selectedContextLabel: String? {
let chosenId = (self.configModel == "__custom__") ? self.customModel : self.configModel let chosenId = (self.configModel == "__custom__") ? self.customModel : self.configModel
guard guard

View File

@@ -404,7 +404,7 @@ struct DebugSettings: View {
.font(.footnote) .font(.footnote)
.foregroundStyle(.secondary) .foregroundStyle(.secondary)
} }
Text("Used by the Config tab model picker; point at a different build when debugging.") Text("Local fallback for model picker when gateway models.list is unavailable.")
.font(.footnote) .font(.footnote)
.foregroundStyle(.tertiary) .foregroundStyle(.tertiary)
} }

View File

@@ -47,6 +47,14 @@ actor GatewayConnection {
case setHeartbeats = "set-heartbeats" case setHeartbeats = "set-heartbeats"
case systemEvent = "system-event" case systemEvent = "system-event"
case health case health
case providersStatus = "providers.status"
case configGet = "config.get"
case configSet = "config.set"
case webLoginStart = "web.login.start"
case webLoginWait = "web.login.wait"
case webLogout = "web.logout"
case telegramLogout = "telegram.logout"
case modelsList = "models.list"
case chatHistory = "chat.history" case chatHistory = "chat.history"
case chatSend = "chat.send" case chatSend = "chat.send"
case chatAbort = "chat.abort" case chatAbort = "chat.abort"

View File

@@ -163,7 +163,7 @@ extension SessionRow {
} }
} }
struct ModelChoice: Identifiable, Hashable { struct ModelChoice: Identifiable, Hashable, Codable {
let id: String let id: String
let name: String let name: String
let provider: String let provider: String