pygpt-net 2.7.6__py3-none-any.whl → 2.7.7__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 +6 -0
- pygpt_net/__init__.py +3 -3
- pygpt_net/controller/chat/remote_tools.py +3 -9
- pygpt_net/controller/chat/stream.py +2 -2
- pygpt_net/controller/chat/{handler/worker.py → stream_worker.py} +13 -35
- pygpt_net/core/debug/models.py +2 -2
- pygpt_net/data/config/config.json +14 -4
- pygpt_net/data/config/models.json +192 -4
- pygpt_net/data/config/settings.json +125 -35
- pygpt_net/data/locale/locale.de.ini +2 -0
- pygpt_net/data/locale/locale.en.ini +32 -8
- pygpt_net/data/locale/locale.es.ini +2 -0
- pygpt_net/data/locale/locale.fr.ini +2 -0
- pygpt_net/data/locale/locale.it.ini +2 -0
- pygpt_net/data/locale/locale.pl.ini +3 -1
- pygpt_net/data/locale/locale.uk.ini +2 -0
- pygpt_net/data/locale/locale.zh.ini +2 -0
- pygpt_net/plugin/cmd_mouse_control/worker.py +2 -1
- pygpt_net/plugin/cmd_mouse_control/worker_sandbox.py +2 -1
- pygpt_net/provider/api/anthropic/__init__.py +8 -3
- pygpt_net/provider/api/anthropic/chat.py +259 -11
- pygpt_net/provider/api/anthropic/computer.py +844 -0
- pygpt_net/provider/api/anthropic/remote_tools.py +172 -0
- pygpt_net/{controller/chat/handler/anthropic_stream.py → provider/api/anthropic/stream.py} +24 -10
- pygpt_net/provider/api/anthropic/tools.py +32 -77
- pygpt_net/provider/api/anthropic/utils.py +30 -0
- pygpt_net/provider/api/google/chat.py +3 -7
- pygpt_net/{controller/chat/handler/google_stream.py → provider/api/google/stream.py} +1 -1
- pygpt_net/provider/api/google/utils.py +185 -0
- pygpt_net/{controller/chat/handler → provider/api/langchain}/__init__.py +0 -0
- pygpt_net/{controller/chat/handler/langchain_stream.py → provider/api/langchain/stream.py} +1 -1
- pygpt_net/provider/api/llama_index/__init__.py +0 -0
- pygpt_net/{controller/chat/handler/llamaindex_stream.py → provider/api/llama_index/stream.py} +1 -1
- pygpt_net/provider/api/openai/image.py +2 -2
- pygpt_net/{controller/chat/handler/openai_stream.py → provider/api/openai/stream.py} +1 -1
- pygpt_net/provider/api/openai/utils.py +69 -3
- pygpt_net/provider/api/x_ai/__init__.py +109 -10
- pygpt_net/provider/api/x_ai/chat.py +0 -0
- pygpt_net/provider/api/x_ai/image.py +149 -47
- pygpt_net/provider/api/x_ai/{remote.py → remote_tools.py} +165 -70
- pygpt_net/provider/api/x_ai/responses.py +507 -0
- pygpt_net/{controller/chat/handler/xai_stream.py → provider/api/x_ai/stream.py} +12 -1
- pygpt_net/provider/api/x_ai/tools.py +59 -8
- pygpt_net/{controller/chat/handler → provider/api/x_ai}/utils.py +1 -2
- pygpt_net/provider/api/x_ai/vision.py +1 -4
- pygpt_net/provider/core/config/patch.py +22 -1
- pygpt_net/provider/core/model/patch.py +26 -1
- pygpt_net/tools/image_viewer/ui/dialogs.py +3 -2
- pygpt_net/tools/text_editor/ui/dialogs.py +3 -2
- pygpt_net/tools/text_editor/ui/widgets.py +0 -0
- pygpt_net/ui/widget/dialog/base.py +16 -5
- pygpt_net/ui/widget/textarea/editor.py +0 -0
- {pygpt_net-2.7.6.dist-info → pygpt_net-2.7.7.dist-info}/METADATA +8 -2
- {pygpt_net-2.7.6.dist-info → pygpt_net-2.7.7.dist-info}/RECORD +54 -48
- {pygpt_net-2.7.6.dist-info → pygpt_net-2.7.7.dist-info}/LICENSE +0 -0
- {pygpt_net-2.7.6.dist-info → pygpt_net-2.7.7.dist-info}/WHEEL +0 -0
- {pygpt_net-2.7.6.dist-info → pygpt_net-2.7.7.dist-info}/entry_points.txt +0 -0
|
@@ -6,27 +6,176 @@
|
|
|
6
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
7
|
# MIT License #
|
|
8
8
|
# Created By : Marcin Szczygliński #
|
|
9
|
-
# Updated Date:
|
|
9
|
+
# Updated Date: 2026.01.04 19:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import json
|
|
13
15
|
from typing import Optional, Dict, Any, List
|
|
14
16
|
|
|
15
17
|
from pygpt_net.item.model import ModelItem
|
|
16
18
|
|
|
19
|
+
# xAI server-side tools
|
|
20
|
+
try:
|
|
21
|
+
from xai_sdk.tools import web_search as x_web_search
|
|
22
|
+
from xai_sdk.tools import x_search as x_x_search
|
|
23
|
+
from xai_sdk.tools import code_execution as x_code_execution
|
|
24
|
+
from xai_sdk.tools import mcp as x_mcp
|
|
25
|
+
except Exception:
|
|
26
|
+
x_web_search = None
|
|
27
|
+
x_x_search = None
|
|
28
|
+
x_code_execution = None
|
|
29
|
+
x_mcp = None
|
|
30
|
+
|
|
17
31
|
|
|
18
32
|
class Remote:
|
|
19
33
|
def __init__(self, window=None):
|
|
20
34
|
"""
|
|
21
|
-
Live Search builder for xAI:
|
|
35
|
+
Live Search builder for xAI (old, grok-3):
|
|
22
36
|
- Returns a dict with 'sdk' (X-only toggle) and 'http' (full search_parameters).
|
|
23
37
|
- SDK path: native streaming (xai_sdk.chat.stream()) works only for basic X search (no advanced filters).
|
|
24
38
|
- HTTP path: full Live Search (web/news/rss/X with filters), streaming via SSE.
|
|
25
39
|
|
|
40
|
+
Server-side Tools builder for xAI (newer SDKs, Responses API):
|
|
41
|
+
- Builds xAI SDK tool objects for web_search, x_search, code_execution, MCP.
|
|
42
|
+
- Returns include flags, max_turns and use_encrypted_content settings.
|
|
43
|
+
|
|
26
44
|
:param window: Window instance
|
|
27
45
|
"""
|
|
28
46
|
self.window = window
|
|
29
47
|
|
|
48
|
+
def build_for_chat(self, model: ModelItem = None, stream: bool = False) -> Dict[str, Any]:
|
|
49
|
+
"""
|
|
50
|
+
Build server-side tools and options for Chat Responses.
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
{
|
|
54
|
+
"tools": [ ... xai_sdk.tools.* ... ],
|
|
55
|
+
"include": [ ... ],
|
|
56
|
+
"use_encrypted_content": bool,
|
|
57
|
+
"max_turns": Optional[int],
|
|
58
|
+
}
|
|
59
|
+
"""
|
|
60
|
+
cfg = self.window.core.config
|
|
61
|
+
include: List[str] = []
|
|
62
|
+
tools: List[object] = []
|
|
63
|
+
|
|
64
|
+
# global remote tools switch
|
|
65
|
+
is_web_enabled = self.window.controller.chat.remote_tools.enabled(model, "web_search")
|
|
66
|
+
is_x_enabled = bool(cfg.get("remote_tools.xai.x_search", False))
|
|
67
|
+
is_code_enabled = bool(cfg.get("remote_tools.xai.code_execution", False))
|
|
68
|
+
is_mcp_enabled = bool(cfg.get("remote_tools.xai.mcp", False))
|
|
69
|
+
|
|
70
|
+
# include flags
|
|
71
|
+
if stream:
|
|
72
|
+
include.append("verbose_streaming")
|
|
73
|
+
if bool(cfg.get("remote_tools.xai.inline_citations", True)):
|
|
74
|
+
include.append("inline_citations")
|
|
75
|
+
if bool(cfg.get("remote_tools.xai.include_code_output", True)):
|
|
76
|
+
include.append("code_execution_call_output")
|
|
77
|
+
|
|
78
|
+
# use_encrypted_content
|
|
79
|
+
use_encrypted = bool(cfg.get("remote_tools.xai.use_encrypted_content", False))
|
|
80
|
+
|
|
81
|
+
# optional max_turns
|
|
82
|
+
max_turns = None
|
|
83
|
+
try:
|
|
84
|
+
mt = cfg.get("remote_tools.xai.max_turns")
|
|
85
|
+
if isinstance(mt, int) and mt > 0:
|
|
86
|
+
max_turns = int(mt)
|
|
87
|
+
except Exception:
|
|
88
|
+
pass
|
|
89
|
+
|
|
90
|
+
# WEB SEARCH
|
|
91
|
+
if is_web_enabled and x_web_search is not None:
|
|
92
|
+
kwargs: Dict[str, Any] = {}
|
|
93
|
+
enable_img = bool(cfg.get("remote_tools.xai.web.enable_image_understanding", False))
|
|
94
|
+
'''
|
|
95
|
+
allowed = self._as_list(cfg.get("remote_tools.xai.web.allowed_websites"), 5)
|
|
96
|
+
excluded = self._as_list(cfg.get("remote_tools.xai.web.excluded_websites"), 5)
|
|
97
|
+
if allowed and not excluded:
|
|
98
|
+
kwargs["allowed_domains"] = allowed
|
|
99
|
+
elif excluded and not allowed:
|
|
100
|
+
kwargs["excluded_domains"] = excluded
|
|
101
|
+
'''
|
|
102
|
+
if enable_img:
|
|
103
|
+
kwargs["enable_image_understanding"] = True
|
|
104
|
+
try:
|
|
105
|
+
tools.append(x_web_search(**kwargs))
|
|
106
|
+
except Exception:
|
|
107
|
+
tools.append(x_web_search())
|
|
108
|
+
|
|
109
|
+
# X SEARCH
|
|
110
|
+
if is_x_enabled and x_x_search is not None:
|
|
111
|
+
kwargs: Dict[str, Any] = {}
|
|
112
|
+
'''
|
|
113
|
+
inc = self._as_list(cfg.get("remote_tools.xai.x.included_handles"), 10)
|
|
114
|
+
exc = self._as_list(cfg.get("remote_tools.xai.x.excluded_handles"), 10)
|
|
115
|
+
if inc and not exc:
|
|
116
|
+
kwargs["allowed_x_handles"] = inc
|
|
117
|
+
elif exc and not inc:
|
|
118
|
+
kwargs["excluded_x_handles"] = exc
|
|
119
|
+
'''
|
|
120
|
+
# optional date range filters (YYYY-MM-DD)
|
|
121
|
+
for k_in, k_out in (("remote_tools.xai.from_date", "from_date"),
|
|
122
|
+
("remote_tools.xai.to_date", "to_date")):
|
|
123
|
+
v = cfg.get(k_in)
|
|
124
|
+
if isinstance(v, str) and v.strip():
|
|
125
|
+
kwargs[k_out] = v.strip()
|
|
126
|
+
|
|
127
|
+
if bool(cfg.get("remote_tools.xai.x.enable_image_understanding", False)):
|
|
128
|
+
kwargs["enable_image_understanding"] = True
|
|
129
|
+
if bool(cfg.get("remote_tools.xai.x.enable_video_understanding", False)):
|
|
130
|
+
kwargs["enable_video_understanding"] = True
|
|
131
|
+
|
|
132
|
+
# optional favorites/views filters (supported by live search)
|
|
133
|
+
try:
|
|
134
|
+
favs = cfg.get("remote_tools.xai.x.min_favs")
|
|
135
|
+
if isinstance(favs, int) and favs > 0:
|
|
136
|
+
kwargs["post_favorite_count"] = int(favs)
|
|
137
|
+
except Exception:
|
|
138
|
+
pass
|
|
139
|
+
try:
|
|
140
|
+
views = cfg.get("remote_tools.xai.x.min_views")
|
|
141
|
+
if isinstance(views, int) and views > 0:
|
|
142
|
+
kwargs["post_view_count"] = int(views)
|
|
143
|
+
except Exception:
|
|
144
|
+
pass
|
|
145
|
+
|
|
146
|
+
try:
|
|
147
|
+
tools.append(x_x_search(**kwargs))
|
|
148
|
+
except Exception:
|
|
149
|
+
tools.append(x_x_search())
|
|
150
|
+
|
|
151
|
+
# CODE EXECUTION
|
|
152
|
+
if is_code_enabled and x_code_execution is not None:
|
|
153
|
+
try:
|
|
154
|
+
tools.append(x_code_execution())
|
|
155
|
+
except Exception:
|
|
156
|
+
pass
|
|
157
|
+
|
|
158
|
+
# MCP
|
|
159
|
+
if is_mcp_enabled and x_mcp is not None:
|
|
160
|
+
kwargs = {}
|
|
161
|
+
mcp_config = cfg.get("remote_tools.xai.mcp.args", "")
|
|
162
|
+
if mcp_config:
|
|
163
|
+
try:
|
|
164
|
+
kwargs = json.loads(mcp_config)
|
|
165
|
+
except Exception:
|
|
166
|
+
pass
|
|
167
|
+
try:
|
|
168
|
+
tools.append(x_mcp(**kwargs))
|
|
169
|
+
except Exception:
|
|
170
|
+
pass
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
"tools": tools,
|
|
174
|
+
"include": include,
|
|
175
|
+
"use_encrypted_content": use_encrypted,
|
|
176
|
+
"max_turns": max_turns,
|
|
177
|
+
}
|
|
178
|
+
|
|
30
179
|
def build_remote_tools(self, model: ModelItem = None) -> Dict[str, Any]:
|
|
31
180
|
"""
|
|
32
181
|
Return live-search config for xAI:
|
|
@@ -58,38 +207,17 @@ class Remote:
|
|
|
58
207
|
"""
|
|
59
208
|
cfg = self.window.core.config
|
|
60
209
|
is_web = self.window.controller.chat.remote_tools.enabled(model, "web_search") # get global config
|
|
61
|
-
|
|
62
|
-
mode = str(cfg.get("remote_tools.xai.mode") or "auto").lower()
|
|
63
|
-
if mode not in ("auto", "on", "off"):
|
|
64
|
-
mode = "auto"
|
|
65
|
-
|
|
66
|
-
if mode == "off":
|
|
67
|
-
if is_web:
|
|
68
|
-
mode = "on" # override off if global web_search enabled
|
|
210
|
+
mode = "on" if is_web else "off"
|
|
69
211
|
|
|
70
212
|
# sources toggles
|
|
71
|
-
s_web =
|
|
72
|
-
s_x =
|
|
73
|
-
s_news = bool(cfg.get("remote_tools.xai.sources.news", False))
|
|
74
|
-
s_rss = bool(cfg.get("remote_tools.xai.sources.rss", False))
|
|
75
|
-
|
|
76
|
-
# advanced flags
|
|
77
|
-
adv_web_allowed = self._has_list(cfg.get("remote_tools.xai.web.allowed_websites"))
|
|
78
|
-
adv_web_excluded = self._has_list(cfg.get("remote_tools.xai.web.excluded_websites"))
|
|
79
|
-
adv_web_country = self._has_str(cfg.get("remote_tools.xai.web.country"))
|
|
80
|
-
adv_web_safe = cfg.get("remote_tools.xai.web.safe_search", None)
|
|
81
|
-
|
|
82
|
-
adv_news_excl = self._has_list(cfg.get("remote_tools.xai.news.excluded_websites"))
|
|
83
|
-
adv_news_country = self._has_str(cfg.get("remote_tools.xai.news.country"))
|
|
84
|
-
adv_news_safe = cfg.get("remote_tools.xai.news.safe_search", None)
|
|
213
|
+
s_web = bool(cfg.get("remote_tools.xai.web_search", False))
|
|
214
|
+
s_x = bool(cfg.get("remote_tools.xai.x_search", False))
|
|
85
215
|
|
|
86
216
|
adv_x_incl = self._has_list(cfg.get("remote_tools.xai.x.included_handles"))
|
|
87
217
|
adv_x_excl = self._has_list(cfg.get("remote_tools.xai.x.excluded_handles"))
|
|
88
218
|
adv_x_favs = self._has_int(cfg.get("remote_tools.xai.x.min_favs"))
|
|
89
219
|
adv_x_views = self._has_int(cfg.get("remote_tools.xai.x.min_views"))
|
|
90
220
|
|
|
91
|
-
adv_rss_link = self._has_str(cfg.get("remote_tools.xai.rss.link"))
|
|
92
|
-
|
|
93
221
|
adv_from = self._has_str(cfg.get("remote_tools.xai.from_date"))
|
|
94
222
|
adv_to = self._has_str(cfg.get("remote_tools.xai.to_date"))
|
|
95
223
|
|
|
@@ -97,7 +225,7 @@ class Remote:
|
|
|
97
225
|
adv_return_cits = cfg.get("remote_tools.xai.return_citations", True) is not True # different than default?
|
|
98
226
|
|
|
99
227
|
# SDK-capable if: mode!=off and ONLY X is enabled and no X filters/date/max_results customizations
|
|
100
|
-
x_only = s_x and not s_web
|
|
228
|
+
x_only = s_x and not s_web
|
|
101
229
|
x_filters = any([adv_x_incl, adv_x_excl, adv_x_favs, adv_x_views])
|
|
102
230
|
sdk_enabled = (mode != "off") and x_only and not any([
|
|
103
231
|
x_filters, adv_from, adv_to, adv_max_results, adv_return_cits
|
|
@@ -110,10 +238,10 @@ class Remote:
|
|
|
110
238
|
|
|
111
239
|
need_http = (mode != "off") and (
|
|
112
240
|
not sdk_enabled or # advanced X filters or other sources/date/results/citations
|
|
113
|
-
s_web
|
|
241
|
+
s_web
|
|
114
242
|
)
|
|
115
243
|
if need_http:
|
|
116
|
-
http_params = self._build_http_params(cfg, mode, s_web, s_x
|
|
244
|
+
http_params = self._build_http_params(cfg, mode, s_web, s_x)
|
|
117
245
|
http_reason = "advanced_sources_or_filters"
|
|
118
246
|
|
|
119
247
|
return {
|
|
@@ -131,8 +259,6 @@ class Remote:
|
|
|
131
259
|
mode: str,
|
|
132
260
|
s_web: bool,
|
|
133
261
|
s_x: bool,
|
|
134
|
-
s_news: bool,
|
|
135
|
-
s_rss: bool
|
|
136
262
|
) -> dict:
|
|
137
263
|
"""
|
|
138
264
|
Build search_parameters for Chat Completions (HTTP).
|
|
@@ -141,8 +267,6 @@ class Remote:
|
|
|
141
267
|
:param mode: "auto"|"on"|"off"
|
|
142
268
|
:param s_web: Include web search
|
|
143
269
|
:param s_x: Include X search
|
|
144
|
-
:param s_news: Include news search
|
|
145
|
-
:param s_rss: Include RSS search
|
|
146
270
|
:return: search_parameters dict
|
|
147
271
|
"""
|
|
148
272
|
params: Dict[str, Any] = {"mode": mode}
|
|
@@ -196,41 +320,12 @@ class Remote:
|
|
|
196
320
|
xsrc["post_view_count"] = int(views)
|
|
197
321
|
sources.append(xsrc)
|
|
198
322
|
|
|
199
|
-
if s_news:
|
|
200
|
-
news: Dict[str, Any] = {"type": "news"}
|
|
201
|
-
country = cfg.get("remote_tools.xai.news.country")
|
|
202
|
-
if isinstance(country, str) and len(country.strip()) == 2:
|
|
203
|
-
news["country"] = country.strip().upper()
|
|
204
|
-
excluded = self._as_list(cfg.get("remote_tools.xai.news.excluded_websites"), 5)
|
|
205
|
-
if excluded:
|
|
206
|
-
news["excluded_websites"] = excluded
|
|
207
|
-
safe = cfg.get("remote_tools.xai.news.safe_search")
|
|
208
|
-
if safe is not None:
|
|
209
|
-
news["safe_search"] = bool(safe)
|
|
210
|
-
sources.append(news)
|
|
211
|
-
|
|
212
|
-
if s_rss:
|
|
213
|
-
link = cfg.get("remote_tools.xai.rss.link")
|
|
214
|
-
if isinstance(link, str) and link.strip():
|
|
215
|
-
sources.append({"type": "rss", "links": [link.strip()]})
|
|
216
|
-
|
|
217
323
|
if sources:
|
|
218
324
|
params["sources"] = sources
|
|
219
325
|
|
|
220
326
|
return params
|
|
221
327
|
|
|
222
|
-
|
|
223
|
-
"""
|
|
224
|
-
Return True if v is a non-empty list/tuple or a non-empty comma-separated string.
|
|
225
|
-
|
|
226
|
-
:param v: Any
|
|
227
|
-
:return: bool
|
|
228
|
-
"""
|
|
229
|
-
if v is None:
|
|
230
|
-
return False
|
|
231
|
-
if isinstance(v, (list, tuple)):
|
|
232
|
-
return len([x for x in v if str(x).strip()]) > 0
|
|
233
|
-
return len([x for x in str(v).split(",") if x.strip()]) > 0
|
|
328
|
+
# ---------- helpers ----------
|
|
234
329
|
|
|
235
330
|
def _has_str(self, v) -> bool:
|
|
236
331
|
"""
|
|
@@ -250,14 +345,14 @@ class Remote:
|
|
|
250
345
|
"""
|
|
251
346
|
return isinstance(v, int) and v > 0
|
|
252
347
|
|
|
253
|
-
def
|
|
254
|
-
|
|
255
|
-
|
|
348
|
+
def _has_list(self, v) -> bool:
|
|
349
|
+
if v is None:
|
|
350
|
+
return False
|
|
351
|
+
if isinstance(v, (list, tuple)):
|
|
352
|
+
return len([x for x in v if str(x).strip()]) > 0
|
|
353
|
+
return len([x for x in str(v).split(",") if x.strip()]) > 0
|
|
256
354
|
|
|
257
|
-
|
|
258
|
-
:param limit: limit number of items
|
|
259
|
-
:return: List of strings
|
|
260
|
-
"""
|
|
355
|
+
def _as_list(self, v, limit: int) -> List[str]:
|
|
261
356
|
if v is None:
|
|
262
357
|
return []
|
|
263
358
|
if isinstance(v, (list, tuple)):
|