204 """ |
204 """ |
205 Public method to ask the 'ollama' server to delete the given model. |
205 Public method to ask the 'ollama' server to delete the given model. |
206 |
206 |
207 @param model name of the model |
207 @param model name of the model |
208 @type str |
208 @type str |
209 """ |
209 @return flag indicating success |
210 # TODO: not implemented yet |
210 @rtype bool |
|
211 """ |
211 ollamaRequest = { |
212 ollamaRequest = { |
212 "name": model, |
213 "model": model, |
213 } |
214 } |
214 self.__sendRequest("delete", data=ollamaRequest) |
215 _, status = self.__sendSyncRequest("delete", data=ollamaRequest, delete=True) |
|
216 return status == 200 # HTTP status 200 OK |
215 |
217 |
216 def list(self): |
218 def list(self): |
217 """ |
219 """ |
218 Public method to request a list of models available locally from the 'ollama' |
220 Public method to request a list of models available locally from the 'ollama' |
219 server. |
221 server. |
230 models = [] |
232 models = [] |
231 with contextlib.suppress(KeyError): |
233 with contextlib.suppress(KeyError): |
232 for model in response["models"]: |
234 for model in response["models"]: |
233 name = model["name"] |
235 name = model["name"] |
234 if name: |
236 if name: |
235 models.append(name.replace(":latest", "")) |
237 models.append(name) |
236 self.modelsList.emit(models) |
238 self.modelsList.emit(models) |
237 |
239 |
238 def listDetails(self): |
240 def listDetails(self): |
239 """ |
241 """ |
240 Public method to request a list of models available locally from the 'ollama' |
242 Public method to request a list of models available locally from the 'ollama' |
241 server with some model details. |
243 server with some model details. |
242 """ |
244 |
243 response = self.__sendSyncRequest("tags") |
245 @return list of dictionaries containing the available models and related data |
|
246 @rtype list[dict[str, Any]] |
|
247 """ |
|
248 response, _ = self.__sendSyncRequest("tags") |
244 |
249 |
245 models = [] |
250 models = [] |
246 if response is not None: |
251 if response is not None: |
247 with contextlib.suppress(KeyError): |
252 with contextlib.suppress(KeyError): |
248 for model in response["models"]: |
253 for model in response["models"]: |
262 return models |
267 return models |
263 |
268 |
264 def listRunning(self): |
269 def listRunning(self): |
265 """ |
270 """ |
266 Public method to request a list of running models from the 'ollama' server. |
271 Public method to request a list of running models from the 'ollama' server. |
267 """ |
272 |
268 response = self.__sendSyncRequest("ps") |
273 @return list of dictionaries containing the running models and related data |
|
274 @rtype list[dict[str, Any]] |
|
275 """ |
|
276 response, _ = self.__sendSyncRequest("ps") |
269 |
277 |
270 models = [] |
278 models = [] |
271 if response is not None: |
279 if response is not None: |
272 with contextlib.suppress(KeyError): |
280 with contextlib.suppress(KeyError): |
273 for model in response["models"]: |
281 for model in response["models"]: |
323 @return current client state |
331 @return current client state |
324 @rtype OllamaClientState |
332 @rtype OllamaClientState |
325 """ |
333 """ |
326 return self.__state |
334 return self.__state |
327 |
335 |
328 def __getServerReply(self, endpoint, data=None): |
336 def __getServerReply(self, endpoint, data=None, delete=False): |
329 """ |
337 """ |
330 Private method to send a request to the 'ollama' server and return a reply |
338 Private method to send a request to the 'ollama' server and return a reply |
331 object. |
339 object. |
332 |
340 |
333 @param endpoint 'ollama' API endpoint to be contacted |
341 @param endpoint 'ollama' API endpoint to be contacted |
334 @type str |
342 @type str |
335 @param data dictionary containing the data to send to the server |
343 @param data dictionary containing the data to send to the server |
336 (defaults to None) |
344 (defaults to None) |
337 @type dict (optional) |
345 @type dict (optional) |
|
346 @param delete flag indicating to send a delete request (defaults to False) |
|
347 @type bool (optional) |
338 @return 'ollama' server reply |
348 @return 'ollama' server reply |
339 @rtype QNetworkReply |
349 @rtype QNetworkReply |
340 """ |
350 """ |
341 ollamaUrl = QUrl( |
351 ollamaUrl = QUrl( |
342 "{0}://{1}:{2}/api/{3}".format( |
352 "{0}://{1}:{2}/api/{3}".format( |
343 self.__plugin.getPreferences("OllamaScheme"), |
353 self.__plugin.getPreferences("OllamaScheme"), |
344 ( |
354 ( |
345 "127.0.0.1" |
355 "127.0.0.1" |
346 if self.__localServer |
356 if self.__localServer |
358 if data is not None: |
368 if data is not None: |
359 request.setHeader( |
369 request.setHeader( |
360 QNetworkRequest.KnownHeaders.ContentTypeHeader, "application/json" |
370 QNetworkRequest.KnownHeaders.ContentTypeHeader, "application/json" |
361 ) |
371 ) |
362 jsonData = json.dumps(data).encode("utf-8") |
372 jsonData = json.dumps(data).encode("utf-8") |
363 reply = self.__networkManager.post(request, jsonData) |
373 if delete: |
|
374 reply = self.__networkManager.sendCustomRequest( |
|
375 request, b"DELETE", jsonData |
|
376 ) |
|
377 else: |
|
378 reply = self.__networkManager.post(request, jsonData) |
364 else: |
379 else: |
365 reply = self.__networkManager.get(request) |
380 reply = self.__networkManager.get(request) |
366 reply.errorOccurred.connect(lambda error: self.__errorOccurred(error, reply)) |
381 reply.errorOccurred.connect(lambda error: self.__errorOccurred(error, reply)) |
367 return reply |
382 return reply |
368 |
383 |
379 @param processResponse function handling the received data (defaults to None) |
394 @param processResponse function handling the received data (defaults to None) |
380 @type function (optional) |
395 @type function (optional) |
381 """ |
396 """ |
382 self.__state = OllamaClientState.Requesting |
397 self.__state = OllamaClientState.Requesting |
383 |
398 |
384 ##ollamaUrl = QUrl( |
|
385 ##"{0}://{1}:{2}/api/{3}".format( |
|
386 ##self.__plugin.getPreferences("OllamaScheme"), |
|
387 ##( |
|
388 ##"127.0.0.1" |
|
389 ##if self.__localServer |
|
390 ##else self.__plugin.getPreferences("OllamaHost") |
|
391 ##), |
|
392 ##( |
|
393 ##self.__plugin.getPreferences("OllamaLocalPort") |
|
394 ##if self.__localServer |
|
395 ##else self.__plugin.getPreferences("OllamaPort") |
|
396 ##), |
|
397 ##endpoint, |
|
398 ##) |
|
399 ##) |
|
400 ##request = QNetworkRequest(ollamaUrl) |
|
401 ##if data is not None: |
|
402 ##request.setHeader( |
|
403 ##QNetworkRequest.KnownHeaders.ContentTypeHeader, "application/json" |
|
404 ##) |
|
405 ##jsonData = json.dumps(data).encode("utf-8") |
|
406 ##reply = self.__networkManager.post(request, jsonData) |
|
407 ##else: |
|
408 ##reply = self.__networkManager.get(request) |
|
409 ## |
|
410 reply = self.__getServerReply(endpoint=endpoint, data=data) |
399 reply = self.__getServerReply(endpoint=endpoint, data=data) |
411 reply.finished.connect(lambda: self.__replyFinished(reply)) |
400 reply.finished.connect(lambda: self.__replyFinished(reply)) |
412 ##reply.errorOccurred.connect(lambda error: self.__errorOccurred(error, reply)) |
|
413 reply.readyRead.connect(lambda: self.__processData(reply, processResponse)) |
401 reply.readyRead.connect(lambda: self.__processData(reply, processResponse)) |
414 self.__replies.append(reply) |
402 self.__replies.append(reply) |
415 |
403 |
416 def __replyFinished(self, reply): |
404 def __replyFinished(self, reply): |
417 """ |
405 """ |
460 with contextlib.suppress(json.JSONDecodeError): |
448 with contextlib.suppress(json.JSONDecodeError): |
461 data = json.loads(buffer) |
449 data = json.loads(buffer) |
462 if data and processResponse: |
450 if data and processResponse: |
463 processResponse(data) |
451 processResponse(data) |
464 |
452 |
465 def __sendSyncRequest(self, endpoint, data=None): |
453 def __sendSyncRequest(self, endpoint, data=None, delete=False): |
466 """ |
454 """ |
467 Private method to send a request to the 'ollama' server and handle its |
455 Private method to send a request to the 'ollama' server and handle its |
468 responses. |
456 responses. |
469 |
457 |
470 @param endpoint 'ollama' API endpoint to be contacted |
458 @param endpoint 'ollama' API endpoint to be contacted |
471 @type str |
459 @type str |
472 @param data dictionary containing the data to send to the server |
460 @param data dictionary containing the data to send to the server |
473 (defaults to None) |
461 (defaults to None) |
474 @type dict (optional) |
462 @type dict (optional) |
|
463 @param delete flag indicating to send a delete request (defaults to False) |
|
464 @type bool (optional) |
|
465 @return tuple containing the data sent by the 'ollama' server and the HTTP |
|
466 status code |
|
467 @rtype tuple of (Any, int) |
475 """ |
468 """ |
476 self.__state = OllamaClientState.Requesting |
469 self.__state = OllamaClientState.Requesting |
477 |
470 |
478 reply = self.__getServerReply(endpoint=endpoint, data=data) |
471 reply = self.__getServerReply(endpoint=endpoint, data=data, delete=delete) |
479 while not reply.isFinished(): |
472 while not reply.isFinished(): |
480 QCoreApplication.processEvents() |
473 QCoreApplication.processEvents() |
481 QThread.msleep(100) |
474 QThread.msleep(100) |
482 |
475 |
483 reply.deleteLater() |
476 reply.deleteLater() |
484 |
477 |
485 self.__state = OllamaClientState.Finished |
478 self.__state = OllamaClientState.Finished |
|
479 |
|
480 statusCode = reply.attribute(QNetworkRequest.Attribute.HttpStatusCodeAttribute) |
486 |
481 |
487 if reply.error() == QNetworkReply.NetworkError.NoError: |
482 if reply.error() == QNetworkReply.NetworkError.NoError: |
488 buffer = bytes(reply.readAll()) |
483 buffer = bytes(reply.readAll()) |
489 with contextlib.suppress(json.JSONDecodeError): |
484 with contextlib.suppress(json.JSONDecodeError): |
490 data = json.loads(buffer) |
485 data = json.loads(buffer) |
491 return data |
486 return data, statusCode |
492 |
487 |
493 return None |
488 return None, statusCode |
494 |
489 |
495 def heartbeat(self): |
490 def heartbeat(self): |
496 """ |
491 """ |
497 Public method to check, if the 'ollama' server has started and is responsive. |
492 Public method to check, if the 'ollama' server has started and is responsive. |
498 |
493 |