diff options
Diffstat (limited to 'agl_service_voiceagent')
-rw-r--r-- | agl_service_voiceagent/client.py | 31 | ||||
-rw-r--r-- | agl_service_voiceagent/config.ini | 12 | ||||
-rw-r--r-- | agl_service_voiceagent/nlu/rasa_interface.py | 2 | ||||
-rw-r--r-- | agl_service_voiceagent/server.py | 6 | ||||
-rw-r--r-- | agl_service_voiceagent/service.py | 70 | ||||
-rw-r--r-- | agl_service_voiceagent/servicers/voice_agent_servicer.py | 50 | ||||
-rw-r--r-- | agl_service_voiceagent/utils/config.py | 30 | ||||
-rw-r--r-- | agl_service_voiceagent/utils/kuksa_interface.py | 35 | ||||
-rw-r--r-- | agl_service_voiceagent/utils/mapper.py | 10 |
9 files changed, 184 insertions, 62 deletions
diff --git a/agl_service_voiceagent/client.py b/agl_service_voiceagent/client.py index 922e08c..12804e1 100644 --- a/agl_service_voiceagent/client.py +++ b/agl_service_voiceagent/client.py @@ -18,11 +18,10 @@ import time import grpc from agl_service_voiceagent.generated import voice_agent_pb2 from agl_service_voiceagent.generated import voice_agent_pb2_grpc -from agl_service_voiceagent.utils.config import get_config_value -def run_client(mode, nlu_model): - SERVER_URL = get_config_value('SERVER_ADDRESS') + ":" + str(get_config_value('SERVER_PORT')) - nlu_model = voice_agent_pb2.SNIPS if nlu_model == "snips" else voice_agent_pb2.RASA +def run_client(server_address, server_port, mode, nlu_engine, recording_time): + SERVER_URL = server_address + ":" + server_port + nlu_engine = voice_agent_pb2.RASA if nlu_engine == "rasa" else voice_agent_pb2.SNIPS print("Starting Voice Agent Client...") print(f"Client connecting to URL: {SERVER_URL}") with grpc.insecure_channel(SERVER_URL) as channel: @@ -30,7 +29,7 @@ def run_client(mode, nlu_model): print("Voice Agent Client started!") if mode == 'wake-word': stub = voice_agent_pb2_grpc.VoiceAgentServiceStub(channel) - print("Listening for wake word...") + print("[+] Listening for wake word...") wake_request = voice_agent_pb2.Empty() wake_results = stub.DetectWakeWord(wake_request) wake_word_detected = False @@ -42,18 +41,20 @@ def run_client(mode, nlu_model): break elif mode == 'auto': - raise ValueError("Auto mode is not implemented yet.") + raise ValueError("[-] Auto mode is not implemented yet.") elif mode == 'manual': stub = voice_agent_pb2_grpc.VoiceAgentServiceStub(channel) - print("Recording voice command...") - record_start_request = voice_agent_pb2.RecognizeControl(action=voice_agent_pb2.START, nlu_model=nlu_model, record_mode=voice_agent_pb2.MANUAL) + print("[+] Recording voice command in manual mode...") + record_start_request = voice_agent_pb2.RecognizeControl(action=voice_agent_pb2.START, nlu_model=nlu_engine, record_mode=voice_agent_pb2.MANUAL) response = stub.RecognizeVoiceCommand(iter([record_start_request])) stream_id = response.stream_id - time.sleep(5) # any arbitrary pause here - record_stop_request = voice_agent_pb2.RecognizeControl(action=voice_agent_pb2.STOP, nlu_model=nlu_model, record_mode=voice_agent_pb2.MANUAL, stream_id=stream_id) + + time.sleep(recording_time) # pause here for the number of seconds passed by user or default 5 seconds + + record_stop_request = voice_agent_pb2.RecognizeControl(action=voice_agent_pb2.STOP, nlu_model=nlu_engine, record_mode=voice_agent_pb2.MANUAL, stream_id=stream_id) record_result = stub.RecognizeVoiceCommand(iter([record_stop_request])) - print("Voice command recorded!") + print("[+] Voice command recording ended!") status = "Uh oh! Status is unknown." if record_result.status == voice_agent_pb2.REC_SUCCESS: @@ -64,8 +65,8 @@ def run_client(mode, nlu_model): status = "Intent not recognized." # Process the response - print("Command:", record_result.command) print("Status:", status) + print("Command:", record_result.command) print("Intent:", record_result.intent) intent_slots = [] for slot in record_result.intent_slots: @@ -74,5 +75,7 @@ def run_client(mode, nlu_model): i_slot = voice_agent_pb2.IntentSlot(name=slot.name, value=slot.value) intent_slots.append(i_slot) - exec_voice_command_request = voice_agent_pb2.ExecuteInput(intent=record_result.intent, intent_slots=intent_slots) - response = stub.ExecuteVoiceCommand(exec_voice_command_request)
\ No newline at end of file + if record_result.status == voice_agent_pb2.REC_SUCCESS: + print("[+] Executing voice command...") + exec_voice_command_request = voice_agent_pb2.ExecuteInput(intent=record_result.intent, intent_slots=intent_slots) + response = stub.ExecuteVoiceCommand(exec_voice_command_request)
\ No newline at end of file diff --git a/agl_service_voiceagent/config.ini b/agl_service_voiceagent/config.ini index 074f6a8..81d4e69 100644 --- a/agl_service_voiceagent/config.ini +++ b/agl_service_voiceagent/config.ini @@ -1,17 +1,17 @@ [General] base_audio_dir = /usr/share/nlu/commands/ -stt_model_path = /usr/share/vosk/vosk-model-small-en-us-0.15/ -wake_word_model_path = /usr/share/vosk/vosk-model-small-en-us-0.15/ +stt_model_path = /usr/share/vosk/VOSK_STT_MODEL_NAME/ +wake_word_model_path = /usr/share/vosk/VOSK_WWD_MODEL_NAME/ snips_model_path = /usr/share/nlu/snips/model/ channels = 1 sample_rate = 16000 bits_per_sample = 16 -wake_word = hello auto +wake_word = WAKE_WORD_VALUE server_port = 51053 server_address = 127.0.0.1 rasa_model_path = /usr/share/nlu/rasa/models/ rasa_server_port = 51054 -rasa_detached_mode = 0 +rasa_detached_mode = 1 base_log_dir = /usr/share/nlu/logs/ store_voice_commands = 0 @@ -20,8 +20,8 @@ ip = 127.0.0.1 port = 8090 protocol = ws insecure = True -token = /usr/lib/python3.10/site-packages/kuksa_certificates/jwt/super-admin.json.token +token = PYTHON_DIR/kuksa_certificates/jwt/super-admin.json.token [Mapper] intents_vss_map = /usr/share/nlu/mappings/intents_vss_map.json -vss_signals_spec = /usr/share/nlu/mappings/vss_signals_spec.json
\ No newline at end of file +vss_signals_spec = /usr/share/nlu/mappings/vss_signals_spec.json diff --git a/agl_service_voiceagent/nlu/rasa_interface.py b/agl_service_voiceagent/nlu/rasa_interface.py index 537a318..350bddf 100644 --- a/agl_service_voiceagent/nlu/rasa_interface.py +++ b/agl_service_voiceagent/nlu/rasa_interface.py @@ -40,7 +40,7 @@ class RASAInterface: self.max_threads = max_threads self.server_process = None self.thread_pool = ThreadPoolExecutor(max_workers=max_threads) - self.log_file = log_dir+"rasa_server_logs.txt" + self.log_file = log_dir+"rasa_server.log" def _start_server(self): diff --git a/agl_service_voiceagent/server.py b/agl_service_voiceagent/server.py index d8ce785..aa107dc 100644 --- a/agl_service_voiceagent/server.py +++ b/agl_service_voiceagent/server.py @@ -18,12 +18,12 @@ import grpc from concurrent import futures from agl_service_voiceagent.generated import voice_agent_pb2_grpc from agl_service_voiceagent.servicers.voice_agent_servicer import VoiceAgentServicer -from agl_service_voiceagent.utils.config import get_config_value +from agl_service_voiceagent.utils.config import get_config_value, get_logger def run_server(): + logger = get_logger() SERVER_URL = get_config_value('SERVER_ADDRESS') + ":" + str(get_config_value('SERVER_PORT')) print("Starting Voice Agent Service...") - print(f"Server running at URL: {SERVER_URL}") print(f"STT Model Path: {get_config_value('STT_MODEL_PATH')}") print(f"Audio Store Directory: {get_config_value('BASE_AUDIO_DIR')}") server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) @@ -31,5 +31,7 @@ def run_server(): server.add_insecure_port(SERVER_URL) print("Press Ctrl+C to stop the server.") print("Voice Agent Server started!") + print(f"Server running at URL: {SERVER_URL}") + logger.info(f"Voice Agent Service started in server mode! Server running at URL: {SERVER_URL}") server.start() server.wait_for_termination()
\ No newline at end of file diff --git a/agl_service_voiceagent/service.py b/agl_service_voiceagent/service.py index 784d8d9..9682b56 100644 --- a/agl_service_voiceagent/service.py +++ b/agl_service_voiceagent/service.py @@ -25,15 +25,14 @@ generated_dir = os.path.join(current_dir, "generated") sys.path.append(generated_dir) import argparse -from agl_service_voiceagent.utils.config import set_config_path, load_config, update_config_value, get_config_value +from agl_service_voiceagent.utils.config import set_config_path, load_config, update_config_value, get_config_value, get_logger from agl_service_voiceagent.utils.common import add_trailing_slash from agl_service_voiceagent.server import run_server from agl_service_voiceagent.client import run_client - def print_version(): print("Automotive Grade Linux (AGL)") - print(f"Voice Agent Service v0.3.0") + print(f"Voice Agent Service v0.4.0") def main(): @@ -59,8 +58,11 @@ def main(): server_parser.add_argument('--audio-store-dir', required=False, help='Directory to store the generated audio files.') server_parser.add_argument('--log-store-dir', required=False, help='Directory to store the generated log files.') + client_parser.add_argument('--server-address', required=True, help='Address of the gRPC server running the Voice Agent Service.') + client_parser.add_argument('--server-port', required=True, help='Port of the gRPC server running the Voice Agent Service.') client_parser.add_argument('--mode', required=True, help='Mode to run the client in. Supported modes: "wake-word", "auto" and "manual".') - client_parser.add_argument('--nlu', required=True, help='NLU engine to use. Supported NLU egnines: "snips" and "rasa".') + client_parser.add_argument('--nlu', help='NLU engine to use. Supported NLU egnines: "snips" and "rasa".') + client_parser.add_argument('--recording-time', help='Number of seconds to continue recording the voice command. Required by the \'manual\' mode. Defaults to 10 seconds.') args = parser.parse_args() @@ -70,19 +72,24 @@ def main(): elif args.subcommand == 'run-server': if not args.default and not args.config: if not args.stt_model_path: - raise ValueError("The --stt-model-path is missing. Please provide a value. Use --help to see available options.") + print("Error: The --stt-model-path is missing. Please provide a value. Use --help to see available options.") + exit(1) if not args.snips_model_path: - raise ValueError("The --snips-model-path is missing. Please provide a value. Use --help to see available options.") + print("Error: The --snips-model-path is missing. Please provide a value. Use --help to see available options.") + exit(1) if not args.rasa_model_path: - raise ValueError("The --rasa-model-path is missing. Please provide a value. Use --help to see available options.") + print("Error: The --rasa-model-path is missing. Please provide a value. Use --help to see available options.") + exit(1) if not args.intents_vss_map_path: - raise ValueError("The --intents-vss-map-path is missing. Please provide a value. Use --help to see available options.") + print("Error: The --intents-vss-map-path is missing. Please provide a value. Use --help to see available options.") + exit(1) if not args.vss_signals_spec_path: - raise ValueError("The --vss-signals-spec is missing. Please provide a value. Use --help to see available options.") + print("Error: The --vss-signals-spec-path is missing. Please provide a value. Use --help to see available options.") + exit(1) # Contruct the default config file path config_path = os.path.join(current_dir, "config.ini") @@ -90,6 +97,9 @@ def main(): # Load the config values from the config file set_config_path(config_path) load_config() + + logger = get_logger() + logger.info("Starting Voice Agent Service in server mode using CLI provided params...") # Get the values provided by the user stt_path = args.stt_model_path @@ -135,6 +145,9 @@ def main(): print(f"New config file path provided: {cli_config_path}. Overriding the default config file path.") set_config_path(cli_config_path) load_config() + + logger = get_logger() + logger.info(f"Starting Voice Agent Service in server mode using provided config file at path '{cli_config_path}' ...") elif args.default: # Contruct the default config file path @@ -144,33 +157,40 @@ def main(): set_config_path(config_path) load_config() + logger = get_logger() + logger.info(f"Starting Voice Agent Service in server mode using the default config file...") + # create the base audio dir if not exists if not os.path.exists(get_config_value('BASE_AUDIO_DIR')): os.makedirs(get_config_value('BASE_AUDIO_DIR')) - - # create the base log dir if not exists - if not os.path.exists(get_config_value('BASE_LOG_DIR')): - os.makedirs(get_config_value('BASE_LOG_DIR')) run_server() elif args.subcommand == 'run-client': - # Contruct the default config file path - config_path = os.path.join(current_dir, "config.ini") - - # Load the config values from the config file - set_config_path(config_path) - load_config() - + server_address = args.server_address + server_port = args.server_port + nlu_engine = "" mode = args.mode + recording_time = 5 + if mode not in ['wake-word', 'auto', 'manual']: - raise ValueError("Invalid mode. Supported modes: 'wake-word', 'auto' and 'manual'. Use --help to see available options.") + print("Error: Invalid value for --mode. Supported modes: 'wake-word', 'auto' and 'manual'. Use --help to see available options.") + exit(1) - model = args.nlu - if model not in ['snips', 'rasa']: - raise ValueError("Invalid NLU engine. Supported NLU engines: 'snips' and 'rasa'. Use --help to see available options.") + if mode in ["auto", "manual"]: + if not args.nlu: + print("Error: The --nlu flag is missing. Please provide a value for intent engine. Supported NLU engines: 'snips' and 'rasa'. Use --help to see available options.") + exit(1) + + nlu_engine = args.nlu + if nlu_engine not in ['snips', 'rasa']: + print("Error: Invalid value for --nlu. Supported NLU engines: 'snips' and 'rasa'. Use --help to see available options.") + exit(1) + + if mode == "manual" and args.recording_time: + recording_time = int(args.recording_time) - run_client(mode, model) + run_client(server_address, server_port, mode, nlu_engine, recording_time) else: print_version() diff --git a/agl_service_voiceagent/servicers/voice_agent_servicer.py b/agl_service_voiceagent/servicers/voice_agent_servicer.py index 69af10b..c9b671d 100644 --- a/agl_service_voiceagent/servicers/voice_agent_servicer.py +++ b/agl_service_voiceagent/servicers/voice_agent_servicer.py @@ -24,7 +24,7 @@ from agl_service_voiceagent.utils.wake_word import WakeWordDetector from agl_service_voiceagent.utils.stt_model import STTModel from agl_service_voiceagent.utils.kuksa_interface import KuksaInterface from agl_service_voiceagent.utils.mapper import Intent2VSSMapper -from agl_service_voiceagent.utils.config import get_config_value +from agl_service_voiceagent.utils.config import get_config_value, get_logger from agl_service_voiceagent.utils.common import generate_unique_uuid, delete_file from agl_service_voiceagent.nlu.snips_interface import SnipsInterface from agl_service_voiceagent.nlu.rasa_interface import RASAInterface @@ -40,7 +40,7 @@ class VoiceAgentServicer(voice_agent_pb2_grpc.VoiceAgentServiceServicer): Constructor for VoiceAgentServicer class. """ # Get the config values - self.service_version = "v0.3.0" + self.service_version = "v0.4.0" self.wake_word = get_config_value('WAKE_WORD') self.base_audio_dir = get_config_value('BASE_AUDIO_DIR') self.channels = int(get_config_value('CHANNELS')) @@ -54,28 +54,48 @@ class VoiceAgentServicer(voice_agent_pb2_grpc.VoiceAgentServiceServicer): self.rasa_detached_mode = bool(int(get_config_value('RASA_DETACHED_MODE'))) self.base_log_dir = get_config_value('BASE_LOG_DIR') self.store_voice_command = bool(int(get_config_value('STORE_VOICE_COMMANDS'))) + self.logger = get_logger() # Initialize class methods + self.logger.info("Loading Speech to Text and Wake Word Model...") self.stt_model = STTModel(self.stt_model_path, self.sample_rate) self.stt_wake_word_model = STTModel(self.wake_word_model_path, self.sample_rate) + self.logger.info("Speech to Text and Wake Word Model loaded successfully.") + + self.logger.info("Starting SNIPS intent engine...") self.snips_interface = SnipsInterface(self.snips_model_path) + self.logger.info("SNIPS intent engine started successfully!") + self.rasa_interface = RASAInterface(self.rasa_server_port, self.rasa_model_path, self.base_log_dir) # Only start RASA server if its not in detached mode, else we assume server is already running if not self.rasa_detached_mode: + self.logger.info(f"Starting RASA intent engine server as a subprocess...") self.rasa_interface.start_server() + self.logger.info(f"RASA intent engine server started successfully! RASA server running at URL: 127.0.0.1:{self.rasa_server_port}") + + else: + self.logger.info(f"RASA intent engine detached mode detected! Assuming RASA server is running at URL: 127.0.0.1:{self.rasa_server_port}") self.rvc_stream_uuids = {} self.kuksa_client = KuksaInterface() self.kuksa_client.connect_kuksa_client() self.kuksa_client.authorize_kuksa_client() + + self.logger.info(f"Loading and parsing mapping files...") self.mapper = Intent2VSSMapper() + self.logger.info(f"Successfully loaded and parsed mapping files.") def CheckServiceStatus(self, request, context): """ Check the status of the Voice Agent service including the version. """ + # Log the unique request ID, client's IP address, and the endpoint + request_id = generate_unique_uuid(8) + client_ip = context.peer() + self.logger.info(f"[ReqID#{request_id}] Client {client_ip} made a request to CheckServiceStatus end-point.") + response = voice_agent_pb2.ServiceStatus( version=self.service_version, status=True @@ -87,6 +107,11 @@ class VoiceAgentServicer(voice_agent_pb2_grpc.VoiceAgentServiceServicer): """ Detect the wake word using the wake word detection model. """ + # Log the unique request ID, client's IP address, and the endpoint + request_id = generate_unique_uuid(8) + client_ip = context.peer() + self.logger.info(f"[ReqID#{request_id}] Client {client_ip} made a request to DetectWakeWord end-point.") + wake_word_detector = WakeWordDetector(self.wake_word, self.stt_model, self.channels, self.sample_rate, self.bits_per_sample) wake_word_detector.create_pipeline() detection_thread = threading.Thread(target=wake_word_detector.start_listening) @@ -118,6 +143,11 @@ class VoiceAgentServicer(voice_agent_pb2_grpc.VoiceAgentServiceServicer): if request.action == voice_agent_pb2.START: status = voice_agent_pb2.REC_PROCESSING stream_uuid = generate_unique_uuid(8) + + # Log the unique request ID, client's IP address, and the endpoint + client_ip = context.peer() + self.logger.info(f"[ReqID#{stream_uuid}] Client {client_ip} made a manual START request to RecognizeVoiceCommand end-point.") + recorder = AudioRecorder(self.stt_model, self.base_audio_dir, self.channels, self.sample_rate, self.bits_per_sample) recorder.set_pipeline_mode("manual") audio_file = recorder.create_pipeline() @@ -133,6 +163,10 @@ class VoiceAgentServicer(voice_agent_pb2_grpc.VoiceAgentServiceServicer): stream_uuid = request.stream_id status = voice_agent_pb2.REC_SUCCESS + # Log the unique request ID, client's IP address, and the endpoint + client_ip = context.peer() + self.logger.info(f"[ReqID#{stream_uuid}] Client {client_ip} made a manual STOP request to RecognizeVoiceCommand end-point.") + recorder = self.rvc_stream_uuids[stream_uuid]["recorder"] audio_file = self.rvc_stream_uuids[stream_uuid]["audio_file"] del self.rvc_stream_uuids[stream_uuid] @@ -189,6 +223,11 @@ class VoiceAgentServicer(voice_agent_pb2_grpc.VoiceAgentServiceServicer): """ Execute the voice command by sending the intent to Kuksa. """ + # Log the unique request ID, client's IP address, and the endpoint + request_id = generate_unique_uuid(8) + client_ip = context.peer() + self.logger.info(f"[ReqID#{request_id}] Client {client_ip} made a request to ExecuteVoiceCommand end-point.") + intent = request.intent intent_slots = request.intent_slots processed_slots = [] @@ -203,6 +242,13 @@ class VoiceAgentServicer(voice_agent_pb2_grpc.VoiceAgentServiceServicer): exec_response = f"Sorry, I failed to execute command against intent '{intent}'. Maybe try again with more specific instructions." exec_status = voice_agent_pb2.EXEC_ERROR + # Check for kuksa status, and try re-connecting again if status is False + if not self.kuksa_client.get_kuksa_status(): + self.logger.error(f"[ReqID#{request_id}] Kuksa client found disconnected. Trying to close old instance and re-connecting...") + self.kuksa_client.close_kuksa_client() + self.kuksa_client.connect_kuksa_client() + self.kuksa_client.authorize_kuksa_client() + for execution_item in execution_list: print(execution_item) action = execution_item["action"] diff --git a/agl_service_voiceagent/utils/config.py b/agl_service_voiceagent/utils/config.py index 7295c7f..e0b053e 100644 --- a/agl_service_voiceagent/utils/config.py +++ b/agl_service_voiceagent/utils/config.py @@ -14,10 +14,13 @@ # See the License for the specific language governing permissions and # limitations under the License. +import os +import logging import configparser config = configparser.ConfigParser() config_path = None +logger = None def set_config_path(path): """ @@ -25,14 +28,25 @@ def set_config_path(path): """ global config_path config_path = path - config.read(config_path) def load_config(): """ - Loads the config file. + Loads the config file and initializes the logger. + + Also creates logging directory if it doesn't already exist. """ if config_path is not None: + global logger config.read(config_path) + + # create the base log dir if not exists + if not os.path.exists(get_config_value('BASE_LOG_DIR')): + os.makedirs(get_config_value('BASE_LOG_DIR')) + + logging.basicConfig(filename=get_config_value('BASE_LOG_DIR')+'voiceagent_server.log', level=logging.DEBUG, format='[%(asctime)s] [%(name)s] [%(levelname)s]: (%(filename)s:%(funcName)s) %(message)s', filemode='a') + logger = logging.getLogger() + logger.info("-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-") + else: raise Exception("Config file path not provided.") @@ -52,3 +66,15 @@ def get_config_value(key, group="General"): Gets a value from the config file. """ return config.get(group, key) + +def get_logger(): + """ + Gets the initialized logger. + """ + if logger is not None: + return logger + + else: + logging.basicConfig(level=logging.DEBUG) + print("[-] Error: Failed to get logger. Logger is not initialized!") + logging.error("Failed to get logger. Logger is not initialized!") diff --git a/agl_service_voiceagent/utils/kuksa_interface.py b/agl_service_voiceagent/utils/kuksa_interface.py index 9270379..0881660 100644 --- a/agl_service_voiceagent/utils/kuksa_interface.py +++ b/agl_service_voiceagent/utils/kuksa_interface.py @@ -18,7 +18,7 @@ import time import json import threading from kuksa_client import KuksaClientThread -from agl_service_voiceagent.utils.config import get_config_value +from agl_service_voiceagent.utils.config import get_config_value, get_logger class KuksaInterface: """ @@ -55,6 +55,7 @@ class KuksaInterface: self.insecure = get_config_value("insecure", "Kuksa") self.protocol = get_config_value("protocol", "Kuksa") self.token = get_config_value("token", "Kuksa") + self.logger = get_logger() print(self.ip, self.port, self.insecure, self.protocol, self.token) @@ -102,11 +103,14 @@ class KuksaInterface: if not self.get_kuksa_status(): print("[-] Error: Connection to Kuksa server failed.") + self.logger.error("Connection to Kuksa server failed.") else: print("[+] Connection to Kuksa established.") + self.logger.info("Connection to Kuksa established.") except Exception as e: print("[-] Error: Connection to Kuksa server failed. ", str(e)) + self.logger.error(f"Connection to Kuksa server failed. {str(e)}") def authorize_kuksa_client(self): @@ -119,10 +123,13 @@ class KuksaInterface: if "error" in response: error_message = response.get("error", "Unknown error") print(f"[-] Error: Authorization failed. {error_message}") + self.logger.error(f"Authorization failed. {error_message}") else: print("[+] Kuksa client authorized successfully.") + self.logger.info("Kuksa client authorized successfully.") else: print("[-] Error: Kuksa client is not initialized. Call `connect_kuksa_client` first.") + self.logger.error("Kuksa client is not initialized. Call `connect_kuksa_client` first.") def send_values(self, path=None, value=None): @@ -137,7 +144,8 @@ class KuksaInterface: """ result = False if self.kuksa_client is None: - print("[-] Error: Kuksa client is not initialized.") + print(f"[-] Error: Failed to send value '{value}' to Kuksa. Kuksa client is not initialized.") + self.logger.error(f"Failed to send value '{value}' to Kuksa. Kuksa client is not initialized.") return if self.get_kuksa_status(): @@ -150,12 +158,15 @@ class KuksaInterface: else: error_message = response.get("error", "Unknown error") print(f"[-] Error: Failed to send value '{value}' to Kuksa. {error_message}") + self.logger.error(f"Failed to send value '{value}' to Kuksa. {error_message}") except Exception as e: - print("[-] Error: Failed to send values to Kuksa. ", str(e)) + print(f"[-] Error: Failed to send value '{value}' to Kuksa. ", str(e)) + self.logger.error(f"Failed to send value '{value}' to Kuksa. {str(e)}") else: - print("[-] Error: Connection to Kuksa failed.") + print(f"[-] Error: Failed to send value '{value}' to Kuksa. Connection to Kuksa failed.") + self.logger.error(f"Failed to send value '{value}' to Kuksa. Connection to Kuksa failed.") return result @@ -171,7 +182,8 @@ class KuksaInterface: """ result = None if self.kuksa_client is None: - print("[-] Error: Kuksa client is not initialized.") + print(f"[-] Error: Failed to get value at path '{path}' from Kuksa. Kuksa client is not initialized.") + self.logger.error(f"Failed to get value at path '{path}' from Kuksa. Kuksa client is not initialized.") return if self.get_kuksa_status(): @@ -185,13 +197,16 @@ class KuksaInterface: else: error_message = response.get("error", "Unknown error") - print(f"[-] Error: Failed to get value from Kuksa. {error_message}") + print(f"[-] Error: Failed to get value at path '{path}' from Kuksa. {error_message}") + self.logger.error(f"Failed to get value at path '{path}' from Kuksa. {error_message}") except Exception as e: - print("[-] Error: Failed to get values from Kuksa. ", str(e)) + print(f"[-] Error: Failed to get value at path '{path}' from Kuksa. ", str(e)) + self.logger.error(f"Failed to get value at path '{path}' from Kuksa. {str(e)}") else: - print("[-] Error: Connection to Kuksa failed.") + print(f"[-] Error: Failed to get value at path '{path}' from Kuksa. Connection to Kuksa failed.") + self.logger.error(f"Failed to get value at path '{path}' from Kuksa. Connection to Kuksa failed.") return result @@ -206,5 +221,7 @@ class KuksaInterface: self.kuksa_client.stop() self.kuksa_client = None print("[+] Kuksa client stopped.") + self.logger.info("Kuksa client stopped.") except Exception as e: - print("[-] Error: Failed to close Kuksa client. ", str(e))
\ No newline at end of file + print("[-] Error: Failed to close Kuksa client. ", str(e)) + self.logger.error(f"Failed to close Kuksa client. {str(e)}")
\ No newline at end of file diff --git a/agl_service_voiceagent/utils/mapper.py b/agl_service_voiceagent/utils/mapper.py index 7529645..f24f44f 100644 --- a/agl_service_voiceagent/utils/mapper.py +++ b/agl_service_voiceagent/utils/mapper.py @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from agl_service_voiceagent.utils.config import get_config_value +from agl_service_voiceagent.utils.config import get_config_value, get_logger from agl_service_voiceagent.utils.common import load_json_file, words_to_number @@ -33,6 +33,7 @@ class Intent2VSSMapper: vss_signals_spec_file = get_config_value("vss_signals_spec", "Mapper") self.intents_vss_map = load_json_file(intents_vss_map_file).get("intents", {}) self.vss_signals_spec = load_json_file(vss_signals_spec_file).get("signals", {}) + self.logger = get_logger() if not self.validate_signal_spec_structure(): raise ValueError("[-] Invalid VSS signal specification structure.") @@ -49,6 +50,7 @@ class Intent2VSSMapper: # Check if the required keys are present in the signal data if not all(key in signal_data for key in ['default_value', 'default_change_factor', 'actions', 'values', 'default_fallback', 'value_set_intents']): print(f"[-] {signal_name}: Missing required keys in signal data.") + self.logger.error(f"{signal_name}: Missing required keys in signal data.") return False actions = signal_data['actions'] @@ -56,12 +58,14 @@ class Intent2VSSMapper: # Check if 'actions' is a dictionary with at least one action if not isinstance(actions, dict) or not actions: print(f"[-] {signal_name}: Invalid 'actions' key in signal data. Must be an object with at least one action.") + self.logger.error(f"{signal_name}: Invalid 'actions' key in signal data. Must be an object with at least one action.") return False # Check if the actions match the allowed actions ["set", "increase", "decrease"] for action in actions.keys(): if action not in ["set", "increase", "decrease"]: print(f"[-] {signal_name}: Invalid action in signal data. Allowed actions: ['set', 'increase', 'decrease']") + self.logger.error(f"{signal_name}: Invalid action in signal data. Allowed actions: ['set', 'increase', 'decrease']") return False # Check if the 'synonyms' list is present for each action and is either a list or None @@ -69,6 +73,7 @@ class Intent2VSSMapper: synonyms = action_data.get('synonyms') if synonyms is not None and (not isinstance(synonyms, list) or not all(isinstance(synonym, str) for synonym in synonyms)): print(f"[-] {signal_name}: Invalid 'synonyms' value in signal data. Must be a list of strings.") + self.logger.error(f"{signal_name}: Invalid 'synonyms' value in signal data. Must be a list of strings.") return False values = signal_data['values'] @@ -76,11 +81,13 @@ class Intent2VSSMapper: # Check if 'values' is a dictionary with the required keys if not isinstance(values, dict) or not all(key in values for key in ['ranged', 'start', 'end', 'ignore', 'additional']): print(f"[-] {signal_name}: Invalid 'values' key in signal data. Required keys: ['ranged', 'start', 'end', 'ignore', 'additional']") + self.logger.error(f"{signal_name}: Invalid 'values' key in signal data. Required keys: ['ranged', 'start', 'end', 'ignore', 'additional']") return False # Check if 'ranged' is a boolean if not isinstance(values['ranged'], bool): print(f"[-] {signal_name}: Invalid 'ranged' value in signal data. Allowed values: [true, false]") + self.logger.error(f"{signal_name}: Invalid 'ranged' value in signal data. Allowed values: [true, false]") return False default_fallback = signal_data['default_fallback'] @@ -88,6 +95,7 @@ class Intent2VSSMapper: # Check if 'default_fallback' is a boolean if not isinstance(default_fallback, bool): print(f"[-] {signal_name}: Invalid 'default_fallback' value in signal data. Allowed values: [true, false]") + self.logger.error(f"{signal_name}: Invalid 'default_fallback' value in signal data. Allowed values: [true, false]") return False # If all checks pass, the self.vss_signals_spec structure is valid |