pygpt-net 2.6.36__py3-none-any.whl → 2.6.37__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 +5 -0
- pygpt_net/__init__.py +3 -3
- pygpt_net/controller/chat/handler/anthropic_stream.py +166 -0
- pygpt_net/controller/chat/handler/google_stream.py +181 -0
- pygpt_net/controller/chat/handler/langchain_stream.py +24 -0
- pygpt_net/controller/chat/handler/llamaindex_stream.py +47 -0
- pygpt_net/controller/chat/handler/openai_stream.py +260 -0
- pygpt_net/controller/chat/handler/utils.py +210 -0
- pygpt_net/controller/chat/handler/worker.py +566 -0
- pygpt_net/controller/chat/handler/xai_stream.py +135 -0
- pygpt_net/controller/chat/stream.py +1 -1
- pygpt_net/controller/ctx/ctx.py +1 -1
- pygpt_net/controller/model/editor.py +3 -0
- pygpt_net/core/bridge/context.py +35 -35
- pygpt_net/core/bridge/worker.py +40 -16
- pygpt_net/core/render/web/body.py +29 -34
- pygpt_net/data/config/config.json +10 -3
- pygpt_net/data/config/models.json +3 -3
- pygpt_net/data/config/settings.json +105 -0
- pygpt_net/data/css/style.dark.css +2 -3
- pygpt_net/data/css/style.light.css +2 -3
- pygpt_net/data/locale/locale.de.ini +3 -1
- pygpt_net/data/locale/locale.en.ini +19 -1
- pygpt_net/data/locale/locale.es.ini +3 -1
- pygpt_net/data/locale/locale.fr.ini +3 -1
- pygpt_net/data/locale/locale.it.ini +3 -1
- pygpt_net/data/locale/locale.pl.ini +4 -2
- pygpt_net/data/locale/locale.uk.ini +3 -1
- pygpt_net/data/locale/locale.zh.ini +3 -1
- pygpt_net/provider/api/__init__.py +5 -3
- pygpt_net/provider/api/anthropic/__init__.py +190 -29
- pygpt_net/provider/api/anthropic/audio.py +30 -0
- pygpt_net/provider/api/anthropic/chat.py +341 -0
- pygpt_net/provider/api/anthropic/image.py +25 -0
- pygpt_net/provider/api/anthropic/tools.py +266 -0
- pygpt_net/provider/api/anthropic/vision.py +142 -0
- pygpt_net/provider/api/google/chat.py +2 -2
- pygpt_net/provider/api/google/tools.py +58 -48
- pygpt_net/provider/api/google/vision.py +7 -1
- pygpt_net/provider/api/openai/chat.py +1 -0
- pygpt_net/provider/api/openai/vision.py +6 -0
- pygpt_net/provider/api/x_ai/__init__.py +247 -0
- pygpt_net/provider/api/x_ai/audio.py +32 -0
- pygpt_net/provider/api/x_ai/chat.py +968 -0
- pygpt_net/provider/api/x_ai/image.py +208 -0
- pygpt_net/provider/api/x_ai/remote.py +262 -0
- pygpt_net/provider/api/x_ai/tools.py +120 -0
- pygpt_net/provider/api/x_ai/vision.py +119 -0
- pygpt_net/provider/core/config/patch.py +28 -0
- pygpt_net/provider/llms/anthropic.py +4 -2
- pygpt_net/ui/base/config_dialog.py +5 -11
- pygpt_net/ui/dialog/models.py +2 -4
- pygpt_net/ui/dialog/plugins.py +40 -43
- pygpt_net/ui/widget/element/labels.py +19 -3
- pygpt_net/ui/widget/textarea/web.py +1 -1
- {pygpt_net-2.6.36.dist-info → pygpt_net-2.6.37.dist-info}/METADATA +11 -6
- {pygpt_net-2.6.36.dist-info → pygpt_net-2.6.37.dist-info}/RECORD +60 -41
- pygpt_net/controller/chat/handler/stream_worker.py +0 -1136
- {pygpt_net-2.6.36.dist-info → pygpt_net-2.6.37.dist-info}/LICENSE +0 -0
- {pygpt_net-2.6.36.dist-info → pygpt_net-2.6.37.dist-info}/WHEEL +0 -0
- {pygpt_net-2.6.36.dist-info → pygpt_net-2.6.37.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,119 @@
|
|
|
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.09.05 01:00:00 #
|
|
10
|
+
# ================================================== #
|
|
11
|
+
|
|
12
|
+
import os
|
|
13
|
+
from typing import Optional, Dict, List
|
|
14
|
+
|
|
15
|
+
from pygpt_net.item.attachment import AttachmentItem
|
|
16
|
+
from pygpt_net.item.ctx import CtxItem
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Vision:
|
|
20
|
+
def __init__(self, window=None):
|
|
21
|
+
"""
|
|
22
|
+
Vision helpers for xAI (image inputs as data: URIs).
|
|
23
|
+
|
|
24
|
+
:param window: Window instance
|
|
25
|
+
"""
|
|
26
|
+
self.window = window
|
|
27
|
+
self.attachments: Dict[str, str] = {}
|
|
28
|
+
self.urls: List[str] = []
|
|
29
|
+
self.input_tokens = 0
|
|
30
|
+
self.allowed_mimes = {"image/jpeg", "image/png"}
|
|
31
|
+
|
|
32
|
+
def build_images_for_chat(self, attachments: Optional[Dict[str, AttachmentItem]]) -> List[str]:
|
|
33
|
+
"""
|
|
34
|
+
Build image inputs for xai_sdk.chat.image(...).
|
|
35
|
+
Returns list of image sources (URLs or data: URIs).
|
|
36
|
+
|
|
37
|
+
:param attachments: Attachments dict (id -> AttachmentItem)
|
|
38
|
+
:return: List of image sources
|
|
39
|
+
"""
|
|
40
|
+
import base64
|
|
41
|
+
|
|
42
|
+
images: List[str] = []
|
|
43
|
+
self.attachments = {}
|
|
44
|
+
self.urls = []
|
|
45
|
+
|
|
46
|
+
if not attachments:
|
|
47
|
+
return images
|
|
48
|
+
|
|
49
|
+
for id_, att in (attachments or {}).items():
|
|
50
|
+
try:
|
|
51
|
+
if att.path and self.window.core.api.xai.vision.is_image(att.path):
|
|
52
|
+
mime = self.window.core.api.xai.vision.guess_mime(att.path)
|
|
53
|
+
# Accept only JPEG/PNG for SDK too (for consistency)
|
|
54
|
+
#if mime not in self.allowed_mimes:
|
|
55
|
+
# continue
|
|
56
|
+
with open(att.path, "rb") as f:
|
|
57
|
+
b64 = base64.b64encode(f.read()).decode("utf-8")
|
|
58
|
+
images.append(f"data:{mime};base64,{b64}")
|
|
59
|
+
self.attachments[id_] = att.path
|
|
60
|
+
att.consumed = True
|
|
61
|
+
except Exception:
|
|
62
|
+
continue
|
|
63
|
+
return images
|
|
64
|
+
|
|
65
|
+
def is_image(self, path: str) -> bool:
|
|
66
|
+
"""
|
|
67
|
+
Return True if path looks like an image file.
|
|
68
|
+
|
|
69
|
+
:param path: File path
|
|
70
|
+
"""
|
|
71
|
+
return path.lower().endswith(('.png', '.jpg', '.jpeg', '.tiff', '.bmp', '.gif', '.webp'))
|
|
72
|
+
|
|
73
|
+
def guess_mime(self, path: str) -> str:
|
|
74
|
+
"""Guess MIME by extension.
|
|
75
|
+
|
|
76
|
+
:param path: File path
|
|
77
|
+
:return: MIME type string
|
|
78
|
+
"""
|
|
79
|
+
ext = os.path.splitext(path)[1].lower().lstrip(".")
|
|
80
|
+
if ext in ("jpg", "jpeg"):
|
|
81
|
+
return "image/jpeg"
|
|
82
|
+
if ext == "png":
|
|
83
|
+
return "image/png"
|
|
84
|
+
if ext == "gif":
|
|
85
|
+
return "image/gif"
|
|
86
|
+
if ext == "bmp":
|
|
87
|
+
return "image/bmp"
|
|
88
|
+
if ext == "webp":
|
|
89
|
+
return "image/webp"
|
|
90
|
+
if ext == "tiff":
|
|
91
|
+
return "image/tiff"
|
|
92
|
+
return "image/jpeg"
|
|
93
|
+
|
|
94
|
+
def append_images(self, ctx: CtxItem):
|
|
95
|
+
"""
|
|
96
|
+
Append sent images list to context for UI/history.
|
|
97
|
+
|
|
98
|
+
:param ctx: CtxItem
|
|
99
|
+
"""
|
|
100
|
+
images = self.get_attachments()
|
|
101
|
+
if len(images) > 0:
|
|
102
|
+
ctx.images = self.window.core.filesystem.make_local_list(list(images.values()))
|
|
103
|
+
|
|
104
|
+
def get_attachments(self) -> Dict[str, str]:
|
|
105
|
+
return self.attachments
|
|
106
|
+
|
|
107
|
+
def get_urls(self) -> List[str]:
|
|
108
|
+
return self.urls
|
|
109
|
+
|
|
110
|
+
def reset_tokens(self):
|
|
111
|
+
self.input_tokens = 0
|
|
112
|
+
|
|
113
|
+
def get_used_tokens(self) -> int:
|
|
114
|
+
return self.input_tokens
|
|
115
|
+
|
|
116
|
+
def reset(self):
|
|
117
|
+
self.attachments = {}
|
|
118
|
+
self.urls = []
|
|
119
|
+
self.input_tokens = 0
|
|
@@ -2447,6 +2447,34 @@ class Patch:
|
|
|
2447
2447
|
patch_css('web-blocks.css', True)
|
|
2448
2448
|
updated = True
|
|
2449
2449
|
|
|
2450
|
+
# < 2.6.37
|
|
2451
|
+
if old < parse_version("2.6.37"):
|
|
2452
|
+
print("Migrating config from < 2.6.37...")
|
|
2453
|
+
|
|
2454
|
+
# add: label-desc CSS
|
|
2455
|
+
patch_css('style.dark.css', True)
|
|
2456
|
+
patch_css('style.light.css', True)
|
|
2457
|
+
|
|
2458
|
+
# add: Anthropic SDK
|
|
2459
|
+
if "api_native_anthropic" not in data:
|
|
2460
|
+
data["api_native_anthropic"] = True
|
|
2461
|
+
if "remote_tools.anthropic.web_search" not in data:
|
|
2462
|
+
data["remote_tools.anthropic.web_search"] = True
|
|
2463
|
+
|
|
2464
|
+
# add: xAI SDK
|
|
2465
|
+
if "api_native_xai" not in data:
|
|
2466
|
+
data["api_native_xai"] = True
|
|
2467
|
+
if "remote_tools.xai.mode" not in data:
|
|
2468
|
+
data["remote_tools.xai.mode"] = "auto"
|
|
2469
|
+
if "remote_tools.xai.sources.web" not in data:
|
|
2470
|
+
data["remote_tools.xai.sources.web"] = True
|
|
2471
|
+
if "remote_tools.xai.sources.x" not in data:
|
|
2472
|
+
data["remote_tools.xai.sources.x"] = True
|
|
2473
|
+
if "remote_tools.xai.sources.news" not in data:
|
|
2474
|
+
data["remote_tools.xai.sources.news"] = False
|
|
2475
|
+
|
|
2476
|
+
updated = True
|
|
2477
|
+
|
|
2450
2478
|
# update file
|
|
2451
2479
|
migrated = False
|
|
2452
2480
|
if updated:
|
|
@@ -15,7 +15,7 @@ from llama_index.core.base.embeddings.base import BaseEmbedding
|
|
|
15
15
|
from llama_index.core.llms.llm import BaseLLM as LlamaBaseLLM
|
|
16
16
|
|
|
17
17
|
from pygpt_net.core.types import (
|
|
18
|
-
MODE_LLAMA_INDEX,
|
|
18
|
+
MODE_LLAMA_INDEX, MODE_CHAT,
|
|
19
19
|
)
|
|
20
20
|
from pygpt_net.provider.llms.base import BaseLLM
|
|
21
21
|
from pygpt_net.item.model import ModelItem
|
|
@@ -93,7 +93,9 @@ class AnthropicLLM(BaseLLM):
|
|
|
93
93
|
:param window: window instance
|
|
94
94
|
:return: list of models
|
|
95
95
|
"""
|
|
96
|
-
|
|
96
|
+
model = ModelItem()
|
|
97
|
+
model.provider = "anthropic"
|
|
98
|
+
client = window.core.api.anthropic.get_client(MODE_CHAT, model)
|
|
97
99
|
models_list = client.models.list()
|
|
98
100
|
items = []
|
|
99
101
|
if models_list.data:
|
|
@@ -6,14 +6,13 @@
|
|
|
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.09.04 00:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from PySide6.QtCore import Qt
|
|
13
13
|
from PySide6.QtWidgets import QHBoxLayout, QLabel, QVBoxLayout, QSizePolicy, QWidget, QFrame
|
|
14
|
-
from PySide6.QtGui import QFont
|
|
15
14
|
|
|
16
|
-
from pygpt_net.ui.widget.element.labels import TitleLabel, UrlLabel
|
|
15
|
+
from pygpt_net.ui.widget.element.labels import TitleLabel, UrlLabel, DescLabel, BaseLabel
|
|
17
16
|
from pygpt_net.ui.widget.option.checkbox import OptionCheckbox
|
|
18
17
|
from pygpt_net.ui.widget.option.checkbox_list import OptionCheckboxList
|
|
19
18
|
from pygpt_net.ui.widget.option.combo import OptionCombo
|
|
@@ -123,7 +122,7 @@ class BaseConfigDialog:
|
|
|
123
122
|
if extra.get('bold'):
|
|
124
123
|
nodes[label_key] = TitleLabel(txt)
|
|
125
124
|
else:
|
|
126
|
-
nodes[label_key] =
|
|
125
|
+
nodes[label_key] = BaseLabel(txt)
|
|
127
126
|
nodes[label_key].setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Preferred)
|
|
128
127
|
nodes[label_key].setMinimumWidth(120)
|
|
129
128
|
nodes[label_key].setWordWrap(True)
|
|
@@ -169,7 +168,7 @@ class BaseConfigDialog:
|
|
|
169
168
|
if extra.get('bold'):
|
|
170
169
|
nodes[label_key] = TitleLabel(txt)
|
|
171
170
|
else:
|
|
172
|
-
nodes[label_key] =
|
|
171
|
+
nodes[label_key] = BaseLabel(txt)
|
|
173
172
|
nodes[label_key].setMinimumHeight(30)
|
|
174
173
|
nodes[label_key].setWordWrap(True)
|
|
175
174
|
|
|
@@ -234,12 +233,7 @@ class BaseConfigDialog:
|
|
|
234
233
|
:param text: text (to translate)
|
|
235
234
|
:return: QLabel
|
|
236
235
|
"""
|
|
237
|
-
|
|
238
|
-
label = QLabel(desc)
|
|
239
|
-
label.setWordWrap(True)
|
|
240
|
-
label.setMaximumHeight(80)
|
|
241
|
-
label.setStyleSheet("font-size: 10px;")
|
|
242
|
-
return label
|
|
236
|
+
return DescLabel(trans(text))
|
|
243
237
|
|
|
244
238
|
def add_urls(self, urls, align=Qt.AlignLeft) -> QWidget:
|
|
245
239
|
"""
|
pygpt_net/ui/dialog/models.py
CHANGED
|
@@ -19,7 +19,7 @@ from PySide6.QtWidgets import QPushButton, QHBoxLayout, QLabel, QVBoxLayout, QSc
|
|
|
19
19
|
from pygpt_net.item.model import ModelItem
|
|
20
20
|
from pygpt_net.ui.widget.dialog.model import ModelDialog
|
|
21
21
|
from pygpt_net.ui.widget.element.group import CollapsedGroup
|
|
22
|
-
from pygpt_net.ui.widget.element.labels import UrlLabel
|
|
22
|
+
from pygpt_net.ui.widget.element.labels import UrlLabel, DescLabel
|
|
23
23
|
from pygpt_net.ui.widget.lists.model_editor import ModelEditorList
|
|
24
24
|
from pygpt_net.ui.widget.option.checkbox import OptionCheckbox
|
|
25
25
|
from pygpt_net.ui.widget.option.checkbox_list import OptionCheckboxList
|
|
@@ -303,10 +303,8 @@ class Models:
|
|
|
303
303
|
layout.addWidget(widget)
|
|
304
304
|
|
|
305
305
|
if desc:
|
|
306
|
-
self.window.ui.nodes[desc_key] =
|
|
307
|
-
self.window.ui.nodes[desc_key].setWordWrap(True)
|
|
306
|
+
self.window.ui.nodes[desc_key] = DescLabel(desc)
|
|
308
307
|
self.window.ui.nodes[desc_key].setMaximumHeight(40)
|
|
309
|
-
self.window.ui.nodes[desc_key].setStyleSheet("font-size: 10px;")
|
|
310
308
|
layout.addWidget(self.window.ui.nodes[desc_key])
|
|
311
309
|
|
|
312
310
|
line = self.add_line() # TODO: change name to separator
|
pygpt_net/ui/dialog/plugins.py
CHANGED
|
@@ -6,18 +6,18 @@
|
|
|
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.09.04 00:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from PySide6.QtCore import Qt
|
|
13
13
|
from PySide6.QtGui import QStandardItemModel
|
|
14
|
-
from PySide6.QtWidgets import QPushButton, QHBoxLayout,
|
|
14
|
+
from PySide6.QtWidgets import QPushButton, QHBoxLayout, QVBoxLayout, QScrollArea, QWidget, QTabWidget, QFrame, \
|
|
15
15
|
QSplitter, QSizePolicy
|
|
16
16
|
|
|
17
17
|
from pygpt_net.plugin.base.plugin import BasePlugin
|
|
18
18
|
from pygpt_net.ui.widget.dialog.settings_plugin import PluginSettingsDialog
|
|
19
19
|
from pygpt_net.ui.widget.element.group import CollapsedGroup
|
|
20
|
-
from pygpt_net.ui.widget.element.labels import UrlLabel, HelpLabel
|
|
20
|
+
from pygpt_net.ui.widget.element.labels import UrlLabel, HelpLabel, DescLabel, BaseLabel
|
|
21
21
|
from pygpt_net.ui.widget.lists.plugin import PluginList
|
|
22
22
|
from pygpt_net.ui.widget.option.checkbox import OptionCheckbox
|
|
23
23
|
from pygpt_net.ui.widget.option.checkbox_list import OptionCheckboxList
|
|
@@ -77,14 +77,15 @@ class Plugins:
|
|
|
77
77
|
self.window.ui.tabs['plugin.settings.tabs'] = {}
|
|
78
78
|
|
|
79
79
|
sorted_ids = self.window.core.plugins.get_ids(sort=True)
|
|
80
|
+
get_plugin = self.window.core.plugins.get
|
|
80
81
|
|
|
81
82
|
# build plugin settings tabs
|
|
82
83
|
for id in sorted_ids:
|
|
83
84
|
content_tabs = {}
|
|
84
85
|
scroll_tabs = {}
|
|
85
86
|
|
|
86
|
-
plugin =
|
|
87
|
-
parent_id = "plugin."
|
|
87
|
+
plugin = get_plugin(id)
|
|
88
|
+
parent_id = f"plugin.{id}"
|
|
88
89
|
|
|
89
90
|
# create plugin options entry if not exists
|
|
90
91
|
if parent_id not in self.window.ui.config:
|
|
@@ -137,7 +138,7 @@ class Plugins:
|
|
|
137
138
|
# append advanced options at the end
|
|
138
139
|
if len(advanced_keys) > 0:
|
|
139
140
|
groups = {}
|
|
140
|
-
group_id =
|
|
141
|
+
group_id = f"plugin.settings.advanced.{id}"
|
|
141
142
|
for key in widgets:
|
|
142
143
|
if key not in advanced_keys: # ignore non-advanced options
|
|
143
144
|
continue
|
|
@@ -157,7 +158,7 @@ class Plugins:
|
|
|
157
158
|
|
|
158
159
|
# add advanced options group to scrolls
|
|
159
160
|
for tab_id in groups:
|
|
160
|
-
full_id = group_id
|
|
161
|
+
full_id = f"{group_id}.{tab_id}"
|
|
161
162
|
content_tabs[tab_id].addWidget(groups[tab_id])
|
|
162
163
|
self.window.ui.groups[full_id] = groups[tab_id]
|
|
163
164
|
|
|
@@ -167,17 +168,16 @@ class Plugins:
|
|
|
167
168
|
|
|
168
169
|
# set description, translate if localization is enabled
|
|
169
170
|
name_txt = plugin.name
|
|
170
|
-
desc_key =
|
|
171
|
+
desc_key = f"plugin.settings.{id}.desc"
|
|
171
172
|
desc_txt = plugin.description
|
|
172
173
|
if plugin.use_locale:
|
|
173
|
-
domain =
|
|
174
|
+
domain = f"plugin.{plugin.id}"
|
|
174
175
|
name_txt = trans('plugin.name', False, domain)
|
|
175
176
|
desc_txt = trans('plugin.description', False, domain)
|
|
176
177
|
|
|
177
|
-
self.window.ui.nodes[desc_key] =
|
|
178
|
-
self.window.ui.nodes[desc_key].setWordWrap(True)
|
|
178
|
+
self.window.ui.nodes[desc_key] = DescLabel(desc_txt)
|
|
179
179
|
self.window.ui.nodes[desc_key].setAlignment(Qt.AlignCenter)
|
|
180
|
-
self.window.ui.nodes[desc_key].setStyleSheet("font-weight: bold;")
|
|
180
|
+
#self.window.ui.nodes[desc_key].setStyleSheet("font-weight: bold;")
|
|
181
181
|
|
|
182
182
|
line = self.add_line()
|
|
183
183
|
|
|
@@ -234,8 +234,7 @@ class Plugins:
|
|
|
234
234
|
|
|
235
235
|
data = {}
|
|
236
236
|
for plugin_id in sorted_ids:
|
|
237
|
-
|
|
238
|
-
data[plugin_id] = plugin
|
|
237
|
+
data[plugin_id] = get_plugin(plugin_id)
|
|
239
238
|
|
|
240
239
|
# plugins list
|
|
241
240
|
id = 'plugin.list'
|
|
@@ -250,12 +249,13 @@ class Plugins:
|
|
|
250
249
|
self.window.ui.nodes[id].setMinimumWidth(self.max_list_width)
|
|
251
250
|
|
|
252
251
|
# splitter
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
252
|
+
splitter = QSplitter(Qt.Horizontal)
|
|
253
|
+
splitter.addWidget(self.window.ui.nodes[id]) # list
|
|
254
|
+
splitter.addWidget(self.window.ui.tabs['plugin.settings']) # tabs
|
|
255
|
+
splitter.setStretchFactor(0, 2)
|
|
256
|
+
splitter.setStretchFactor(1, 5)
|
|
257
|
+
splitter.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
|
258
|
+
self.window.ui.splitters['dialog.plugins'] = splitter
|
|
259
259
|
|
|
260
260
|
main_layout = QHBoxLayout()
|
|
261
261
|
main_layout.addWidget(self.window.ui.splitters['dialog.plugins'])
|
|
@@ -279,7 +279,7 @@ class Plugins:
|
|
|
279
279
|
self.window.ui.tabs['plugin.settings'].setCurrentIndex(idx)
|
|
280
280
|
self.window.controller.plugins.set_by_tab(idx)
|
|
281
281
|
except Exception as e:
|
|
282
|
-
print(
|
|
282
|
+
print(f"Failed restore plugin settings tab: {idx}", e)
|
|
283
283
|
else:
|
|
284
284
|
self.window.controller.plugins.set_by_tab(0)
|
|
285
285
|
|
|
@@ -317,15 +317,16 @@ class Plugins:
|
|
|
317
317
|
:return: dict of widgets
|
|
318
318
|
"""
|
|
319
319
|
id = plugin.id
|
|
320
|
-
parent = "plugin."
|
|
320
|
+
parent = f"plugin.{id}" # parent id for plugins is in format: plugin.<plugin_id>
|
|
321
321
|
widgets = {}
|
|
322
|
+
base_types = ('text', 'int', 'float')
|
|
323
|
+
number_types = ('int', 'float')
|
|
322
324
|
|
|
323
325
|
for key in options:
|
|
324
326
|
option = options[key]
|
|
325
327
|
# create widget by option type
|
|
326
|
-
if option['type']
|
|
327
|
-
if 'slider' in option and option['slider']
|
|
328
|
-
and (option['type'] == 'int' or option['type'] == 'float'):
|
|
328
|
+
if option['type'] in base_types:
|
|
329
|
+
if 'slider' in option and option['slider'] and option['type'] in number_types:
|
|
329
330
|
widgets[key] = OptionSlider(self.window, parent, key, option) # slider + text input
|
|
330
331
|
else:
|
|
331
332
|
if 'secret' in option and option['secret']:
|
|
@@ -396,14 +397,14 @@ class Plugins:
|
|
|
396
397
|
:param option: option dict
|
|
397
398
|
:return: QVBoxLayout
|
|
398
399
|
"""
|
|
399
|
-
one_column_types =
|
|
400
|
-
no_label_types =
|
|
401
|
-
no_desc_types =
|
|
400
|
+
one_column_types = ('textarea', 'dict', 'bool', 'cmd')
|
|
401
|
+
no_label_types = ('bool', 'cmd')
|
|
402
|
+
no_desc_types = 'cmd'
|
|
402
403
|
allow_locale = True
|
|
403
404
|
|
|
404
405
|
key = option['id']
|
|
405
|
-
label_key =
|
|
406
|
-
desc_key =
|
|
406
|
+
label_key = f"plugin.{plugin.id}.{key}.label"
|
|
407
|
+
desc_key = f"plugin.{plugin.id}.{key}.desc"
|
|
407
408
|
|
|
408
409
|
# get option label and description
|
|
409
410
|
txt_title = option['label']
|
|
@@ -415,14 +416,14 @@ class Plugins:
|
|
|
415
416
|
|
|
416
417
|
# translate if localization is enabled
|
|
417
418
|
if plugin.use_locale and allow_locale:
|
|
418
|
-
domain =
|
|
419
|
-
txt_title = trans(key
|
|
420
|
-
txt_desc = trans(key
|
|
421
|
-
txt_tooltip = trans(key
|
|
419
|
+
domain = f"plugin.{plugin.id}"
|
|
420
|
+
txt_title = trans(f"{key}.label", False, domain)
|
|
421
|
+
txt_desc = trans(f"{key}.description", False, domain)
|
|
422
|
+
# txt_tooltip = trans(f"{key}.tooltip", False, domain)
|
|
422
423
|
|
|
423
424
|
# if empty tooltip then use description
|
|
424
|
-
if txt_tooltip == key
|
|
425
|
-
txt_tooltip = txt_desc
|
|
425
|
+
# if txt_tooltip == f"{key}.tooltip":
|
|
426
|
+
# txt_tooltip = txt_desc
|
|
426
427
|
|
|
427
428
|
|
|
428
429
|
"""
|
|
@@ -431,7 +432,7 @@ class Plugins:
|
|
|
431
432
|
"""
|
|
432
433
|
|
|
433
434
|
if option['type'] not in no_label_types:
|
|
434
|
-
self.window.ui.nodes[label_key] =
|
|
435
|
+
self.window.ui.nodes[label_key] = BaseLabel(txt_title)
|
|
435
436
|
self.window.ui.nodes[label_key].setStyleSheet("font-weight: bold;")
|
|
436
437
|
|
|
437
438
|
# 2-columns layout
|
|
@@ -447,10 +448,8 @@ class Plugins:
|
|
|
447
448
|
cols_widget.setLayout(cols)
|
|
448
449
|
# cols_widget.setMaximumHeight(90)
|
|
449
450
|
|
|
450
|
-
self.window.ui.nodes[desc_key] =
|
|
451
|
-
self.window.ui.nodes[desc_key].setWordWrap(True)
|
|
451
|
+
self.window.ui.nodes[desc_key] = DescLabel(txt_desc)
|
|
452
452
|
self.window.ui.nodes[desc_key].setMaximumHeight(40)
|
|
453
|
-
self.window.ui.nodes[desc_key].setStyleSheet("font-size: 10px;")
|
|
454
453
|
# self.window.ui.nodes[desc_key].setToolTip(txt_tooltip)
|
|
455
454
|
|
|
456
455
|
layout = QVBoxLayout()
|
|
@@ -468,10 +467,8 @@ class Plugins:
|
|
|
468
467
|
layout.addWidget(widget)
|
|
469
468
|
|
|
470
469
|
if option['type'] not in no_desc_types:
|
|
471
|
-
self.window.ui.nodes[desc_key] =
|
|
472
|
-
self.window.ui.nodes[desc_key].setWordWrap(True)
|
|
470
|
+
self.window.ui.nodes[desc_key] = DescLabel(txt_desc)
|
|
473
471
|
self.window.ui.nodes[desc_key].setMaximumHeight(40)
|
|
474
|
-
self.window.ui.nodes[desc_key].setStyleSheet("font-size: 10px;")
|
|
475
472
|
# self.window.ui.nodes[desc_key].setToolTip(txt_tooltip)
|
|
476
473
|
layout.addWidget(self.window.ui.nodes[desc_key])
|
|
477
474
|
|
|
@@ -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.09.04 00:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from PySide6.QtCore import Qt, QTimer, QRect, Signal, QUrl, QEvent
|
|
@@ -16,20 +16,34 @@ from PySide6.QtWidgets import QLabel, QLineEdit, QToolTip
|
|
|
16
16
|
from pygpt_net.utils import trans
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
class
|
|
19
|
+
class BaseLabel(QLabel):
|
|
20
20
|
def __init__(self, text, window=None):
|
|
21
21
|
super().__init__(text, window)
|
|
22
22
|
self.window = window
|
|
23
23
|
self.setWordWrap(True)
|
|
24
24
|
self.setContentsMargins(3, 3, 3, 3)
|
|
25
|
+
self.setTextInteractionFlags(Qt.TextSelectableByMouse)
|
|
26
|
+
|
|
27
|
+
class HelpLabel(BaseLabel):
|
|
28
|
+
def __init__(self, text, window=None):
|
|
29
|
+
super().__init__(text, window)
|
|
30
|
+
self.window = window
|
|
25
31
|
self.setProperty('class', 'label-help')
|
|
26
32
|
|
|
33
|
+
class DescLabel(BaseLabel):
|
|
34
|
+
def __init__(self, text, window=None):
|
|
35
|
+
super().__init__(text, window)
|
|
36
|
+
self.window = window
|
|
37
|
+
self.setMaximumHeight(80)
|
|
38
|
+
self.setProperty('class', 'label-desc')
|
|
39
|
+
|
|
27
40
|
|
|
28
41
|
class TitleLabel(QLabel):
|
|
29
42
|
def __init__(self, text, window=None):
|
|
30
43
|
super().__init__(text, window)
|
|
31
44
|
self.window = window
|
|
32
45
|
self.setProperty('class', 'label-title')
|
|
46
|
+
self.setTextInteractionFlags(Qt.TextSelectableByMouse)
|
|
33
47
|
|
|
34
48
|
|
|
35
49
|
class ChatStatusLabel(QLabel):
|
|
@@ -39,6 +53,7 @@ class ChatStatusLabel(QLabel):
|
|
|
39
53
|
self.setWordWrap(True)
|
|
40
54
|
self.setAlignment(Qt.AlignRight)
|
|
41
55
|
self.setProperty('class', 'label-chat-status')
|
|
56
|
+
self.setTextInteractionFlags(Qt.TextSelectableByMouse)
|
|
42
57
|
|
|
43
58
|
|
|
44
59
|
class UrlLabel(QLabel):
|
|
@@ -49,6 +64,7 @@ class UrlLabel(QLabel):
|
|
|
49
64
|
self.update_url()
|
|
50
65
|
self.setCursor(QCursor(Qt.PointingHandCursor))
|
|
51
66
|
self.setToolTip(url)
|
|
67
|
+
self.setTextInteractionFlags(Qt.TextSelectableByMouse)
|
|
52
68
|
# self.setWordWrap(True)
|
|
53
69
|
|
|
54
70
|
def update_url(self):
|
|
@@ -59,7 +75,7 @@ class UrlLabel(QLabel):
|
|
|
59
75
|
self.setText(text)
|
|
60
76
|
|
|
61
77
|
def mousePressEvent(self, event):
|
|
62
|
-
if self.window:
|
|
78
|
+
if self.window and hasattr(self.window, 'controller'):
|
|
63
79
|
self.window.controller.dialogs.info.open_url(self.url)
|
|
64
80
|
else:
|
|
65
81
|
QDesktopServices.openUrl(QUrl(self.url))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: pygpt-net
|
|
3
|
-
Version: 2.6.
|
|
3
|
+
Version: 2.6.37
|
|
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, 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
|
|
@@ -118,7 +118,7 @@ Description-Content-Type: text/markdown
|
|
|
118
118
|
|
|
119
119
|
[](https://snapcraft.io/pygpt)
|
|
120
120
|
|
|
121
|
-
Release: **2.6.
|
|
121
|
+
Release: **2.6.37** | build: **2025-09-05** | Python: **>=3.10, <3.14**
|
|
122
122
|
|
|
123
123
|
> Official website: https://pygpt.net | Documentation: https://pygpt.readthedocs.io
|
|
124
124
|
>
|
|
@@ -520,16 +520,16 @@ Here, you can add or manage API keys for any supported provider.
|
|
|
520
520
|
|
|
521
521
|
**+ Inline Vision and Image generation**
|
|
522
522
|
|
|
523
|
-
In **PyGPT**, this mode mirrors `ChatGPT`, allowing you to chat with models like `GPT-5`, `GPT-4`, `o1`, `o3`, `Claude`, `Gemini`, `Grok`, `Perplexity (Sonar)`, `Deepseek`, and more. It works
|
|
523
|
+
In **PyGPT**, this mode mirrors `ChatGPT`, allowing you to chat with models like `GPT-5`, `GPT-4`, `o1`, `o3`, `Claude`, `Gemini`, `Grok`, `Perplexity (Sonar)`, `Deepseek`, and more. It works with the OpenAI SDK using the `Responses API` and `ChatCompletions API. You can also use SDKs from Google GenAI, Anthropic, or xAI if the native SDK is enabled. You can set the endpoint for `ChatCompletions in Config -> Settings -> API Keys`.
|
|
524
524
|
|
|
525
|
-
**Tip:** This mode uses the provider SDK directly. If there's no native client built into the app, models like
|
|
525
|
+
**Tip:** This mode uses the provider SDK directly. If there's no native client built into the app, models like Sonar, or Llama3 are supported in Chat mode via LlamaIndex or OpenAI-compatible API endpoints. The app automatically switches to these endpoints when using non-OpenAI models. You can enable or disable the use of the native API SDK (per provider) in `Settings -> API Keys`. If the native SDK is disabled, the OpenAI SDK will be used via the compatible ChatCompletions API endpoint.
|
|
526
526
|
|
|
527
527
|
Currently built-in native clients:
|
|
528
528
|
|
|
529
|
+
- Anthropic SDK
|
|
529
530
|
- OpenAI SDK
|
|
530
531
|
- Google GenAI SDK
|
|
531
|
-
|
|
532
|
-
Support for Anthropic and xAI native clients is coming soon.
|
|
532
|
+
- xAI SDK
|
|
533
533
|
|
|
534
534
|
The main part of the interface is a chat window where you see your conversations. Below it is a message box for typing. On the right side, you can set up or change the model and system prompt. You can also save these settings as presets to easily switch between models or tasks.
|
|
535
535
|
|
|
@@ -3565,6 +3565,11 @@ may consume additional tokens that are not displayed in the main window.
|
|
|
3565
3565
|
|
|
3566
3566
|
## Recent changes:
|
|
3567
3567
|
|
|
3568
|
+
**2.6.37 (2025-09-05)**
|
|
3569
|
+
|
|
3570
|
+
- Fixed: Function parameters sanitization in the Google Gen AI SDK.
|
|
3571
|
+
- Added: Native support for Anthropic SDK and xAI SDK (with remote tools).
|
|
3572
|
+
|
|
3568
3573
|
**2.6.36 (2025-09-04)**
|
|
3569
3574
|
|
|
3570
3575
|
- Optimized rendering of large code blocks.
|