fix: improve word replacement precision and prompt handling - Enhanced word replacement logic, added examples, fixed prompt state management
This commit is contained in:
parent
763967bc35
commit
3aec3b369b
@ -29,6 +29,9 @@ actor WhisperContext {
|
||||
func fullTranscribe(samples: [Float]) {
|
||||
guard let context = context else { return }
|
||||
|
||||
// Capture prompt state at start
|
||||
let currentPrompt = prompt
|
||||
|
||||
// Leave 2 processors free (i.e. the high-efficiency cores).
|
||||
let maxThreads = max(1, min(8, cpuCount() - 2))
|
||||
print("Selecting \(maxThreads) threads")
|
||||
@ -49,12 +52,12 @@ actor WhisperContext {
|
||||
}
|
||||
|
||||
// Only use prompt for English language
|
||||
if selectedLanguage == "en" && prompt != nil {
|
||||
promptCString = Array(prompt!.utf8CString)
|
||||
if selectedLanguage == "en" && currentPrompt != nil {
|
||||
promptCString = Array(currentPrompt!.utf8CString)
|
||||
params.initial_prompt = promptCString?.withUnsafeBufferPointer { ptr in
|
||||
ptr.baseAddress
|
||||
}
|
||||
print("Using prompt for English transcription: \(prompt!)")
|
||||
print("Using prompt for English transcription: \(currentPrompt!)")
|
||||
} else {
|
||||
promptCString = nil
|
||||
params.initial_prompt = nil
|
||||
|
||||
@ -69,7 +69,8 @@ enum AIPrompts {
|
||||
IMPORTANT: Only apply replacements if specific words are provided
|
||||
- Skip any replacement activity if no replacement options are available
|
||||
- When replacements are provided:
|
||||
- Replace specified words or phrases exactly as provided
|
||||
- Replace ONLY exact matches of the specified words/phrases
|
||||
- Do NOT replace partial matches or similar words
|
||||
- Apply replacements before other enhancements
|
||||
- Maintain case sensitivity when applying replacements
|
||||
- Preserve the flow and readability of the text
|
||||
|
||||
@ -2,6 +2,7 @@ import SwiftUI
|
||||
|
||||
struct DictionarySettingsView: View {
|
||||
@State private var selectedSection: DictionarySection = .spellings
|
||||
@EnvironmentObject private var whisperState: WhisperState
|
||||
|
||||
enum DictionarySection: String, CaseIterable {
|
||||
case spellings = "Correct Spellings"
|
||||
@ -93,7 +94,7 @@ struct DictionarySettingsView: View {
|
||||
VStack(alignment: .leading, spacing: 20) {
|
||||
switch selectedSection {
|
||||
case .spellings:
|
||||
DictionaryView()
|
||||
DictionaryView(whisperState: whisperState)
|
||||
.background(Color(.windowBackgroundColor).opacity(0.4))
|
||||
.cornerRadius(10)
|
||||
case .replacements:
|
||||
|
||||
@ -33,9 +33,10 @@ struct DictionaryItem: Identifiable, Hashable, Codable {
|
||||
class DictionaryManager: ObservableObject {
|
||||
@Published var items: [DictionaryItem] = []
|
||||
private let saveKey = "CustomDictionaryItems"
|
||||
var onDictionaryChanged: (([String]) -> Void)?
|
||||
@Published var whisperState: WhisperState
|
||||
|
||||
init() {
|
||||
init(whisperState: WhisperState) {
|
||||
self.whisperState = whisperState
|
||||
loadItems()
|
||||
}
|
||||
|
||||
@ -53,22 +54,20 @@ class DictionaryManager: ObservableObject {
|
||||
saveItems()
|
||||
}
|
||||
}
|
||||
notifyDictionaryChanged()
|
||||
Task { @MainActor in
|
||||
await whisperState.saveDictionaryItems(items)
|
||||
}
|
||||
}
|
||||
|
||||
private func saveItems() {
|
||||
if let encoded = try? JSONEncoder().encode(items) {
|
||||
UserDefaults.standard.set(encoded, forKey: saveKey)
|
||||
notifyDictionaryChanged()
|
||||
Task { @MainActor in
|
||||
await whisperState.saveDictionaryItems(items)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func notifyDictionaryChanged() {
|
||||
// Only include enabled words in the dictionary
|
||||
let enabledWords = items.filter { $0.isEnabled }.map { $0.word }
|
||||
onDictionaryChanged?(enabledWords)
|
||||
}
|
||||
|
||||
func addWord(_ word: String) {
|
||||
let normalizedWord = word.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !items.contains(where: { $0.word.lowercased() == normalizedWord.lowercased() }) else {
|
||||
@ -98,12 +97,16 @@ class DictionaryManager: ObservableObject {
|
||||
}
|
||||
|
||||
struct DictionaryView: View {
|
||||
@StateObject private var dictionaryManager = DictionaryManager()
|
||||
@StateObject private var dictionaryManager: DictionaryManager
|
||||
@EnvironmentObject private var whisperState: WhisperState
|
||||
@State private var newWord = ""
|
||||
@State private var showAlert = false
|
||||
@State private var alertMessage = ""
|
||||
|
||||
init(whisperState: WhisperState) {
|
||||
_dictionaryManager = StateObject(wrappedValue: DictionaryManager(whisperState: whisperState))
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 20) {
|
||||
// Information Section
|
||||
@ -177,10 +180,6 @@ struct DictionaryView: View {
|
||||
Text(alertMessage)
|
||||
}
|
||||
.onAppear {
|
||||
dictionaryManager.onDictionaryChanged = { words in
|
||||
whisperState.updateDictionaryWords(words)
|
||||
}
|
||||
// Initial update
|
||||
whisperState.updateDictionaryWords(dictionaryManager.allWords)
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,6 +89,7 @@ class WhisperState: NSObject, ObservableObject, AVAudioRecorderDelegate {
|
||||
private let recorder = Recorder()
|
||||
private var recordedFile: URL? = nil
|
||||
private var dictionaryWords: [String] = []
|
||||
private let saveKey = "CustomDictionaryItems"
|
||||
|
||||
let modelContext: ModelContext
|
||||
|
||||
@ -150,6 +151,7 @@ class WhisperState: NSObject, ObservableObject, AVAudioRecorderDelegate {
|
||||
createModelsDirectoryIfNeeded()
|
||||
createRecordingsDirectoryIfNeeded()
|
||||
loadAvailableModels()
|
||||
loadDictionaryItems()
|
||||
|
||||
// Load saved model
|
||||
if let savedModelName = UserDefaults.standard.string(forKey: "CurrentModel"),
|
||||
@ -193,6 +195,17 @@ class WhisperState: NSObject, ObservableObject, AVAudioRecorderDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
private func loadDictionaryItems() {
|
||||
guard let data = UserDefaults.standard.data(forKey: saveKey) else { return }
|
||||
|
||||
// Try loading with new format first
|
||||
if let savedItems = try? JSONDecoder().decode([DictionaryItem].self, from: data) {
|
||||
let enabledWords = savedItems.filter { $0.isEnabled }.map { $0.word }
|
||||
dictionaryWords = enabledWords
|
||||
updateTranscriptionPrompt()
|
||||
}
|
||||
}
|
||||
|
||||
// Modify loadModel to be private and async
|
||||
private func loadModel(_ model: WhisperModel) async throws {
|
||||
guard whisperContext == nil else { return } // Model already loaded
|
||||
@ -828,6 +841,15 @@ class WhisperState: NSObject, ObservableObject, AVAudioRecorderDelegate {
|
||||
|
||||
return permanentURL
|
||||
}
|
||||
|
||||
func saveDictionaryItems(_ items: [DictionaryItem]) async {
|
||||
if let encoded = try? JSONEncoder().encode(items) {
|
||||
UserDefaults.standard.set(encoded, forKey: saveKey)
|
||||
let enabledWords = items.filter { $0.isEnabled }.map { $0.word }
|
||||
dictionaryWords = enabledWords
|
||||
updateTranscriptionPrompt()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct WhisperModel: Identifiable {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user