feat: generation defaults moved to model management

This commit is contained in:
2026-03-21 20:13:44 +01:00
parent 6b14d7d46c
commit 6e20bd538b
2 changed files with 35 additions and 27 deletions

View File

@@ -84,11 +84,15 @@ struct ModelManagementView: View {
baselineModel: modelManager.baselineModel(repoId: model.repoId) ?? model, baselineModel: modelManager.baselineModel(repoId: model.repoId) ?? model,
detectedLocalModel: modelManager.discoveredLocalModelInfo(repoId: model.repoId), detectedLocalModel: modelManager.discoveredLocalModelInfo(repoId: model.repoId),
hasSavedOverride: Preferences.hasModelMetadataOverride(forRepoId: model.repoId), hasSavedOverride: Preferences.hasModelMetadataOverride(forRepoId: model.repoId),
hasSavedGenerationDefaults: Preferences.hasGenerationSettings(forModelId: model.id),
onSave: { override in onSave: { override in
modelManager.saveMetadataOverride(override, for: model) modelManager.saveMetadataOverride(override, for: model)
}, },
onReset: { onReset: {
modelManager.clearMetadataOverride(for: model) modelManager.clearMetadataOverride(for: model)
},
onSaveGenerationSettings: { settings in
Preferences.setGenerationSettings(settings, forModelId: model.id)
} }
) )
} }
@@ -241,32 +245,40 @@ private struct ModelMetadataEditorView: View {
let baselineModel: ModelConfig let baselineModel: ModelConfig
let detectedLocalModel: LocalModelResolver.LocalModelInfo? let detectedLocalModel: LocalModelResolver.LocalModelInfo?
let hasSavedOverride: Bool let hasSavedOverride: Bool
let hasSavedGenerationDefaults: Bool
let onSave: (ModelMetadataOverride) -> Void let onSave: (ModelMetadataOverride) -> Void
let onReset: () -> Void let onReset: () -> Void
let onSaveGenerationSettings: (GenerationSettings) -> Void
@State private var contextLengthText: String @State private var contextLengthText: String
@State private var primaryLoaderKind: ModelConfig.LoaderKind @State private var primaryLoaderKind: ModelConfig.LoaderKind
@State private var supportsImages: Bool @State private var supportsImages: Bool
@State private var supportsTools: Bool @State private var supportsTools: Bool
@State private var generationSettings: GenerationSettings
init( init(
model: ModelConfig, model: ModelConfig,
baselineModel: ModelConfig, baselineModel: ModelConfig,
detectedLocalModel: LocalModelResolver.LocalModelInfo?, detectedLocalModel: LocalModelResolver.LocalModelInfo?,
hasSavedOverride: Bool, hasSavedOverride: Bool,
hasSavedGenerationDefaults: Bool,
onSave: @escaping (ModelMetadataOverride) -> Void, onSave: @escaping (ModelMetadataOverride) -> Void,
onReset: @escaping () -> Void onReset: @escaping () -> Void,
onSaveGenerationSettings: @escaping (GenerationSettings) -> Void
) { ) {
self.model = model self.model = model
self.baselineModel = baselineModel self.baselineModel = baselineModel
self.detectedLocalModel = detectedLocalModel self.detectedLocalModel = detectedLocalModel
self.hasSavedOverride = hasSavedOverride self.hasSavedOverride = hasSavedOverride
self.hasSavedGenerationDefaults = hasSavedGenerationDefaults
self.onSave = onSave self.onSave = onSave
self.onReset = onReset self.onReset = onReset
self.onSaveGenerationSettings = onSaveGenerationSettings
_contextLengthText = State(initialValue: String(model.contextLength)) _contextLengthText = State(initialValue: String(model.contextLength))
_primaryLoaderKind = State(initialValue: model.primaryLoaderKind) _primaryLoaderKind = State(initialValue: model.primaryLoaderKind)
_supportsImages = State(initialValue: model.supportsImages) _supportsImages = State(initialValue: model.supportsImages)
_supportsTools = State(initialValue: model.supportsTools) _supportsTools = State(initialValue: model.supportsTools)
_generationSettings = State(initialValue: Preferences.generationSettings(forModelId: model.id))
} }
var body: some View { var body: some View {
@@ -337,10 +349,18 @@ private struct ModelMetadataEditorView: View {
} }
} }
} }
Section("Generation Defaults") {
GenerationDefaultsEditor(settings: $generationSettings)
Text(generationDefaultsSummary)
.font(.caption)
.foregroundStyle(.secondary)
}
} }
.formStyle(.grouped) .formStyle(.grouped)
.navigationTitle(model.displayName) .navigationTitle(model.displayName)
.frame(minWidth: 520, minHeight: 380) .frame(minWidth: 560, minHeight: 620)
.toolbar { .toolbar {
ToolbarItem(placement: .cancellationAction) { ToolbarItem(placement: .cancellationAction) {
Button("Cancel") { Button("Cancel") {
@@ -352,6 +372,7 @@ private struct ModelMetadataEditorView: View {
Button("Save") { Button("Save") {
guard let currentOverride else { return } guard let currentOverride else { return }
onSave(currentOverride) onSave(currentOverride)
onSaveGenerationSettings(generationSettings.normalized())
dismiss() dismiss()
} }
.disabled(currentOverride == nil) .disabled(currentOverride == nil)
@@ -428,4 +449,16 @@ private struct ModelMetadataEditorView: View {
private func yesNo(_ value: Bool) -> String { private func yesNo(_ value: Bool) -> String {
value ? "Yes" : "No" value ? "Yes" : "No"
} }
private var generationDefaultsSummary: String {
if hasSavedGenerationDefaults {
return "These saved generation defaults apply to new chats and to API requests that omit generation parameters for this model."
}
if model.isCurated {
return "These defaults currently match the model's built-in defaults. Save to store a custom per-model default for chats and API requests."
}
return "These defaults currently match the general fallback defaults for this model. Save to store a custom per-model default for chats and API requests."
}
} }

View File

@@ -9,7 +9,6 @@ struct SettingsView: View {
@State private var apiAutoStart: Bool = Preferences.apiAutoStart @State private var apiAutoStart: Bool = Preferences.apiAutoStart
@State private var idleUnloadMinutes: String = String(Preferences.idleUnloadMinutes) @State private var idleUnloadMinutes: String = String(Preferences.idleUnloadMinutes)
@State private var defaultModelId: String = Preferences.defaultModelId ?? ModelConfig.default.id @State private var defaultModelId: String = Preferences.defaultModelId ?? ModelConfig.default.id
@State private var generationDefaultsModelId: String = Preferences.defaultModelId ?? ModelConfig.default.id
@State private var kvQuantizationEnabled: Bool = Preferences.kvQuantizationEnabled @State private var kvQuantizationEnabled: Bool = Preferences.kvQuantizationEnabled
@State private var kvQuantizationBits: Int = Preferences.kvQuantizationBits @State private var kvQuantizationBits: Int = Preferences.kvQuantizationBits
@@ -43,20 +42,6 @@ struct SettingsView: View {
.foregroundStyle(.secondary) .foregroundStyle(.secondary)
} }
Section("Generation Defaults") {
Picker("Defaults for model", selection: $generationDefaultsModelId) {
ForEach(modelManager.availableModels) { model in
Text(model.displayName).tag(model.id)
}
}
GenerationDefaultsEditor(settings: generationDefaultsBinding)
Text("These are the per-model defaults used by chat sessions and by the API server whenever a request omits a generation parameter. Lower temperature and stronger repetition penalties are usually better for technical work; higher temperature is usually better for improvisation and roleplay.")
.font(.caption)
.foregroundStyle(.secondary)
}
Section("System Prompt") { Section("System Prompt") {
TextEditor(text: $systemPrompt) TextEditor(text: $systemPrompt)
.font(.body.monospaced()) .font(.body.monospaced())
@@ -170,16 +155,6 @@ struct SettingsView: View {
if !modelManager.availableModels.contains(where: { $0.id == defaultModelId }) { if !modelManager.availableModels.contains(where: { $0.id == defaultModelId }) {
defaultModelId = modelManager.availableModels.first?.id ?? ModelConfig.default.id defaultModelId = modelManager.availableModels.first?.id ?? ModelConfig.default.id
} }
if !modelManager.availableModels.contains(where: { $0.id == generationDefaultsModelId }) {
generationDefaultsModelId = defaultModelId
}
} }
} }
private var generationDefaultsBinding: Binding<GenerationSettings> {
Binding(
get: { Preferences.generationSettings(forModelId: generationDefaultsModelId) },
set: { Preferences.setGenerationSettings($0, forModelId: generationDefaultsModelId) }
)
}
} }