pygpt-net 2.6.55__py3-none-any.whl → 2.6.57__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 (218) hide show
  1. pygpt_net/CHANGELOG.txt +12 -0
  2. pygpt_net/__init__.py +3 -3
  3. pygpt_net/app.py +26 -22
  4. pygpt_net/config.py +44 -0
  5. pygpt_net/controller/audio/audio.py +0 -0
  6. pygpt_net/controller/calendar/calendar.py +0 -0
  7. pygpt_net/controller/calendar/note.py +0 -0
  8. pygpt_net/controller/chat/chat.py +0 -0
  9. pygpt_net/controller/chat/handler/openai_stream.py +2 -1
  10. pygpt_net/controller/chat/handler/worker.py +0 -0
  11. pygpt_net/controller/chat/remote_tools.py +5 -3
  12. pygpt_net/controller/chat/render.py +0 -0
  13. pygpt_net/controller/chat/text.py +0 -0
  14. pygpt_net/controller/ctx/common.py +0 -0
  15. pygpt_net/controller/debug/debug.py +26 -2
  16. pygpt_net/controller/debug/fixtures.py +1 -1
  17. pygpt_net/controller/dialogs/confirm.py +15 -1
  18. pygpt_net/controller/dialogs/debug.py +2 -0
  19. pygpt_net/controller/lang/mapping.py +0 -0
  20. pygpt_net/controller/launcher/launcher.py +0 -0
  21. pygpt_net/controller/mode/mode.py +0 -0
  22. pygpt_net/controller/presets/presets.py +0 -0
  23. pygpt_net/controller/realtime/realtime.py +0 -0
  24. pygpt_net/controller/theme/theme.py +0 -0
  25. pygpt_net/controller/ui/mode.py +5 -3
  26. pygpt_net/controller/ui/tabs.py +0 -0
  27. pygpt_net/core/agents/agents.py +3 -1
  28. pygpt_net/core/agents/custom.py +150 -0
  29. pygpt_net/core/agents/provider.py +0 -0
  30. pygpt_net/core/builder/__init__.py +12 -0
  31. pygpt_net/core/builder/graph.py +478 -0
  32. pygpt_net/core/calendar/calendar.py +0 -0
  33. pygpt_net/core/ctx/ctx.py +0 -0
  34. pygpt_net/core/ctx/output.py +0 -0
  35. pygpt_net/core/debug/agent.py +0 -0
  36. pygpt_net/core/debug/agent_builder.py +29 -0
  37. pygpt_net/core/debug/console/console.py +0 -0
  38. pygpt_net/core/debug/db.py +0 -0
  39. pygpt_net/core/debug/debug.py +0 -0
  40. pygpt_net/core/debug/events.py +0 -0
  41. pygpt_net/core/debug/indexes.py +0 -0
  42. pygpt_net/core/debug/kernel.py +0 -0
  43. pygpt_net/core/debug/tabs.py +0 -0
  44. pygpt_net/core/filesystem/filesystem.py +0 -0
  45. pygpt_net/core/fixtures/__init__ +0 -0
  46. pygpt_net/core/fixtures/stream/__init__.py +0 -0
  47. pygpt_net/core/fixtures/stream/generator.py +0 -0
  48. pygpt_net/core/models/models.py +2 -1
  49. pygpt_net/core/plugins/plugins.py +60 -0
  50. pygpt_net/core/render/plain/pid.py +0 -0
  51. pygpt_net/core/render/plain/renderer.py +26 -4
  52. pygpt_net/core/render/web/body.py +46 -4
  53. pygpt_net/core/render/web/debug.py +0 -0
  54. pygpt_net/core/render/web/helpers.py +0 -0
  55. pygpt_net/core/render/web/pid.py +0 -0
  56. pygpt_net/core/render/web/renderer.py +15 -20
  57. pygpt_net/core/tabs/tab.py +0 -0
  58. pygpt_net/core/tabs/tabs.py +0 -0
  59. pygpt_net/core/text/utils.py +0 -0
  60. pygpt_net/css.qrc +0 -0
  61. pygpt_net/css_rc.py +0 -0
  62. pygpt_net/data/config/config.json +8 -7
  63. pygpt_net/data/config/models.json +3 -3
  64. pygpt_net/data/config/settings.json +14 -0
  65. pygpt_net/data/css/web-blocks.css +9 -0
  66. pygpt_net/data/css/web-blocks.dark.css +6 -0
  67. pygpt_net/data/css/web-blocks.darkest.css +6 -0
  68. pygpt_net/data/css/web-chatgpt.css +14 -6
  69. pygpt_net/data/css/web-chatgpt.dark.css +6 -0
  70. pygpt_net/data/css/web-chatgpt.darkest.css +6 -0
  71. pygpt_net/data/css/web-chatgpt.light.css +6 -0
  72. pygpt_net/data/css/web-chatgpt_wide.css +14 -6
  73. pygpt_net/data/css/web-chatgpt_wide.dark.css +6 -0
  74. pygpt_net/data/css/web-chatgpt_wide.darkest.css +6 -0
  75. pygpt_net/data/css/web-chatgpt_wide.light.css +6 -0
  76. pygpt_net/data/fixtures/fake_stream.txt +14 -1
  77. pygpt_net/data/icons/case.svg +0 -0
  78. pygpt_net/data/icons/chat1.svg +0 -0
  79. pygpt_net/data/icons/chat2.svg +0 -0
  80. pygpt_net/data/icons/chat3.svg +0 -0
  81. pygpt_net/data/icons/chat4.svg +0 -0
  82. pygpt_net/data/icons/fit.svg +0 -0
  83. pygpt_net/data/icons/note1.svg +0 -0
  84. pygpt_net/data/icons/note2.svg +0 -0
  85. pygpt_net/data/icons/note3.svg +0 -0
  86. pygpt_net/data/icons/stt.svg +0 -0
  87. pygpt_net/data/icons/translate.svg +0 -0
  88. pygpt_net/data/icons/tts.svg +0 -0
  89. pygpt_net/data/icons/url.svg +0 -0
  90. pygpt_net/data/icons/vision.svg +0 -0
  91. pygpt_net/data/icons/web_off.svg +0 -0
  92. pygpt_net/data/icons/web_on.svg +0 -0
  93. pygpt_net/data/js/app/async.js +166 -0
  94. pygpt_net/data/js/app/bridge.js +88 -0
  95. pygpt_net/data/js/app/common.js +212 -0
  96. pygpt_net/data/js/app/config.js +223 -0
  97. pygpt_net/data/js/app/custom.js +961 -0
  98. pygpt_net/data/js/app/data.js +84 -0
  99. pygpt_net/data/js/app/dom.js +322 -0
  100. pygpt_net/data/js/app/events.js +400 -0
  101. pygpt_net/data/js/app/highlight.js +542 -0
  102. pygpt_net/data/js/app/logger.js +305 -0
  103. pygpt_net/data/js/app/markdown.js +1137 -0
  104. pygpt_net/data/js/app/math.js +167 -0
  105. pygpt_net/data/js/app/nodes.js +395 -0
  106. pygpt_net/data/js/app/queue.js +260 -0
  107. pygpt_net/data/js/app/raf.js +250 -0
  108. pygpt_net/data/js/app/runtime.js +582 -0
  109. pygpt_net/data/js/app/scroll.js +433 -0
  110. pygpt_net/data/js/app/stream.js +2708 -0
  111. pygpt_net/data/js/app/template.js +287 -0
  112. pygpt_net/data/js/app/tool.js +87 -0
  113. pygpt_net/data/js/app/ui.js +86 -0
  114. pygpt_net/data/js/app/user.js +380 -0
  115. pygpt_net/data/js/app/utils.js +64 -0
  116. pygpt_net/data/js/app.min.js +880 -0
  117. pygpt_net/data/js/markdown-it/markdown-it-katex.min.js +1 -1
  118. pygpt_net/data/js/markdown-it/markdown-it.min.js +0 -0
  119. pygpt_net/data/locale/locale.de.ini +3 -1
  120. pygpt_net/data/locale/locale.en.ini +8 -0
  121. pygpt_net/data/locale/locale.es.ini +2 -0
  122. pygpt_net/data/locale/locale.fr.ini +2 -0
  123. pygpt_net/data/locale/locale.it.ini +2 -0
  124. pygpt_net/data/locale/locale.pl.ini +3 -1
  125. pygpt_net/data/locale/locale.uk.ini +3 -1
  126. pygpt_net/data/locale/locale.zh.ini +2 -0
  127. pygpt_net/data/locale/plugin.osm.en.ini +24 -24
  128. pygpt_net/data/locale/plugin.wolfram.en.ini +9 -9
  129. pygpt_net/fonts.qrc +0 -0
  130. pygpt_net/fonts_rc.py +0 -0
  131. pygpt_net/icons.qrc +0 -0
  132. pygpt_net/icons_rc.py +0 -0
  133. pygpt_net/item/agent.py +62 -0
  134. pygpt_net/item/builder_layout.py +62 -0
  135. pygpt_net/js.qrc +24 -1
  136. pygpt_net/js_rc.py +51394 -33687
  137. pygpt_net/plugin/base/worker.py +0 -0
  138. pygpt_net/plugin/cmd_web/config.py +17 -17
  139. pygpt_net/plugin/cmd_web/worker.py +325 -171
  140. pygpt_net/plugin/mcp/__init__.py +0 -0
  141. pygpt_net/plugin/mcp/config.py +0 -0
  142. pygpt_net/plugin/mcp/plugin.py +0 -0
  143. pygpt_net/plugin/mcp/worker.py +0 -0
  144. pygpt_net/plugin/osm/__init__.py +0 -0
  145. pygpt_net/plugin/osm/config.py +0 -0
  146. pygpt_net/plugin/osm/plugin.py +0 -0
  147. pygpt_net/plugin/osm/worker.py +0 -0
  148. pygpt_net/plugin/wolfram/__init__.py +0 -0
  149. pygpt_net/plugin/wolfram/config.py +0 -0
  150. pygpt_net/plugin/wolfram/plugin.py +0 -0
  151. pygpt_net/plugin/wolfram/worker.py +0 -0
  152. pygpt_net/provider/api/anthropic/tools.py +0 -0
  153. pygpt_net/provider/api/google/__init__.py +0 -0
  154. pygpt_net/provider/api/google/video.py +0 -0
  155. pygpt_net/provider/api/openai/agents/experts.py +0 -0
  156. pygpt_net/provider/api/openai/agents/remote_tools.py +0 -0
  157. pygpt_net/provider/api/openai/remote_tools.py +0 -0
  158. pygpt_net/provider/api/openai/responses.py +0 -0
  159. pygpt_net/provider/api/x_ai/__init__.py +2 -0
  160. pygpt_net/provider/api/x_ai/remote.py +0 -0
  161. pygpt_net/provider/core/agent/__init__.py +10 -0
  162. pygpt_net/provider/core/agent/base.py +51 -0
  163. pygpt_net/provider/core/agent/json_file.py +200 -0
  164. pygpt_net/provider/core/config/patch.py +33 -0
  165. pygpt_net/provider/core/config/patches/__init__.py +0 -0
  166. pygpt_net/provider/core/config/patches/patch_before_2_6_42.py +1 -0
  167. pygpt_net/provider/core/ctx/db_sqlite/storage.py +0 -0
  168. pygpt_net/provider/core/model/patches/__init__.py +0 -0
  169. pygpt_net/provider/core/model/patches/patch_before_2_6_42.py +0 -0
  170. pygpt_net/provider/core/preset/patch.py +0 -0
  171. pygpt_net/provider/core/preset/patches/__init__.py +0 -0
  172. pygpt_net/provider/core/preset/patches/patch_before_2_6_42.py +0 -0
  173. pygpt_net/provider/llms/anthropic.py +4 -0
  174. pygpt_net/provider/llms/base.py +2 -0
  175. pygpt_net/provider/llms/deepseek_api.py +2 -0
  176. pygpt_net/provider/llms/google.py +2 -0
  177. pygpt_net/provider/llms/hugging_face_api.py +4 -0
  178. pygpt_net/provider/llms/hugging_face_embedding.py +0 -0
  179. pygpt_net/provider/llms/hugging_face_router.py +2 -0
  180. pygpt_net/provider/llms/local.py +0 -0
  181. pygpt_net/provider/llms/mistral.py +4 -0
  182. pygpt_net/provider/llms/open_router.py +0 -0
  183. pygpt_net/provider/llms/perplexity.py +2 -0
  184. pygpt_net/provider/llms/utils.py +0 -0
  185. pygpt_net/provider/llms/voyage.py +0 -0
  186. pygpt_net/provider/llms/x_ai.py +2 -0
  187. pygpt_net/tools/agent_builder/__init__.py +12 -0
  188. pygpt_net/tools/agent_builder/tool.py +292 -0
  189. pygpt_net/tools/agent_builder/ui/__init__.py +0 -0
  190. pygpt_net/tools/agent_builder/ui/dialogs.py +152 -0
  191. pygpt_net/tools/agent_builder/ui/list.py +228 -0
  192. pygpt_net/tools/code_interpreter/ui/html.py +0 -0
  193. pygpt_net/tools/code_interpreter/ui/widgets.py +0 -0
  194. pygpt_net/tools/html_canvas/tool.py +23 -6
  195. pygpt_net/tools/html_canvas/ui/widgets.py +224 -2
  196. pygpt_net/ui/layout/chat/chat.py +0 -0
  197. pygpt_net/ui/layout/chat/output.py +5 -5
  198. pygpt_net/ui/main.py +10 -9
  199. pygpt_net/ui/menu/debug.py +39 -1
  200. pygpt_net/ui/widget/builder/__init__.py +12 -0
  201. pygpt_net/ui/widget/builder/editor.py +2001 -0
  202. pygpt_net/ui/widget/dialog/base.py +4 -1
  203. pygpt_net/ui/widget/draw/painter.py +0 -0
  204. pygpt_net/ui/widget/element/labels.py +9 -4
  205. pygpt_net/ui/widget/lists/db.py +0 -0
  206. pygpt_net/ui/widget/lists/debug.py +0 -0
  207. pygpt_net/ui/widget/tabs/body.py +0 -0
  208. pygpt_net/ui/widget/textarea/html.py +1 -0
  209. pygpt_net/ui/widget/textarea/input.py +28 -10
  210. pygpt_net/ui/widget/textarea/output.py +21 -1
  211. pygpt_net/ui/widget/textarea/web.py +31 -3
  212. pygpt_net/utils.py +40 -0
  213. {pygpt_net-2.6.55.dist-info → pygpt_net-2.6.57.dist-info}/METADATA +16 -2
  214. {pygpt_net-2.6.55.dist-info → pygpt_net-2.6.57.dist-info}/RECORD +116 -77
  215. pygpt_net/data/js/app.js +0 -5869
  216. {pygpt_net-2.6.55.dist-info → pygpt_net-2.6.57.dist-info}/LICENSE +0 -0
  217. {pygpt_net-2.6.55.dist-info → pygpt_net-2.6.57.dist-info}/WHEEL +0 -0
  218. {pygpt_net-2.6.55.dist-info → pygpt_net-2.6.57.dist-info}/entry_points.txt +0 -0
@@ -10,7 +10,7 @@
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6.QtWidgets import QApplication
13
- from PySide6.QtCore import QRect, QSize, QPoint, QEvent
13
+ from PySide6.QtCore import QRect, QSize, QPoint, QEvent, Qt
14
14
  from PySide6.QtWidgets import QDialog
15
15
 
16
16
 
@@ -26,6 +26,9 @@ class BaseDialog(QDialog):
26
26
  self.window = window
27
27
  self.id = id
28
28
  self.disable_geometry_store = False
29
+ self.setWindowFlags(
30
+ self.windowFlags() | Qt.WindowMaximizeButtonHint
31
+ )
29
32
 
30
33
  def showEvent(self, event):
31
34
  """
File without changes
@@ -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: 2025.09.16 22:00:00 #
9
+ # Updated Date: 2025.09.19 00:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6.QtCore import Qt, QTimer, QRect, Signal, QUrl, QEvent
@@ -124,15 +124,16 @@ class CmdLabel(QLineEdit):
124
124
  class IconLabel(QLabel):
125
125
  clicked = Signal()
126
126
 
127
- def __init__(self, icon: str, window=None):
127
+ def __init__(self, icon: str, window=None, hover=True):
128
128
  super().__init__("", window)
129
129
  self.window = window
130
130
  self.setWordWrap(True)
131
131
  self.setAlignment(Qt.AlignRight)
132
132
  self.setProperty('class', 'label-chat-status')
133
133
  self.setContentsMargins(0, 0, 0, 0)
134
- self.setMouseTracking(True)
135
-
134
+ self.hover = hover
135
+ if hover:
136
+ self.setMouseTracking(True)
136
137
  self.original_pixmap = None
137
138
  self.hover_pixmap = None
138
139
  self.set_icon(icon)
@@ -181,6 +182,8 @@ class IconLabel(QLabel):
181
182
  return recolored
182
183
 
183
184
  def enterEvent(self, event: QEvent):
185
+ if not self.hover:
186
+ return
184
187
  self.setCursor(Qt.PointingHandCursor)
185
188
  self.hover_pixmap = self.create_hover_icon(self.original_pixmap)
186
189
  if self.hover_pixmap is not None and not self.hover_pixmap.isNull():
@@ -190,6 +193,8 @@ class IconLabel(QLabel):
190
193
  super().enterEvent(event)
191
194
 
192
195
  def leaveEvent(self, event: QEvent):
196
+ if not self.hover:
197
+ return
193
198
  self.unsetCursor()
194
199
  self.setPixmap(self.original_pixmap)
195
200
  super().leaveEvent(event)
File without changes
File without changes
File without changes
@@ -41,6 +41,7 @@ class HtmlOutput(QWebEngineView):
41
41
  self.html_content = ""
42
42
  self.meta = None
43
43
  self.tab = None
44
+ self.setMouseTracking(True)
44
45
  self.installEventFilter(self)
45
46
 
46
47
  def on_delete(self):
@@ -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: 2025.09.03 00:00:00 #
9
+ # Updated Date: 2025.09.22 12:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from typing import Optional
@@ -30,10 +30,12 @@ class ChatInput(QTextEdit):
30
30
  ICON_PASTE = QIcon(":/icons/paste.svg")
31
31
  ICON_VOLUME = QIcon(":/icons/volume.svg")
32
32
  ICON_SAVE = QIcon(":/icons/save.svg")
33
- ICON_ATTACHMENT = QIcon(":/icons/add.svg")
34
- #ICON_ATTACHMENT = QIcon(":/icons/attachment.svg")
33
+ # ICON_ATTACHMENT = QIcon(":/icons/add.svg")
34
+ ICON_ATTACHMENT = QIcon(":/icons/attachment.svg")
35
35
  ICON_MIC_ON = QIcon(":/icons/mic.svg")
36
36
  ICON_MIC_OFF = QIcon(":/icons/mic_off.svg")
37
+ ICON_WEB_ON = QIcon(":/icons/web_on.svg")
38
+ ICON_WEB_OFF = QIcon(":/icons/web_off.svg")
37
39
 
38
40
  def __init__(self, window=None):
39
41
  """
@@ -87,6 +89,16 @@ class ChatInput(QTextEdit):
87
89
  callback=self.action_toggle_mic,
88
90
  visible=False,
89
91
  )
92
+ # Add a web search toggle button
93
+ self.add_icon(
94
+ key="web",
95
+ icon=self.ICON_WEB_OFF,
96
+ alt_icon=self.ICON_WEB_ON,
97
+ tooltip=trans('icon.remote_tool.web'),
98
+ alt_tooltip=trans('icon.remote_tool.web'),
99
+ callback=self.action_toggle_web,
100
+ visible=True,
101
+ )
90
102
 
91
103
  # Apply initial margins (top padding + left space for icons)
92
104
  self._apply_margins()
@@ -108,7 +120,7 @@ class ChatInput(QTextEdit):
108
120
 
109
121
  self._tokens_timer = QTimer(self)
110
122
  self._tokens_timer.setSingleShot(True)
111
- self._tokens_timer.setInterval(500)
123
+ self._tokens_timer.setInterval(1500)
112
124
  self._tokens_timer.timeout.connect(self.window.controller.ui.update_tokens)
113
125
  self.textChanged.connect(self._on_text_changed_tokens)
114
126
 
@@ -221,27 +233,29 @@ class ChatInput(QTextEdit):
221
233
  def keyPressEvent(self, event):
222
234
  """
223
235
  Key press event
224
-
225
- :param event: key event
226
236
  """
227
237
  handled = False
228
238
  key = event.key()
239
+
229
240
  if key in (Qt.Key_Return, Qt.Key_Enter):
230
241
  mode = self.window.core.config.get('send_mode')
231
242
  if mode > 0:
232
- modifiers = event.modifiers()
243
+ mods = event.modifiers()
244
+ has_shift_or_ctrl = bool(mods & (Qt.ShiftModifier | Qt.ControlModifier))
245
+
233
246
  if mode == 2:
234
- if modifiers == Qt.ShiftModifier or modifiers == Qt.ControlModifier:
247
+ if has_shift_or_ctrl:
235
248
  self.window.controller.chat.input.send_input()
236
249
  handled = True
237
250
  else:
238
- if modifiers != Qt.ShiftModifier and modifiers != Qt.ControlModifier:
251
+ if not has_shift_or_ctrl:
239
252
  self.window.controller.chat.input.send_input()
240
253
  handled = True
254
+
241
255
  self.setFocus()
242
- # Collapse to minimum after sending
243
256
  if handled:
244
257
  QTimer.singleShot(0, self.collapse_to_min)
258
+
245
259
  elif key == Qt.Key_Escape and self.window.controller.ctx.extra.is_editing():
246
260
  self.window.controller.ctx.extra.edit_cancel()
247
261
  handled = True
@@ -288,6 +302,10 @@ class ChatInput(QTextEdit):
288
302
  """Toggle microphone (button click)."""
289
303
  self.window.dispatch(Event(Event.AUDIO_INPUT_RECORD_TOGGLE))
290
304
 
305
+ def action_toggle_web(self):
306
+ """Toggle web search (button click)."""
307
+ self.window.controller.chat.remote_tools.toggle('web_search')
308
+
291
309
  # -------------------- Left icon bar --------------------
292
310
  # - Add icons: add_icon(...) or add_icons([...])
293
311
  # - Show/hide: set_icon_visible(key, bool)
@@ -45,6 +45,9 @@ class ChatOutput(QTextBrowser):
45
45
  self.tab = None
46
46
  self.installEventFilter(self)
47
47
  self.setProperty('class', 'layout-output-plain')
48
+ self._auto_scroll = True
49
+ self._auto_scroll_margin = None
50
+ self.verticalScrollBar().valueChanged.connect(self._on_vsb_value_changed)
48
51
 
49
52
  def on_delete(self):
50
53
  """Clean up on delete"""
@@ -197,4 +200,21 @@ class ChatOutput(QTextBrowser):
197
200
  :param e: focus event
198
201
  """
199
202
  super().focusInEvent(e)
200
- self.window.controller.finder.focus_in(self.finder)
203
+ self.window.controller.finder.focus_in(self.finder)
204
+
205
+ def _calc_auto_scroll_margin(self) -> int:
206
+ vsb = self.verticalScrollBar()
207
+ ps = vsb.pageStep()
208
+ if ps <= 0:
209
+ return 2
210
+ return max(2, min(64, ps // 10))
211
+
212
+ def _on_vsb_value_changed(self, value: int):
213
+ self._auto_scroll = self.was_at_bottom()
214
+
215
+ def was_at_bottom(self) -> bool:
216
+ vsb = self.verticalScrollBar()
217
+ return (vsb.maximum() - vsb.value()) <= self._calc_auto_scroll_margin()
218
+
219
+ def is_auto_scroll_enabled(self) -> bool:
220
+ return self._auto_scroll
@@ -8,13 +8,13 @@
8
8
  # Created By : Marcin Szczygliński #
9
9
  # Updated Date: 2025.09.16 02:00:00 #
10
10
  # ================================================== #
11
-
11
+ from PySide6 import QtCore
12
12
  from PySide6.QtCore import Qt, QObject, Signal, Slot, QEvent, QUrl, QCoreApplication, QEventLoop
13
13
  from PySide6.QtWebChannel import QWebChannel
14
14
  from PySide6.QtWebEngineCore import QWebEngineSettings, QWebEnginePage, QWebEngineProfile
15
15
  from PySide6.QtWebEngineWidgets import QWebEngineView
16
16
  from PySide6.QtGui import QAction, QIcon
17
- from PySide6.QtWidgets import QMenu
17
+ from PySide6.QtWidgets import QMenu, QDialog, QVBoxLayout
18
18
 
19
19
  from pygpt_net.core.events import RenderEvent
20
20
  from pygpt_net.item.ctx import CtxMeta
@@ -44,6 +44,7 @@ class ChatWebOutput(QWebEngineView):
44
44
  self.meta = None
45
45
  self.tab = None
46
46
  self.setProperty('class', 'layout-output-web')
47
+ self.setMouseTracking(True)
47
48
 
48
49
  # OpenGL widgets
49
50
  self._glwidget = None
@@ -87,6 +88,33 @@ class ChatWebOutput(QWebEngineView):
87
88
  pass
88
89
  # self.window.core.debug.log(e)
89
90
 
91
+ def show_devtools(self, modal: bool = False, title: str = "DevTools"):
92
+ """Show DevTools window"""
93
+ if getattr(self, "_devtools_dlg", None) is None:
94
+ dlg = QDialog(self.window)
95
+ dlg.setWindowTitle(title)
96
+ dlg.setModal(modal)
97
+ dlg.setAttribute(Qt.WA_DeleteOnClose, True)
98
+ dlg.resize(1100, 750)
99
+
100
+ layout = QVBoxLayout(dlg)
101
+ view = QWebEngineView(dlg)
102
+ layout.addWidget(view)
103
+
104
+ profile = self.page().profile()
105
+ view.setPage(QWebEnginePage(profile, view))
106
+
107
+ self.page().setDevToolsPage(view.page())
108
+
109
+ dlg.destroyed.connect(lambda: setattr(self, "_devtools_dlg", None))
110
+
111
+ self._devtools_dlg = dlg
112
+ self._devtools_view = view
113
+
114
+ self._devtools_dlg.show()
115
+ self._devtools_dlg.raise_()
116
+ self._devtools_dlg.activateWindow()
117
+
90
118
  def unload(self):
91
119
  """Unload the current page and free resources"""
92
120
  try:
@@ -503,7 +531,7 @@ class CustomWebEnginePage(QWebEnginePage):
503
531
  return super().acceptNavigationRequest(url, _type, isMainFrame)
504
532
 
505
533
  def javaScriptConsoleMessage(self, level, message, line_number, source_id):
506
- # print("[JS CONSOLE] Line", line_number, ":", message)
534
+ print("[JS CONSOLE] Line", line_number, ":", message)
507
535
  self.signals.js_message.emit(line_number, message, source_id) # handled in debug controller
508
536
 
509
537
  def cleanup(self):
pygpt_net/utils.py CHANGED
@@ -78,6 +78,46 @@ def sizeof_fmt(num: Any, suffix: str = 'B'):
78
78
  num /= 1024.0
79
79
  return f"{num:.1f} Yi{suffix}".replace('.', ',')
80
80
 
81
+ def set_env(key: str, value: Any, append: bool = False, allow_overwrite: bool = False):
82
+ """
83
+ Setup environment flag / variable
84
+
85
+ :param key: env key
86
+ :param value: env value
87
+ :param append: append to existing value
88
+ :param allow_overwrite: allow overwrite value before setting
89
+ """
90
+ if allow_overwrite and key in os.environ:
91
+ return
92
+ if append:
93
+ if key in os.environ and os.environ[key]:
94
+ os.environ[key] = f"{os.environ[key]} {value}"
95
+ else:
96
+ os.environ[key] = str(value)
97
+ else:
98
+ os.environ[key] = str(value)
99
+
100
+ def has_env(key: str) -> bool:
101
+ """
102
+ Check if environment variable is set
103
+
104
+ :param key: env key
105
+ :return: True if env variable is set
106
+ """
107
+ return key in os.environ and os.environ[key] != ""
108
+
109
+ def get_env(key: str, default: Any = None) -> Any:
110
+ """
111
+ Get environment variable
112
+
113
+ :param key: env key
114
+ :param default: default value if env variable is not set
115
+ :return: env variable value or default
116
+ """
117
+ if key in os.environ and os.environ[key]:
118
+ return os.environ[key]
119
+ return default
120
+
81
121
 
82
122
  @contextmanager
83
123
  def freeze_updates(widget):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: pygpt-net
3
- Version: 2.6.55
3
+ Version: 2.6.57
4
4
  Summary: Desktop AI Assistant powered by: OpenAI GPT-5, GPT-4, o1, o3, Gemini, Claude, Grok, DeepSeek, and other models supported by Llama Index, and Ollama. Chatbot, agents, completion, image generation, vision analysis, speech-to-text, plugins, MCP, internet access, file handling, command execution and more.
5
5
  License: MIT
6
6
  Keywords: ai,api,api key,app,assistant,bielik,chat,chatbot,chatgpt,claude,dall-e,deepseek,desktop,gemini,gpt,gpt-3.5,gpt-4,gpt-4-vision,gpt-4o,gpt-5,gpt-oss,gpt3.5,gpt4,grok,langchain,llama-index,llama3,mistral,o1,o3,ollama,openai,presets,py-gpt,py_gpt,pygpt,pyside,qt,text completion,tts,ui,vision,whisper
@@ -119,7 +119,7 @@ Description-Content-Type: text/markdown
119
119
 
120
120
  [![pygpt](https://snapcraft.io/pygpt/badge.svg)](https://snapcraft.io/pygpt)
121
121
 
122
- Release: **2.6.55** | build: **2025-09-18** | Python: **>=3.10, <3.14**
122
+ Release: **2.6.57** | build: **2025-09-22** | Python: **>=3.10, <3.14**
123
123
 
124
124
  > Official website: https://pygpt.net | Documentation: https://pygpt.readthedocs.io
125
125
  >
@@ -2321,6 +2321,8 @@ Config -> Settings...
2321
2321
 
2322
2322
  - `OpenGL hardware acceleration`: enables hardware acceleration in `WebEngine / Chromium` renderer. Default: False.
2323
2323
 
2324
+ - `Use proxy`: Enable this option to use a proxy for connections to APIs. Default: False.
2325
+
2324
2326
  - `Proxy address`: Proxy address to be used for connection in API SDKs; supports HTTP/SOCKS, e.g. http://proxy.example.com or socks5://user:pass@host:port
2325
2327
 
2326
2328
  - `Application environment (os.environ)`: Additional environment vars to set on application start.
@@ -3646,6 +3648,18 @@ may consume additional tokens that are not displayed in the main window.
3646
3648
 
3647
3649
  ## Recent changes:
3648
3650
 
3651
+ **2.6.57 (2025-09-22)**
3652
+
3653
+ - Changed: The web search icon has been moved to the input area.
3654
+ - Improved: The local web search plugin has been enhanced to retrieve multiple URLs at once.
3655
+ - Added: Use proxy switch in Settings.
3656
+
3657
+ **2.6.56 (2025-09-22)**
3658
+
3659
+ - Optimized: Memory usage and performance in streaming and rendering large contexts.
3660
+ - Added: Copy to clipboard functionality in user messages.
3661
+ - Added: Manual scroll in plain-text mode.
3662
+
3649
3663
  **2.6.55 (2025-09-18)**
3650
3664
 
3651
3665
  - Fixed: Unnecessary context loading from the database.