8 Commits

6 changed files with 89 additions and 84 deletions
+2 -1
View File
@@ -67,7 +67,8 @@ def chat(url_server = "http://localhost:11434/api/generate", model = "phi3:mini"
# Get the message from the JSON in the request body # Get the message from the JSON in the request body
data = request.get_json() data = request.get_json()
message = data.get('query') message = data.get('query')
url_server = data.get('url_server', url_server) # Use provided URL or default
model = data.get('model', model) # Use provided model or default
logger.debug("data = %s\nmessage = %s", str(data), str(message)) logger.debug("data = %s\nmessage = %s", str(data), str(message))
try: try:
url = url_server url = url_server
+18 -2
View File
@@ -14,13 +14,28 @@
<textarea id="userInput" placeholder="Type your message..." rows="5"></textarea> <textarea id="userInput" placeholder="Type your message..." rows="5"></textarea>
<button onclick="sendMessage()">Send</button> <button onclick="sendMessage()">Send</button>
<!-- Get the apiEndpoint -->
<script>
const apiEndpoint = window.apiEndpoint;
// Debugging log messages
// if (apiEndpoint) {
// console.log("client.html - apiEndpoint: ", apiEndpoint);
// }
// else {
// console.log("client.html - cannot find apiEndpoint");
// }
</script>
<!-- Get the javascript handling communication with the backene -->
<script src="/js/frontend.js"></script> <script src="/js/frontend.js"></script>
<script> <script>
const chatContainer = document.getElementById('chatbox'); const chatContainer = document.getElementById('chatbox');
// Handle resize events // Handle resize events
window.addEventListener('resize', function() { window.addEventListener('resize',
chatContainer.style.height = 'auto'; function() {
chatContainer.style.height = 'auto';
}); });
const userInputElement = document.getElementById('userInput'); const userInputElement = document.getElementById('userInput');
@@ -37,6 +52,7 @@
}); });
</script> </script>
</body> </body>
</html> </html>
+8 -15
View File
@@ -5,13 +5,13 @@ import yaml
import json import json
import socket import socket
import urllib.parse import urllib.parse
from backend import run_flask
import logging import logging
import utils import utils
from utils import configure_logging from utils import GlobalState
from backend import run_flask
configure_logging() # Configure root logger. The level will be adjusted later based on config file global_state = GlobalState() # Configure root logger. The level will be adjusted later based on config file
logger = logging.getLogger(__name__) # Logger for this module, inherit properties of the root logger logger = global_state.getLogger(__name__) # Logger for this module, inherit properties of the root logger
def configure(): def configure():
""" """
@@ -46,17 +46,10 @@ def configure():
# Extract global logging level # Extract global logging level
#################################### ####################################
if isinstance(updated_config.get('logging'), dict): # Look for 'logging' key in config file if isinstance(updated_config.get('logging'), dict): # Look for 'logging' key in config file
# logging.info("found key 'logging' in config file")
logging_config = updated_config['logging'] logging_config = updated_config['logging']
if isinstance(logging_config.get('level'), str): # Set to value of the yaml file if specified if isinstance(logging_config.get('level'), str): # Set to value of the yaml file if specified
# logging.info("found key 'level' in config file") global_state.set_log_level(logging_config['level'])
utils.log_level = logging_config['level'] logger.debug("configure(): This logger now has effective log level %s", logger.getEffectiveLevel())
numeric_log_level = getattr(logging, utils.log_level, None)
rlogger = logging.getLogger() # Get the root logger
rlogger.setLevel(numeric_log_level)
# set_local_logger(logger) # Set log level for logger based on log_level
logger.info("Global variable utils.log_level set to: {}".format(utils.log_level))
#################################### ####################################
# Extract and export API endpoint as # Extract and export API endpoint as
@@ -70,7 +63,7 @@ def configure():
api = updated_config['backend'].get('api') api = updated_config['backend'].get('api')
backend_api_ep = url+api # Extract API endpoint if defined backend_api_ep = url+api # Extract API endpoint if defined
logger.debug("BE_API_ENDPOINT is set to '{}'".format(backend_api_ep)) logger.debug("BE_API_ENDPOINT is set to '{}'".format(backend_api_ep))
os.environ['BE_API_ENDPOINT'] = backend_api_ep os.environ['BE_API_ENDPOINT'] = backend_api_ep # Look into alternative way to share this with backend.py
return updated_config return updated_config
@@ -113,6 +106,6 @@ def start_backend(config):
if __name__ == '__main__': if __name__ == '__main__':
conf = configure() # Read config from file and set up config dict conf = configure() # Read config from file and set up config dict
logger.debug('conf dictionary set to {}'.format(json.dumps(conf, indent=4))) logger.debug('conf dictionary set to {}'.format(json.dumps(conf, indent=4)))
# start_frontend(config=conf) # start_frontend(config=conf) # Not needed as we are using Flask for backend now
start_backend(config=conf) start_backend(config=conf)
+3 -33
View File
@@ -3,17 +3,6 @@
const chatbox = document.getElementById('chatbox'); const chatbox = document.getElementById('chatbox');
const userInput = document.getElementById('userInput'); const userInput = document.getElementById('userInput');
// Get API endpoint for chat from environment variable
// Handle resize events
window.addEventListener('resize', () => {
chatbox.style.height = 'auto';
});
// ... other code for sending messages ...
// Define a function to send the user's message to the AI // Define a function to send the user's message to the AI
function sendMessage() { function sendMessage() {
// Get the user's input message and trim any whitespace // Get the user's input message and trim any whitespace
@@ -21,12 +10,12 @@ function sendMessage() {
// Check if the message is not empty // Check if the message is not empty
if (query !== '') { if (query !== '') {
// Send a POST request to the /api/chat endpoint with the message
// fetch('http://localhost:5005/api/chat', {
fetch(`${apiEndpoint}`, { fetch(`${apiEndpoint}`, {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query }), body: JSON.stringify({ query, model: "phi3:mini" }), // Add these parameters here
// body: JSON.stringify({ query, url_server: "http://your-custom-url", model: "phi3:mini" }), // Add these parameters here
}) })
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
@@ -60,22 +49,3 @@ function renderMessage(text, className) {
// Append the message element to the chatbox // Append the message element to the chatbox
chatbox.appendChild(messageElement); chatbox.appendChild(messageElement);
} }
function handleIframeLoad() {
const clientFrame = document.getElementById('client-frame');
if (clientFrame) { // Check if the iframe exists
const apiEndpoint = clientFrame.getAttribute('data-api-endpoint') || clientFrame.srcdoc.split('?')[1].split('=')[1];
console.log("The API Endpoint is:", apiEndpoint);
// Now you can use this 'apiEndpoint' for communication
} else {
setTimeout(handleIframeLoad, 200); // Retry in a short while if iframe not found yet
console.log("The iframe with id client-frame not found yet");
}
}
window.addEventListener('load', handleIframeLoad);
+12 -6
View File
@@ -4,19 +4,25 @@
<!-- <title>Frontend</title> --> <!-- <title>Frontend</title> -->
</head> </head>
<body> <body>
<script>
// Extract apiEndpoint for use in your frontend code...
const apiEndpoint = '{{ api_endpoint }}'; // Templating syntax (Jinja2)
console.log("index.html - API Endpoint: ", apiEndpoint);
</script>
<!-- This iframe will hold the content from client.html --> <!-- This iframe will hold the content from client.html -->
<!-- Passing the API endpoint as a query parameter to the srcdoc attribute --> <!-- Passing the API endpoint as a query parameter to the srcdoc attribute -->
<!-- srcdoc="{{ client_content }}?apiEndpoint={{ api_endpoint }}/"> -->
<iframe id="client-frame" <iframe id="client-frame"
style="width: 100%; height: 100vh;" style="width: 100%; height: 100vh;"
srcdoc="{{ client_content }}?apiEndpoint={{ api_endpoint }}/"> srcdoc="{{ client_content }}">
</iframe> </iframe>
<script>
// Extract apiEndpoint for use in your frontend code...
const apiEndpoint = '{{ api_endpoint }}'; // Templating syntax (Jinja2)
// Tell the iframe about the apiEndpoing
document.getElementById('client-frame').contentWindow.apiEndpoint = apiEndpoint;
console.log("index.html - API Endpoint: ", apiEndpoint);
</script>
<!-- Responsive scaling and some padding --> <!-- Responsive scaling and some padding -->
<script> <script>
const clientFrame = document.getElementById('client-frame'); const clientFrame = document.getElementById('client-frame');
+44 -25
View File
@@ -2,36 +2,55 @@
# imported to more than one other module. The rational for defining these things here # imported to more than one other module. The rational for defining these things here
# is that it is easier to avoid circular imports when they are defined in a central location. # is that it is easier to avoid circular imports when they are defined in a central location.
import logging import logging
global log_level # Remember, in Python globals are only global in the module it is defined in
log_level = 'INFO' # Default logging level if not specified in config file
def configure_logging(level=log_level): class GlobalState:
""" """
Set up logging for the project. This is the root logger instance. This class holds various variables and methods which are accessible across
All child loggers inherit from this logger. different modules in the Python project using the Singleton design pattern.
This ensures that only one instance of the class is created and shared among
all modules, preventing circular imports and providing a centralized location
for managing shared resources.
""" """
numeric_level = getattr(logging, level.upper()) # Convert string to numeric level _instance = None # Private class attribute to hold the single instance of the class
logger = logging.getLogger() # Get the root logger def __new__(cls):
logger.setLevel(numeric_level) if cls._instance is None:
cls._instance = super(GlobalState, cls).__new__(cls)
cls._instance.log_level = 'INFO' # Default logging level
cls._instance.logger = logging.getLogger() # Get root logger for the caller module
handler = logging.StreamHandler() # Or other handler (FileHandler for logs to file)
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
handler.setFormatter(formatter)
cls._instance.logger.addHandler(handler)
cls._instance.logger.setLevel(getattr(logging, cls._instance.log_level)) # Initialize root logger level
cls._instance.logger.info(" __new__(cls): Logger in GlobalState created: %s", cls._instance.logger)
return cls._instance
handler = logging.StreamHandler() # Or other handler (FileHandler for logs to file) def configure_logging(self, level=None):
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") """Set up logging for the project."""
handler.setFormatter(formatter) if level is None:
logger.addHandler(handler) level = self.log_level
# numeric_level = getattr(logging, level.upper()) # Convert string to numeric level
numeric_level = getattr(logging, level.upper()) # Convert string to numeric level
self.logger.setLevel(numeric_level)
self.logger.debug(f"utils.py -- configure_logging(): effective log level is {level} which is {self.logger.getEffectiveLevel()}")
# To be removed? def set_log_level(self, level = 'INFO'):
# def set_local_logger(log_instance): """Set the logging level."""
# """ self.log_level = level
# Configure logging based on the global variable log_level self.configure_logging()
# Logging is controlled by integer values, where DEBUG < INFO < WARNING < ERROR < CRITICAL.
# To turn off logging completely, set numeric_log_level to at least CRITICAL + 1.
# """
# global log_level
# numeric_log_level = getattr(logging, log_level, None)
# if not isinstance(numeric_log_level, int):
# raise ValueError('Invalid log level: %s' % log_level)
# log_instance.basicConfig(level=numeric_log_level) # Set the root logger level to the configured level def get_log_level(self):
# log_instance.info('Current log level set to {}'.format(log_instance.getLogger().getEffectiveLevel())) # Example usage of the logger """Getter for log_level attribute."""
return self.log_level
def get_effective_log_level(self):
"""Getter for effective log level of loggerattribute."""
return self.logger.getEffectiveLevel()
def getLogger(self, module_name=None):
"""Return a logger based on the module name."""
if module_name is None:
module_name = __name__
logger = logging.getLogger(module_name)
return logger