# Start all services import subprocess import os import yaml import json import socket import urllib.parse from backend import run_flask import logging import utils from utils import GlobalState global_state = GlobalState() # Configure root logger. The level will be adjusted later based on config file logger = global_state.getLogger(__name__) # Logger for this module, inherit properties of the root logger def configure(): """ Reads YAML configruation file into dictionary, parse it and fill all referenceed environment variables with their values. """ #################################### # Read YAML config #################################### # Load configuration file that defines parameters for services with open('./smartassist/config/smartassist.yaml') as f: config = yaml.safe_load(f) def resolve_env_var(value): if isinstance(value, str) and value.startswith("${") and value.endswith("}"): env_var_name = value[2:-1] # Extract name between ${} return os.getenv(env_var_name, None) return value def update_dict_with_env_vars(d): for key in d: if isinstance(d[key], dict): update_dict_with_env_vars(d[key]) # Recursively check nested dictionaries elif isinstance(d[key], str): d[key] = resolve_env_var(d[key]) return d # Update the config dictionary with resolved environment variables updated_config = update_dict_with_env_vars(config) #################################### # Extract global logging level #################################### if isinstance(updated_config.get('logging'), dict): # Look for 'logging' key in config file logging_config = updated_config['logging'] if isinstance(logging_config.get('level'), str): # Set to value of the yaml file if specified global_state.set_log_level(logging_config['level']) logger.debug("configure(): This logger now has effective log level %s", logger.getEffectiveLevel()) #################################### # Extract and export API endpoint as # envrionment variable #################################### backend_api_ep = 'http://localhost:5005/api/chat' # Default API endpoint if isinstance(updated_config.get('backend'), dict): # Look for 'backend' key in config file if isinstance(updated_config['backend'].get('url'), str): # Look for 'url' key in config file url = updated_config['backend'].get('url') if isinstance(updated_config['backend'].get('api'), str): # Look for 'api' key in config file api = updated_config['backend'].get('api') backend_api_ep = url+api # Extract API endpoint if defined logger.debug("BE_API_ENDPOINT is set to '{}'".format(backend_api_ep)) os.environ['BE_API_ENDPOINT'] = backend_api_ep # Look into alternative way to share this with backend.py return updated_config def start_frontend(config): parsed_url = urllib.parse.urlparse(config['frontend']['url']) hostname = parsed_url.netloc.split(':')[0] # Split by ':' and take the first part, i.e., 'localhost', IP, or domain name port = parsed_url.port # This is the server port # Use the socket module in Python to check whether a port is in use, # which would indicate that a server is already running on that port. with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: try: s.bind((hostname, port)) logger.debug("No server is running on %s -— starting one.", parsed_url.netloc) # Start frontend (web server) as a separate process subprocess.Popen(["python", "-m", "http.server", str(port)]) except socket.error as e: if e.errno == 48: logger.debug("A server is already running on %s -— will use this.", parsed_url.netloc) else: raise # Unexpected error, re-raise it so we can see the traceback except Exception as e: logger.error("Failed to start frontend: %s", str(e)) # Corresponds to print(f"Failed to start frontend: {e}") def start_backend(config): parsed_url = urllib.parse.urlparse(config['backend']['url']) # hostname = parsed_url.netloc.split(':')[0] # Split by ':' and take the first part, i.e., 'localhost', IP, or domain name port = parsed_url.port # This is the server port logger.debug('Backend parsed url set to {}'.format(parsed_url)) logger.debug('Backend port set to {}'.format(port)) try: run_flask(fport = port) except Exception as e: logger.error("Failed to start backend: %s", str(e)) # Corresponds to print(f"Failed to start backend: {e}") if __name__ == '__main__': conf = configure() # Read config from file and set up config dict logger.debug('conf dictionary set to {}'.format(json.dumps(conf, indent=4))) # start_frontend(config=conf) # Not needed as we are using Flask for backend now start_backend(config=conf)