OllamaInterface/OllamaClient.py

changeset 8
3118d16e526e
parent 7
eb1dec15b2f0
child 9
c471738b75b3
--- a/OllamaInterface/OllamaClient.py	Thu Aug 08 18:33:49 2024 +0200
+++ b/OllamaInterface/OllamaClient.py	Sun Aug 25 19:44:24 2024 +0200
@@ -45,11 +45,6 @@
         from the 'ollama' server was received
     @signal modelsList(modelNames:list[str]) emitted after the list of model
         names was obtained from the 'ollama' server
-    @signal detailedModelsList(models:list[dict]) emitted after the list of
-        models was obtained from the 'ollama' server giving some model details
-    @signal runningModelsList(models:list[dict]) emitted after the list of
-        running models was obtained from the 'ollama' server giving some model
-        execution details
     @signal pullStatus(msg:str, id:str, total:int, completed:int) emitted to indicate
         the status of a pull request as reported by the 'ollama' server
     @signal serverVersion(version:str) emitted after the server version was obtained
@@ -63,8 +58,6 @@
 
     replyReceived = pyqtSignal(str, str, bool)
     modelsList = pyqtSignal(list)
-    detailedModelsList = pyqtSignal(list)
-    runningModelsList = pyqtSignal(list)
     pullStatus = pyqtSignal(str, str, int, int)
     serverVersion = pyqtSignal(str)
     finished = pyqtSignal()
@@ -247,79 +240,65 @@
         Public method to request a list of models available locally from the 'ollama'
         server with some model details.
         """
-        # TODO: not implemented yet
-        self.__sendRequest("tags", processResponse=self.__processDetailedModelsList)
+        response = self.__sendSyncRequest("tags")
 
-    def __processDetailedModelsList(self, response):
-        """
-        Private method to process the tags response of the 'ollama' server extracting
-        some model details.
-
-        @param response dictionary containing the tags response
-        @type dict
-        """
         models = []
-        with contextlib.suppress(KeyError):
-            for model in response["models"]:
-                name = model["name"]
-                if name:
-                    models.append(
-                        {
-                            "name": name,
-                            "id": model["digest"][:20],  # first 20 characters only
-                            "size": model["size"],
-                            "modified": datetime.datetime.fromisoformat(
-                                model["modified_at"]
-                            ),
-                        }
-                    )
-        self.detailedModelsList.emit(models)
+        if response is not None:
+            with contextlib.suppress(KeyError):
+                for model in response["models"]:
+                    name = model["name"]
+                    if name:
+                        models.append(
+                            {
+                                "name": name,
+                                "id": model["digest"][:20],  # first 20 characters only
+                                "size": model["size"],
+                                "modified": datetime.datetime.fromisoformat(
+                                    model["modified_at"]
+                                ),
+                            }
+                        )
+
+        return models
 
     def listRunning(self):
         """
         Public method to request a list of running models from the 'ollama' server.
         """
-        # TODO: not implemented yet
-        self.__sendRequest("ps", processResponse=self.__processRunningModelsList)
+        response = self.__sendSyncRequest("ps")
 
-    def __processRunningModelsList(self, response):
-        """
-        Private method to process the ps response of the 'ollama' server extracting
-        some model execution details.
-
-        @param response dictionary containing the ps response
-        @type dict
-        """
         models = []
-        with contextlib.suppress(KeyError):
-            for model in response["models"]:
-                name = model["name"]
-                if name:
-                    if model["size_vram"] == 0:
-                        processor = self.tr("100% CPU")
-                    elif model["size_vram"] == model["size"]:
-                        processor = self.tr("100% GPU")
-                    elif model["size_vram"] > model["size_"] or model["size"] == 0:
-                        processor = self.tr("unknown")
-                    else:
-                        sizeCpu = model["size"] - model["size_vram"]
-                        cpuPercent = round(sizeCpu / model["size_vram"] * 100)
-                        processor = self.tr("{0}% / {1}% CPU / GPU").format(
-                            cpuPercent, 100 - cpuPercent
+        if response is not None:
+            with contextlib.suppress(KeyError):
+                for model in response["models"]:
+                    name = model["name"]
+                    if name:
+                        if model["size_vram"] == 0:
+                            processor = self.tr("100% CPU")
+                        elif model["size_vram"] == model["size"]:
+                            processor = self.tr("100% GPU")
+                        elif model["size_vram"] > model["size"] or model["size"] == 0:
+                            processor = self.tr("unknown")
+                        else:
+                            sizeCpu = model["size"] - model["size_vram"]
+                            cpuPercent = round(sizeCpu / model["size_vram"] * 100)
+                            processor = self.tr("{0}% / {1}% CPU / GPU").format(
+                                cpuPercent, 100 - cpuPercent
+                            )
+                        models.append(
+                            {
+                                "name": name,
+                                "id": model["digest"][:20],  # first 20 characters only
+                                "size": model["size"],
+                                "size_vram": model["size_vram"],
+                                "processor": processor,
+                                "expires": datetime.datetime.fromisoformat(
+                                    model["expires_at"]
+                                ),
+                            }
                         )
-                    models.append(
-                        {
-                            "name": name,
-                            "id": model["digest"][:20],  # first 20 characters only
-                            "size": model["size"],
-                            "size_vram": model["size_vram"],
-                            "processor": processor,
-                            "expires": datetime.datetime.fromisoformat(
-                                model["expires_at"]
-                            ),
-                        }
-                    )
-        self.runningModelsList.emit(models)
+
+        return models
 
     def version(self):
         """
@@ -346,22 +325,20 @@
         """
         return self.__state
 
-    def __sendRequest(self, endpoint, data=None, processResponse=None):
+    def __getServerReply(self, endpoint, data=None):
         """
-        Private method to send a request to the 'ollama' server and handle its
-        responses.
+        Private method to send a request to the 'ollama' server and return a reply
+        object.
 
         @param endpoint 'ollama' API endpoint to be contacted
         @type str
         @param data dictionary containing the data to send to the server
             (defaults to None)
         @type dict (optional)
-        @param processResponse function handling the received data (defaults to None)
-        @type function (optional)
+        @return 'ollama' server reply
+        @rtype QNetworkReply
         """
-        self.__state = OllamaClientState.Requesting
-
-        ollamaUrl = QUrl(
+        ollamaUrl =  QUrl(
             "{0}://{1}:{2}/api/{3}".format(
                 self.__plugin.getPreferences("OllamaScheme"),
                 (
@@ -386,9 +363,53 @@
             reply = self.__networkManager.post(request, jsonData)
         else:
             reply = self.__networkManager.get(request)
+        reply.errorOccurred.connect(lambda error: self.__errorOccurred(error, reply))
+        return reply
 
+    def __sendRequest(self, endpoint, data=None, processResponse=None):
+        """
+        Private method to send a request to the 'ollama' server and handle its
+        responses.
+
+        @param endpoint 'ollama' API endpoint to be contacted
+        @type str
+        @param data dictionary containing the data to send to the server
+            (defaults to None)
+        @type dict (optional)
+        @param processResponse function handling the received data (defaults to None)
+        @type function (optional)
+        """
+        self.__state = OllamaClientState.Requesting
+
+        ##ollamaUrl = QUrl(
+            ##"{0}://{1}:{2}/api/{3}".format(
+                ##self.__plugin.getPreferences("OllamaScheme"),
+                ##(
+                    ##"127.0.0.1"
+                    ##if self.__localServer
+                    ##else self.__plugin.getPreferences("OllamaHost")
+                ##),
+                ##(
+                    ##self.__plugin.getPreferences("OllamaLocalPort")
+                    ##if self.__localServer
+                    ##else self.__plugin.getPreferences("OllamaPort")
+                ##),
+                ##endpoint,
+            ##)
+        ##)
+        ##request = QNetworkRequest(ollamaUrl)
+        ##if data is not None:
+            ##request.setHeader(
+                ##QNetworkRequest.KnownHeaders.ContentTypeHeader, "application/json"
+            ##)
+            ##jsonData = json.dumps(data).encode("utf-8")
+            ##reply = self.__networkManager.post(request, jsonData)
+        ##else:
+            ##reply = self.__networkManager.get(request)
+##
+        reply = self.__getServerReply(endpoint=endpoint, data=data)
         reply.finished.connect(lambda: self.__replyFinished(reply))
-        reply.errorOccurred.connect(lambda error: self.__errorOccurred(error, reply))
+        ##reply.errorOccurred.connect(lambda error: self.__errorOccurred(error, reply))
         reply.readyRead.connect(lambda: self.__processData(reply, processResponse))
         self.__replies.append(reply)
 
@@ -441,6 +462,36 @@
                 if data and processResponse:
                     processResponse(data)
 
+    def __sendSyncRequest(self, endpoint, data=None):
+        """
+        Private method to send a request to the 'ollama' server and handle its
+        responses.
+
+        @param endpoint 'ollama' API endpoint to be contacted
+        @type str
+        @param data dictionary containing the data to send to the server
+            (defaults to None)
+        @type dict (optional)
+        """
+        self.__state = OllamaClientState.Requesting
+
+        reply = self.__getServerReply(endpoint=endpoint, data=data)
+        while not reply.isFinished():
+            QCoreApplication.processEvents()
+            QThread.msleep(100)
+
+        reply.deleteLater()
+
+        self.__state = OllamaClientState.Finished
+
+        if reply.error() == QNetworkReply.NetworkError.NoError:
+            buffer = bytes(reply.readAll())
+            with contextlib.suppress(json.JSONDecodeError):
+                data = json.loads(buffer)
+                return data
+
+        return None
+
     def heartbeat(self):
         """
         Public method to check, if the 'ollama' server has started and is responsive.

eric ide

mercurial