pygpt-net 2.5.94__py3-none-any.whl → 2.5.95__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.
- pygpt_net/CHANGELOG.txt +7 -0
- pygpt_net/__init__.py +1 -1
- pygpt_net/controller/chat/text.py +3 -1
- pygpt_net/controller/dialogs/confirm.py +4 -1
- pygpt_net/controller/presets/editor.py +117 -1
- pygpt_net/controller/ui/mode.py +1 -3
- pygpt_net/controller/ui/tabs.py +58 -40
- pygpt_net/core/ctx/ctx.py +4 -2
- pygpt_net/core/presets/presets.py +12 -1
- pygpt_net/core/prompt/prompt.py +10 -1
- pygpt_net/core/render/web/body.py +14 -22
- pygpt_net/core/render/web/helpers.py +9 -2
- pygpt_net/core/render/web/renderer.py +44 -3
- pygpt_net/core/tabs/tabs.py +40 -5
- pygpt_net/core/text/text.py +31 -2
- pygpt_net/data/config/config.json +4 -2
- pygpt_net/data/config/models.json +2 -2
- pygpt_net/data/config/settings.json +28 -1
- pygpt_net/data/config/settings_section.json +3 -0
- pygpt_net/data/css/web-blocks.css +13 -2
- pygpt_net/data/css/web-blocks.dark.css +3 -0
- pygpt_net/data/css/web-blocks.light.css +3 -0
- pygpt_net/data/css/web-chatgpt.css +19 -2
- pygpt_net/data/css/web-chatgpt.dark.css +3 -0
- pygpt_net/data/css/web-chatgpt.light.css +3 -0
- pygpt_net/data/css/web-chatgpt_wide.css +19 -2
- pygpt_net/data/css/web-chatgpt_wide.dark.css +3 -0
- pygpt_net/data/css/web-chatgpt_wide.light.css +3 -0
- pygpt_net/data/locale/locale.de.ini +19 -0
- pygpt_net/data/locale/locale.en.ini +19 -0
- pygpt_net/data/locale/locale.es.ini +19 -0
- pygpt_net/data/locale/locale.fr.ini +19 -0
- pygpt_net/data/locale/locale.it.ini +19 -0
- pygpt_net/data/locale/locale.pl.ini +19 -0
- pygpt_net/data/locale/locale.uk.ini +19 -0
- pygpt_net/data/locale/locale.zh.ini +19 -0
- pygpt_net/item/preset.py +9 -1
- pygpt_net/provider/agents/openai/agent_b2b.py +17 -12
- pygpt_net/provider/core/config/patch.py +21 -1
- pygpt_net/provider/core/preset/json_file.py +6 -0
- pygpt_net/tools/translator/tool.py +9 -2
- pygpt_net/tools/translator/ui/widgets.py +45 -3
- pygpt_net/ui/base/config_dialog.py +1 -1
- pygpt_net/ui/dialog/preset.py +126 -8
- pygpt_net/ui/widget/textarea/search_input.py +68 -2
- {pygpt_net-2.5.94.dist-info → pygpt_net-2.5.95.dist-info}/METADATA +15 -2
- {pygpt_net-2.5.94.dist-info → pygpt_net-2.5.95.dist-info}/RECORD +50 -50
- {pygpt_net-2.5.94.dist-info → pygpt_net-2.5.95.dist-info}/LICENSE +0 -0
- {pygpt_net-2.5.94.dist-info → pygpt_net-2.5.95.dist-info}/WHEEL +0 -0
- {pygpt_net-2.5.94.dist-info → pygpt_net-2.5.95.dist-info}/entry_points.txt +0 -0
pygpt_net/CHANGELOG.txt
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
2.5.95 (2025-08-09)
|
|
2
|
+
|
|
3
|
+
- Added user info personalization in Config -> Personalization, where you can provide information about yourself to the model.
|
|
4
|
+
- Added presets personalization with configurable AI names and avatars.
|
|
5
|
+
- Added a search field in the Translator tool.
|
|
6
|
+
- Fixed <> tags replacement in code blocks.
|
|
7
|
+
|
|
1
8
|
2.5.94 (2025-08-09)
|
|
2
9
|
|
|
3
10
|
- Added a new LLM provider: HuggingFace Router.
|
pygpt_net/__init__.py
CHANGED
|
@@ -13,7 +13,7 @@ __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.
|
|
16
|
+
__version__ = "2.5.95"
|
|
17
17
|
__build__ = "2025-08-09"
|
|
18
18
|
__maintainer__ = "Marcin Szczygliński"
|
|
19
19
|
__github__ = "https://github.com/szczyglis-dev/py-gpt"
|
|
@@ -102,8 +102,10 @@ class Text:
|
|
|
102
102
|
stream_mode = False
|
|
103
103
|
|
|
104
104
|
# create ctx item
|
|
105
|
+
meta = self.window.core.ctx.get_current_meta()
|
|
106
|
+
meta.preset = self.window.core.config.get('preset') # current preset
|
|
105
107
|
ctx = CtxItem()
|
|
106
|
-
ctx.meta =
|
|
108
|
+
ctx.meta = meta # CtxMeta (owner object)
|
|
107
109
|
ctx.internal = internal
|
|
108
110
|
ctx.current = True # mark as current context item
|
|
109
111
|
ctx.mode = mode # store current selected mode (not inline 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.
|
|
9
|
+
# Updated Date: 2025.08.09 19:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from typing import Any, Optional
|
|
@@ -94,6 +94,9 @@ class Confirm:
|
|
|
94
94
|
elif type == 'translator.clear.right':
|
|
95
95
|
self.window.tools.get("translator").clear_right(force=True)
|
|
96
96
|
|
|
97
|
+
elif type == "preset.avatar.delete":
|
|
98
|
+
self.window.controller.presets.editor.remove_avatar(True)
|
|
99
|
+
|
|
97
100
|
# audio transcribe
|
|
98
101
|
elif type == 'audio.transcribe':
|
|
99
102
|
self.window.tools.get("transcriber").transcribe(id, force=True)
|
|
@@ -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: 2025.08.
|
|
9
|
+
# Updated Date: 2025.08.09 19:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import datetime
|
|
13
13
|
import os
|
|
14
|
+
import shutil
|
|
14
15
|
from typing import Any, Optional, Dict
|
|
15
16
|
|
|
16
17
|
from PySide6.QtWidgets import QVBoxLayout, QWidget, QHBoxLayout
|
|
@@ -48,6 +49,7 @@ class Editor:
|
|
|
48
49
|
self.built = False
|
|
49
50
|
self.tab_options_idx = {}
|
|
50
51
|
self.opened = False
|
|
52
|
+
self.tmp_avatar = None
|
|
51
53
|
self.options = {
|
|
52
54
|
"filename": {
|
|
53
55
|
"type": "text",
|
|
@@ -61,6 +63,15 @@ class Editor:
|
|
|
61
63
|
"type": "text",
|
|
62
64
|
"label": "preset.ai_name",
|
|
63
65
|
},
|
|
66
|
+
"ai_avatar": {
|
|
67
|
+
"type": "text",
|
|
68
|
+
"label": "preset.ai_avatar",
|
|
69
|
+
},
|
|
70
|
+
"ai_personalize": {
|
|
71
|
+
"type": "bool",
|
|
72
|
+
"label": "preset.ai_personalize",
|
|
73
|
+
"description": "preset.ai_personalize.desc",
|
|
74
|
+
},
|
|
64
75
|
"user_name": {
|
|
65
76
|
"type": "text",
|
|
66
77
|
"label": "preset.user_name",
|
|
@@ -716,6 +727,9 @@ class Editor:
|
|
|
716
727
|
# update experts list, after ID loaded
|
|
717
728
|
self.experts.update_list()
|
|
718
729
|
|
|
730
|
+
# setup avatar config
|
|
731
|
+
self.update_avatar_config(data)
|
|
732
|
+
|
|
719
733
|
# restore functions
|
|
720
734
|
if data.has_functions():
|
|
721
735
|
functions = data.get_functions()
|
|
@@ -836,6 +850,14 @@ class Editor:
|
|
|
836
850
|
# assign data from fields to preset object in items
|
|
837
851
|
self.assign_data(id)
|
|
838
852
|
|
|
853
|
+
if is_new:
|
|
854
|
+
# assign tmp avatar
|
|
855
|
+
if self.tmp_avatar is not None:
|
|
856
|
+
self.window.core.presets.items[id].ai_avatar = self.tmp_avatar
|
|
857
|
+
self.tmp_avatar = None
|
|
858
|
+
else:
|
|
859
|
+
self.tmp_avatar = None
|
|
860
|
+
|
|
839
861
|
# if agent, assign experts and select only agent mode
|
|
840
862
|
curr_mode = self.window.core.config.get('mode')
|
|
841
863
|
if curr_mode == MODE_AGENT:
|
|
@@ -956,6 +978,9 @@ class Editor:
|
|
|
956
978
|
# extra options
|
|
957
979
|
self.append_extra_options(preset)
|
|
958
980
|
|
|
981
|
+
# avatar update
|
|
982
|
+
self.update_avatar_config(preset)
|
|
983
|
+
|
|
959
984
|
def to_current(self, preset: PresetItem):
|
|
960
985
|
"""
|
|
961
986
|
Update preset field from editor
|
|
@@ -1018,3 +1043,94 @@ class Editor:
|
|
|
1018
1043
|
preset.ai_name = value
|
|
1019
1044
|
self.window.core.config.set('ai_name', preset.ai_name)
|
|
1020
1045
|
self.window.core.presets.save(preset_id)
|
|
1046
|
+
|
|
1047
|
+
def upload_avatar(self, file_path: str):
|
|
1048
|
+
"""
|
|
1049
|
+
Update avatar config for preset
|
|
1050
|
+
|
|
1051
|
+
:param file_path: path to the avatar file
|
|
1052
|
+
"""
|
|
1053
|
+
preset = self.window.core.presets.get_by_uuid(self.current)
|
|
1054
|
+
presets_dir = self.window.core.config.get_user_dir("presets")
|
|
1055
|
+
avatars_dir = os.path.join(presets_dir, "avatars")
|
|
1056
|
+
preset_name = "_" if preset is None else preset.filename
|
|
1057
|
+
if not os.path.exists(avatars_dir):
|
|
1058
|
+
os.makedirs(avatars_dir, exist_ok=True)
|
|
1059
|
+
file_ext = os.path.splitext(file_path)[1]
|
|
1060
|
+
store_name = preset_name + "_" + datetime.datetime.now().strftime('%Y%m%d%H%M%S') + file_ext
|
|
1061
|
+
avatar_path = os.path.join(avatars_dir, store_name)
|
|
1062
|
+
|
|
1063
|
+
# copy avatar to avatars directory
|
|
1064
|
+
if os.path.exists(avatar_path):
|
|
1065
|
+
os.remove(avatar_path)
|
|
1066
|
+
if os.path.exists(file_path):
|
|
1067
|
+
shutil.copy(file_path, avatar_path)
|
|
1068
|
+
if preset:
|
|
1069
|
+
preset.ai_avatar = store_name
|
|
1070
|
+
else:
|
|
1071
|
+
self.tmp_avatar = store_name
|
|
1072
|
+
self.window.controller.config.apply_value(
|
|
1073
|
+
parent_id=self.id,
|
|
1074
|
+
key="ai_avatar",
|
|
1075
|
+
option=self.options["ai_avatar"],
|
|
1076
|
+
value=store_name,
|
|
1077
|
+
)
|
|
1078
|
+
self.window.ui.nodes['preset.editor.avatar'].load_avatar(avatar_path)
|
|
1079
|
+
self.window.ui.nodes['preset.editor.avatar'].enable_remove_button(True)
|
|
1080
|
+
return avatar_path
|
|
1081
|
+
|
|
1082
|
+
def update_avatar_config(self, preset: PresetItem):
|
|
1083
|
+
"""
|
|
1084
|
+
Update avatar config for preset
|
|
1085
|
+
|
|
1086
|
+
:param preset: preset item
|
|
1087
|
+
"""
|
|
1088
|
+
avatar_path = preset.ai_avatar
|
|
1089
|
+
if avatar_path:
|
|
1090
|
+
file_path = os.path.join(
|
|
1091
|
+
self.window.core.config.get_user_dir("presets"),
|
|
1092
|
+
"avatars",
|
|
1093
|
+
avatar_path,
|
|
1094
|
+
)
|
|
1095
|
+
if not os.path.exists(file_path):
|
|
1096
|
+
self.window.ui.nodes['preset.editor.avatar'].remove_avatar()
|
|
1097
|
+
print("Avatar file does not exist:", file_path)
|
|
1098
|
+
return
|
|
1099
|
+
self.window.ui.nodes['preset.editor.avatar'].load_avatar(file_path)
|
|
1100
|
+
self.window.ui.nodes['preset.editor.avatar'].enable_remove_button(True)
|
|
1101
|
+
else:
|
|
1102
|
+
self.window.ui.nodes['preset.editor.avatar'].remove_avatar()
|
|
1103
|
+
|
|
1104
|
+
def remove_avatar(self, force: bool = False):
|
|
1105
|
+
"""
|
|
1106
|
+
Remove avatar from preset editor
|
|
1107
|
+
|
|
1108
|
+
:param force: force remove avatar
|
|
1109
|
+
"""
|
|
1110
|
+
if not force:
|
|
1111
|
+
self.window.ui.dialogs.confirm(
|
|
1112
|
+
type='preset.avatar.delete',
|
|
1113
|
+
id="",
|
|
1114
|
+
msg=trans('confirm.preset.avatar.delete'),
|
|
1115
|
+
)
|
|
1116
|
+
return
|
|
1117
|
+
preset = self.window.core.presets.get_by_uuid(self.current)
|
|
1118
|
+
if preset:
|
|
1119
|
+
current = preset.ai_avatar
|
|
1120
|
+
if current:
|
|
1121
|
+
presets_dir = self.window.core.config.get_user_dir("presets")
|
|
1122
|
+
avatars_dir = os.path.join(presets_dir, "avatars")
|
|
1123
|
+
avatar_path = os.path.join(avatars_dir, current)
|
|
1124
|
+
if os.path.exists(avatar_path):
|
|
1125
|
+
os.remove(avatar_path)
|
|
1126
|
+
preset.ai_avatar = ""
|
|
1127
|
+
else:
|
|
1128
|
+
self.tmp_avatar = None
|
|
1129
|
+
|
|
1130
|
+
self.window.ui.nodes['preset.editor.avatar'].remove_avatar()
|
|
1131
|
+
self.window.controller.config.apply_value(
|
|
1132
|
+
parent_id=self.id,
|
|
1133
|
+
key="ai_avatar",
|
|
1134
|
+
option=self.options["ai_avatar"],
|
|
1135
|
+
value="",
|
|
1136
|
+
)
|
pygpt_net/controller/ui/mode.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.
|
|
9
|
+
# Updated Date: 2025.08.09 19:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from pygpt_net.core.types import (
|
|
@@ -82,10 +82,8 @@ class Mode:
|
|
|
82
82
|
self.window.ui.nodes['preset.editor.remote_tools'].setVisible(False)
|
|
83
83
|
|
|
84
84
|
if mode == MODE_COMPLETION:
|
|
85
|
-
self.window.ui.nodes['preset.editor.ai_name'].setVisible(True)
|
|
86
85
|
self.window.ui.nodes['preset.editor.user_name'].setVisible(True)
|
|
87
86
|
else:
|
|
88
|
-
self.window.ui.nodes['preset.editor.ai_name'].setVisible(False)
|
|
89
87
|
self.window.ui.nodes['preset.editor.user_name'].setVisible(False)
|
|
90
88
|
|
|
91
89
|
if mode == MODE_AGENT_OPENAI:
|
pygpt_net/controller/ui/tabs.py
CHANGED
|
@@ -861,52 +861,70 @@ class Tabs:
|
|
|
861
861
|
:param title: new tab name (optional, for chat tab)
|
|
862
862
|
:param meta: context meta (optional, for chat tab)
|
|
863
863
|
"""
|
|
864
|
-
#
|
|
864
|
+
# try to focus tab
|
|
865
865
|
if self.get_current_type() != type:
|
|
866
|
-
# first, check in second column
|
|
867
|
-
second_column_idx = 1 if self.column_idx == 0 else 0
|
|
868
|
-
# get current tab from second column
|
|
869
|
-
tabs = self.window.ui.layout.get_tabs_by_idx(second_column_idx)
|
|
870
|
-
second_tabs_idx = tabs.currentIndex()
|
|
871
|
-
second_tab = self.window.core.tabs.get_tab_by_index(second_tabs_idx, second_column_idx)
|
|
872
|
-
if second_tab is not None and second_tab.type == type:
|
|
873
|
-
# switch to second column
|
|
874
|
-
self.on_column_focus(second_column_idx)
|
|
875
|
-
tabs.setCurrentIndex(second_tabs_idx)
|
|
876
|
-
if meta:
|
|
877
|
-
QTimer.singleShot(100, lambda: self.window.controller.ctx.load(meta.id))
|
|
878
|
-
self.debug()
|
|
879
|
-
return
|
|
880
866
|
|
|
881
|
-
|
|
867
|
+
# find the closest tab in current column (on left side)
|
|
868
|
+
current = self.get_current_tab()
|
|
869
|
+
exists = False
|
|
870
|
+
if current:
|
|
871
|
+
idx, column_idx, exists = self.window.core.tabs.get_closest_idx_by_type_exists(
|
|
872
|
+
current,
|
|
873
|
+
type,
|
|
874
|
+
self.column_idx
|
|
875
|
+
)
|
|
882
876
|
if exists:
|
|
883
|
-
|
|
884
|
-
if tabs and idx:
|
|
885
|
-
tabs.setCurrentIndex(idx)
|
|
886
|
-
self.debug()
|
|
887
|
-
return
|
|
877
|
+
tab = self.window.core.tabs.get_tab_by_index(idx, column_idx)
|
|
888
878
|
else:
|
|
889
|
-
# if
|
|
879
|
+
# if not exists in current col, then find first idx in any column
|
|
890
880
|
tab = self.window.core.tabs.get_first_by_type(type)
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
self.
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
881
|
+
|
|
882
|
+
if tab:
|
|
883
|
+
# if tab is found in current column, switch to it
|
|
884
|
+
tabs = self.window.ui.layout.get_tabs_by_idx(tab.column_idx)
|
|
885
|
+
if tabs:
|
|
886
|
+
idx = tab.idx
|
|
887
|
+
if data_id is not None:
|
|
888
|
+
tab.data_id = data_id
|
|
889
|
+
if title is not None:
|
|
890
|
+
self.update_title_current(title)
|
|
891
|
+
else:
|
|
892
|
+
self.on_column_focus(tab.column_idx)
|
|
893
|
+
if meta is not None:
|
|
894
|
+
self.on_column_focus(tab.column_idx)
|
|
895
|
+
self.window.controller.ctx.load(meta.id)
|
|
896
|
+
QTimer.singleShot(100, lambda: self.window.controller.ctx.load(meta.id))
|
|
897
|
+
self.on_column_focus(tab.column_idx)
|
|
898
|
+
tabs.setCurrentIndex(idx)
|
|
899
|
+
else:
|
|
900
|
+
# if not found in current column, then check in second column
|
|
901
|
+
second_column_idx = 1 if self.column_idx == 0 else 0
|
|
902
|
+
# get current tab from second column
|
|
903
|
+
tabs = self.window.ui.layout.get_tabs_by_idx(second_column_idx)
|
|
904
|
+
second_tabs_idx = tabs.currentIndex()
|
|
905
|
+
second_tab = self.window.core.tabs.get_tab_by_index(second_tabs_idx, second_column_idx)
|
|
906
|
+
if second_tab is not None and second_tab.type == type:
|
|
907
|
+
# switch to second column
|
|
908
|
+
self.on_column_focus(second_column_idx)
|
|
909
|
+
tabs.setCurrentIndex(second_tabs_idx)
|
|
910
|
+
if meta:
|
|
911
|
+
QTimer.singleShot(100, lambda: self.window.controller.ctx.load(meta.id))
|
|
912
|
+
|
|
913
|
+
# if second and split screen disabled, then enable it
|
|
914
|
+
if tab and tab.column_idx == 1:
|
|
915
|
+
if not self.is_split_screen_enabled():
|
|
916
|
+
self.enable_split_screen(update_switch=True)
|
|
907
917
|
|
|
908
918
|
self.debug()
|
|
909
919
|
|
|
920
|
+
def is_split_screen_enabled(self) -> bool:
|
|
921
|
+
"""
|
|
922
|
+
Check if split screen mode is enabled
|
|
923
|
+
|
|
924
|
+
:return: True if split screen is enabled, False otherwise
|
|
925
|
+
"""
|
|
926
|
+
return self.window.core.config.get("layout.split", False)
|
|
927
|
+
|
|
910
928
|
|
|
911
929
|
def on_split_screen_changed(self, state: bool):
|
|
912
930
|
"""
|
|
@@ -914,7 +932,7 @@ class Tabs:
|
|
|
914
932
|
|
|
915
933
|
:param state: True if split screen is enabled
|
|
916
934
|
"""
|
|
917
|
-
prev_state = self.
|
|
935
|
+
prev_state = self.is_split_screen_enabled()
|
|
918
936
|
self.window.core.config.set("layout.split", state)
|
|
919
937
|
if prev_state != state:
|
|
920
938
|
if self.window.ui.nodes['layout.split'].box.isChecked() != state:
|
|
@@ -927,7 +945,7 @@ class Tabs:
|
|
|
927
945
|
|
|
928
946
|
:param update_switch: True if switch should be updated
|
|
929
947
|
"""
|
|
930
|
-
if self.
|
|
948
|
+
if self.is_split_screen_enabled():
|
|
931
949
|
return
|
|
932
950
|
|
|
933
951
|
self.window.ui.splitters['columns'].setSizes([1, 1])
|
pygpt_net/core/ctx/ctx.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.
|
|
9
|
+
# Updated Date: 2025.08.09 20:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import copy
|
|
@@ -429,6 +429,8 @@ class Ctx:
|
|
|
429
429
|
if meta is None:
|
|
430
430
|
self.window.core.debug.log("Error creating new ctx")
|
|
431
431
|
return
|
|
432
|
+
preset = self.window.core.config.get('preset')
|
|
433
|
+
meta.preset = preset
|
|
432
434
|
self.meta[meta.id] = meta
|
|
433
435
|
self.tmp_meta = meta
|
|
434
436
|
self.current = meta.id
|
|
@@ -436,7 +438,7 @@ class Ctx:
|
|
|
436
438
|
self.assistant = None
|
|
437
439
|
self.mode = self.window.core.config.get('mode')
|
|
438
440
|
self.model = self.window.core.config.get('model')
|
|
439
|
-
self.preset =
|
|
441
|
+
self.preset = preset
|
|
440
442
|
self.clear_items()
|
|
441
443
|
self.save(meta.id)
|
|
442
444
|
|
|
@@ -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.
|
|
9
|
+
# Updated Date: 2025.08.09 19:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import copy
|
|
@@ -295,6 +295,17 @@ class Presets:
|
|
|
295
295
|
return presets[id]
|
|
296
296
|
return None
|
|
297
297
|
|
|
298
|
+
def get(self, id: str) -> Optional[PresetItem]:
|
|
299
|
+
"""
|
|
300
|
+
Return preset by ID
|
|
301
|
+
|
|
302
|
+
:param id: preset ID
|
|
303
|
+
:return: preset item
|
|
304
|
+
"""
|
|
305
|
+
if id in self.items:
|
|
306
|
+
return self.items[id]
|
|
307
|
+
return None
|
|
308
|
+
|
|
298
309
|
def get_by_uuid(self, uuid: str) -> Optional[PresetItem]:
|
|
299
310
|
"""
|
|
300
311
|
Return preset by UUID
|
pygpt_net/core/prompt/prompt.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.
|
|
9
|
+
# Updated Date: 2025.08.09 19:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from pygpt_net.core.events import Event
|
|
@@ -149,6 +149,15 @@ class Prompt:
|
|
|
149
149
|
self.window.dispatch(event)
|
|
150
150
|
sys_prompt = event.data['value']
|
|
151
151
|
|
|
152
|
+
# append personalized about
|
|
153
|
+
about = self.window.core.config.get("personalize.about", "")
|
|
154
|
+
modes = self.window.core.config.get("personalize.modes", "")
|
|
155
|
+
if modes:
|
|
156
|
+
modes_list = modes.split(',')
|
|
157
|
+
if mode in modes_list:
|
|
158
|
+
if about:
|
|
159
|
+
sys_prompt += f"\n\n{about}\n\n"
|
|
160
|
+
|
|
152
161
|
# event: post prompt (post-handle system prompt)
|
|
153
162
|
event = Event(Event.POST_PROMPT, {
|
|
154
163
|
'mode': mode,
|
|
@@ -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.
|
|
9
|
+
# Updated Date: 2025.08.09 19:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import os
|
|
@@ -497,15 +497,7 @@ class Body:
|
|
|
497
497
|
hideTips();
|
|
498
498
|
}
|
|
499
499
|
function sanitize(content) {
|
|
500
|
-
|
|
501
|
-
var doc = parser.parseFromString(content, "text/html");
|
|
502
|
-
var codeElements = doc.querySelectorAll('code, pre');
|
|
503
|
-
codeElements.forEach(function(element) {
|
|
504
|
-
var html = element.outerHTML;
|
|
505
|
-
var newHtml = html.replace(/&lt;/g, '<').replace(/&gt;/g, '>');
|
|
506
|
-
element.outerHTML = newHtml;
|
|
507
|
-
});
|
|
508
|
-
return doc.documentElement.outerHTML;
|
|
500
|
+
return content.replace(/&lt;/g, '<').replace(/&gt;/g, '>');
|
|
509
501
|
}
|
|
510
502
|
function highlightCode(withMath = true) {
|
|
511
503
|
document.querySelectorAll('pre code').forEach(el => {
|
|
@@ -711,7 +703,7 @@ class Body:
|
|
|
711
703
|
function endStream() {
|
|
712
704
|
clearOutput();
|
|
713
705
|
}
|
|
714
|
-
function appendStream(
|
|
706
|
+
function appendStream(name_header, content, chunk, replace = false, is_code_block = false) {
|
|
715
707
|
hideTips();
|
|
716
708
|
const element = getStreamContainer();
|
|
717
709
|
doHighlight = true;
|
|
@@ -724,13 +716,13 @@ class Body:
|
|
|
724
716
|
box = document.createElement('div');
|
|
725
717
|
box.classList.add('msg-box');
|
|
726
718
|
box.classList.add('msg-bot');
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
719
|
+
if (name_header != '') {
|
|
720
|
+
const name = document.createElement('div');
|
|
721
|
+
name.innerHTML = name_header;
|
|
722
|
+
box.appendChild(name);
|
|
723
|
+
}
|
|
731
724
|
msg = document.createElement('div');
|
|
732
725
|
msg.classList.add('msg');
|
|
733
|
-
box.appendChild(name);
|
|
734
726
|
box.appendChild(msg);
|
|
735
727
|
element.appendChild(box);
|
|
736
728
|
} else {
|
|
@@ -799,7 +791,7 @@ class Body:
|
|
|
799
791
|
scrollToBottom();
|
|
800
792
|
}
|
|
801
793
|
}
|
|
802
|
-
function replaceOutput(
|
|
794
|
+
function replaceOutput(name_header, content) {
|
|
803
795
|
hideTips();
|
|
804
796
|
const element = getStreamContainer();
|
|
805
797
|
if (element) {
|
|
@@ -809,13 +801,13 @@ class Body:
|
|
|
809
801
|
box = document.createElement('div');
|
|
810
802
|
box.classList.add('msg-box');
|
|
811
803
|
box.classList.add('msg-bot');
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
804
|
+
if (name_header != '') {
|
|
805
|
+
const name = document.createElement('div');
|
|
806
|
+
name.innerHTML = name_header;
|
|
807
|
+
box.appendChild(name);
|
|
808
|
+
}
|
|
816
809
|
msg = document.createElement('div');
|
|
817
810
|
msg.classList.add('msg');
|
|
818
|
-
box.appendChild(name);
|
|
819
811
|
box.appendChild(msg);
|
|
820
812
|
element.appendChild(box);
|
|
821
813
|
} else {
|
|
@@ -65,8 +65,15 @@ class Helpers:
|
|
|
65
65
|
# replace HTML tags
|
|
66
66
|
text = text.replace("<think>", "{{{{think}}}}")
|
|
67
67
|
text = text.replace("</think>", "{{{{/think}}}}")
|
|
68
|
-
text = text.replace("<", "<")
|
|
69
|
-
text = text.replace(">", ">")
|
|
68
|
+
# text = text.replace("<", "<")
|
|
69
|
+
# text = text.replace(">", ">")
|
|
70
|
+
text = re.sub(
|
|
71
|
+
r'(\\\[.*?\\\])|(<)|(>)',
|
|
72
|
+
lambda m: m.group(1) if m.group(1)
|
|
73
|
+
else "<" if m.group(2)
|
|
74
|
+
else ">",
|
|
75
|
+
text, flags=re.DOTALL
|
|
76
|
+
) # leave math formula tags
|
|
70
77
|
text = text.replace("{{{{think}}}}", "<think>")
|
|
71
78
|
text = text.replace("{{{{/think}}}}", "</think>")
|
|
72
79
|
text = text.replace("<think>\n", "<think>")
|
|
@@ -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.
|
|
9
|
+
# Updated Date: 2025.08.09 19:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import json
|
|
@@ -452,6 +452,7 @@ class Renderer(BaseRenderer):
|
|
|
452
452
|
self.pids[pid].buffer = "" # always reset buffer
|
|
453
453
|
return
|
|
454
454
|
|
|
455
|
+
name_header = self.get_name_header(ctx)
|
|
455
456
|
self.update_names(meta, ctx)
|
|
456
457
|
raw_chunk = str(text_chunk)
|
|
457
458
|
raw_chunk = raw_chunk.replace("<", "<")
|
|
@@ -521,6 +522,7 @@ class Renderer(BaseRenderer):
|
|
|
521
522
|
raw_chunk = "\n" + raw_chunk # add newline to chunk
|
|
522
523
|
escaped_chunk = json.dumps(raw_chunk)
|
|
523
524
|
escaped_buffer = json.dumps(html)
|
|
525
|
+
name_header = json.dumps(name_header)
|
|
524
526
|
|
|
525
527
|
if replace == "true":
|
|
526
528
|
self.prev_chunk_replace = True
|
|
@@ -529,7 +531,7 @@ class Renderer(BaseRenderer):
|
|
|
529
531
|
|
|
530
532
|
try:
|
|
531
533
|
self.get_output_node(meta).page().runJavaScript(
|
|
532
|
-
f"appendStream(
|
|
534
|
+
f"appendStream({name_header}, {escaped_buffer}, {escaped_chunk}, {replace}, {code_block_arg});")
|
|
533
535
|
except Exception as e:
|
|
534
536
|
pass
|
|
535
537
|
|
|
@@ -1209,9 +1211,10 @@ class Renderer(BaseRenderer):
|
|
|
1209
1211
|
if self.is_debug():
|
|
1210
1212
|
debug = self.append_debug(ctx, pid, "output")
|
|
1211
1213
|
|
|
1214
|
+
name_header = self.get_name_header(ctx)
|
|
1212
1215
|
html = (
|
|
1213
1216
|
'<div class="msg-box msg-bot" id="{msg_id}">'
|
|
1214
|
-
|
|
1217
|
+
+ name_header +
|
|
1215
1218
|
'<div class="msg">'
|
|
1216
1219
|
'{html}'
|
|
1217
1220
|
'<div class="msg-tool-extra">{tool_extra}</div>'
|
|
@@ -1234,6 +1237,44 @@ class Renderer(BaseRenderer):
|
|
|
1234
1237
|
|
|
1235
1238
|
return html
|
|
1236
1239
|
|
|
1240
|
+
def get_name_header(self, ctx: CtxItem) -> str:
|
|
1241
|
+
"""
|
|
1242
|
+
Get name header for the bot
|
|
1243
|
+
|
|
1244
|
+
:param ctx: CtxItem instance
|
|
1245
|
+
:return: HTML name header
|
|
1246
|
+
"""
|
|
1247
|
+
meta = ctx.meta
|
|
1248
|
+
if meta is None:
|
|
1249
|
+
return ""
|
|
1250
|
+
preset_id = meta.preset
|
|
1251
|
+
if preset_id is None or preset_id == "":
|
|
1252
|
+
return ""
|
|
1253
|
+
preset = self.window.core.presets.get(preset_id)
|
|
1254
|
+
if preset is None:
|
|
1255
|
+
return ""
|
|
1256
|
+
if not preset.ai_personalize:
|
|
1257
|
+
return ""
|
|
1258
|
+
|
|
1259
|
+
output_name = ""
|
|
1260
|
+
avatar_html = ""
|
|
1261
|
+
if preset.ai_name:
|
|
1262
|
+
output_name = preset.ai_name
|
|
1263
|
+
if preset.ai_avatar:
|
|
1264
|
+
presets_dir = self.window.core.config.get_user_dir("presets")
|
|
1265
|
+
avatars_dir = os.path.join(presets_dir, "avatars")
|
|
1266
|
+
avatar_path = os.path.join(avatars_dir, preset.ai_avatar)
|
|
1267
|
+
if os.path.exists(avatar_path):
|
|
1268
|
+
if self.window.core.platforms.is_windows():
|
|
1269
|
+
prefix = 'file:///'
|
|
1270
|
+
else:
|
|
1271
|
+
prefix = 'file://'
|
|
1272
|
+
avatar_html = "<img src=\"" +prefix + avatar_path + "\" class=\"avatar\"> "
|
|
1273
|
+
|
|
1274
|
+
if not output_name and not avatar_html:
|
|
1275
|
+
return ""
|
|
1276
|
+
return "<div class=\"name-header name-bot\">" +avatar_html + output_name + "</div>"
|
|
1277
|
+
|
|
1237
1278
|
def flush_output(
|
|
1238
1279
|
self,
|
|
1239
1280
|
pid: Optional[int],
|