pygpt-net 2.4.28__py3-none-any.whl → 2.4.34__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. CHANGELOG.md +40 -0
  2. README.md +62 -5
  3. pygpt_net/CHANGELOG.txt +40 -0
  4. pygpt_net/__init__.py +3 -3
  5. pygpt_net/controller/access/__init__.py +5 -5
  6. pygpt_net/controller/access/control.py +3 -2
  7. pygpt_net/controller/attachment.py +67 -1
  8. pygpt_net/controller/audio/__init__.py +34 -6
  9. pygpt_net/controller/chat/__init__.py +3 -1
  10. pygpt_net/controller/chat/attachment.py +239 -37
  11. pygpt_net/controller/chat/audio.py +99 -0
  12. pygpt_net/controller/chat/input.py +10 -3
  13. pygpt_net/controller/chat/output.py +4 -1
  14. pygpt_net/controller/chat/text.py +10 -5
  15. pygpt_net/controller/dialogs/confirm.py +17 -1
  16. pygpt_net/controller/kernel/reply.py +5 -8
  17. pygpt_net/controller/lang/custom.py +3 -1
  18. pygpt_net/controller/mode.py +2 -1
  19. pygpt_net/controller/presets/editor.py +11 -2
  20. pygpt_net/core/access/voice.py +2 -2
  21. pygpt_net/core/agents/legacy.py +3 -1
  22. pygpt_net/core/attachments/__init__.py +11 -7
  23. pygpt_net/core/attachments/context.py +226 -44
  24. pygpt_net/core/{audio.py → audio/__init__.py} +1 -1
  25. pygpt_net/core/audio/context.py +34 -0
  26. pygpt_net/core/bridge/context.py +29 -1
  27. pygpt_net/core/bridge/worker.py +16 -1
  28. pygpt_net/core/ctx/__init__.py +4 -1
  29. pygpt_net/core/db/__init__.py +4 -2
  30. pygpt_net/core/debug/attachments.py +3 -1
  31. pygpt_net/core/debug/context.py +5 -1
  32. pygpt_net/core/debug/presets.py +3 -1
  33. pygpt_net/core/docker/__init__.py +170 -16
  34. pygpt_net/core/docker/builder.py +6 -2
  35. pygpt_net/core/events/event.py +3 -1
  36. pygpt_net/core/experts/__init__.py +24 -6
  37. pygpt_net/core/idx/chat.py +55 -4
  38. pygpt_net/core/idx/indexing.py +123 -15
  39. pygpt_net/core/modes.py +3 -1
  40. pygpt_net/core/presets.py +13 -2
  41. pygpt_net/core/render/markdown/pid.py +2 -1
  42. pygpt_net/core/render/plain/pid.py +2 -1
  43. pygpt_net/core/render/web/body.py +34 -12
  44. pygpt_net/core/render/web/pid.py +2 -1
  45. pygpt_net/core/render/web/renderer.py +12 -3
  46. pygpt_net/core/tokens.py +4 -2
  47. pygpt_net/core/types/mode.py +2 -1
  48. pygpt_net/data/config/config.json +7 -4
  49. pygpt_net/data/config/models.json +191 -6
  50. pygpt_net/data/config/modes.json +11 -5
  51. pygpt_net/data/config/presets/current.audio.json +34 -0
  52. pygpt_net/data/config/settings.json +15 -1
  53. pygpt_net/data/css/web.css +70 -0
  54. pygpt_net/data/css/web.dark.css +4 -1
  55. pygpt_net/data/css/web.light.css +1 -1
  56. pygpt_net/data/locale/locale.de.ini +33 -20
  57. pygpt_net/data/locale/locale.en.ini +73 -58
  58. pygpt_net/data/locale/locale.es.ini +33 -20
  59. pygpt_net/data/locale/locale.fr.ini +35 -22
  60. pygpt_net/data/locale/locale.it.ini +33 -20
  61. pygpt_net/data/locale/locale.pl.ini +36 -23
  62. pygpt_net/data/locale/locale.uk.ini +33 -20
  63. pygpt_net/data/locale/locale.zh.ini +40 -27
  64. pygpt_net/data/locale/plugin.cmd_code_interpreter.de.ini +6 -0
  65. pygpt_net/data/locale/plugin.cmd_code_interpreter.en.ini +15 -7
  66. pygpt_net/data/locale/plugin.cmd_code_interpreter.es.ini +6 -0
  67. pygpt_net/data/locale/plugin.cmd_code_interpreter.fr.ini +6 -0
  68. pygpt_net/data/locale/plugin.cmd_code_interpreter.it.ini +6 -0
  69. pygpt_net/data/locale/plugin.cmd_code_interpreter.pl.ini +6 -0
  70. pygpt_net/data/locale/plugin.cmd_code_interpreter.uk.ini +6 -0
  71. pygpt_net/data/locale/plugin.cmd_code_interpreter.zh.ini +6 -0
  72. pygpt_net/data/locale/plugin.cmd_files.de.ini +4 -4
  73. pygpt_net/data/locale/plugin.cmd_files.en.ini +4 -4
  74. pygpt_net/data/locale/plugin.cmd_files.es.ini +4 -4
  75. pygpt_net/data/locale/plugin.cmd_files.fr.ini +4 -4
  76. pygpt_net/data/locale/plugin.cmd_files.it.ini +4 -4
  77. pygpt_net/data/locale/plugin.cmd_files.pl.ini +4 -4
  78. pygpt_net/data/locale/plugin.cmd_files.uk.ini +4 -4
  79. pygpt_net/data/locale/plugin.cmd_files.zh.ini +4 -4
  80. pygpt_net/data/locale/plugin.cmd_system.de.ini +6 -6
  81. pygpt_net/data/locale/plugin.cmd_system.en.ini +12 -6
  82. pygpt_net/data/locale/plugin.cmd_system.es.ini +6 -6
  83. pygpt_net/data/locale/plugin.cmd_system.fr.ini +6 -6
  84. pygpt_net/data/locale/plugin.cmd_system.it.ini +6 -6
  85. pygpt_net/data/locale/plugin.cmd_system.pl.ini +6 -6
  86. pygpt_net/data/locale/plugin.cmd_system.uk.ini +6 -6
  87. pygpt_net/data/locale/plugin.cmd_system.zh.ini +6 -6
  88. pygpt_net/data/locale/plugin.cmd_web.de.ini +5 -5
  89. pygpt_net/data/locale/plugin.cmd_web.en.ini +5 -5
  90. pygpt_net/data/locale/plugin.cmd_web.es.ini +5 -5
  91. pygpt_net/data/locale/plugin.cmd_web.fr.ini +5 -5
  92. pygpt_net/data/locale/plugin.cmd_web.it.ini +5 -5
  93. pygpt_net/data/locale/plugin.cmd_web.pl.ini +5 -5
  94. pygpt_net/data/locale/plugin.cmd_web.uk.ini +5 -5
  95. pygpt_net/data/locale/plugin.cmd_web.zh.ini +5 -5
  96. pygpt_net/data/locale/plugin.idx_llama_index.de.ini +12 -12
  97. pygpt_net/data/locale/plugin.idx_llama_index.en.ini +12 -12
  98. pygpt_net/data/locale/plugin.idx_llama_index.es.ini +12 -12
  99. pygpt_net/data/locale/plugin.idx_llama_index.fr.ini +12 -12
  100. pygpt_net/data/locale/plugin.idx_llama_index.it.ini +12 -12
  101. pygpt_net/data/locale/plugin.idx_llama_index.pl.ini +12 -12
  102. pygpt_net/data/locale/plugin.idx_llama_index.uk.ini +12 -12
  103. pygpt_net/data/locale/plugin.idx_llama_index.zh.ini +12 -12
  104. pygpt_net/item/attachment.py +9 -1
  105. pygpt_net/item/ctx.py +9 -1
  106. pygpt_net/item/preset.py +5 -1
  107. pygpt_net/launcher.py +3 -1
  108. pygpt_net/migrations/Version20241126170000.py +28 -0
  109. pygpt_net/migrations/__init__.py +3 -1
  110. pygpt_net/plugin/audio_input/__init__.py +11 -1
  111. pygpt_net/plugin/audio_input/worker.py +9 -1
  112. pygpt_net/plugin/audio_output/__init__.py +37 -7
  113. pygpt_net/plugin/audio_output/worker.py +38 -41
  114. pygpt_net/plugin/cmd_code_interpreter/__init__.py +51 -35
  115. pygpt_net/plugin/cmd_code_interpreter/builder.py +16 -4
  116. pygpt_net/plugin/cmd_code_interpreter/config.py +98 -39
  117. pygpt_net/plugin/cmd_code_interpreter/docker.py +4 -0
  118. pygpt_net/plugin/cmd_code_interpreter/ipython/__init__.py +13 -0
  119. pygpt_net/plugin/cmd_code_interpreter/{ipython.py → ipython/docker_kernel.py} +10 -3
  120. pygpt_net/plugin/cmd_code_interpreter/ipython/local_kernel.py +220 -0
  121. pygpt_net/plugin/cmd_code_interpreter/runner.py +5 -5
  122. pygpt_net/plugin/cmd_mouse_control/__init__.py +4 -2
  123. pygpt_net/plugin/cmd_system/config.py +50 -0
  124. pygpt_net/plugin/cmd_system/docker.py +4 -0
  125. pygpt_net/plugin/idx_llama_index/__init__.py +23 -1
  126. pygpt_net/plugin/idx_llama_index/worker.py +10 -0
  127. pygpt_net/plugin/openai_dalle/__init__.py +3 -1
  128. pygpt_net/plugin/openai_vision/__init__.py +3 -1
  129. pygpt_net/provider/core/attachment/json_file.py +4 -1
  130. pygpt_net/provider/core/config/patch.py +25 -0
  131. pygpt_net/provider/core/ctx/db_sqlite/storage.py +14 -4
  132. pygpt_net/provider/core/ctx/db_sqlite/utils.py +19 -2
  133. pygpt_net/provider/core/model/patch.py +7 -1
  134. pygpt_net/provider/core/preset/json_file.py +5 -1
  135. pygpt_net/provider/gpt/__init__.py +14 -2
  136. pygpt_net/provider/gpt/audio.py +63 -0
  137. pygpt_net/provider/gpt/chat.py +76 -44
  138. pygpt_net/provider/gpt/utils.py +27 -0
  139. pygpt_net/provider/gpt/vision.py +37 -15
  140. pygpt_net/provider/loaders/base.py +10 -1
  141. pygpt_net/provider/loaders/web_yt.py +19 -1
  142. pygpt_net/tools/code_interpreter/__init__.py +1 -0
  143. pygpt_net/tools/image_viewer/ui/dialogs.py +3 -1
  144. pygpt_net/ui/dialog/preset.py +3 -1
  145. pygpt_net/ui/dialog/url.py +29 -0
  146. pygpt_net/ui/dialogs.py +5 -1
  147. pygpt_net/ui/layout/chat/attachments.py +42 -6
  148. pygpt_net/ui/layout/chat/attachments_ctx.py +14 -4
  149. pygpt_net/ui/layout/chat/attachments_uploaded.py +8 -4
  150. pygpt_net/ui/layout/toolbox/agent.py +8 -7
  151. pygpt_net/ui/layout/toolbox/agent_llama.py +5 -4
  152. pygpt_net/ui/layout/toolbox/prompt.py +8 -6
  153. pygpt_net/ui/menu/tools.py +17 -11
  154. pygpt_net/ui/widget/anims/toggles.py +167 -0
  155. pygpt_net/ui/widget/dialog/url.py +59 -0
  156. pygpt_net/ui/widget/element/group.py +2 -1
  157. pygpt_net/ui/widget/lists/attachment.py +22 -17
  158. pygpt_net/ui/widget/lists/attachment_ctx.py +65 -3
  159. pygpt_net/ui/widget/option/checkbox.py +69 -5
  160. pygpt_net/ui/widget/option/cmd.py +4 -5
  161. pygpt_net/ui/widget/option/toggle.py +62 -0
  162. pygpt_net/ui/widget/option/toggle_label.py +79 -0
  163. pygpt_net/ui/widget/textarea/url.py +43 -0
  164. {pygpt_net-2.4.28.dist-info → pygpt_net-2.4.34.dist-info}/METADATA +65 -7
  165. {pygpt_net-2.4.28.dist-info → pygpt_net-2.4.34.dist-info}/RECORD +168 -154
  166. {pygpt_net-2.4.28.dist-info → pygpt_net-2.4.34.dist-info}/LICENSE +0 -0
  167. {pygpt_net-2.4.28.dist-info → pygpt_net-2.4.34.dist-info}/WHEEL +0 -0
  168. {pygpt_net-2.4.28.dist-info → pygpt_net-2.4.34.dist-info}/entry_points.txt +0 -0
@@ -6,11 +6,12 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2024.11.20 03:00:00 #
9
+ # Updated Date: 2024.11.26 19:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6.QtCore import Slot
13
13
 
14
+ from pygpt_net.core.types import MODE_AUDIO
14
15
  from pygpt_net.plugin.base.plugin import BasePlugin
15
16
  from pygpt_net.provider.audio_output.base import BaseProvider
16
17
  from pygpt_net.core.events import Event
@@ -118,15 +119,21 @@ class Plugin(BasePlugin):
118
119
  name = event.name
119
120
  data = event.data
120
121
  ctx = event.ctx
122
+ mode = self.window.core.config.get("mode")
121
123
 
122
124
  if name == Event.INPUT_BEFORE:
123
125
  self.on_input_before(data['value'])
124
126
 
125
- elif name in [
126
- Event.CTX_AFTER,
127
- Event.AUDIO_READ_TEXT
128
- ]:
129
- self.on_ctx_after(ctx, event)
127
+ elif name == Event.CTX_AFTER:
128
+ if mode == MODE_AUDIO:
129
+ return # skip if audio mode
130
+ self.on_generate(ctx, event)
131
+
132
+ elif name == Event.AUDIO_READ_TEXT:
133
+ self.on_generate(ctx, event)
134
+
135
+ elif name == Event.AUDIO_PLAYBACK:
136
+ self.on_playback(ctx, event)
130
137
 
131
138
  elif name == Event.AUDIO_OUTPUT_STOP:
132
139
  self.stop_audio()
@@ -139,7 +146,7 @@ class Plugin(BasePlugin):
139
146
  """
140
147
  self.input_text = text
141
148
 
142
- def on_ctx_after(self, ctx: CtxItem, event: Event):
149
+ def on_generate(self, ctx: CtxItem, event: Event):
143
150
  """
144
151
  Events: CTX_AFTER, AUDIO_READ_TEXT
145
152
 
@@ -166,6 +173,7 @@ class Plugin(BasePlugin):
166
173
  worker.event = name
167
174
  worker.cache_file = cache_file
168
175
  worker.text = self.window.core.audio.clean_text(text)
176
+ worker.mode = "generate"
169
177
 
170
178
  # signals
171
179
  worker.signals.playback.connect(self.handle_playback)
@@ -180,6 +188,28 @@ class Plugin(BasePlugin):
180
188
  except Exception as e:
181
189
  self.error(e)
182
190
 
191
+ def on_playback(self, ctx: CtxItem, event: Event):
192
+ """
193
+ Events: AUDIO_PLAYBACK
194
+
195
+ :param ctx: CtxItem
196
+ :param event: Event
197
+ """
198
+ try:
199
+ worker = Worker()
200
+ worker.from_defaults(self)
201
+ worker.audio_file = event.data["audio_file"]
202
+ worker.mode = "playback"
203
+
204
+ # signals
205
+ worker.signals.playback.connect(self.handle_playback)
206
+ worker.signals.stop.connect(self.handle_stop)
207
+
208
+ worker.run_async()
209
+
210
+ except Exception as e:
211
+ self.error(e)
212
+
183
213
  def destroy(self):
184
214
  """Destroy thread"""
185
215
  pass
@@ -6,7 +6,7 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2024.11.18 21:00:00 #
9
+ # Updated Date: 2024.11.26 19:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import time
@@ -30,28 +30,50 @@ class Worker(BaseWorker):
30
30
  self.text = None
31
31
  self.event = None
32
32
  self.cache_file = None # path to cache file
33
+ self.mode = "generate" # generate|playback
34
+ self.audio_file = None
33
35
 
34
36
  @Slot()
35
37
  def run(self):
36
- from pygame import mixer
37
38
  try:
38
- if self.text is None or self.text == "":
39
- time.sleep(0.2) # wait
40
- return
41
- path = self.plugin.get_provider().speech(self.text)
42
- if path:
43
- mixer.init()
44
- playback = mixer.Sound(path)
45
- self.stop_playback() # stop previous playback
46
- playback.play()
47
- self.send(playback) # send playback object to main thread
48
-
49
- # store in cache if enabled
50
- if self.cache_file:
51
- self.cache_audio_file(path, self.cache_file)
39
+ if self.mode == "generate":
40
+ self.generate()
41
+ elif self.mode == "playback":
42
+ self.play()
52
43
  except Exception as e:
53
44
  self.error(e)
54
45
 
46
+ def generate(self):
47
+ """
48
+ Generate and play audio file
49
+ """
50
+ if self.text is None or self.text == "":
51
+ time.sleep(0.2) # wait
52
+ return
53
+ path = self.plugin.get_provider().speech(self.text)
54
+ if path:
55
+ from pygame import mixer
56
+ mixer.init()
57
+ playback = mixer.Sound(path)
58
+ self.stop_playback() # stop previous playback
59
+ playback.play()
60
+ self.send(playback) # send playback object to main thread to allow force stop
61
+
62
+ # store in cache if enabled
63
+ if self.cache_file:
64
+ self.cache_audio_file(path, self.cache_file)
65
+
66
+ def play(self):
67
+ """
68
+ Play audio file only
69
+ """
70
+ if self.audio_file:
71
+ from pygame import mixer
72
+ mixer.init()
73
+ playback = mixer.Sound(self.audio_file)
74
+ playback.play()
75
+ self.send(playback) # send playback object to main thread to allow force stop
76
+
55
77
  def cache_audio_file(self, src: str, dst: str):
56
78
  """
57
79
  Store audio file in cache
@@ -81,28 +103,3 @@ class Worker(BaseWorker):
81
103
  def stop(self):
82
104
  """Send stop signal to main thread"""
83
105
  self.signals.stop.emit()
84
-
85
-
86
- class PlayWorker(BaseWorker):
87
- def __init__(self, *args, **kwargs):
88
- super(PlayWorker, self).__init__()
89
- self.signals = WorkerSignals()
90
- self.args = args
91
- self.kwargs = kwargs
92
- self.window = None
93
- self.path = None
94
-
95
- @Slot()
96
- def run(self):
97
- from pygame import mixer
98
- try:
99
- if self.path:
100
- mixer.init()
101
- playback = mixer.Sound(self.path)
102
- playback.play()
103
- except Exception as e:
104
- self.error(e)
105
-
106
-
107
-
108
-
@@ -6,7 +6,7 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2024.11.24 06:00:00 #
9
+ # Updated Date: 2024.11.25 02:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import os
@@ -20,7 +20,8 @@ from pygpt_net.item.ctx import CtxItem
20
20
  from .config import Config
21
21
  from .docker import Docker
22
22
  from .builder import Builder
23
- from .ipython import IPythonInterpreter
23
+ from .ipython import LocalKernel
24
+ from .ipython import DockerKernel
24
25
  from .output import Output
25
26
  from .runner import Runner
26
27
  from .worker import Worker
@@ -55,7 +56,8 @@ class Plugin(BasePlugin):
55
56
  self.use_locale = True
56
57
  self.docker = Docker(self)
57
58
  self.runner = Runner(self)
58
- self.ipython = IPythonInterpreter(self)
59
+ self.ipython_docker = DockerKernel(self)
60
+ self.ipython_local = LocalKernel(self)
59
61
  self.builder = Builder(self)
60
62
  self.output = Output(self)
61
63
  self.worker = None
@@ -100,17 +102,20 @@ class Plugin(BasePlugin):
100
102
  :param data: event data dict
101
103
  """
102
104
  # get current working directory
103
- cwd = self.window.core.config.get_user_dir('data')
104
- ipython_data = os.path.join(cwd, 'ipython')
105
- if self.get_option_value("sandbox_docker"):
106
- cwd = "/data (in docker sandbox)"
105
+ legacy_data = self.window.core.config.get_user_dir('data')
106
+ ipython_data = os.path.join(legacy_data, 'ipython')
107
107
 
108
108
  for item in self.allowed_cmds:
109
109
  if self.has_cmd(item):
110
110
  cmd = self.get_cmd(item)
111
- if item == "ipython_execute" or item == "ipython_execute_new":
112
- cmd["instruction"] += ("\nIPython works in Docker container. Directory /data is the container's workdir - "
113
- "directory is bound in host machine to: {}").format(ipython_data)
111
+ if item in ["ipython_execute", "ipython_execute_new"]:
112
+ if self.get_option_value("sandbox_ipython"):
113
+ cmd["instruction"] += ("\nIPython works in Docker container. Directory /data is the container's workdir - "
114
+ "directory is mapped as volume in host machine to: {}").format(ipython_data)
115
+ elif item in ["code_execute", "code_execute_file", "code_execute_all"]:
116
+ if self.get_option_value("sandbox_docker"):
117
+ cmd["instruction"] += ("\nPython works in Docker container. Directory /data is the container's workdir - "
118
+ "directory is mapped as volume in host machine to: {}").format(legacy_data)
114
119
  data['cmd'].append(cmd) # append command
115
120
 
116
121
  @Slot(object, str)
@@ -139,6 +144,16 @@ class Plugin(BasePlugin):
139
144
  """
140
145
  self.window.tools.get("html_canvas").set_output(data)
141
146
  self.window.tools.get("html_canvas").open()
147
+ def get_interpreter(self):
148
+ """
149
+ Get interpreter
150
+
151
+ :return: interpreter
152
+ """
153
+ if self.get_option_value("sandbox_ipython"):
154
+ return self.ipython_docker
155
+ else:
156
+ return self.ipython_local
142
157
 
143
158
  def cmd(self, ctx: CtxItem, cmds: list, silent: bool = False):
144
159
  """
@@ -161,30 +176,31 @@ class Plugin(BasePlugin):
161
176
  if not is_cmd:
162
177
  return
163
178
 
164
- ipython_commands = [
165
- "ipython_execute_new",
166
- "ipython_execute",
167
- "ipython_kernel_restart",
168
- ]
169
179
  # ipython
170
- if any(x in [x["cmd"] for x in my_commands] for x in ipython_commands):
171
- # check for Docker installed
172
- if not self.ipython.is_docker_installed():
173
- # snap version
174
- if self.window.core.platforms.is_snap():
175
- self.error(trans('ipython.docker.install.snap'))
176
- self.window.update_status(trans('ipython.docker.install.snap'))
177
- # other versions
178
- else:
179
- self.error(trans('ipython.docker.install'))
180
- self.window.update_status(trans('ipython.docker.install'))
181
- return
182
- # check if image exists
183
- if not self.ipython.is_image():
184
- self.error(trans('ipython.image.build'))
185
- self.window.update_status(trans('ipython.docker.build.start'))
186
- self.builder.build_image()
187
- return
180
+ if self.get_option_value("sandbox_ipython"):
181
+ ipython_commands = [
182
+ "ipython_execute_new",
183
+ "ipython_execute",
184
+ "ipython_kernel_restart",
185
+ ]
186
+ if any(x in [x["cmd"] for x in my_commands] for x in ipython_commands):
187
+ # check for Docker installed
188
+ if not self.get_interpreter().is_docker_installed():
189
+ # snap version
190
+ if self.window.core.platforms.is_snap():
191
+ self.error(trans('ipython.docker.install.snap'))
192
+ self.window.update_status(trans('ipython.docker.install.snap'))
193
+ # other versions
194
+ else:
195
+ self.error(trans('ipython.docker.install'))
196
+ self.window.update_status(trans('ipython.docker.install'))
197
+ return
198
+ # check if image exists
199
+ if not self.get_interpreter().is_image():
200
+ self.error(trans('ipython.image.build'))
201
+ self.window.update_status(trans('ipython.docker.build.start'))
202
+ self.builder.build_image()
203
+ return
188
204
 
189
205
  # legacy python
190
206
  if self.get_option_value("sandbox_docker"):
@@ -227,7 +243,7 @@ class Plugin(BasePlugin):
227
243
  worker.signals.clear.connect(self.handle_interpreter_clear)
228
244
  worker.signals.html_output.connect(self.handle_html_output)
229
245
  worker.signals.ipython_output.connect(self.handle_ipython_output)
230
- self.ipython.attach_signals(worker.signals)
246
+ self.get_interpreter().attach_signals(worker.signals)
231
247
  self.runner.attach_signals(worker.signals)
232
248
 
233
249
  if not self.is_async(ctx) and not force:
@@ -250,7 +266,7 @@ class Plugin(BasePlugin):
250
266
  # if self.is_threaded():
251
267
  # return
252
268
  # print(data)
253
- cleaned_data = self.ipython.remove_ansi(data)
269
+ cleaned_data = self.get_interpreter().remove_ansi(data)
254
270
  self.window.tools.get("interpreter").append_output(cleaned_data)
255
271
  if self.window.tools.get("interpreter").opened:
256
272
  self.window.update_status("")
@@ -6,7 +6,7 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2024.11.20 21:00:00 #
9
+ # Updated Date: 2024.11.25 02:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6.QtCore import Slot, Signal, QObject
@@ -21,11 +21,20 @@ class Builder(QObject):
21
21
  self.plugin = plugin
22
22
  self.worker = None
23
23
 
24
- def build_image(self):
25
- """Run IPython image build"""
24
+ def build_and_restart(self):
25
+ """Run IPython image build and restart container"""
26
+ self.build_image(restart=True)
27
+
28
+ def build_image(self, restart: bool = False):
29
+ """
30
+ Run IPython image build
31
+
32
+ :param restart: Restart container
33
+ """
26
34
  try:
27
35
  self.worker = Worker()
28
36
  self.worker.plugin = self.plugin
37
+ self.worker.restart = restart
29
38
  self.worker.signals.build_finished.connect(self.handle_build_finished)
30
39
  self.worker.signals.error.connect(self.handle_build_failed)
31
40
  self.plugin.window.threadpool.start(self.worker)
@@ -60,11 +69,14 @@ class Worker(BaseWorker):
60
69
  self.args = args
61
70
  self.kwargs = kwargs
62
71
  self.plugin = None
72
+ self.restart = False
63
73
 
64
74
  @Slot()
65
75
  def run(self):
66
76
  try:
67
- self.plugin.ipython.build_image()
77
+ self.plugin.get_interpreter().build_image()
78
+ if self.restart:
79
+ self.plugin.get_interpreter().restart()
68
80
  self.signals.build_finished.emit()
69
81
  except Exception as e:
70
82
  self.signals.error.emit(e)
@@ -6,7 +6,7 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2024.11.24 06:00:00 #
9
+ # Updated Date: 2024.11.25 02:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from pygpt_net.plugin.base.config import BaseConfig, BasePlugin
@@ -23,35 +23,36 @@ class Config(BaseConfig):
23
23
 
24
24
  :param plugin: plugin instance
25
25
  """
26
- dockerfile = '# Tip: After making changes to this Dockerfile, you must rebuild the image to apply the changes'
27
- dockerfile += '(Menu -> Tools -> Rebuild IPython Docker Image)'
28
- dockerfile += '\n\n'
29
- dockerfile += 'FROM python:3.9'
30
- dockerfile += '\n\n'
31
- dockerfile += '# You can customize the packages installed by default here:'
32
- dockerfile += '\n# ========================================================'
33
- dockerfile += '\nRUN pip install jupyter ipykernel'
34
- dockerfile += '\n# ========================================================'
35
- dockerfile += '\n\n'
36
- dockerfile += 'RUN mkdir /data'
37
- dockerfile += '\n\n'
38
- dockerfile += '# Expose the necessary ports for Jupyter kernel communication'
39
- dockerfile += '\nEXPOSE 5555 5556 5557 5558 5559'
40
- dockerfile += '\n\n'
41
- dockerfile += '# Data directory, bound as a volume to the local \'data/ipython\' directory'
42
- dockerfile += '\nWORKDIR /data'
43
- dockerfile += '\n\n'
44
- dockerfile += '# Start the IPython kernel with specified ports and settings'
45
- dockerfile += '\nCMD ["ipython", "kernel", \\'
46
- dockerfile += '\n--ip=0.0.0.0, \\'
47
- dockerfile += '\n--transport=tcp, \\'
48
- dockerfile += '\n--shell=5555, \\'
49
- dockerfile += '\n--iopub=5556, \\'
50
- dockerfile += '\n--stdin=5557, \\'
51
- dockerfile += '\n--control=5558, \\'
52
- dockerfile += '\n--hb=5559, \\'
53
- dockerfile += '\n--Session.key=19749810-8febfa748186a01da2f7b28c, \\'
54
- dockerfile += '\n--Session.signature_scheme=hmac-sha256]'
26
+ dockerfile = '''
27
+ # Tip: After making changes to this Dockerfile, you must rebuild the image to apply the changes(Menu -> Tools -> Rebuild IPython Docker Image)
28
+
29
+ FROM python:3.9
30
+
31
+ # You can customize the packages installed by default here:
32
+ # ========================================================
33
+ RUN pip install jupyter ipykernel
34
+ # ========================================================
35
+
36
+ RUN mkdir /data
37
+
38
+ # Expose the necessary ports for Jupyter kernel communication
39
+ EXPOSE 5555 5556 5557 5558 5559
40
+
41
+ # Data directory, bound as a volume to the local 'data/ipython' directory
42
+ WORKDIR /data
43
+
44
+ # Start the IPython kernel with specified ports and settings
45
+ CMD ["ipython", "kernel", \
46
+ "--ip=0.0.0.0", \
47
+ "--transport=tcp", \
48
+ "--shell=5555", \
49
+ "--iopub=5556", \
50
+ "--stdin=5557", \
51
+ "--control=5558", \
52
+ "--hb=5559", \
53
+ "--Session.key=19749810-8febfa748186a01da2f7b28c", \
54
+ "--Session.signature_scheme=hmac-sha256"]
55
+ '''
55
56
 
56
57
  dockerfile_legacy = 'FROM python:3.9-alpine'
57
58
  dockerfile_legacy += '\n\n'
@@ -60,6 +61,15 @@ class Config(BaseConfig):
60
61
  dockerfile_legacy += '# Data directory, bound as a volume to the local \'data/\' directory'
61
62
  dockerfile_legacy += '\nWORKDIR /data'
62
63
 
64
+ plugin.add_option(
65
+ "sandbox_ipython",
66
+ type="bool",
67
+ value=False,
68
+ label="Sandbox (docker container)",
69
+ description="Executes commands in sandbox (docker container). "
70
+ "Docker must be installed and running.",
71
+ tab="ipython",
72
+ )
63
73
  plugin.add_option(
64
74
  "ipython_dockerfile",
65
75
  type="textarea",
@@ -131,6 +141,26 @@ class Config(BaseConfig):
131
141
  tab="ipython",
132
142
  )
133
143
  """
144
+
145
+ volumes_keys = {
146
+ "enabled": "bool",
147
+ "docker": "text",
148
+ "host": "text",
149
+ }
150
+ volumes_items = [
151
+ {
152
+ "enabled": True,
153
+ "docker": "/data",
154
+ "host": "{workdir}",
155
+ },
156
+ ]
157
+ ports_keys = {
158
+ "enabled": "bool",
159
+ "docker": "text",
160
+ "host": "int",
161
+ }
162
+ ports_items = []
163
+
134
164
  plugin.add_cmd(
135
165
  "ipython_kernel_restart",
136
166
  instruction="restart IPython kernel",
@@ -179,15 +209,6 @@ class Config(BaseConfig):
179
209
  tab="ipython",
180
210
  advanced=True,
181
211
  )
182
-
183
- plugin.add_option(
184
- "python_cmd_tpl",
185
- type="text",
186
- value="python3 {filename}",
187
- label="Python command template",
188
- description="Python command template to execute, use {filename} for filename placeholder",
189
- tab="python_legacy",
190
- )
191
212
  plugin.add_option(
192
213
  "sandbox_docker",
193
214
  type="bool",
@@ -197,6 +218,14 @@ class Config(BaseConfig):
197
218
  "Docker must be installed and running.",
198
219
  tab="python_legacy",
199
220
  )
221
+ plugin.add_option(
222
+ "python_cmd_tpl",
223
+ type="text",
224
+ value="python3 {filename}",
225
+ label="Python command template",
226
+ description="Python command template to execute, use {filename} for filename placeholder",
227
+ tab="python_legacy",
228
+ )
200
229
  plugin.add_option(
201
230
  "dockerfile",
202
231
  type="textarea",
@@ -220,6 +249,36 @@ class Config(BaseConfig):
220
249
  label="Docker container name",
221
250
  tab="python_legacy",
222
251
  )
252
+ plugin.add_option(
253
+ "docker_entrypoint",
254
+ type="text",
255
+ value='tail -f /dev/null',
256
+ label="Docker run command",
257
+ tab="python_legacy",
258
+ advanced=True,
259
+ )
260
+ plugin.add_option(
261
+ "docker_volumes",
262
+ type="dict",
263
+ value=volumes_items,
264
+ label="Docker volumes",
265
+ description="Docker volumes mapping",
266
+ tooltip="Docker volumes mapping",
267
+ keys=volumes_keys,
268
+ tab="python_legacy",
269
+ advanced=True,
270
+ )
271
+ plugin.add_option(
272
+ "docker_ports",
273
+ type="dict",
274
+ value=ports_items,
275
+ label="Docker ports",
276
+ description="Docker ports mapping",
277
+ tooltip="Docker ports mapping",
278
+ keys=ports_keys,
279
+ tab="python_legacy",
280
+ advanced=True,
281
+ )
223
282
  plugin.add_option(
224
283
  "attach_output",
225
284
  type="bool",
@@ -26,6 +26,10 @@ class Docker(BaseDocker):
26
26
  """Run image build"""
27
27
  self.builder.build_image()
28
28
 
29
+ def build_and_restart(self):
30
+ """Run image build and restart container"""
31
+ self.builder.build_image(restart=True)
32
+
29
33
  def get_dockerfile(self) -> str:
30
34
  """
31
35
  Get the Dockerfile
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # ================================================== #
4
+ # This file is a part of PYGPT package #
5
+ # Website: https://pygpt.net #
6
+ # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
+ # MIT License #
8
+ # Created By : Marcin Szczygliński #
9
+ # Updated Date: 2024.11.25 02:00:00 #
10
+ # ================================================== #
11
+
12
+ from .local_kernel import LocalKernel
13
+ from .docker_kernel import DockerKernel
@@ -6,7 +6,7 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2024.11.17 17:00:00 #
9
+ # Updated Date: 2024.11.25 02:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import os
@@ -19,7 +19,7 @@ import tarfile
19
19
  from jupyter_client import BlockingKernelClient
20
20
  from docker.errors import DockerException
21
21
 
22
- class IPythonInterpreter:
22
+ class DockerKernel:
23
23
  def __init__(self, plugin = None):
24
24
  self.plugin = plugin
25
25
  self.kernel_file = ".interpreter.kernel.json"
@@ -96,6 +96,10 @@ class IPythonInterpreter:
96
96
  except docker.errors.ImageNotFound:
97
97
  return False
98
98
 
99
+ def restart(self):
100
+ """Restart the container."""
101
+ self.restart_container(self.get_container_name())
102
+
99
103
  def build_image(self):
100
104
  """Build the Docker image for the IPython kernel."""
101
105
  client = self.get_docker_client()
@@ -231,6 +235,8 @@ class IPythonInterpreter:
231
235
 
232
236
  # run the container
233
237
  try:
238
+ print("Running container {}...".format(name))
239
+ self.prepare_conn()
234
240
  local_data_dir = self.get_local_data_dir()
235
241
  client.containers.run(
236
242
  self.get_image_name(),
@@ -383,7 +389,8 @@ class IPythonInterpreter:
383
389
  self.restart_container(self.get_container_name())
384
390
  if self.client is not None:
385
391
  self.client.stop_channels()
386
- self.client = BlockingKernelClient(connection_file=self.get_kernel_file_path())
392
+ else:
393
+ self.client = BlockingKernelClient(connection_file=self.get_kernel_file_path())
387
394
  self.client.load_connection_file()
388
395
  self.client.start_channels()
389
396
  self.client.wait_for_ready()