2.5.0 #10
@@ -25,7 +25,6 @@ import SwiftUI
|
|||||||
|
|
||||||
struct CombineConversationsSheet: View {
|
struct CombineConversationsSheet: View {
|
||||||
@Environment(\.dismiss) var dismiss
|
@Environment(\.dismiss) var dismiss
|
||||||
@Environment(ChatViewModel.self) private var chatViewModel
|
|
||||||
|
|
||||||
let conversations: [Conversation]
|
let conversations: [Conversation]
|
||||||
var onCompleted: (Conversation) -> Void
|
var onCompleted: (Conversation) -> Void
|
||||||
@@ -37,6 +36,8 @@ struct CombineConversationsSheet: View {
|
|||||||
@State private var errorMessage: String?
|
@State private var errorMessage: String?
|
||||||
@State private var mergeModel: ModelInfo?
|
@State private var mergeModel: ModelInfo?
|
||||||
@State private var mergeProvider: Settings.Provider
|
@State private var mergeProvider: Settings.Provider
|
||||||
|
@State private var mergeModels: [ModelInfo] = []
|
||||||
|
@State private var isLoadingMergeModels = false
|
||||||
@State private var showModelPicker = false
|
@State private var showModelPicker = false
|
||||||
|
|
||||||
private let settings = SettingsService.shared
|
private let settings = SettingsService.shared
|
||||||
@@ -50,11 +51,8 @@ struct CombineConversationsSheet: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private var mergeModelLabel: String? {
|
private var mergeModelLabel: String? {
|
||||||
if let mergeModel {
|
guard let mergeModel else { return nil }
|
||||||
return "\(mergeProvider.displayName) / \(mergeModel.name)"
|
return "\(mergeProvider.displayName) / \(mergeModel.name)"
|
||||||
}
|
|
||||||
guard let model = settings.defaultModel, !model.isEmpty else { return nil }
|
|
||||||
return "\(settings.defaultProvider.displayName) / \(model)"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private var isValid: Bool {
|
private var isValid: Bool {
|
||||||
@@ -125,12 +123,37 @@ struct CombineConversationsSheet: View {
|
|||||||
Label("No model selected", systemImage: "exclamationmark.triangle.fill")
|
Label("No model selected", systemImage: "exclamationmark.triangle.fill")
|
||||||
.font(.caption).foregroundStyle(.orange)
|
.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
|
showModelPicker = true
|
||||||
}
|
}
|
||||||
.buttonStyle(.link)
|
.buttonStyle(.link)
|
||||||
.font(.caption)
|
.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)
|
.frame(minWidth: 520, idealWidth: 560, minHeight: 460, idealHeight: 520)
|
||||||
.task {
|
.task {
|
||||||
if chatViewModel.availableModels.count <= 10 {
|
await loadMergeModels()
|
||||||
await chatViewModel.loadAvailableModels()
|
if let defaultModel = settings.defaultModel {
|
||||||
}
|
mergeModel = mergeModels.first(where: { $0.id == defaultModel })
|
||||||
if mergeModel == nil, let defaultModel = settings.defaultModel {
|
|
||||||
mergeModel = chatViewModel.availableModels.first(where: { $0.id == defaultModel })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.sheet(isPresented: $showModelPicker) {
|
.sheet(isPresented: $showModelPicker) {
|
||||||
ModelSelectorView(
|
ModelSelectorView(
|
||||||
models: chatViewModel.availableModels,
|
models: mergeModels,
|
||||||
selectedModel: mergeModel,
|
selectedModel: mergeModel,
|
||||||
onSelect: { model in
|
onSelect: { model in
|
||||||
mergeModel = model
|
mergeModel = model
|
||||||
mergeProvider = chatViewModel.inferProviderPublic(from: model.id) ?? mergeProvider
|
|
||||||
showModelPicker = false
|
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() {
|
private func combine() {
|
||||||
isProcessing = true
|
isProcessing = true
|
||||||
errorMessage = nil
|
errorMessage = nil
|
||||||
|
|||||||
Reference in New Issue
Block a user