pygpt-net 2.5.92__py3-none-any.whl → 2.5.94__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 (63) hide show
  1. pygpt_net/CHANGELOG.txt +12 -0
  2. pygpt_net/__init__.py +3 -3
  3. pygpt_net/app.py +8 -2
  4. pygpt_net/container.py +3 -1
  5. pygpt_net/controller/chat/image.py +0 -3
  6. pygpt_net/controller/chat/response.py +5 -1
  7. pygpt_net/controller/config/field/slider.py +9 -5
  8. pygpt_net/controller/config/placeholder.py +11 -1
  9. pygpt_net/controller/ctx/ctx.py +23 -18
  10. pygpt_net/controller/dialogs/confirm.py +9 -1
  11. pygpt_net/controller/settings/editor.py +0 -6
  12. pygpt_net/controller/ui/tabs.py +2 -2
  13. pygpt_net/core/models/models.py +6 -1
  14. pygpt_net/core/render/web/body.py +67 -2
  15. pygpt_net/core/text/__init__.py +1 -0
  16. pygpt_net/core/text/text.py +59 -0
  17. pygpt_net/core/types/openai.py +14 -1
  18. pygpt_net/data/config/config.json +4 -4
  19. pygpt_net/data/config/models.json +406 -393
  20. pygpt_net/data/config/presets/agent_openai_b2b.json +54 -0
  21. pygpt_net/data/config/settings.json +16 -13
  22. pygpt_net/data/css/web-blocks.css +14 -0
  23. pygpt_net/data/css/web-blocks.dark.css +4 -0
  24. pygpt_net/data/css/web-blocks.light.css +4 -0
  25. pygpt_net/data/css/web-chatgpt.css +14 -0
  26. pygpt_net/data/css/web-chatgpt.dark.css +4 -0
  27. pygpt_net/data/css/web-chatgpt.light.css +4 -0
  28. pygpt_net/data/css/web-chatgpt_wide.css +14 -0
  29. pygpt_net/data/css/web-chatgpt_wide.dark.css +4 -0
  30. pygpt_net/data/css/web-chatgpt_wide.light.css +4 -0
  31. pygpt_net/data/languages.csv +186 -0
  32. pygpt_net/data/locale/locale.de.ini +26 -0
  33. pygpt_net/data/locale/locale.en.ini +28 -0
  34. pygpt_net/data/locale/locale.es.ini +26 -0
  35. pygpt_net/data/locale/locale.fr.ini +26 -0
  36. pygpt_net/data/locale/locale.it.ini +26 -0
  37. pygpt_net/data/locale/locale.pl.ini +28 -1
  38. pygpt_net/data/locale/locale.uk.ini +26 -0
  39. pygpt_net/data/locale/locale.zh.ini +26 -0
  40. pygpt_net/item/model.py +6 -14
  41. pygpt_net/provider/agents/openai/agent_b2b.py +391 -0
  42. pygpt_net/provider/core/config/patch.py +19 -1
  43. pygpt_net/provider/core/model/patch.py +34 -2
  44. pygpt_net/provider/core/preset/patch.py +17 -0
  45. pygpt_net/provider/gpt/__init__.py +4 -4
  46. pygpt_net/provider/gpt/summarizer.py +2 -2
  47. pygpt_net/provider/llms/hugging_face_router.py +132 -0
  48. pygpt_net/tools/code_interpreter/ui/html.py +2 -2
  49. pygpt_net/tools/translator/__init__.py +12 -0
  50. pygpt_net/tools/translator/tool.py +490 -0
  51. pygpt_net/tools/translator/ui/__init__.py +0 -0
  52. pygpt_net/tools/translator/ui/dialogs.py +144 -0
  53. pygpt_net/tools/translator/ui/widgets.py +483 -0
  54. pygpt_net/ui/base/context_menu.py +18 -1
  55. pygpt_net/ui/dialog/image.py +0 -6
  56. pygpt_net/ui/widget/option/combo.py +2 -0
  57. pygpt_net/ui/widget/textarea/html.py +2 -2
  58. pygpt_net/ui/widget/textarea/web.py +3 -2
  59. {pygpt_net-2.5.92.dist-info → pygpt_net-2.5.94.dist-info}/METADATA +39 -26
  60. {pygpt_net-2.5.92.dist-info → pygpt_net-2.5.94.dist-info}/RECORD +63 -53
  61. {pygpt_net-2.5.92.dist-info → pygpt_net-2.5.94.dist-info}/LICENSE +0 -0
  62. {pygpt_net-2.5.92.dist-info → pygpt_net-2.5.94.dist-info}/WHEEL +0 -0
  63. {pygpt_net-2.5.92.dist-info → pygpt_net-2.5.94.dist-info}/entry_points.txt +0 -0
pygpt_net/CHANGELOG.txt CHANGED
@@ -1,3 +1,15 @@
1
+ 2.5.94 (2025-08-09)
2
+
3
+ - Added a new LLM provider: HuggingFace Router.
4
+ - Introduced a new model: gpt-oss (OpenAI open-source model available in HuggingFace and Ollama).
5
+ - Added a new agent mode in OpenAI Agents: Bot 2 Bot.
6
+ - Fixed: Storing the last used context ID when empty.
7
+ - Fixed: Reloading items when an agent run is stopped.
8
+
9
+ 2.5.93 (2025-08-08)
10
+
11
+ - Added a new tool: Translate - in menu Tools - feature #123.
12
+
1
13
  2.5.92 (2025-08-08)
2
14
 
3
15
  - Added max files to store config option in Audio -> Cache.
pygpt_net/__init__.py CHANGED
@@ -6,15 +6,15 @@
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.08.08 00:00:00 #
9
+ # Updated Date: 2025.08.09 00:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  __author__ = "Marcin Szczygliński"
13
13
  __copyright__ = "Copyright 2025, Marcin Szczygliński"
14
14
  __credits__ = ["Marcin Szczygliński"]
15
15
  __license__ = "MIT"
16
- __version__ = "2.5.92"
17
- __build__ = "2025-08-08"
16
+ __version__ = "2.5.94"
17
+ __build__ = "2025-08-09"
18
18
  __maintainer__ = "Marcin Szczygliński"
19
19
  __github__ = "https://github.com/szczyglis-dev/py-gpt"
20
20
  __report__ = "https://github.com/szczyglis-dev/py-gpt/issues"
pygpt_net/app.py CHANGED
@@ -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.08.07 03:00:00 #
9
+ # Updated Date: 2025.08.09 01:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import os
@@ -16,7 +16,7 @@ import platform
16
16
 
17
17
  # disable warnings
18
18
  os.environ["TRANSFORMERS_NO_ADVISORY_WARNINGS"] = "1"
19
- os.environ["QT_LOGGING_RULES"] = "qt.multimedia.ffmpeg=false"
19
+ os.environ["QT_LOGGING_RULES"] = "qt.multimedia.ffmpeg=false;qt.qpa.fonts=false"
20
20
 
21
21
  if platform.system() == 'Windows':
22
22
  # fix ffmpeg bug: [SWR] Output channel layout "" is invalid or unsupported.
@@ -85,6 +85,7 @@ from pygpt_net.provider.agents.openai.agent_with_feedback import Agent as OpenAI
85
85
  from pygpt_net.provider.agents.openai.bot_researcher import Agent as OpenAIAgentBotResearcher
86
86
  from pygpt_net.provider.agents.openai.agent_planner import Agent as OpenAIAgentPlanner
87
87
  from pygpt_net.provider.agents.openai.evolve import Agent as OpenAIAgentsEvolve
88
+ from pygpt_net.provider.agents.openai.agent_b2b import Agent as OpenAIAgentsB2B
88
89
 
89
90
  # LLM wrapper providers (langchain, llama-index, embeddings)
90
91
  from pygpt_net.provider.llms.anthropic import AnthropicLLM
@@ -93,6 +94,7 @@ from pygpt_net.provider.llms.deepseek_api import DeepseekApiLLM
93
94
  from pygpt_net.provider.llms.google import GoogleLLM
94
95
  # from pygpt_net.provider.llms.hugging_face import HuggingFaceLLM
95
96
  from pygpt_net.provider.llms.hugging_face_api import HuggingFaceApiLLM
97
+ from pygpt_net.provider.llms.hugging_face_router import HuggingFaceRouterLLM
96
98
  from pygpt_net.provider.llms.local import LocalLLM
97
99
  from pygpt_net.provider.llms.mistral import MistralAILLM
98
100
  from pygpt_net.provider.llms.ollama import OllamaLLM
@@ -161,6 +163,7 @@ from pygpt_net.tools.image_viewer import ImageViewer as ImageViewerTool
161
163
  from pygpt_net.tools.media_player import MediaPlayer as MediaPlayerTool
162
164
  from pygpt_net.tools.text_editor import TextEditor as TextEditorTool
163
165
  from pygpt_net.tools.html_canvas import HtmlCanvas as HtmlCanvasTool
166
+ from pygpt_net.tools.translator import Translator as TranslatorTool
164
167
 
165
168
  def run(**kwargs):
166
169
  """
@@ -389,6 +392,7 @@ def run(**kwargs):
389
392
  launcher.add_llm(GoogleLLM())
390
393
  # launcher.add_llm(HuggingFaceLLM())
391
394
  launcher.add_llm(HuggingFaceApiLLM())
395
+ launcher.add_llm(HuggingFaceRouterLLM())
392
396
  launcher.add_llm(LocalLLM())
393
397
  launcher.add_llm(MistralAILLM())
394
398
  launcher.add_llm(OllamaLLM())
@@ -429,6 +433,7 @@ def run(**kwargs):
429
433
  launcher.add_agent(OpenAIAgentBotResearcher()) # openai-agents
430
434
  launcher.add_agent(OpenAIAgentsExpertsFeedback()) # openai-agents
431
435
  launcher.add_agent(OpenAIAgentsEvolve()) # openai-agents
436
+ launcher.add_agent(OpenAIAgentsB2B()) # openai-agents
432
437
 
433
438
  # register custom agents
434
439
  agents = kwargs.get('agents', None)
@@ -444,6 +449,7 @@ def run(**kwargs):
444
449
  launcher.add_tool(AudioTranscriberTool())
445
450
  launcher.add_tool(CodeInterpreterTool())
446
451
  launcher.add_tool(HtmlCanvasTool())
452
+ launcher.add_tool(TranslatorTool())
447
453
 
448
454
  # register custom tools
449
455
  tools = kwargs.get('tools', None)
pygpt_net/container.py CHANGED
@@ -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.06.28 16:00:00 #
9
+ # Updated Date: 2025.08.08 05:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from pygpt_net.config import Config
@@ -40,6 +40,7 @@ from pygpt_net.core.presets import Presets
40
40
  from pygpt_net.core.prompt import Prompt
41
41
  from pygpt_net.core.settings import Settings
42
42
  from pygpt_net.core.tabs import Tabs
43
+ from pygpt_net.core.text import Text
43
44
  from pygpt_net.core.tokens import Tokens
44
45
  from pygpt_net.core.updater import Updater
45
46
  from pygpt_net.core.vision import Vision
@@ -89,6 +90,7 @@ class Container:
89
90
  self.prompt = Prompt(window)
90
91
  self.settings = Settings(window)
91
92
  self.tabs = Tabs(window)
93
+ self.text = Text(window)
92
94
  self.tokens = Tokens(window)
93
95
  self.updater = Updater(window)
94
96
  self.vision = Vision(window)
@@ -149,9 +149,6 @@ class Image:
149
149
  string += "[{}]({})".format(basename, path) + "\n"
150
150
  i += 1
151
151
 
152
- if self.window.core.config.get('img_dialog_open'):
153
- self.window.tools.get("viewer").open_images(paths) # use viewer tool
154
-
155
152
  if not self.window.core.config.get('img_raw'):
156
153
  string += "\nPrompt: "
157
154
  string += prompt
@@ -19,7 +19,7 @@ from pygpt_net.core.types import (
19
19
  MODE_CHAT,
20
20
  )
21
21
  from pygpt_net.core.bridge.context import BridgeContext
22
- from pygpt_net.core.events import RenderEvent, KernelEvent
22
+ from pygpt_net.core.events import RenderEvent, KernelEvent, AppEvent
23
23
  from pygpt_net.item.ctx import CtxItem
24
24
  from pygpt_net.utils import trans
25
25
 
@@ -172,6 +172,10 @@ class Response:
172
172
  ctx.msg_id = None
173
173
  self.window.core.ctx.add(ctx) # store context to prevent current output from being lost
174
174
  self.window.controller.ctx.prepare_name(ctx) # summarize if not yet
175
+
176
+ # finish render
177
+ self.window.dispatch(AppEvent(AppEvent.CTX_END)) # app event
178
+ self.window.dispatch(RenderEvent(RenderEvent.RELOAD)) # reload chat window
175
179
  return
176
180
 
177
181
  # at first, handle previous context (user input) if not handled yet
@@ -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.06.29 18:00:00 #
9
+ # Updated Date: 2025.08.08 21:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from typing import Any, Optional, Dict, Union
@@ -65,7 +65,8 @@ class Slider:
65
65
  value = option["max"]
66
66
 
67
67
  # update connected input field
68
- self.window.ui.config[parent_id][key].input.setText(str(value))
68
+ if parent_id in self.window.ui.config and key in self.window.ui.config[parent_id]:
69
+ self.window.ui.config[parent_id][key].input.setText(str(value))
69
70
 
70
71
  slider_value = round(float(value) * multiplier, 0)
71
72
 
@@ -83,12 +84,15 @@ class Slider:
83
84
  slider_value = option["min"] * multiplier
84
85
  elif "max" in option and slider_value > option["max"] * multiplier:
85
86
  slider_value = option["max"] * multiplier
86
- self.window.ui.config[parent_id][key].slider.setValue(slider_value)
87
+
88
+ if parent_id in self.window.ui.config and key in self.window.ui.config[parent_id]:
89
+ self.window.ui.config[parent_id][key].slider.setValue(slider_value)
87
90
 
88
91
  # from value
89
92
  else:
90
- self.window.ui.config[parent_id][key].input.setText(str(value))
91
- self.window.ui.config[parent_id][key].slider.setValue(slider_value)
93
+ if parent_id in self.window.ui.config and key in self.window.ui.config[parent_id]:
94
+ self.window.ui.config[parent_id][key].input.setText(str(value))
95
+ self.window.ui.config[parent_id][key].slider.setValue(slider_value)
92
96
 
93
97
  def on_update(
94
98
  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.08.07 03:00:00 #
9
+ # Updated Date: 2025.08.08 05:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from typing import Dict, Any, List
@@ -86,6 +86,8 @@ class Placeholder:
86
86
  return self.get_modes(params)
87
87
  elif id == "models":
88
88
  return self.get_models(params)
89
+ elif id == "languages":
90
+ return self.get_languages()
89
91
  elif id == "multimodal":
90
92
  return self.get_multimodal(params)
91
93
  elif id == "langchain_providers":
@@ -456,6 +458,14 @@ class Placeholder:
456
458
  data.append({id: name})
457
459
  return data
458
460
 
461
+ def get_languages(self) -> List[Dict[str, str]]:
462
+ """
463
+ Get world languages list
464
+
465
+ :return: Languages placeholders list
466
+ """
467
+ return self.window.core.text.get_language_choices()
468
+
459
469
  def get_idx(self, params: dict = None) -> List[Dict[str, str]]:
460
470
  """
461
471
  Get indexes placeholders list
@@ -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.08.07 03:00:00 #
9
+ # Updated Date: 2025.08.08 23:00:00 #
10
10
  # ================================================== #
11
11
  import gc
12
12
  from typing import Optional, List
@@ -15,7 +15,7 @@ from PySide6.QtCore import QModelIndex, QTimer
15
15
  from PySide6.QtGui import QStandardItem
16
16
 
17
17
  from pygpt_net.core.events import Event, AppEvent, RenderEvent
18
- from pygpt_net.item.ctx import CtxItem
18
+ from pygpt_net.item.ctx import CtxItem, CtxMeta
19
19
 
20
20
  from .common import Common
21
21
  from .summarizer import Summarizer
@@ -283,12 +283,7 @@ class Ctx:
283
283
  self.window.core.config.set('assistant_thread', None) # reset assistant thread id
284
284
  self.update()
285
285
 
286
- # render reset
287
- data = {
288
- "meta": meta,
289
- }
290
- event = RenderEvent(RenderEvent.FRESH, data)
291
- self.window.dispatch(event)
286
+ self.fresh_output(meta) # render reset
292
287
 
293
288
  if not force: # only if real click on new context button
294
289
  self.window.controller.chat.common.unlock_input()
@@ -407,16 +402,7 @@ class Ctx:
407
402
 
408
403
  # reset appended data / prepare new ctx
409
404
  if meta is not None:
410
- data = {
411
- "meta": meta,
412
- }
413
- event = RenderEvent(RenderEvent.FRESH, data)
414
- self.window.dispatch(event)
415
- data = {
416
- "meta": meta,
417
- }
418
- event = RenderEvent(RenderEvent.ON_LOAD, data)
419
- self.window.dispatch(event)
405
+ self.fresh_output(meta) # render reset
420
406
 
421
407
  self.reload_config()
422
408
 
@@ -1244,3 +1230,22 @@ class Ctx:
1244
1230
  def clear_selected(self):
1245
1231
  """Clear selected list"""
1246
1232
  self.selected = []
1233
+
1234
+
1235
+ def fresh_output(self, meta: CtxMeta):
1236
+ """
1237
+ Fresh output for new context
1238
+
1239
+ :param meta: CtxItem
1240
+ """
1241
+ # render reset
1242
+ data = {
1243
+ "meta": meta,
1244
+ }
1245
+ event = RenderEvent(RenderEvent.FRESH, data)
1246
+ self.window.dispatch(event)
1247
+ data = {
1248
+ "meta": meta,
1249
+ }
1250
+ event = RenderEvent(RenderEvent.ON_LOAD, data)
1251
+ self.window.dispatch(event)
@@ -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.07.19 17:00:00 #
9
+ # Updated Date: 2025.08.08 05:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from typing import Any, Optional
@@ -86,6 +86,14 @@ class Confirm:
86
86
  elif type == 'html_canvas.clear':
87
87
  self.window.tools.get("html_canvas").clear(force=True)
88
88
 
89
+ # translator
90
+ elif type == 'translator.clear':
91
+ self.window.tools.get("translator").clear(force=True)
92
+ elif type == 'translator.clear.left':
93
+ self.window.tools.get("translator").clear_left(force=True)
94
+ elif type == 'translator.clear.right':
95
+ self.window.tools.get("translator").clear_right(force=True)
96
+
89
97
  # audio transcribe
90
98
  elif type == 'audio.transcribe':
91
99
  self.window.tools.get("transcriber").transcribe(id, force=True)
@@ -60,7 +60,6 @@ class Editor:
60
60
  self.window.ui.add_hook("update.config.ctx.records.folders.top", self.hook_update)
61
61
  self.window.ui.add_hook("update.config.layout.density", self.hook_update)
62
62
  self.window.ui.add_hook("update.config.layout.tooltips", self.hook_update)
63
- self.window.ui.add_hook("update.config.img_dialog_open", self.hook_update)
64
63
  self.window.ui.add_hook("update.config.access.voice_control", self.hook_update)
65
64
  self.window.ui.add_hook("update.config.debug", self.hook_update)
66
65
  self.window.ui.add_hook("update.config.notepad.num", self.hook_update)
@@ -327,11 +326,6 @@ class Editor:
327
326
  self.window.controller.theme.reload()
328
327
  self.window.controller.theme.menu.update_density()
329
328
 
330
- # toggle image dialog auto-open
331
- elif key == "img_dialog_open":
332
- self.window.core.config.set(key, value)
333
- self.window.ui.nodes['dialog.image.open.toggle'].setChecked(value)
334
-
335
329
  # debug: menu
336
330
  elif key == "debug":
337
331
  self.window.core.config.set(key, value)
@@ -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.08.07 18:00:00 #
9
+ # Updated Date: 2025.08.08 23:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from typing import Any, Optional, Tuple
@@ -614,7 +614,7 @@ class Tabs:
614
614
  :param name: new title
615
615
  :param close: close dialog
616
616
  """
617
- self.window.core.tabs.update_title(idx, name)
617
+ self.window.core.tabs.update_title(idx, name, name)
618
618
  if close:
619
619
  self.window.ui.dialog['rename'].close()
620
620
  self.debug()
@@ -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.07.19 00:00:00 #
9
+ # Updated Date: 2025.08.08 19:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import copy
@@ -527,6 +527,11 @@ class Models:
527
527
  args["api_key"] = self.window.core.config.get('api_key_mistral', "")
528
528
  args["base_url"] = self.window.core.config.get('api_endpoint_mistral', "")
529
529
  self.window.core.debug.info("[api] Using client: Mistral AI API")
530
+ # HuggingFace Router
531
+ elif model.provider == "huggingface_router":
532
+ args["api_key"] = self.window.core.config.get('api_key_hugging_face', "")
533
+ args["base_url"] = self.window.core.config.get('api_endpoint_hugging_face', "")
534
+ self.window.core.debug.info("[api] Using client: HuggingFace Router API")
530
535
  else:
531
536
  self.window.core.debug.info("[api] Using client: OpenAI (default)")
532
537
 
@@ -6,10 +6,11 @@
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.08.05 00:00:00 #
9
+ # Updated Date: 2025.08.08 19:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import os
13
+ import random
13
14
  from typing import Optional, List, Dict
14
15
 
15
16
  from pygpt_net.core.events import Event
@@ -24,6 +25,8 @@ import pygpt_net.fonts_rc
24
25
 
25
26
  class Body:
26
27
 
28
+ NUM_TIPS = 13
29
+
27
30
  def __init__(self, window=None):
28
31
  """
29
32
  HTML Body
@@ -402,12 +405,28 @@ class Body:
402
405
  html += "</div>"
403
406
  return html
404
407
 
408
+ def get_all_tips(self) -> str:
409
+ """
410
+ Generuje listę wszystkich tipów jako elementy HTML,
411
+ tasuje je i zwraca jako string JSON.
412
+ """
413
+ if not self.window.core.config.get("layout.tooltips", False):
414
+ return "[]"
415
+ tips_list = []
416
+ for i in range(1, self.NUM_TIPS + 1):
417
+ tip_html = "<p><b>" + trans("output.tips.prefix") + "</b>: " + trans("output.tips." + str(i)) + "</p>"
418
+ tips_list.append(tip_html)
419
+ random.shuffle(tips_list)
420
+ import json
421
+ return json.dumps(tips_list)
422
+
405
423
  def get_html(self, pid: int) -> str:
406
424
  """
407
425
  Build webview HTML code
408
426
 
409
427
  :return: HTML code
410
428
  """
429
+ tips_json = self.get_all_tips()
411
430
  classes = []
412
431
  classes_str = ""
413
432
  style = self.window.core.config.get("theme.style", "blocks")
@@ -449,6 +468,8 @@ class Body:
449
468
  let domLastCodeBlock = null;
450
469
  let domLastParagraphBlock = null;
451
470
  let htmlBuffer = "";
471
+ let tips = """ + tips_json + """;
472
+ let tips_hidden = false;
452
473
 
453
474
  history.scrollRestoration = "manual";
454
475
  document.addEventListener('keydown', function(event) {
@@ -473,6 +494,7 @@ class Body:
473
494
  }
474
495
  function prepare() {
475
496
  collapsed_idx = []; // clear collapsed code
497
+ hideTips();
476
498
  }
477
499
  function sanitize(content) {
478
500
  var parser = new DOMParser();
@@ -494,6 +516,37 @@ class Body:
494
516
  }
495
517
  restoreCollapsedCode();
496
518
  }
519
+ function hideTips() {
520
+ if (tips_hidden) return;
521
+ document.getElementById('tips').style.display = 'none';
522
+ tips_hidden = true;
523
+ }
524
+ function showTips() {
525
+ if (tips_hidden) return;
526
+ if (tips.length === 0) return;
527
+ document.getElementById('tips').style.display = 'block';
528
+ tips_hidden = false;
529
+ }
530
+ function cycleTips() {
531
+ if (tips_hidden) return;
532
+ if (tips.length === 0) return;
533
+ let tipContainer = document.getElementById('tips');
534
+ let currentTip = 0;
535
+ function showNextTip() {
536
+ if (tips_hidden) return;
537
+ tipContainer.innerHTML = tips[currentTip];
538
+ tipContainer.classList.add('visible');
539
+ setTimeout(function() {
540
+ if (tips_hidden) return;
541
+ tipContainer.classList.remove('visible');
542
+ setTimeout(function(){
543
+ currentTip = (currentTip + 1) % tips.length;
544
+ showNextTip();
545
+ }, 1000);
546
+ }, 10000);
547
+ }
548
+ showNextTip();
549
+ }
497
550
  function renderMath() {
498
551
  const scripts = document.querySelectorAll('script[type^="math/tex"]');
499
552
  scripts.forEach(function(script) {
@@ -525,6 +578,7 @@ class Body:
525
578
  scrollToBottom();
526
579
  }
527
580
  function appendToOutput(bot_name, content) {
581
+ hideTips();
528
582
  const element = document.getElementById('_append_output_');
529
583
  if (element) {
530
584
  let box = element.querySelector('.msg-box');
@@ -564,6 +618,7 @@ class Body:
564
618
  scrollToBottom();
565
619
  }
566
620
  function appendExtra(id, content) {
621
+ hideTips();
567
622
  prevScroll = 0;
568
623
  const element = document.getElementById('msg-bot-' + id);
569
624
  if (element) {
@@ -619,6 +674,7 @@ class Body:
619
674
  return element;
620
675
  }
621
676
  function clearStream() {
677
+ hideTips();
622
678
  domLastParagraphBlock = null;
623
679
  domLastCodeBlock = null;
624
680
  domOutputStream = null;
@@ -649,12 +705,14 @@ class Body:
649
705
  }
650
706
  }
651
707
  function beginStream() {
708
+ hideTips();
652
709
  clearOutput();
653
710
  }
654
711
  function endStream() {
655
712
  clearOutput();
656
713
  }
657
714
  function appendStream(bot_name, content, chunk, replace = false, is_code_block = false) {
715
+ hideTips();
658
716
  const element = getStreamContainer();
659
717
  doHighlight = true;
660
718
  doMath = true;
@@ -742,6 +800,7 @@ class Body:
742
800
  }
743
801
  }
744
802
  function replaceOutput(bot_name, content) {
803
+ hideTips();
745
804
  const element = getStreamContainer();
746
805
  if (element) {
747
806
  let box = element.querySelector('.msg-box');
@@ -770,6 +829,7 @@ class Body:
770
829
  scrollToBottom();
771
830
  }
772
831
  function nextStream() {
832
+ hideTips();
773
833
  // Clear the current stream output and copy it to the before output
774
834
  // 1. copy current output from _append_output_ to _append_output_before_
775
835
  // 2. clear _append_output_
@@ -784,12 +844,14 @@ class Body:
784
844
  }
785
845
  }
786
846
  function clearStreamBefore() {
847
+ hideTips();
787
848
  const element = document.getElementById('_append_output_before_');
788
849
  if (element) {
789
850
  element.innerHTML = ''; // clear previous content
790
851
  }
791
852
  }
792
- function replaceOutput(bot_name, content) {
853
+ function replaceOutput(bot_name, content) {
854
+ hideTips();
793
855
  const element = getStreamContainer();
794
856
  if (element) {
795
857
  let box = element.querySelector('.msg-box');
@@ -1074,6 +1136,7 @@ class Body:
1074
1136
  prevScroll = parseInt(pos);
1075
1137
  }
1076
1138
  function showLoading() {
1139
+ hideTips();
1077
1140
  const el = document.getElementById('_loader_');
1078
1141
  if (el) {
1079
1142
  if (el.classList.contains('hidden')) {
@@ -1196,6 +1259,7 @@ class Body:
1196
1259
  }
1197
1260
  });
1198
1261
  });
1262
+ setTimeout(cycleTips, 20000);
1199
1263
  </script>
1200
1264
  </head>
1201
1265
  <body """ + classes_str + """>
@@ -1209,6 +1273,7 @@ class Body:
1209
1273
  <div id="_loader_" class="loader-global hidden">
1210
1274
  <div class="lds-ring"><div></div><div></div><div></div><div></div></div>
1211
1275
  </div>
1276
+ <div id="tips" class="tips"></div>
1212
1277
  </div>
1213
1278
  </body>
1214
1279
  </html>
@@ -0,0 +1 @@
1
+ from .text import *
@@ -0,0 +1,59 @@
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: 2025.08.08 05:00:00 #
10
+ # ================================================== #
11
+ import os
12
+
13
+
14
+ class Text:
15
+ def __init__(self, window=None):
16
+ """
17
+ Text helpers
18
+
19
+ :param window: Window instance
20
+ """
21
+ self.window = window
22
+
23
+ def get_language_choices(self) -> list:
24
+ """
25
+ Get available language choices
26
+
27
+ :return: list of dictionaries with language codes and names
28
+ """
29
+ choices = []
30
+ choices.append({"-": "--- AUTO DETECT ---"})
31
+ csv_path = os.path.join(self.window.core.config.get_app_path(), 'data', 'languages.csv')
32
+ if os.path.exists(csv_path):
33
+ with open(csv_path, 'r', encoding='utf-8') as file:
34
+ for line in file.readlines()[1:]:
35
+ parts = line.strip().split(',')
36
+ if len(parts) >= 4:
37
+ lang_code = parts[0].strip()
38
+ lang_name = parts[3].strip()
39
+ lang_orig_name = parts[4].strip()
40
+ name = f"{lang_name} ({lang_orig_name})" if lang_orig_name else lang_name
41
+ name = name.replace("'", "").replace('"', "")
42
+ choices.append({lang_code: name})
43
+
44
+ # sort choices by language name
45
+ choices.sort(key=lambda x: list(x.values())[0].lower())
46
+ return choices
47
+
48
+ def get_language_name(self, lang_code: str) -> str:
49
+ """
50
+ Get language name by code
51
+
52
+ :param lang_code: language code
53
+ :return: language name or empty string if not found
54
+ """
55
+ choices = self.get_language_choices()
56
+ for choice in choices:
57
+ if lang_code in choice:
58
+ return choice[lang_code]
59
+ return ""
@@ -6,9 +6,22 @@
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.08.03 14:00:00 #
9
+ # Updated Date: 2025.08.08 19:00:00 #
10
10
  # ================================================== #
11
11
 
12
+ OPENAI_COMPATIBLE_PROVIDERS = [
13
+ "anthropic",
14
+ "openai",
15
+ "azure_openai",
16
+ "google",
17
+ "huggingface_router",
18
+ "local_ai",
19
+ "mistral_ai",
20
+ "perplexity",
21
+ "deepseek_api",
22
+ "x_ai",
23
+ ]
24
+
12
25
  OPENAI_DISABLE_TOOLS = [
13
26
  "o1-mini",
14
27
  "o1-preview"