112 lines
5.0 KiB
Python
112 lines
5.0 KiB
Python
# 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)
|
|
|