|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2020 - 2024 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing a dialog to run the ollama server locally. |
|
8 """ |
|
9 |
|
10 from PyQt6.QtCore import QProcess, Qt, QTimer, pyqtSignal, pyqtSlot |
|
11 from PyQt6.QtWidgets import QDialog, QDialogButtonBox |
|
12 |
|
13 from eric7.EricWidgets import EricMessageBox |
|
14 |
|
15 from .Ui_RunOllamaServerDialog import Ui_RunOllamaServerDialog |
|
16 |
|
17 |
|
18 class RunOllamaServerDialog(QDialog, Ui_RunOllamaServerDialog): |
|
19 """ |
|
20 Class implementing a dialog to run the ollama server locally. |
|
21 |
|
22 @signal serverStarted() emitted after the start of the 'ollama' server |
|
23 @signal serverStopped() emitted after the 'ollama' server was stopped |
|
24 """ |
|
25 |
|
26 serverStarted = pyqtSignal() |
|
27 serverStopped = pyqtSignal() |
|
28 |
|
29 def __init__(self, ollamaClient, plugin, parent=None): |
|
30 """ |
|
31 Constructor |
|
32 |
|
33 @param ollamaClient reference to the 'ollama' client object |
|
34 @type OllamaClient |
|
35 @param plugin reference to the plug-in object |
|
36 @type PluginOllamaInterface |
|
37 @param parent reference to the parent widget |
|
38 @type QWidget |
|
39 """ |
|
40 super().__init__(parent) |
|
41 self.setupUi(self) |
|
42 |
|
43 self.__plugin = plugin |
|
44 self.__ui = parent |
|
45 self.__client = ollamaClient |
|
46 |
|
47 self.__process = None |
|
48 |
|
49 self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled(True) |
|
50 self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setDefault(True) |
|
51 |
|
52 self.__defaultTextFormat = self.outputEdit.currentCharFormat() |
|
53 |
|
54 def startServer(self): |
|
55 """ |
|
56 Public method to start the ollama server process. |
|
57 |
|
58 @return flag indicating success |
|
59 @rtype bool |
|
60 """ |
|
61 env = self.__ui.prepareServerRuntimeEnvironment() |
|
62 self.__process = QProcess() |
|
63 self.__process.setProcessEnvironment(env) |
|
64 self.__process.setProcessChannelMode(QProcess.ProcessChannelMode.MergedChannels) |
|
65 |
|
66 self.__process.readyReadStandardOutput.connect(self.__readStdOut) |
|
67 self.__process.finished.connect(self.__processFinished) |
|
68 |
|
69 self.outputEdit.clear() |
|
70 |
|
71 command = "ollama" |
|
72 args = ["serve"] |
|
73 |
|
74 self.__process.start(command, args) |
|
75 ok = self.__process.waitForStarted(10000) |
|
76 if not ok: |
|
77 EricMessageBox.critical( |
|
78 None, |
|
79 self.tr("Run Local ollama Server"), |
|
80 self.tr("""The loacl ollama server process could not be started."""), |
|
81 ) |
|
82 else: |
|
83 self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled( |
|
84 False |
|
85 ) |
|
86 self.stopServerButton.setEnabled(True) |
|
87 self.stopServerButton.setDefault(True) |
|
88 self.restartServerButton.setEnabled(True) |
|
89 |
|
90 self.serverStarted.emit() |
|
91 |
|
92 return ok |
|
93 |
|
94 def closeEvent(self, evt): |
|
95 """ |
|
96 Protected method handling a close event. |
|
97 |
|
98 @param evt reference to the close event |
|
99 @type QCloseEvent |
|
100 """ |
|
101 self.on_stopServerButton_clicked() |
|
102 evt.accept() |
|
103 |
|
104 @pyqtSlot() |
|
105 def __readStdOut(self): |
|
106 """ |
|
107 Private slot to add the server process output to the output pane. |
|
108 """ |
|
109 if self.__process is not None: |
|
110 out = str(self.__process.readAllStandardOutput(), "utf-8") |
|
111 self.outputEdit.insertPlainText(out) |
|
112 |
|
113 @pyqtSlot() |
|
114 def __processFinished(self): |
|
115 """ |
|
116 Private slot handling the finishing of the server process. |
|
117 """ |
|
118 if ( |
|
119 self.__process is not None |
|
120 and self.__process.state() != QProcess.ProcessState.NotRunning |
|
121 ): |
|
122 self.__process.terminate() |
|
123 QTimer.singleShot(2000, self.__process.kill) |
|
124 self.__process.waitForFinished(3000) |
|
125 |
|
126 self.__process = None |
|
127 |
|
128 self.restartServerButton.setEnabled(True) |
|
129 self.stopServerButton.setEnabled(False) |
|
130 self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled(True) |
|
131 self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setDefault(True) |
|
132 self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setFocus( |
|
133 Qt.FocusReason.OtherFocusReason |
|
134 ) |
|
135 |
|
136 self.serverStopped.emit() |
|
137 |
|
138 @pyqtSlot() |
|
139 def on_stopServerButton_clicked(self): |
|
140 """ |
|
141 Private slot to stop the running server. |
|
142 """ |
|
143 self.__process.terminate() |
|
144 |
|
145 @pyqtSlot() |
|
146 def on_restartServerButton_clicked(self): |
|
147 """ |
|
148 Private slot to re-start the ollama server. |
|
149 """ |
|
150 # step 1: stop the current server |
|
151 if self.__process is not None: |
|
152 self.on_stopServerButton_clicked() |
|
153 |
|
154 # step 2: start a new server |
|
155 self.startServer() |