ideasman42 / nerd-dictation

Simple, hackable offline speech to text - using the VOSK-API.
GNU General Public License v3.0
1.21k stars 104 forks source link

Reload --vosk-grammar-file JSON on SIGHUP #88

Open KJ7LNW opened 1 year ago

KJ7LNW commented 1 year ago

This commit adds support for reloading the grammar configuration on SIGHUP. This is accomplished by moving the recognizer initialization into its own function so that the signal handler can call it on reload.

A reload of the recognizer is fast because it does not reload the model and only applies the grammar.

Signed-off-by: Eric Wheeler vosk-git@z.ewheeler.org

KJ7LNW commented 1 year ago

fixed merge conflict

KJ7LNW commented 1 year ago

Fixed mypy --strict with return type -> None.

BTW, the diff shown by github isn't pretty because of the indented code (whitespace), but the logic change is small:

Here is a diff ignoring whitespace:

# git show -w HEAD
diff --git a/nerd-dictation b/nerd-dictation
index e5d8196..c4adc49 100755
--- a/nerd-dictation
+++ b/nerd-dictation
@@ -944,22 +944,30 @@ def text_from_vosk_pipe(

     vosk.SetLogLevel(-1)

+    # Allow for loading the model to take some time:
+    if verbose >= 1:
+        sys.stderr.write("Loading model...\n")
+    model = vosk.Model(vosk_model_dir)
+
+    rec = None
+
+    def vosk_load_recognizer() -> None:
+        nonlocal vosk_grammar_file
+        nonlocal rec
+
         if not vosk_grammar_file:
             grammar_json = ""
         else:
             with open(vosk_grammar_file, encoding="utf-8") as fh:
                 grammar_json = fh.read()

-    # Allow for loading the model to take some time:
-    if verbose >= 1:
-        sys.stderr.write("Loading model...\n")
-    model = vosk.Model(vosk_model_dir)
-
         if grammar_json == "":
             rec = vosk.KaldiRecognizer(model, sample_rate)
         else:
             rec = vosk.KaldiRecognizer(model, sample_rate, grammar_json)

+    vosk_load_recognizer()
+
     if verbose >= 1:
         sys.stderr.write("Model loaded.\n")

@@ -1135,6 +1143,7 @@ def text_from_vosk_pipe(
         if verbose >= 1:
             sys.stderr.write("Reload.\n")
         process_fn("")
+        vosk_load_recognizer()

     import signal
KJ7LNW commented 1 year ago

@ideasman42,

Just realized I still have mypy issues to work out on this PR, but I'm not sure what to do.

Scoping rec = None outside of the new vosk_load_recognizer() function causes mypy to complain as follows because None is not properly typed:

nerd-dictation:1052: error: Item "None" of "Optional[Any]" has no attribute "FinalResult"
nerd-dictation:1069: error: Item "None" of "Optional[Any]" has no attribute "PartialResult"
nerd-dictation:1100: error: Item "None" of "Optional[Any]" has no attribute "Reset"
nerd-dictation:1208: error: Item "None" of "Optional[Any]" has no attribute "AcceptWaveform"

I tried a few things but I'm not very familiar with Python's typing. For example, changing rec = None to rec = vosk.KaldiRecognizer(model, sample_rate) and then still calling vosk_load_recognizer() to load the JSON grammar "fixes" the typing issue, but the extra call to vosk.KaldiRecognizer() seems unnecessary.

Do you know what should be done here?

ideasman42 commented 1 year ago

In this case mypy needs to know that it's not None.

KJ7LNW commented 1 year ago

The only reason I set rec = None is to scope outside of the vosk_load_recognizer() function.

Questions: