From 6e20bd538b6bc9d2908dfbfe63a9abe5e2173b4f Mon Sep 17 00:00:00 2001 From: Chili Palmer Date: Sat, 21 Mar 2026 20:13:44 +0100 Subject: [PATCH] feat: generation defaults moved to model management --- MLXServer/Views/ModelManagementView.swift | 37 +++++++++++++++++++++-- MLXServer/Views/SettingsView.swift | 25 --------------- 2 files changed, 35 insertions(+), 27 deletions(-) diff --git a/MLXServer/Views/ModelManagementView.swift b/MLXServer/Views/ModelManagementView.swift index dd651d3..582babf 100644 --- a/MLXServer/Views/ModelManagementView.swift +++ b/MLXServer/Views/ModelManagementView.swift @@ -84,11 +84,15 @@ struct ModelManagementView: View { baselineModel: modelManager.baselineModel(repoId: model.repoId) ?? model, detectedLocalModel: modelManager.discoveredLocalModelInfo(repoId: model.repoId), hasSavedOverride: Preferences.hasModelMetadataOverride(forRepoId: model.repoId), + hasSavedGenerationDefaults: Preferences.hasGenerationSettings(forModelId: model.id), onSave: { override in modelManager.saveMetadataOverride(override, for: model) }, onReset: { 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 detectedLocalModel: LocalModelResolver.LocalModelInfo? let hasSavedOverride: Bool + let hasSavedGenerationDefaults: Bool let onSave: (ModelMetadataOverride) -> Void let onReset: () -> Void + let onSaveGenerationSettings: (GenerationSettings) -> Void @State private var contextLengthText: String @State private var primaryLoaderKind: ModelConfig.LoaderKind @State private var supportsImages: Bool @State private var supportsTools: Bool + @State private var generationSettings: GenerationSettings init( model: ModelConfig, baselineModel: ModelConfig, detectedLocalModel: LocalModelResolver.LocalModelInfo?, hasSavedOverride: Bool, + hasSavedGenerationDefaults: Bool, onSave: @escaping (ModelMetadataOverride) -> Void, - onReset: @escaping () -> Void + onReset: @escaping () -> Void, + onSaveGenerationSettings: @escaping (GenerationSettings) -> Void ) { self.model = model self.baselineModel = baselineModel self.detectedLocalModel = detectedLocalModel self.hasSavedOverride = hasSavedOverride + self.hasSavedGenerationDefaults = hasSavedGenerationDefaults self.onSave = onSave self.onReset = onReset + self.onSaveGenerationSettings = onSaveGenerationSettings _contextLengthText = State(initialValue: String(model.contextLength)) _primaryLoaderKind = State(initialValue: model.primaryLoaderKind) _supportsImages = State(initialValue: model.supportsImages) _supportsTools = State(initialValue: model.supportsTools) + _generationSettings = State(initialValue: Preferences.generationSettings(forModelId: model.id)) } 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) .navigationTitle(model.displayName) - .frame(minWidth: 520, minHeight: 380) + .frame(minWidth: 560, minHeight: 620) .toolbar { ToolbarItem(placement: .cancellationAction) { Button("Cancel") { @@ -352,6 +372,7 @@ private struct ModelMetadataEditorView: View { Button("Save") { guard let currentOverride else { return } onSave(currentOverride) + onSaveGenerationSettings(generationSettings.normalized()) dismiss() } .disabled(currentOverride == nil) @@ -428,4 +449,16 @@ private struct ModelMetadataEditorView: View { private func yesNo(_ value: Bool) -> String { 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." + } } \ No newline at end of file diff --git a/MLXServer/Views/SettingsView.swift b/MLXServer/Views/SettingsView.swift index cb71930..39fe2f8 100644 --- a/MLXServer/Views/SettingsView.swift +++ b/MLXServer/Views/SettingsView.swift @@ -9,7 +9,6 @@ struct SettingsView: View { @State private var apiAutoStart: Bool = Preferences.apiAutoStart @State private var idleUnloadMinutes: String = String(Preferences.idleUnloadMinutes) @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 kvQuantizationBits: Int = Preferences.kvQuantizationBits @@ -43,20 +42,6 @@ struct SettingsView: View { .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") { TextEditor(text: $systemPrompt) .font(.body.monospaced()) @@ -170,16 +155,6 @@ struct SettingsView: View { if !modelManager.availableModels.contains(where: { $0.id == defaultModelId }) { defaultModelId = modelManager.availableModels.first?.id ?? ModelConfig.default.id } - if !modelManager.availableModels.contains(where: { $0.id == generationDefaultsModelId }) { - generationDefaultsModelId = defaultModelId - } } } - - private var generationDefaultsBinding: Binding { - Binding( - get: { Preferences.generationSettings(forModelId: generationDefaultsModelId) }, - set: { Preferences.setGenerationSettings($0, forModelId: generationDefaultsModelId) } - ) - } }