diff --git a/oAI/Views/Screens/CombineConversationsSheet.swift b/oAI/Views/Screens/CombineConversationsSheet.swift index 5561ae8..3e45b7f 100644 --- a/oAI/Views/Screens/CombineConversationsSheet.swift +++ b/oAI/Views/Screens/CombineConversationsSheet.swift @@ -25,7 +25,6 @@ import SwiftUI struct CombineConversationsSheet: View { @Environment(\.dismiss) var dismiss - @Environment(ChatViewModel.self) private var chatViewModel let conversations: [Conversation] var onCompleted: (Conversation) -> Void @@ -37,6 +36,8 @@ struct CombineConversationsSheet: View { @State private var errorMessage: String? @State private var mergeModel: ModelInfo? @State private var mergeProvider: Settings.Provider + @State private var mergeModels: [ModelInfo] = [] + @State private var isLoadingMergeModels = false @State private var showModelPicker = false private let settings = SettingsService.shared @@ -50,11 +51,8 @@ struct CombineConversationsSheet: View { } private var mergeModelLabel: String? { - if let mergeModel { - return "\(mergeProvider.displayName) / \(mergeModel.name)" - } - guard let model = settings.defaultModel, !model.isEmpty else { return nil } - return "\(settings.defaultProvider.displayName) / \(model)" + guard let mergeModel else { return nil } + return "\(mergeProvider.displayName) / \(mergeModel.name)" } private var isValid: Bool { @@ -125,12 +123,37 @@ struct CombineConversationsSheet: View { Label("No model selected", systemImage: "exclamationmark.triangle.fill") .font(.caption).foregroundStyle(.orange) } - Button("Change…") { + + Menu { + ForEach(ProviderRegistry.shared.configuredProviders, id: \.self) { p in + Button { + switchMergeProvider(to: p) + } label: { + HStack { + Image(systemName: p.iconName) + Text(p.displayName) + if p == mergeProvider { Image(systemName: "checkmark") } + } + } + } + } label: { + Text(mergeProvider.displayName) + } + .menuStyle(.borderlessButton) + .fixedSize() + .font(.caption) + .disabled(isProcessing || isLoadingMergeModels) + + Button("Change Model…") { showModelPicker = true } .buttonStyle(.link) .font(.caption) - .disabled(isProcessing) + .disabled(isProcessing || isLoadingMergeModels || mergeModels.isEmpty) + + if isLoadingMergeModels { + ProgressView().controlSize(.mini) + } } } } @@ -173,26 +196,46 @@ struct CombineConversationsSheet: View { } .frame(minWidth: 520, idealWidth: 560, minHeight: 460, idealHeight: 520) .task { - if chatViewModel.availableModels.count <= 10 { - await chatViewModel.loadAvailableModels() - } - if mergeModel == nil, let defaultModel = settings.defaultModel { - mergeModel = chatViewModel.availableModels.first(where: { $0.id == defaultModel }) + await loadMergeModels() + if let defaultModel = settings.defaultModel { + mergeModel = mergeModels.first(where: { $0.id == defaultModel }) } } .sheet(isPresented: $showModelPicker) { ModelSelectorView( - models: chatViewModel.availableModels, + models: mergeModels, selectedModel: mergeModel, onSelect: { model in mergeModel = model - mergeProvider = chatViewModel.inferProviderPublic(from: model.id) ?? mergeProvider showModelPicker = false } ) } } + private func switchMergeProvider(to newProvider: Settings.Provider) { + guard newProvider != mergeProvider else { return } + mergeProvider = newProvider + mergeModel = nil + mergeModels = [] + Task { await loadMergeModels() } + } + + private func loadMergeModels() async { + guard let provider = ProviderRegistry.shared.getProvider(for: mergeProvider) else { + mergeModels = [] + return + } + isLoadingMergeModels = true + defer { isLoadingMergeModels = false } + do { + mergeModels = try await provider.listModels() + } catch { + Log.api.error("Failed to load models for merge provider \(mergeProvider.rawValue): \(error.localizedDescription)") + mergeModels = [] + } + } + private func combine() { isProcessing = true errorMessage = nil