database
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -168,3 +168,4 @@ cython_debug/
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
|
||||
app_database/
|
||||
@@ -8,5 +8,6 @@
|
||||
|
||||
from semeion.services.llm_service import LLMService
|
||||
from semeion.services.qdrant_service import QdrantService
|
||||
from semeion.services.localdb import DatabaseService, AppSettings
|
||||
|
||||
__all__ = ["LLMService", "QdrantService"]
|
||||
__all__ = ["LLMService", "QdrantService", "DatabaseService", "AppSettings"]
|
||||
84
src/semeion/services/localdb.py
Normal file
84
src/semeion/services/localdb.py
Normal file
@@ -0,0 +1,84 @@
|
||||
#
|
||||
# Copyright (c) 2025, mstoeck3
|
||||
# All rights reserved.
|
||||
#
|
||||
# This source code is licensed under the BSD-3-Clause license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
#
|
||||
|
||||
import sqlite3
|
||||
import os
|
||||
from dotenv import dotenv_values
|
||||
DB_DIR = "app_database"
|
||||
DB_FILE = os.path.join(DB_DIR, "management_db.sqlite")
|
||||
|
||||
class DatabaseService():
|
||||
def __init__(self) -> None:
|
||||
print("DBSV: INITIALIZE DATABASE")
|
||||
self.conn = self.connect_db()
|
||||
|
||||
def connect_db(self) -> sqlite3.Connection:
|
||||
os.makedirs(DB_DIR, exist_ok=True)
|
||||
try:
|
||||
conn = sqlite3.connect(DB_FILE)
|
||||
print("DBSV: DATABASE AVAILABLE")
|
||||
return conn
|
||||
except sqlite3.Error as e:
|
||||
raise
|
||||
|
||||
def check_exist(self) -> bool:
|
||||
if os.path.exists(DB_FILE):
|
||||
print("DBSV: DATABASE EXISTS")
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
class AppSettings(DatabaseService):
|
||||
def __init__(self)->None:
|
||||
print("DBSV:APPSETTINGS: SETTINGS AVAILABLE")
|
||||
self.conn = self.connect_db()
|
||||
self.create_initial_configs()
|
||||
|
||||
def check_exist(self):
|
||||
try:
|
||||
with self.conn as conn:
|
||||
cur = conn.cursor()
|
||||
statement = """
|
||||
SELECT key,val FROM settings;
|
||||
"""
|
||||
cur.execute(statement)
|
||||
rows = cur.fetchall()
|
||||
if len(rows)==6: # expecting 6 initial settings
|
||||
print("DBSV:APPSETTINGS: SETTINGS EXIST")
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
except sqlite3.DatabaseError as e:
|
||||
print("DBSV: DB NOT FOUND. CREATING...")
|
||||
return e
|
||||
|
||||
def create_initial_configs(self)->None:
|
||||
if self.check_exist()==True:
|
||||
|
||||
return
|
||||
else:
|
||||
print("DBSV:APPSETTINGS: CREATING INITIAL SETTINGS")
|
||||
initial_config =dotenv_values()
|
||||
with self.conn as conn:
|
||||
cur = conn.cursor()
|
||||
statement = """
|
||||
CREATE TABLE IF NOT EXISTS settings (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
key text NOT NULL,
|
||||
val text NOT NULL
|
||||
);
|
||||
"""
|
||||
cur.execute(statement)
|
||||
for key,val in initial_config.items():
|
||||
task = (key,val)
|
||||
statement = f"""
|
||||
INSERT INTO settings(key,val) VALUES(?,?);
|
||||
"""
|
||||
cur.execute(statement, task)
|
||||
conn.commit()
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
#
|
||||
# Copyright (c) 2025, mstoeck3
|
||||
# All rights reserved.
|
||||
#
|
||||
# This source code is licensed under the BSD-3-Clause license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
#
|
||||
|
||||
|
||||
|
||||
class LLMQueryWorker(QThread):
|
||||
query_finished = Signal(object)
|
||||
|
||||
def __init__(self, llm_client, prompt: str):
|
||||
super().__init__()
|
||||
self.llm_client = llm_client
|
||||
self.prompt = prompt
|
||||
|
||||
def run(self):
|
||||
response = self.llm_client.query(self.prompt)
|
||||
self.query_finished.emit(response)
|
||||
|
||||
class LLMQueryService(QObject):
|
||||
query_finished = Signal(object)
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
print("SRVC:LLMQUERYSERVICE INIT")
|
||||
self.llm_client = LLMInterface()
|
||||
print("SRVC:LLMQUERYSERVICE TARGET")
|
||||
self._worker = None
|
||||
|
||||
@Slot(str)
|
||||
def query_llm(self, prompt: str):
|
||||
self._worker = LLMQueryWorker(self.llm_client, prompt)
|
||||
self._worker.query_finished.connect(self.query_finished.emit)
|
||||
self._worker.finished.connect(self._worker.deleteLater)
|
||||
self._worker.start()
|
||||
|
||||
|
||||
class QDrantService(QObject):
|
||||
collections_discovered = Signal(dict)
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.qdrant_client = QdrantInterface()
|
||||
|
||||
|
||||
|
||||
@@ -5,44 +5,53 @@
|
||||
# This source code is licensed under the BSD-3-Clause license found in the
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
#
|
||||
from PySide6.QtWidgets import QWidget, QVBoxLayout, QLabel, QPushButton, QLineEdit, QToolBar
|
||||
from PySide6.QtGui import QAction
|
||||
from PySide6.QtWidgets import QMainWindow, QWidget, QVBoxLayout, QLabel, QPushButton, QLineEdit, QMenu, QHBoxLayout
|
||||
from PySide6.QtCore import Qt, Signal
|
||||
from . import styling
|
||||
from semeion.services import LLMService, QdrantService
|
||||
from semeion.services import LLMService, QdrantService, DatabaseService, AppSettings
|
||||
|
||||
|
||||
class MainWindow(QWidget):
|
||||
class MainWindow(QMainWindow):
|
||||
main_exec_start = Signal(str)
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
print("MAIN: INITIALIZING GUI")
|
||||
self.create_ui()
|
||||
self.db = DatabaseService()
|
||||
self.settings = AppSettings()
|
||||
print("MAIN: CREATING SERVICES")
|
||||
self.create_services()
|
||||
print("MAIN: INITIALIZING GUI")
|
||||
self.create_ui()
|
||||
self.connect_ui_elements()
|
||||
|
||||
|
||||
def create_ui(self):
|
||||
self.setWindowTitle("Semeion Interface")
|
||||
self.setGeometry(100, 100, 800, 600)
|
||||
self.setStyleSheet(styling.main_window_style)
|
||||
|
||||
central_widget = QWidget()
|
||||
self.setCentralWidget(central_widget)
|
||||
|
||||
self.main_layout = QVBoxLayout()
|
||||
self.setLayout(self.main_layout)
|
||||
central_widget.setLayout(self.main_layout)
|
||||
|
||||
# Toolbar
|
||||
self.toolbar = QToolBar()
|
||||
self.main_layout.addWidget(self.toolbar)
|
||||
action_CaseManager = QAction("Case", self)
|
||||
action_IngestionManager = QAction("Ingest Artifacts", self)
|
||||
action_ConnManager = QAction("Server Connections", self)
|
||||
action_SettingsMenu = QAction("Settings", self)
|
||||
action_AboutMenu = QAction("About", self)
|
||||
self.toolbar.addAction(action_CaseManager)
|
||||
self.toolbar.addAction(action_IngestionManager)
|
||||
self.toolbar.addAction(action_ConnManager)
|
||||
self.toolbar.addAction(action_SettingsMenu)
|
||||
self.toolbar.addAction(action_AboutMenu)
|
||||
self.create_menuBar()
|
||||
|
||||
self.conn_status_layout = QHBoxLayout()
|
||||
conn_widget_qdrant = QLabel("Qdrant Interface available")
|
||||
conn_widget_llm = QLabel("LLM Interface available")
|
||||
conn_widget_embeddings = QLabel("EMB_PLACEHOLDER")
|
||||
status_widgets = []
|
||||
status_widgets.append(conn_widget_qdrant)
|
||||
status_widgets.append(conn_widget_llm)
|
||||
status_widgets.append(conn_widget_embeddings)
|
||||
for w in status_widgets:
|
||||
w.setStyleSheet(styling.status_widget_style)
|
||||
self.conn_status_layout.addWidget(w)
|
||||
self.main_layout.addLayout(self.conn_status_layout)
|
||||
|
||||
|
||||
# spacing
|
||||
self.main_layout.addStretch(1)
|
||||
@@ -58,7 +67,6 @@ class MainWindow(QWidget):
|
||||
self.searchInput.setFixedWidth(400)
|
||||
self.searchInput.setMinimumHeight(30)
|
||||
self.searchInput.setStyleSheet(styling.input_style)
|
||||
self.searchInput.returnPressed.connect(self.execute_from_input)
|
||||
self.main_layout.addWidget(self.searchInput, alignment=Qt.AlignmentFlag.AlignCenter)
|
||||
self.main_layout.addSpacing(10)
|
||||
|
||||
@@ -66,10 +74,24 @@ class MainWindow(QWidget):
|
||||
self.executeButton.setAttribute(Qt.WidgetAttribute.WA_Hover)
|
||||
self.executeButton.setStyleSheet(styling.button_style)
|
||||
self.main_layout.addWidget(self.executeButton, alignment=Qt.AlignmentFlag.AlignCenter)
|
||||
self.executeButton.clicked.connect(self.execute_from_input)
|
||||
|
||||
self.main_layout.addStretch(1)
|
||||
|
||||
def create_menuBar(self):
|
||||
menuBar = self.menuBar()
|
||||
action_caseManager = menuBar.addAction("&Case Manager", self.open_case_manager)
|
||||
action_caseManager.setStatusTip("Manage Cases")
|
||||
action_ingestionManager = menuBar.addAction("&Ingestion Manager", self.open_ingestion_manager)
|
||||
action_ingestionManager.setStatusTip("Ingest various Artifacts into dataset")
|
||||
menuBar_settingsMenu = QMenu("&Settings", self)
|
||||
action_connectionManager = menuBar_settingsMenu.addAction("&Manage Server Connections", self.open_connection_manager)
|
||||
menuBar.addMenu(menuBar_settingsMenu)
|
||||
menuBar_settingsMenu.setStatusTip("Application Settings")
|
||||
menuBar_about = QMenu("&About", self)
|
||||
action_aboutModal = menuBar_about.addAction("&About...", self.open_about_window)
|
||||
menuBar.addMenu(menuBar_about)
|
||||
menuBar.setStyleSheet(styling.toolbar_style)
|
||||
|
||||
def create_services(self):
|
||||
self.llm_service = LLMService()
|
||||
self.qdrant_service = QdrantService()
|
||||
@@ -84,15 +106,40 @@ class MainWindow(QWidget):
|
||||
# Qdrant: execute collection discovery on startup
|
||||
self.qdrant_service.discover_collections()
|
||||
|
||||
def connect_ui_elements(self):
|
||||
self.searchInput.returnPressed.connect(self.execute_from_input)
|
||||
self.executeButton.clicked.connect(self.execute_from_input)
|
||||
|
||||
def execute_from_input(self) -> str|None:
|
||||
query = self.searchInput.text().strip()
|
||||
if not query:
|
||||
return
|
||||
self.main_exec_start.emit(query)
|
||||
self.executeButton.setDisabled(True)
|
||||
self.searchInput.setDisabled(True)
|
||||
self.reenable_list = [self.executeButton, self.searchInput]
|
||||
print("MAIN: LLM QUERY SUBMIT:", query)
|
||||
|
||||
def handle_llm_response(self, response):
|
||||
self.enable_input(self.reenable_list)
|
||||
print("MAIN: LLM RESPONSE:", response)
|
||||
|
||||
def handle_qdrant_response(self, response):
|
||||
print("MAIN: QDRANT COLLECTIONS:", response)
|
||||
|
||||
def enable_input(self, elements: list):
|
||||
for e in elements:
|
||||
e.setEnabled(True)
|
||||
|
||||
def open_case_manager(self):
|
||||
print("clicked case manager")
|
||||
|
||||
def open_ingestion_manager(self):
|
||||
print("clicked ingestion manager")
|
||||
|
||||
def open_connection_manager(self):
|
||||
print("clicked open connection manager")
|
||||
|
||||
def open_about_window(self):
|
||||
print("clicked open about")
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
# LICENSE file in the root directory of this source tree.
|
||||
#
|
||||
|
||||
## DISCLAIMER: Styles are mostly AI-generated
|
||||
|
||||
main_window_style = """
|
||||
QWidget {
|
||||
background-color: #f0f0f0;
|
||||
@@ -39,8 +41,35 @@ QLineEdit {
|
||||
padding: 10px 20px;
|
||||
font-size: 16px;
|
||||
}
|
||||
QLineEdit:focus {
|
||||
QLineEdit:focus {
|
||||
border: 1px solid #aaa;
|
||||
outline: none;
|
||||
}
|
||||
"""
|
||||
|
||||
toolbar_style = """
|
||||
QMenuBar::item {
|
||||
padding: 6px 12px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
QMenuBar::item:selected {
|
||||
background-color: #e8e8e8;
|
||||
}
|
||||
QMenu {
|
||||
border: 2px solid #c0c0c0;
|
||||
border-radius: 6px;
|
||||
padding: 6px 4px;
|
||||
}
|
||||
QMenu::item {
|
||||
padding: 8px 24px 8px 12px;
|
||||
border-radius: 4px;
|
||||
margin: 2px;
|
||||
}
|
||||
QMenu::item:selected {
|
||||
background-color: #e8e8e8;
|
||||
}
|
||||
"""
|
||||
|
||||
status_widget_style = """
|
||||
|
||||
"""
|
||||
Reference in New Issue
Block a user