amgadhasan's picture
Update src/utils.py
11dbe7a verified
import json
import logging
import os
import sqlite3
import time
from contextlib import closing
from pathlib import Path
def query_books_database(
sql_query: str, db_url: str = "data/books_data.db"
) -> list[dict]:
"""
Execute a read-only SQL query on the books database and return the results.
Args:
sql_query (str): SQL query string to execute.
db_url (str): URL to the database file, defaults to "data/books_data.db".
Returns:
list[dict[str, str | float | int]]: A list of rows as dictionaries
"""
with closing(sqlite3.connect(f"file:{db_url}?mode=ro", uri=True)) as connection:
connection.row_factory = lambda cursor, row: {
col[0]: row[i] for i, col in enumerate(cursor.description)
}
with closing(connection.cursor()) as cursor:
rows = cursor.execute(sql_query).fetchall()
return rows
def handle_function_calls(
function_map: dict, response_message, thread: list
) -> list | None:
"""
Handle function tool calls and map them to actual function executions.
Arguments:
function_map (dict): A dictionary mapping function names to function objects.
response_message: The message containing tool call information.
thread (list): List to append results of function calls.
Returns:
list: Updated list of messages.
Raises:
ValueError: If no tool calls are present in the response message.
KeyError: If a function mapping is not found.
"""
if not response_message.tool_calls:
raise ValueError("No tool calls found in the response message.")
for tool_call in response_message.tool_calls:
function_name = tool_call.function.name
if function_name in function_map:
function_args = json.loads(tool_call.function.arguments)
print(f"Function arguments: {function_args}")
function_to_call = function_map[function_name]
try:
function_response = function_to_call(**function_args)
except Exception as e:
function_response = str(e)
thread.append(
{
"tool_call_id": tool_call.id,
"role": "tool",
"name": function_name,
"content": str(function_response),
}
)
return thread
else:
print(f"Function {function_name} not found.")
raise KeyError(f"Function {function_name} not found in function map.")
def create_logger(logger_name: str, log_file: str, log_level: str) -> logging.Logger:
"""
Create and configure a logger with specified name, log file, and log level.
Arguments:
logger_name (str): Name of the logger.
log_file (str): Path to the log file.
log_level (str): Logging level as a string (e.g., 'INFO', 'DEBUG').
Returns:
logging.Logger: Configured logger object.
"""
LOG_FORMAT = "[%(asctime)s | %(name)s | %(levelname)s | %(funcName)s | %(message)s]"
log_level = getattr(logging, log_level.upper())
logger = logging.getLogger(logger_name)
logger.setLevel(logging.DEBUG)
# file_handler = logging.FileHandler(log_file)
# file_handler.setLevel(logging.DEBUG)
# file_handler.setFormatter(logging.Formatter(LOG_FORMAT))
# logger.addHandler(file_handler)
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
console_handler.setFormatter(logging.Formatter(LOG_FORMAT))
logger.addHandler(console_handler)
return logger
def log_execution_time(logger: logging.Logger):
"""
Decorator factory to log the execution time of a function using a specified logger.
Arguments:
logger (logging.Logger): Logger object used for logging.
Returns:
function: A decorator that logs the execution time of the wrapped function.
"""
def decorator(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
execution_time = end_time - start_time
logger.info(f"Executing {func.__name__} took {execution_time:.4f} seconds")
return result
return wrapper
return decorator