From 30e0456ece106c196056744ac0b58085c013d031 Mon Sep 17 00:00:00 2001 From: alexk2037 <87374775+alexk2037@users.noreply.github.com> Date: Thu, 26 Aug 2021 11:34:57 -0400 Subject: [PATCH 1/2] Create asr_server_pf.py pf = profanity filter --- websocket/asr_server_pf.py | 138 +++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 websocket/asr_server_pf.py diff --git a/websocket/asr_server_pf.py b/websocket/asr_server_pf.py new file mode 100644 index 0000000..c731852 --- /dev/null +++ b/websocket/asr_server_pf.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python3 + +import json +import os +import sys +import asyncio +import pathlib +import websockets +import concurrent.futures +import logging +from vosk import Model, KaldiRecognizer +from profanity_filter import ProfanityFilter +from profanity_check import predict + +def process_chunk(rec, message): + if message == '{"eof" : 1}': + return rec.FinalResult(), True + elif rec.AcceptWaveform(message): + return rec.Result(), False + else: + return rec.PartialResult(), False + +async def recognize(websocket, path, should_filter_profanity = True): + global model + global args + global loop + global pool + + rec = None + phrase_list = None + sample_rate = args.sample_rate + show_words = args.show_words + max_alternatives = args.max_alternatives + profanity_filter = None + + logging.info('Connection from %s', websocket.remote_address); + + + while True: + + message = await websocket.recv() + + # Load configuration if provided + if isinstance(message, str) and 'config' in message: + jobj = json.loads(message)['config'] + logging.info("Config %s", jobj) + if 'phrase_list' in jobj: + phrase_list = jobj['phrase_list'] + if 'sample_rate' in jobj: + sample_rate = float(jobj['sample_rate']) + if 'words' in jobj: + show_words = bool(jobj['words']) + if 'max_alternatives' in jobj: + max_alternatives = int(jobj['max_alternatives']) + continue + + # Create the recognizer, word list is temporary disabled since not every model supports it + if not rec: + if phrase_list: + rec = KaldiRecognizer(model, sample_rate, json.dumps(phrase_list, ensure_ascii=False)) + else: + rec = KaldiRecognizer(model, sample_rate) + rec.SetWords(show_words) + rec.SetMaxAlternatives(max_alternatives) + + response, stop = await loop.run_in_executor(pool, process_chunk, rec, message) + + if should_filter_profanity: + py_json_response = json.loads(response) + if profanity_filter is None: + profanity_filter = ProfanityFilter() + py_json_response = filter_profanity(py_json_response, profanity_filter) + response = json.dumps(py_json_response) + + await websocket.send(response) + if stop: break + +def filter_profanity(response: dict, pf: ProfanityFilter): + if "partial" in response: + text_type = "partial" + elif "text" in response: + text_type = "text" + transcript = response[text_type] + has_profanity = predict([transcript])[0] + #logging.info("Transcript is profane? %s", (transcript, has_profanity)) + if has_profanity: + censored_transcript = pf.censor(transcript) + response[text_type] = censored_transcript + return response + +def start(): + + global model + global args + global loop + global pool + + # Enable loging if needed + # + # logger = logging.getLogger('websockets') + # logger.setLevel(logging.INFO) + # logger.addHandler(logging.StreamHandler()) + logging.basicConfig(level=logging.INFO) + + args = type('', (), {})() + + args.interface = os.environ.get('VOSK_SERVER_INTERFACE', '0.0.0.0') + args.port = int(os.environ.get('VOSK_SERVER_PORT', 2700)) + args.model_path = os.environ.get('VOSK_MODEL_PATH', 'model') + args.sample_rate = float(os.environ.get('VOSK_SAMPLE_RATE', 8000)) + args.max_alternatives = int(os.environ.get('VOSK_ALTERNATIVES', 0)) + args.show_words = bool(os.environ.get('VOSK_SHOW_WORDS', True)) + + if len(sys.argv) > 1: + args.model_path = sys.argv[1] + + # Gpu part, uncomment if vosk-api has gpu support + # + # from vosk import GpuInit, GpuInstantiate + # GpuInit() + # def thread_init(): + # GpuInstantiate() + # pool = concurrent.futures.ThreadPoolExecutor(initializer=thread_init) + + model = Model(args.model_path) + pool = concurrent.futures.ThreadPoolExecutor((os.cpu_count() or 1)) + loop = asyncio.get_event_loop() + + start_server = websockets.serve( + recognize, args.interface, args.port) + + logging.info("Listening on %s:%d", args.interface, args.port) + loop.run_until_complete(start_server) + loop.run_forever() + + +if __name__ == '__main__': + start() From 4cfbd3a10c81eb6de15766d04a9f89195cd59163 Mon Sep 17 00:00:00 2001 From: Alex Kappen Date: Fri, 27 Aug 2021 12:51:15 +0000 Subject: [PATCH 2/2] Add profanity filter --- websocket/asr_server.py | 9 ++- websocket/asr_server_filter.py | 28 +++++++ websocket/asr_server_pf.py | 138 --------------------------------- 3 files changed, 36 insertions(+), 139 deletions(-) create mode 100644 websocket/asr_server_filter.py delete mode 100644 websocket/asr_server_pf.py diff --git a/websocket/asr_server.py b/websocket/asr_server.py index d4a8410..578d748 100755 --- a/websocket/asr_server.py +++ b/websocket/asr_server.py @@ -9,6 +9,7 @@ import concurrent.futures import logging from vosk import Model, SpkModel, KaldiRecognizer +from asr_server_filter import Filter def process_chunk(rec, message): if message == '{"eof" : 1}': @@ -30,6 +31,8 @@ async def recognize(websocket, path): sample_rate = args.sample_rate show_words = args.show_words max_alternatives = args.max_alternatives + apply_filter = args.apply_filter + p_filter = None if not apply_filter else Filter() logging.info('Connection from %s', websocket.remote_address); @@ -63,11 +66,14 @@ async def recognize(websocket, path): rec.SetSpkModel(spk_model) response, stop = await loop.run_in_executor(pool, process_chunk, rec, message) + + if apply_filter: + response = p_filter.filter(response) + await websocket.send(response) if stop: break - def start(): global model @@ -92,6 +98,7 @@ def start(): args.sample_rate = float(os.environ.get('VOSK_SAMPLE_RATE', 8000)) args.max_alternatives = int(os.environ.get('VOSK_ALTERNATIVES', 0)) args.show_words = bool(os.environ.get('VOSK_SHOW_WORDS', True)) + args.apply_filter = bool(os.environ.get('VOSK_FILTER', True)) if len(sys.argv) > 1: args.model_path = sys.argv[1] diff --git a/websocket/asr_server_filter.py b/websocket/asr_server_filter.py new file mode 100644 index 0000000..aabf68a --- /dev/null +++ b/websocket/asr_server_filter.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 + +import json +#import logging +from profanity_filter import ProfanityFilter +from profanity_check import predict + +class Filter: + + def __init__(self): + self.pf = ProfanityFilter() + + def filter(self, response: str): + py_json_response = self.apply_filter(json.loads(response)) + return json.dumps(py_json_response) + + def apply_filter(self, response: dict): + if "partial" in response: + text_type = "partial" + elif "text" in response: + text_type = "text" + transcript = response[text_type] + has_profanity = predict([transcript])[0] + #logging.info("Transcript is profane? %s", (transcript, has_profanity)) + if has_profanity: + censored_transcript = self.pf.censor(transcript) + response[text_type] = censored_transcript + return response diff --git a/websocket/asr_server_pf.py b/websocket/asr_server_pf.py deleted file mode 100644 index c731852..0000000 --- a/websocket/asr_server_pf.py +++ /dev/null @@ -1,138 +0,0 @@ -#!/usr/bin/env python3 - -import json -import os -import sys -import asyncio -import pathlib -import websockets -import concurrent.futures -import logging -from vosk import Model, KaldiRecognizer -from profanity_filter import ProfanityFilter -from profanity_check import predict - -def process_chunk(rec, message): - if message == '{"eof" : 1}': - return rec.FinalResult(), True - elif rec.AcceptWaveform(message): - return rec.Result(), False - else: - return rec.PartialResult(), False - -async def recognize(websocket, path, should_filter_profanity = True): - global model - global args - global loop - global pool - - rec = None - phrase_list = None - sample_rate = args.sample_rate - show_words = args.show_words - max_alternatives = args.max_alternatives - profanity_filter = None - - logging.info('Connection from %s', websocket.remote_address); - - - while True: - - message = await websocket.recv() - - # Load configuration if provided - if isinstance(message, str) and 'config' in message: - jobj = json.loads(message)['config'] - logging.info("Config %s", jobj) - if 'phrase_list' in jobj: - phrase_list = jobj['phrase_list'] - if 'sample_rate' in jobj: - sample_rate = float(jobj['sample_rate']) - if 'words' in jobj: - show_words = bool(jobj['words']) - if 'max_alternatives' in jobj: - max_alternatives = int(jobj['max_alternatives']) - continue - - # Create the recognizer, word list is temporary disabled since not every model supports it - if not rec: - if phrase_list: - rec = KaldiRecognizer(model, sample_rate, json.dumps(phrase_list, ensure_ascii=False)) - else: - rec = KaldiRecognizer(model, sample_rate) - rec.SetWords(show_words) - rec.SetMaxAlternatives(max_alternatives) - - response, stop = await loop.run_in_executor(pool, process_chunk, rec, message) - - if should_filter_profanity: - py_json_response = json.loads(response) - if profanity_filter is None: - profanity_filter = ProfanityFilter() - py_json_response = filter_profanity(py_json_response, profanity_filter) - response = json.dumps(py_json_response) - - await websocket.send(response) - if stop: break - -def filter_profanity(response: dict, pf: ProfanityFilter): - if "partial" in response: - text_type = "partial" - elif "text" in response: - text_type = "text" - transcript = response[text_type] - has_profanity = predict([transcript])[0] - #logging.info("Transcript is profane? %s", (transcript, has_profanity)) - if has_profanity: - censored_transcript = pf.censor(transcript) - response[text_type] = censored_transcript - return response - -def start(): - - global model - global args - global loop - global pool - - # Enable loging if needed - # - # logger = logging.getLogger('websockets') - # logger.setLevel(logging.INFO) - # logger.addHandler(logging.StreamHandler()) - logging.basicConfig(level=logging.INFO) - - args = type('', (), {})() - - args.interface = os.environ.get('VOSK_SERVER_INTERFACE', '0.0.0.0') - args.port = int(os.environ.get('VOSK_SERVER_PORT', 2700)) - args.model_path = os.environ.get('VOSK_MODEL_PATH', 'model') - args.sample_rate = float(os.environ.get('VOSK_SAMPLE_RATE', 8000)) - args.max_alternatives = int(os.environ.get('VOSK_ALTERNATIVES', 0)) - args.show_words = bool(os.environ.get('VOSK_SHOW_WORDS', True)) - - if len(sys.argv) > 1: - args.model_path = sys.argv[1] - - # Gpu part, uncomment if vosk-api has gpu support - # - # from vosk import GpuInit, GpuInstantiate - # GpuInit() - # def thread_init(): - # GpuInstantiate() - # pool = concurrent.futures.ThreadPoolExecutor(initializer=thread_init) - - model = Model(args.model_path) - pool = concurrent.futures.ThreadPoolExecutor((os.cpu_count() or 1)) - loop = asyncio.get_event_loop() - - start_server = websockets.serve( - recognize, args.interface, args.port) - - logging.info("Listening on %s:%d", args.interface, args.port) - loop.run_until_complete(start_server) - loop.run_forever() - - -if __name__ == '__main__': - start()