persistent multithreading
This commit is contained in:
@@ -44,4 +44,4 @@ class LLMInterface():
|
|||||||
return response
|
return response
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"CLNT:LLMCLIENT ERR: {e}")
|
print(f"CLNT:LLMCLIENT ERR: {e}")
|
||||||
return Exception
|
raise
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
# LICENSE file in the root directory of this source tree.
|
# LICENSE file in the root directory of this source tree.
|
||||||
#
|
#
|
||||||
|
|
||||||
from semeion.services.llm_service import LLMQueryService
|
from semeion.services.llm_service import LLMService
|
||||||
from semeion.services.qdrant_service import QDrantService
|
from semeion.services.qdrant_service import QdrantService
|
||||||
|
|
||||||
__all__ = ["LLMQueryService", "QDrantService"]
|
__all__ = ["LLMService", "QdrantService"]
|
||||||
@@ -9,66 +9,68 @@
|
|||||||
from semeion.interfaces.llm.llm_client import LLMInterface
|
from semeion.interfaces.llm.llm_client import LLMInterface
|
||||||
from PySide6.QtCore import QObject, Signal, Slot, QThread
|
from PySide6.QtCore import QObject, Signal, Slot, QThread
|
||||||
|
|
||||||
class LLMQueryWorker(QThread):
|
|
||||||
target = Signal(object)
|
class LLMWorker(QObject): # initializes once and is queried repeatedly
|
||||||
|
result = Signal(object)
|
||||||
error = Signal(str)
|
error = Signal(str)
|
||||||
status = Signal(str)
|
status = Signal(str)
|
||||||
|
|
||||||
def __init__(self, prompt: str):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.prompt = prompt
|
self.client = None
|
||||||
self.stop_flag = False
|
|
||||||
|
|
||||||
@Slot()
|
@Slot()
|
||||||
def simple_query(self):
|
def initialize(self):
|
||||||
try:
|
self.client = LLMInterface()
|
||||||
self.status.emit("SRVC:LLM: TASK SIMPLE_QUERY STARTED")
|
self.status.emit("LLM: ready")
|
||||||
interface = LLMInterface()
|
|
||||||
response = interface.simple_query(self.prompt)
|
|
||||||
if self.stop_flag:
|
|
||||||
self.status.emit("SRVC:LLM: TASK SIMPLE_QUERY STOPPED")
|
|
||||||
return
|
|
||||||
|
|
||||||
self.status.emit("SRVC:LLM: TASK SIMPLE_QUERY FINISHED")
|
@Slot(str)
|
||||||
self.target.emit(response)
|
def run_query(self, prompt: str):
|
||||||
|
if self.client is None:
|
||||||
|
self.error.emit("LLM client not initialized")
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
self.status.emit("LLM: started")
|
||||||
|
raw_response = self.client.simple_query(prompt)
|
||||||
|
response = raw_response.choices[0].message.content or ""
|
||||||
|
self.status.emit("LLM: finished")
|
||||||
|
self.result.emit(response)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.error.emit(str(e))
|
self.error.emit(str(e))
|
||||||
|
|
||||||
@Slot()
|
|
||||||
def stop(self):
|
|
||||||
self.stop_flag = True
|
|
||||||
self.status.emit("SRVC:LLM: TASK STOP REQUESTED")
|
|
||||||
|
|
||||||
class LLMQueryService(QObject):
|
|
||||||
|
class LLMService(QObject):
|
||||||
|
"""persistent service"""
|
||||||
query_finished = Signal(object)
|
query_finished = Signal(object)
|
||||||
query_error = Signal(str)
|
query_error = Signal(str)
|
||||||
query_status = Signal(str)
|
query_status = Signal(str)
|
||||||
|
|
||||||
|
# signal to send to active worker
|
||||||
|
_do_query = Signal(str)
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
print("SRVC:LLMQUERYSERVICE INIT")
|
self._thread = QThread()
|
||||||
self.worker = None
|
self.worker = LLMWorker()
|
||||||
self.new_thread = None
|
|
||||||
|
|
||||||
@Slot(str)
|
self.worker.moveToThread(self._thread)
|
||||||
def query_llm_simple(self, prompt: str):
|
|
||||||
self.new_thread = QThread()
|
|
||||||
print("SRVC:LLMQUERYSERVICE QUERY_LLM_SIMPLE - NEW THREAD")
|
|
||||||
self.worker = LLMQueryWorker(prompt)
|
|
||||||
print("SRVC:LLMQUERYSERVICE QUERY_LLM_SIMPLE - WORKER INIT")
|
|
||||||
|
|
||||||
self.worker.moveToThread(self.new_thread)
|
self._thread.started.connect(self.worker.initialize)
|
||||||
self.new_thread.started.connect(self.worker.simple_query)
|
|
||||||
print("SRVC:LLMQUERYSERVICE QUERY_LLM_SIMPLE - WORKER STARED AND CONNECTED")
|
|
||||||
|
|
||||||
# send to GUI
|
self._do_query.connect(self.worker.run_query)
|
||||||
self.worker.target.connect(self.query_finished.emit)
|
|
||||||
|
self.worker.result.connect(self.query_finished.emit)
|
||||||
self.worker.error.connect(self.query_error.emit)
|
self.worker.error.connect(self.query_error.emit)
|
||||||
self.worker.status.connect(self.query_status.emit)
|
self.worker.status.connect(self.query_status.emit)
|
||||||
|
|
||||||
# cleanup
|
self._thread.start()
|
||||||
self.worker.target.connect(self.new_thread.quit)
|
|
||||||
self.worker.error.connect(self.new_thread.quit)
|
@Slot(str)
|
||||||
self.worker.status.connect(self.new_thread.quit)
|
def query(self, prompt: str):
|
||||||
self.new_thread.finished.connect(self.worker.deleteLater)
|
self._do_query.emit(prompt) # start query via signal
|
||||||
self.new_thread.finished.connect(self.new_thread.deleteLater)
|
|
||||||
|
def stop(self):
|
||||||
|
self._thread.quit()
|
||||||
|
self._thread.wait()
|
||||||
|
self.worker.deleteLater()
|
||||||
|
self._thread.deleteLater()
|
||||||
@@ -10,9 +10,58 @@ from semeion.interfaces.qdrant.qdrant_client import QdrantInterface
|
|||||||
from PySide6.QtCore import QObject, Signal, Slot, QThread
|
from PySide6.QtCore import QObject, Signal, Slot, QThread
|
||||||
|
|
||||||
class QdrantWorker(QObject):
|
class QdrantWorker(QObject):
|
||||||
collections_discovered = Signal(object)
|
result = Signal(object)
|
||||||
|
error = Signal(str)
|
||||||
|
status = Signal(str)
|
||||||
|
|
||||||
def __init__(self, qdrant_client: QdrantInterface):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.qdrant_client = qdrant_client
|
self.client = None
|
||||||
|
|
||||||
|
@Slot()
|
||||||
|
def initialize(self):
|
||||||
|
self.client = QdrantInterface()
|
||||||
|
self.status.emit("Qdrant: ready")
|
||||||
|
|
||||||
|
@Slot()
|
||||||
|
def get_collections(self):
|
||||||
|
try:
|
||||||
|
if self.client is None:
|
||||||
|
self.error.emit("Qdrant: Failed to establish connection to QDrant")
|
||||||
|
return
|
||||||
|
self.status.emit("Qdrant: fetching collections")
|
||||||
|
collections = self.client.get_collections()
|
||||||
|
self.status.emit("Qdrant: Successfully fetched collections")
|
||||||
|
self.result.emit(collections)
|
||||||
|
except Exception as e:
|
||||||
|
self.error.emit(str(e))
|
||||||
|
|
||||||
|
|
||||||
|
# Service = manages Worker + QThread, UI connects to this
|
||||||
|
class QdrantService(QObject):
|
||||||
|
query_finished = Signal(object)
|
||||||
|
query_error = Signal(str)
|
||||||
|
query_status = Signal(str)
|
||||||
|
|
||||||
|
_do_discover = Signal()
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self._thread = QThread()
|
||||||
|
self.worker =QdrantWorker()
|
||||||
|
|
||||||
|
self.worker.moveToThread(self._thread)
|
||||||
|
|
||||||
|
self._thread.started.connect(self.worker.initialize)
|
||||||
|
|
||||||
|
self._do_discover.connect(self.worker.get_collections)
|
||||||
|
|
||||||
|
self.worker.result.connect(self.query_finished.emit)
|
||||||
|
self.worker.error.connect(self.query_error.emit)
|
||||||
|
self.worker.status.connect(self.query_status.emit)
|
||||||
|
|
||||||
|
self._thread.start()
|
||||||
|
|
||||||
|
@Slot()
|
||||||
|
def discover_collections(self):
|
||||||
|
self._do_discover.emit()
|
||||||
@@ -9,7 +9,7 @@ from PySide6.QtWidgets import QWidget, QVBoxLayout, QLabel, QPushButton, QLineEd
|
|||||||
from PySide6.QtGui import QAction
|
from PySide6.QtGui import QAction
|
||||||
from PySide6.QtCore import Qt, Signal
|
from PySide6.QtCore import Qt, Signal
|
||||||
from . import styling
|
from . import styling
|
||||||
from semeion.services import LLMQueryService, QDrantService
|
from semeion.services import LLMService, QdrantService
|
||||||
|
|
||||||
|
|
||||||
class MainWindow(QWidget):
|
class MainWindow(QWidget):
|
||||||
@@ -58,6 +58,7 @@ class MainWindow(QWidget):
|
|||||||
self.searchInput.setFixedWidth(400)
|
self.searchInput.setFixedWidth(400)
|
||||||
self.searchInput.setMinimumHeight(30)
|
self.searchInput.setMinimumHeight(30)
|
||||||
self.searchInput.setStyleSheet(styling.input_style)
|
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.addWidget(self.searchInput, alignment=Qt.AlignmentFlag.AlignCenter)
|
||||||
self.main_layout.addSpacing(10)
|
self.main_layout.addSpacing(10)
|
||||||
|
|
||||||
@@ -70,20 +71,28 @@ class MainWindow(QWidget):
|
|||||||
self.main_layout.addStretch(1)
|
self.main_layout.addStretch(1)
|
||||||
|
|
||||||
def create_services(self):
|
def create_services(self):
|
||||||
self.llm_service = LLMQueryService()
|
self.llm_service = LLMService()
|
||||||
self.qdrant_service = QDrantService()
|
self.qdrant_service = QdrantService()
|
||||||
|
|
||||||
print("MAIN: SETTING UP LLM SERVICE")
|
# Button -> executes LLM Service
|
||||||
self.main_exec_start.connect(self.llm_service.query_llm_simple)
|
self.main_exec_start.connect(self.llm_service.query)
|
||||||
|
# response handling
|
||||||
self.llm_service.query_finished.connect(self.handle_llm_response)
|
self.llm_service.query_finished.connect(self.handle_llm_response)
|
||||||
|
|
||||||
|
# connect qdrant signals
|
||||||
|
self.qdrant_service.query_finished.connect(self.handle_qdrant_response)
|
||||||
|
# Qdrant: execute collection discovery on startup
|
||||||
|
self.qdrant_service.discover_collections()
|
||||||
|
|
||||||
def execute_from_input(self) -> str|None:
|
def execute_from_input(self) -> str|None:
|
||||||
query = self.searchInput.text().strip()
|
query = self.searchInput.text().strip()
|
||||||
if not query:
|
if not query:
|
||||||
return
|
return
|
||||||
# TODO: implement logic
|
|
||||||
self.main_exec_start.emit(query)
|
self.main_exec_start.emit(query)
|
||||||
print("MAIN: LLM QUERY SUBMIT:", query)
|
print("MAIN: LLM QUERY SUBMIT:", query)
|
||||||
|
|
||||||
def handle_llm_response(self, response):
|
def handle_llm_response(self, response):
|
||||||
print("MAIN: LLM QUERY RECEIVED:", response)
|
print("MAIN: LLM RESPONSE:", response)
|
||||||
|
|
||||||
|
def handle_qdrant_response(self, response):
|
||||||
|
print("MAIN: QDRANT COLLECTIONS:", response)
|
||||||
Reference in New Issue
Block a user