From 63db44935550f1745e61aefb9415903c0654a8ce Mon Sep 17 00:00:00 2001 From: Beingpax Date: Sun, 28 Sep 2025 15:18:58 +0545 Subject: [PATCH] Added switch for using default system instructions for custom prompt. --- VoiceInk/Models/CustomPrompt.swift | 30 +++++++++++++++++++- VoiceInk/Models/PredefinedPrompts.swift | 6 ++-- VoiceInk/Services/AIEnhancementService.swift | 13 +++++---- VoiceInk/Views/PromptEditorView.swift | 21 ++++++++++++-- 4 files changed, 59 insertions(+), 11 deletions(-) diff --git a/VoiceInk/Models/CustomPrompt.swift b/VoiceInk/Models/CustomPrompt.swift index 36203d6..fb9edd5 100644 --- a/VoiceInk/Models/CustomPrompt.swift +++ b/VoiceInk/Models/CustomPrompt.swift @@ -84,6 +84,7 @@ struct CustomPrompt: Identifiable, Codable, Equatable { let description: String? let isPredefined: Bool let triggerWords: [String] + let useSystemInstructions: Bool init( id: UUID = UUID(), @@ -93,7 +94,8 @@ struct CustomPrompt: Identifiable, Codable, Equatable { icon: PromptIcon = .documentFill, description: String? = nil, isPredefined: Bool = false, - triggerWords: [String] = [] + triggerWords: [String] = [], + useSystemInstructions: Bool = true ) { self.id = id self.title = title @@ -103,6 +105,32 @@ struct CustomPrompt: Identifiable, Codable, Equatable { self.description = description self.isPredefined = isPredefined self.triggerWords = triggerWords + self.useSystemInstructions = useSystemInstructions + } + + enum CodingKeys: String, CodingKey { + case id, title, promptText, isActive, icon, description, isPredefined, triggerWords, useSystemInstructions + } + + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + id = try container.decode(UUID.self, forKey: .id) + title = try container.decode(String.self, forKey: .title) + promptText = try container.decode(String.self, forKey: .promptText) + isActive = try container.decode(Bool.self, forKey: .isActive) + icon = try container.decode(PromptIcon.self, forKey: .icon) + description = try container.decodeIfPresent(String.self, forKey: .description) + isPredefined = try container.decode(Bool.self, forKey: .isPredefined) + triggerWords = try container.decode([String].self, forKey: .triggerWords) + useSystemInstructions = try container.decodeIfPresent(Bool.self, forKey: .useSystemInstructions) ?? true + } + + var finalPromptText: String { + if useSystemInstructions { + return String(format: AIPrompts.customPromptTemplate, self.promptText) + } else { + return self.promptText + } } } diff --git a/VoiceInk/Models/PredefinedPrompts.swift b/VoiceInk/Models/PredefinedPrompts.swift index f5d5c03..69a86a2 100644 --- a/VoiceInk/Models/PredefinedPrompts.swift +++ b/VoiceInk/Models/PredefinedPrompts.swift @@ -21,7 +21,8 @@ enum PredefinedPrompts { promptText: PromptTemplates.all.first { $0.title == "System Default" }?.promptText ?? "", icon: .sealedFill, description: "Default mode to improved clarity and accuracy of the transcription", - isPredefined: true + isPredefined: true, + useSystemInstructions: true ), CustomPrompt( @@ -30,7 +31,8 @@ enum PredefinedPrompts { promptText: AIPrompts.assistantMode, icon: .chatFill, description: "AI assistant that provides direct answers to queries", - isPredefined: true + isPredefined: true, + useSystemInstructions: false ) ] } diff --git a/VoiceInk/Services/AIEnhancementService.swift b/VoiceInk/Services/AIEnhancementService.swift index bbab7f1..a616664 100644 --- a/VoiceInk/Services/AIEnhancementService.swift +++ b/VoiceInk/Services/AIEnhancementService.swift @@ -186,7 +186,7 @@ class AIEnhancementService: ObservableObject { guard let activePrompt = activePrompt else { if let defaultPrompt = allPrompts.first(where: { $0.id == PredefinedPrompts.defaultPromptId }) { - var systemMessage = String(format: AIPrompts.customPromptTemplate, defaultPrompt.promptText) + var systemMessage = defaultPrompt.finalPromptText systemMessage += generalContextSection + dictionaryContextSection return systemMessage } @@ -196,8 +196,8 @@ class AIEnhancementService: ObservableObject { if activePrompt.id == PredefinedPrompts.assistantPromptId { return activePrompt.promptText + generalContextSection + dictionaryContextSection } - - var systemMessage = String(format: AIPrompts.customPromptTemplate, activePrompt.promptText) + + var systemMessage = activePrompt.finalPromptText systemMessage += generalContextSection + dictionaryContextSection return systemMessage } @@ -422,8 +422,8 @@ class AIEnhancementService: ObservableObject { } } - func addPrompt(title: String, promptText: String, icon: PromptIcon = .documentFill, description: String? = nil, triggerWords: [String] = []) { - let newPrompt = CustomPrompt(title: title, promptText: promptText, icon: icon, description: description, isPredefined: false, triggerWords: triggerWords) + func addPrompt(title: String, promptText: String, icon: PromptIcon = .documentFill, description: String? = nil, triggerWords: [String] = [], useSystemInstructions: Bool = true) { + let newPrompt = CustomPrompt(title: title, promptText: promptText, icon: icon, description: description, isPredefined: false, triggerWords: triggerWords, useSystemInstructions: useSystemInstructions) customPrompts.append(newPrompt) if customPrompts.count == 1 { selectedPromptId = newPrompt.id @@ -461,7 +461,8 @@ class AIEnhancementService: ObservableObject { icon: template.icon, description: template.description, isPredefined: true, - triggerWords: updatedPrompt.triggerWords + triggerWords: updatedPrompt.triggerWords, + useSystemInstructions: template.useSystemInstructions ) customPrompts[existingIndex] = updatedPrompt } else { diff --git a/VoiceInk/Views/PromptEditorView.swift b/VoiceInk/Views/PromptEditorView.swift index 5cbf354..31498fc 100644 --- a/VoiceInk/Views/PromptEditorView.swift +++ b/VoiceInk/Views/PromptEditorView.swift @@ -26,6 +26,7 @@ struct PromptEditorView: View { @State private var description: String @State private var triggerWords: [String] @State private var showingPredefinedPrompts = false + @State private var useSystemInstructions: Bool private var isEditingPredefinedPrompt: Bool { if case .edit(let prompt) = mode { @@ -43,12 +44,14 @@ struct PromptEditorView: View { _selectedIcon = State(initialValue: .documentFill) _description = State(initialValue: "") _triggerWords = State(initialValue: []) + _useSystemInstructions = State(initialValue: true) case .edit(let prompt): _title = State(initialValue: prompt.title) _promptText = State(initialValue: prompt.promptText) _selectedIcon = State(initialValue: prompt.icon) _description = State(initialValue: prompt.description ?? "") _triggerWords = State(initialValue: prompt.triggerWords) + _useSystemInstructions = State(initialValue: prompt.useSystemInstructions) } } @@ -183,6 +186,18 @@ struct PromptEditorView: View { .font(.subheadline) .foregroundColor(.secondary) + if !isEditingPredefinedPrompt { + HStack(spacing: 8) { + Toggle("Use System Instructions", isOn: $useSystemInstructions) + + InfoTip( + title: "System Instructions", + message: "If enabled, your instructions are combined with a general-purpose template to improve transcription quality.\n\nDisable for full control over the AI's system prompt (for advanced users)." + ) + } + .padding(.bottom, 4) + } + TextEditor(text: $promptText) .font(.system(.body, design: .monospaced)) .frame(minHeight: 200) @@ -250,7 +265,8 @@ struct PromptEditorView: View { promptText: promptText, icon: selectedIcon, description: description.isEmpty ? nil : description, - triggerWords: triggerWords + triggerWords: triggerWords, + useSystemInstructions: useSystemInstructions ) case .edit(let prompt): let updatedPrompt = CustomPrompt( @@ -261,7 +277,8 @@ struct PromptEditorView: View { icon: prompt.isPredefined ? prompt.icon : selectedIcon, description: prompt.isPredefined ? prompt.description : (description.isEmpty ? nil : description), isPredefined: prompt.isPredefined, - triggerWords: triggerWords + triggerWords: triggerWords, + useSystemInstructions: useSystemInstructions ) enhancementService.updatePrompt(updatedPrompt) }