pygpt-net 2.5.93__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 +15 -0
- pygpt_net/__init__.py +3 -3
- pygpt_net/app.py +6 -2
- pygpt_net/controller/chat/image.py +0 -3
- pygpt_net/controller/chat/response.py +5 -1
- pygpt_net/controller/chat/text.py +3 -1
- pygpt_net/controller/config/field/slider.py +9 -5
- pygpt_net/controller/ctx/ctx.py +23 -18
- pygpt_net/controller/dialogs/confirm.py +4 -1
- pygpt_net/controller/presets/editor.py +117 -1
- pygpt_net/controller/settings/editor.py +0 -6
- pygpt_net/controller/ui/mode.py +1 -3
- pygpt_net/controller/ui/tabs.py +60 -42
- pygpt_net/core/ctx/ctx.py +4 -2
- pygpt_net/core/models/models.py +6 -1
- pygpt_net/core/presets/presets.py +12 -1
- pygpt_net/core/prompt/prompt.py +10 -1
- pygpt_net/core/render/web/body.py +80 -23
- 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/core/types/openai.py +14 -1
- pygpt_net/data/config/config.json +6 -4
- pygpt_net/data/config/models.json +203 -532
- pygpt_net/data/config/presets/agent_openai_b2b.json +54 -0
- pygpt_net/data/config/settings.json +44 -14
- pygpt_net/data/config/settings_section.json +3 -0
- pygpt_net/data/css/web-blocks.css +27 -2
- pygpt_net/data/css/web-blocks.dark.css +7 -0
- pygpt_net/data/css/web-blocks.light.css +7 -0
- pygpt_net/data/css/web-chatgpt.css +33 -2
- pygpt_net/data/css/web-chatgpt.dark.css +7 -0
- pygpt_net/data/css/web-chatgpt.light.css +7 -0
- pygpt_net/data/css/web-chatgpt_wide.css +33 -2
- pygpt_net/data/css/web-chatgpt_wide.dark.css +7 -0
- pygpt_net/data/css/web-chatgpt_wide.light.css +7 -0
- pygpt_net/data/locale/locale.de.ini +33 -0
- pygpt_net/data/locale/locale.en.ini +35 -0
- pygpt_net/data/locale/locale.es.ini +33 -0
- pygpt_net/data/locale/locale.fr.ini +33 -0
- pygpt_net/data/locale/locale.it.ini +33 -0
- pygpt_net/data/locale/locale.pl.ini +35 -1
- pygpt_net/data/locale/locale.uk.ini +33 -0
- pygpt_net/data/locale/locale.zh.ini +33 -0
- pygpt_net/item/model.py +6 -14
- pygpt_net/item/preset.py +9 -1
- pygpt_net/provider/agents/openai/agent_b2b.py +396 -0
- pygpt_net/provider/core/config/patch.py +39 -1
- pygpt_net/provider/core/model/patch.py +16 -1
- pygpt_net/provider/core/preset/json_file.py +6 -0
- pygpt_net/provider/core/preset/patch.py +17 -0
- pygpt_net/provider/llms/hugging_face_router.py +132 -0
- pygpt_net/tools/code_interpreter/ui/html.py +2 -2
- 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/image.py +0 -6
- pygpt_net/ui/dialog/preset.py +126 -8
- pygpt_net/ui/widget/option/combo.py +2 -0
- pygpt_net/ui/widget/textarea/html.py +2 -2
- pygpt_net/ui/widget/textarea/search_input.py +68 -2
- pygpt_net/ui/widget/textarea/web.py +3 -2
- {pygpt_net-2.5.93.dist-info → pygpt_net-2.5.95.dist-info}/METADATA +48 -26
- {pygpt_net-2.5.93.dist-info → pygpt_net-2.5.95.dist-info}/RECORD +68 -65
- {pygpt_net-2.5.93.dist-info → pygpt_net-2.5.95.dist-info}/LICENSE +0 -0
- {pygpt_net-2.5.93.dist-info → pygpt_net-2.5.95.dist-info}/WHEEL +0 -0
- {pygpt_net-2.5.93.dist-info → pygpt_net-2.5.95.dist-info}/entry_points.txt +0 -0
pygpt_net/CHANGELOG.txt
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
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
|
+
|
|
8
|
+
2.5.94 (2025-08-09)
|
|
9
|
+
|
|
10
|
+
- Added a new LLM provider: HuggingFace Router.
|
|
11
|
+
- Introduced a new model: gpt-oss (OpenAI open-source model available in HuggingFace and Ollama).
|
|
12
|
+
- Added a new agent mode in OpenAI Agents: Bot 2 Bot.
|
|
13
|
+
- Fixed: Storing the last used context ID when empty.
|
|
14
|
+
- Fixed: Reloading items when an agent run is stopped.
|
|
15
|
+
|
|
1
16
|
2.5.93 (2025-08-08)
|
|
2
17
|
|
|
3
18
|
- Added a new tool: Translate - in menu Tools - feature #123.
|
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.
|
|
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.
|
|
17
|
-
__build__ = "2025-08-
|
|
16
|
+
__version__ = "2.5.95"
|
|
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.
|
|
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
|
|
@@ -390,6 +392,7 @@ def run(**kwargs):
|
|
|
390
392
|
launcher.add_llm(GoogleLLM())
|
|
391
393
|
# launcher.add_llm(HuggingFaceLLM())
|
|
392
394
|
launcher.add_llm(HuggingFaceApiLLM())
|
|
395
|
+
launcher.add_llm(HuggingFaceRouterLLM())
|
|
393
396
|
launcher.add_llm(LocalLLM())
|
|
394
397
|
launcher.add_llm(MistralAILLM())
|
|
395
398
|
launcher.add_llm(OllamaLLM())
|
|
@@ -430,6 +433,7 @@ def run(**kwargs):
|
|
|
430
433
|
launcher.add_agent(OpenAIAgentBotResearcher()) # openai-agents
|
|
431
434
|
launcher.add_agent(OpenAIAgentsExpertsFeedback()) # openai-agents
|
|
432
435
|
launcher.add_agent(OpenAIAgentsEvolve()) # openai-agents
|
|
436
|
+
launcher.add_agent(OpenAIAgentsB2B()) # openai-agents
|
|
433
437
|
|
|
434
438
|
# register custom agents
|
|
435
439
|
agents = kwargs.get('agents', None)
|
|
@@ -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
|
|
@@ -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.
|
|
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]
|
|
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
|
-
|
|
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]
|
|
91
|
-
|
|
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,
|
pygpt_net/controller/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.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
|
-
|
|
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.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
|
+
)
|
|
@@ -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)
|
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
|
@@ -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.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()
|
|
@@ -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
|
|
pygpt_net/core/models/models.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.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
|
|