pygpt-net 2.7.9__py3-none-any.whl → 2.7.10__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 +9 -0
- pygpt_net/LICENSE +1 -1
- pygpt_net/__init__.py +3 -3
- pygpt_net/config.py +15 -1
- pygpt_net/controller/chat/common.py +5 -4
- pygpt_net/controller/chat/image.py +3 -3
- pygpt_net/controller/chat/stream.py +76 -41
- pygpt_net/controller/chat/stream_worker.py +3 -3
- pygpt_net/controller/ctx/extra.py +3 -1
- pygpt_net/controller/dialogs/debug.py +37 -8
- pygpt_net/controller/kernel/kernel.py +3 -7
- pygpt_net/controller/lang/custom.py +25 -12
- pygpt_net/controller/lang/lang.py +45 -3
- pygpt_net/controller/lang/mapping.py +15 -2
- pygpt_net/controller/notepad/notepad.py +68 -25
- pygpt_net/controller/presets/editor.py +5 -1
- pygpt_net/controller/presets/presets.py +17 -5
- pygpt_net/controller/theme/theme.py +11 -2
- pygpt_net/controller/ui/tabs.py +1 -1
- pygpt_net/core/ctx/output.py +38 -12
- pygpt_net/core/db/database.py +4 -2
- pygpt_net/core/debug/console/console.py +30 -2
- pygpt_net/core/debug/context.py +2 -1
- pygpt_net/core/debug/ui.py +26 -4
- pygpt_net/core/filesystem/filesystem.py +6 -2
- pygpt_net/core/notepad/notepad.py +2 -2
- pygpt_net/core/tabs/tabs.py +79 -19
- pygpt_net/data/config/config.json +3 -3
- pygpt_net/data/config/models.json +3 -3
- pygpt_net/data/config/settings.json +12 -0
- pygpt_net/data/locale/locale.ar.ini +1833 -0
- pygpt_net/data/locale/locale.bg.ini +1833 -0
- pygpt_net/data/locale/locale.cs.ini +1833 -0
- pygpt_net/data/locale/locale.da.ini +1833 -0
- pygpt_net/data/locale/locale.de.ini +4 -1
- pygpt_net/data/locale/locale.en.ini +70 -67
- pygpt_net/data/locale/locale.es.ini +4 -1
- pygpt_net/data/locale/locale.fi.ini +1833 -0
- pygpt_net/data/locale/locale.fr.ini +4 -1
- pygpt_net/data/locale/locale.he.ini +1833 -0
- pygpt_net/data/locale/locale.hi.ini +1833 -0
- pygpt_net/data/locale/locale.hu.ini +1833 -0
- pygpt_net/data/locale/locale.it.ini +4 -1
- pygpt_net/data/locale/locale.ja.ini +1833 -0
- pygpt_net/data/locale/locale.ko.ini +1833 -0
- pygpt_net/data/locale/locale.nl.ini +1833 -0
- pygpt_net/data/locale/locale.no.ini +1833 -0
- pygpt_net/data/locale/locale.pl.ini +5 -2
- pygpt_net/data/locale/locale.pt.ini +1833 -0
- pygpt_net/data/locale/locale.ro.ini +1833 -0
- pygpt_net/data/locale/locale.ru.ini +1833 -0
- pygpt_net/data/locale/locale.sk.ini +1833 -0
- pygpt_net/data/locale/locale.sv.ini +1833 -0
- pygpt_net/data/locale/locale.tr.ini +1833 -0
- pygpt_net/data/locale/locale.uk.ini +4 -1
- pygpt_net/data/locale/locale.zh.ini +4 -1
- pygpt_net/item/notepad.py +8 -2
- pygpt_net/migrations/Version20260121190000.py +25 -0
- pygpt_net/migrations/Version20260122140000.py +25 -0
- pygpt_net/migrations/__init__.py +5 -1
- pygpt_net/preload.py +246 -3
- pygpt_net/provider/api/__init__.py +16 -2
- pygpt_net/provider/api/anthropic/__init__.py +21 -7
- pygpt_net/provider/api/google/__init__.py +21 -7
- pygpt_net/provider/api/google/image.py +89 -2
- pygpt_net/provider/api/google/video.py +2 -2
- pygpt_net/provider/api/openai/__init__.py +26 -11
- pygpt_net/provider/api/openai/image.py +79 -3
- pygpt_net/provider/api/openai/responses.py +11 -31
- pygpt_net/provider/api/openai/video.py +2 -2
- pygpt_net/provider/api/x_ai/__init__.py +21 -7
- pygpt_net/provider/core/notepad/db_sqlite/storage.py +53 -10
- pygpt_net/tools/agent_builder/ui/dialogs.py +2 -1
- pygpt_net/tools/audio_transcriber/ui/dialogs.py +2 -1
- pygpt_net/tools/code_interpreter/ui/dialogs.py +2 -1
- pygpt_net/tools/html_canvas/ui/dialogs.py +2 -1
- pygpt_net/tools/image_viewer/ui/dialogs.py +3 -5
- pygpt_net/tools/indexer/ui/dialogs.py +2 -1
- pygpt_net/tools/media_player/ui/dialogs.py +2 -1
- pygpt_net/tools/translator/ui/dialogs.py +2 -1
- pygpt_net/tools/translator/ui/widgets.py +6 -2
- pygpt_net/ui/dialog/about.py +2 -2
- pygpt_net/ui/dialog/db.py +2 -1
- pygpt_net/ui/dialog/debug.py +169 -6
- pygpt_net/ui/dialog/logger.py +6 -2
- pygpt_net/ui/dialog/models.py +36 -3
- pygpt_net/ui/dialog/preset.py +5 -1
- pygpt_net/ui/dialog/remote_store.py +2 -1
- pygpt_net/ui/main.py +3 -2
- pygpt_net/ui/widget/dialog/editor_file.py +2 -1
- pygpt_net/ui/widget/lists/debug.py +12 -7
- pygpt_net/ui/widget/option/checkbox.py +2 -8
- pygpt_net/ui/widget/option/combo.py +10 -2
- pygpt_net/ui/widget/textarea/console.py +156 -7
- pygpt_net/ui/widget/textarea/highlight.py +66 -0
- pygpt_net/ui/widget/textarea/input.py +624 -57
- pygpt_net/ui/widget/textarea/notepad.py +294 -27
- {pygpt_net-2.7.9.dist-info → pygpt_net-2.7.10.dist-info}/LICENSE +1 -1
- {pygpt_net-2.7.9.dist-info → pygpt_net-2.7.10.dist-info}/METADATA +11 -64
- {pygpt_net-2.7.9.dist-info → pygpt_net-2.7.10.dist-info}/RECORD +102 -81
- {pygpt_net-2.7.9.dist-info → pygpt_net-2.7.10.dist-info}/WHEEL +0 -0
- {pygpt_net-2.7.9.dist-info → pygpt_net-2.7.10.dist-info}/entry_points.txt +0 -0
|
@@ -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:
|
|
9
|
+
# Updated Date: 2026.01.23 23:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import mimetypes
|
|
@@ -234,6 +234,13 @@ class ImageWorker(QRunnable):
|
|
|
234
234
|
reference_images=[raw_ref, mask_ref],
|
|
235
235
|
config=cfg,
|
|
236
236
|
)
|
|
237
|
+
|
|
238
|
+
# record usage if provided
|
|
239
|
+
try:
|
|
240
|
+
self._record_usage_google(resp)
|
|
241
|
+
except Exception:
|
|
242
|
+
pass
|
|
243
|
+
|
|
237
244
|
imgs = getattr(resp, "generated_images", None) or []
|
|
238
245
|
for idx, gi in enumerate(imgs[: min(self.num, self.imagen_max_num)]):
|
|
239
246
|
data = self._extract_imagen_bytes(gi)
|
|
@@ -258,6 +265,13 @@ class ImageWorker(QRunnable):
|
|
|
258
265
|
image_config=img_cfg,
|
|
259
266
|
),
|
|
260
267
|
)
|
|
268
|
+
|
|
269
|
+
# record usage if provided
|
|
270
|
+
try:
|
|
271
|
+
self._record_usage_google(resp)
|
|
272
|
+
except Exception:
|
|
273
|
+
pass
|
|
274
|
+
|
|
261
275
|
saved = 0
|
|
262
276
|
for cand in getattr(resp, "candidates", []) or []:
|
|
263
277
|
parts = getattr(getattr(cand, "content", None), "parts", None) or []
|
|
@@ -291,6 +305,13 @@ class ImageWorker(QRunnable):
|
|
|
291
305
|
if self._using_vertex():
|
|
292
306
|
# Vertex Imagen edit API (preferred)
|
|
293
307
|
resp = self._imagen_edit(self.input_prompt, self.attachments, self.num)
|
|
308
|
+
|
|
309
|
+
# record usage if provided
|
|
310
|
+
try:
|
|
311
|
+
self._record_usage_google(resp)
|
|
312
|
+
except Exception:
|
|
313
|
+
pass
|
|
314
|
+
|
|
294
315
|
imgs = getattr(resp, "generated_images", None) or []
|
|
295
316
|
for idx, gi in enumerate(imgs[: self.num]):
|
|
296
317
|
data = self._extract_imagen_bytes(gi)
|
|
@@ -303,6 +324,13 @@ class ImageWorker(QRunnable):
|
|
|
303
324
|
else:
|
|
304
325
|
# Gemini Developer API via Gemini image models (Nano Banana / Nano Banana Pro)
|
|
305
326
|
resp = self._gemini_edit(self.input_prompt, self.attachments, self.num)
|
|
327
|
+
|
|
328
|
+
# record usage if provided
|
|
329
|
+
try:
|
|
330
|
+
self._record_usage_google(resp)
|
|
331
|
+
except Exception:
|
|
332
|
+
pass
|
|
333
|
+
|
|
306
334
|
saved = 0
|
|
307
335
|
for cand in getattr(resp, "candidates", []) or []:
|
|
308
336
|
parts = getattr(getattr(cand, "content", None), "parts", None) or []
|
|
@@ -326,6 +354,13 @@ class ImageWorker(QRunnable):
|
|
|
326
354
|
if self._is_imagen_generate(self.model) and self._using_vertex():
|
|
327
355
|
num = min(self.num, self.imagen_max_num)
|
|
328
356
|
resp = self._imagen_generate(self.input_prompt, num, self.resolution)
|
|
357
|
+
|
|
358
|
+
# record usage if provided
|
|
359
|
+
try:
|
|
360
|
+
self._record_usage_google(resp)
|
|
361
|
+
except Exception:
|
|
362
|
+
pass
|
|
363
|
+
|
|
329
364
|
imgs = getattr(resp, "generated_images", None) or []
|
|
330
365
|
for idx, gi in enumerate(imgs[: num]):
|
|
331
366
|
data = self._extract_imagen_bytes(gi)
|
|
@@ -338,6 +373,13 @@ class ImageWorker(QRunnable):
|
|
|
338
373
|
else:
|
|
339
374
|
# Gemini Developer API image generation (Nano Banana / Nano Banana Pro) with robust sizing + optional reference images
|
|
340
375
|
resp = self._gemini_generate_image(self.input_prompt, self.model, self.resolution)
|
|
376
|
+
|
|
377
|
+
# record usage if provided
|
|
378
|
+
try:
|
|
379
|
+
self._record_usage_google(resp)
|
|
380
|
+
except Exception:
|
|
381
|
+
pass
|
|
382
|
+
|
|
341
383
|
saved = 0
|
|
342
384
|
for cand in getattr(resp, "candidates", []) or []:
|
|
343
385
|
parts = getattr(getattr(cand, "content", None), "parts", None) or []
|
|
@@ -809,7 +851,7 @@ class ImageWorker(QRunnable):
|
|
|
809
851
|
try:
|
|
810
852
|
if not isinstance(self.ctx.extra, dict):
|
|
811
853
|
self.ctx.extra = {}
|
|
812
|
-
self.ctx.extra["image_id"] = str(value)
|
|
854
|
+
self.ctx.extra["image_id"] = self.window.core.filesystem.make_local(str(value))
|
|
813
855
|
self.window.core.ctx.update_item(self.ctx)
|
|
814
856
|
except Exception:
|
|
815
857
|
pass
|
|
@@ -853,6 +895,51 @@ class ImageWorker(QRunnable):
|
|
|
853
895
|
mime, _ = mimetypes.guess_type(uri)
|
|
854
896
|
return mime or None
|
|
855
897
|
|
|
898
|
+
# ---------- usage helpers (Google GenAI) ----------
|
|
899
|
+
|
|
900
|
+
def _record_usage_google(self, response: Any) -> None:
|
|
901
|
+
"""
|
|
902
|
+
Extract usage_metadata from Google GenAI response if present and store in ctx.
|
|
903
|
+
Saves to:
|
|
904
|
+
- ctx.set_tokens(prompt_token_count, candidates_token_count)
|
|
905
|
+
- ctx.extra["usage"] = {...}
|
|
906
|
+
"""
|
|
907
|
+
try:
|
|
908
|
+
usage = getattr(response, "usage_metadata", None)
|
|
909
|
+
if not usage:
|
|
910
|
+
return
|
|
911
|
+
|
|
912
|
+
def _as_int(v) -> int:
|
|
913
|
+
try:
|
|
914
|
+
return int(v)
|
|
915
|
+
except Exception:
|
|
916
|
+
try:
|
|
917
|
+
return int(float(v))
|
|
918
|
+
except Exception:
|
|
919
|
+
return 0
|
|
920
|
+
|
|
921
|
+
p = _as_int(getattr(usage, "prompt_token_count", 0) or 0)
|
|
922
|
+
c = _as_int(getattr(usage, "candidates_token_count", 0) or 0)
|
|
923
|
+
t = _as_int(getattr(usage, "total_token_count", (p + c)) or (p + c))
|
|
924
|
+
|
|
925
|
+
if self.ctx:
|
|
926
|
+
self.ctx.set_tokens(p, c)
|
|
927
|
+
|
|
928
|
+
if not isinstance(self.ctx.extra, dict):
|
|
929
|
+
self.ctx.extra = {}
|
|
930
|
+
|
|
931
|
+
self.ctx.extra["usage"] = {
|
|
932
|
+
"vendor": "google",
|
|
933
|
+
"model": str(self.model),
|
|
934
|
+
"input_tokens": p,
|
|
935
|
+
"output_tokens": c,
|
|
936
|
+
"total_tokens": t,
|
|
937
|
+
"source": "image",
|
|
938
|
+
}
|
|
939
|
+
except Exception:
|
|
940
|
+
# best-effort; ignore failures
|
|
941
|
+
pass
|
|
942
|
+
|
|
856
943
|
def _cleanup(self):
|
|
857
944
|
"""Cleanup resources."""
|
|
858
945
|
sig = self.signals
|
|
@@ -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:
|
|
9
|
+
# Updated Date: 2026.01.23 23:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import base64, datetime, os, requests
|
|
@@ -480,7 +480,7 @@ class VideoWorker(QRunnable):
|
|
|
480
480
|
|
|
481
481
|
if not isinstance(self.ctx.extra, dict):
|
|
482
482
|
self.ctx.extra = {}
|
|
483
|
-
self.ctx.extra["video_id"] = ref
|
|
483
|
+
self.ctx.extra["video_id"] = self.window.core.filesystem.make_local(ref)
|
|
484
484
|
self.window.core.ctx.update_item(self.ctx)
|
|
485
485
|
except Exception:
|
|
486
486
|
pass
|
|
@@ -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: 2026.01.
|
|
9
|
+
# Updated Date: 2026.01.21 13:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from openai import OpenAI
|
|
@@ -263,7 +263,30 @@ class ApiOpenAI:
|
|
|
263
263
|
|
|
264
264
|
return True
|
|
265
265
|
|
|
266
|
-
def
|
|
266
|
+
def redirect_call(
|
|
267
|
+
self,
|
|
268
|
+
context: BridgeContext,
|
|
269
|
+
extra: dict = None
|
|
270
|
+
) -> str:
|
|
271
|
+
"""
|
|
272
|
+
Redirect quick call to standard call and return the output text
|
|
273
|
+
|
|
274
|
+
:param context: BridgeContext
|
|
275
|
+
:param extra: Extra parameters
|
|
276
|
+
:return: Output text
|
|
277
|
+
"""
|
|
278
|
+
context.stream = False
|
|
279
|
+
context.mode = MODE_CHAT
|
|
280
|
+
self.locked = True
|
|
281
|
+
self.call(context, extra)
|
|
282
|
+
self.locked = False
|
|
283
|
+
return context.ctx.output
|
|
284
|
+
|
|
285
|
+
def quick_call(
|
|
286
|
+
self,
|
|
287
|
+
context: BridgeContext,
|
|
288
|
+
extra: dict = None
|
|
289
|
+
) -> str:
|
|
267
290
|
"""
|
|
268
291
|
Quick call OpenAI API with custom prompt
|
|
269
292
|
|
|
@@ -273,19 +296,13 @@ class ApiOpenAI:
|
|
|
273
296
|
"""
|
|
274
297
|
# if normal request call then redirect
|
|
275
298
|
if context.request:
|
|
276
|
-
context
|
|
277
|
-
context.mode = "chat" # fake mode for redirect
|
|
278
|
-
self.locked = True
|
|
279
|
-
self.call(context, extra)
|
|
280
|
-
self.locked = False
|
|
281
|
-
return context.ctx.output
|
|
299
|
+
return self.redirect_call(context, extra)
|
|
282
300
|
|
|
283
301
|
self.locked = True
|
|
284
302
|
ctx = context.ctx
|
|
285
303
|
mode = context.mode
|
|
286
304
|
prompt = context.prompt
|
|
287
305
|
system_prompt = context.system_prompt
|
|
288
|
-
max_tokens = context.max_tokens
|
|
289
306
|
temperature = context.temperature
|
|
290
307
|
functions = context.external_functions
|
|
291
308
|
history = context.history
|
|
@@ -309,8 +326,6 @@ class ApiOpenAI:
|
|
|
309
326
|
})
|
|
310
327
|
messages.append({"role": "user", "content": prompt})
|
|
311
328
|
additional_kwargs = {}
|
|
312
|
-
# if max_tokens > 0:
|
|
313
|
-
# additional_kwargs["max_tokens"] = max_tokens
|
|
314
329
|
|
|
315
330
|
# tools / functions
|
|
316
331
|
tools = self.window.core.api.openai.tools.prepare(model, functions)
|
|
@@ -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: 2026.01.
|
|
9
|
+
# Updated Date: 2026.01.23 23:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import base64
|
|
@@ -350,6 +350,12 @@ class ImageWorker(QRunnable):
|
|
|
350
350
|
self.signals.status.emit("API Error: empty response")
|
|
351
351
|
return
|
|
352
352
|
|
|
353
|
+
# record usage if provided by API
|
|
354
|
+
try:
|
|
355
|
+
self._record_usage_openai(response)
|
|
356
|
+
except Exception:
|
|
357
|
+
pass
|
|
358
|
+
|
|
353
359
|
# download images
|
|
354
360
|
for i in range(self.num):
|
|
355
361
|
if i >= len(response.data):
|
|
@@ -383,7 +389,7 @@ class ImageWorker(QRunnable):
|
|
|
383
389
|
try:
|
|
384
390
|
if not isinstance(self.ctx.extra, dict):
|
|
385
391
|
self.ctx.extra = {}
|
|
386
|
-
self.ctx.extra["image_id"] = paths[0]
|
|
392
|
+
self.ctx.extra["image_id"] = self.window.core.filesystem.make_local(paths[0])
|
|
387
393
|
self.window.core.ctx.update_item(self.ctx)
|
|
388
394
|
except Exception:
|
|
389
395
|
pass
|
|
@@ -430,4 +436,74 @@ class ImageWorker(QRunnable):
|
|
|
430
436
|
neg = (negative or "").strip()
|
|
431
437
|
if not neg:
|
|
432
438
|
return base
|
|
433
|
-
return (base + ("\n" if base else "") + f"Negative prompt: {neg}").strip()
|
|
439
|
+
return (base + ("\n" if base else "") + f"Negative prompt: {neg}").strip()
|
|
440
|
+
|
|
441
|
+
# ---------- usage helpers (OpenAI Images API) ----------
|
|
442
|
+
|
|
443
|
+
def _record_usage_openai(self, response: Any) -> None:
|
|
444
|
+
"""
|
|
445
|
+
Extract and store token usage from OpenAI Images API response if present.
|
|
446
|
+
Saves to:
|
|
447
|
+
- ctx.set_tokens(input_tokens, output_tokens)
|
|
448
|
+
- ctx.extra["usage"] = {...}
|
|
449
|
+
"""
|
|
450
|
+
try:
|
|
451
|
+
usage = getattr(response, "usage", None)
|
|
452
|
+
if usage is None and isinstance(response, dict):
|
|
453
|
+
usage = response.get("usage")
|
|
454
|
+
|
|
455
|
+
if not usage:
|
|
456
|
+
return
|
|
457
|
+
|
|
458
|
+
def _as_int(v) -> int:
|
|
459
|
+
try:
|
|
460
|
+
return int(v)
|
|
461
|
+
except Exception:
|
|
462
|
+
try:
|
|
463
|
+
return int(float(v))
|
|
464
|
+
except Exception:
|
|
465
|
+
return 0
|
|
466
|
+
|
|
467
|
+
# handle both attr and dict style
|
|
468
|
+
getv = lambda o, k: getattr(o, k, None) if not isinstance(o, dict) else o.get(k)
|
|
469
|
+
|
|
470
|
+
inp = _as_int(getv(usage, "input_tokens") or getv(usage, "prompt_tokens") or 0)
|
|
471
|
+
outp = _as_int(getv(usage, "output_tokens") or getv(usage, "completion_tokens") or 0)
|
|
472
|
+
total = _as_int(getv(usage, "total_tokens") or (inp + outp))
|
|
473
|
+
|
|
474
|
+
# store basic tokens
|
|
475
|
+
if self.ctx:
|
|
476
|
+
self.ctx.set_tokens(inp, outp)
|
|
477
|
+
|
|
478
|
+
# store detailed usage in ctx.extra["usage"]
|
|
479
|
+
if not isinstance(self.ctx.extra, dict):
|
|
480
|
+
self.ctx.extra = {}
|
|
481
|
+
|
|
482
|
+
# pass through details if available
|
|
483
|
+
input_details = getv(usage, "input_tokens_details") or getv(usage, "prompt_tokens_details") or {}
|
|
484
|
+
output_details = getv(usage, "output_tokens_details") or getv(usage, "completion_tokens_details") or {}
|
|
485
|
+
|
|
486
|
+
# normalize dict-like objects
|
|
487
|
+
def _to_plain(o):
|
|
488
|
+
try:
|
|
489
|
+
if hasattr(o, "model_dump"):
|
|
490
|
+
return o.model_dump()
|
|
491
|
+
if hasattr(o, "to_dict"):
|
|
492
|
+
return o.to_dict()
|
|
493
|
+
except Exception:
|
|
494
|
+
pass
|
|
495
|
+
return o if isinstance(o, dict) else {}
|
|
496
|
+
|
|
497
|
+
self.ctx.extra["usage"] = {
|
|
498
|
+
"vendor": "openai",
|
|
499
|
+
"model": str(self.model),
|
|
500
|
+
"input_tokens": inp,
|
|
501
|
+
"output_tokens": outp,
|
|
502
|
+
"total_tokens": total,
|
|
503
|
+
"input_tokens_details": _to_plain(input_details),
|
|
504
|
+
"output_tokens_details": _to_plain(output_details),
|
|
505
|
+
"source": "images",
|
|
506
|
+
}
|
|
507
|
+
except Exception:
|
|
508
|
+
# do not raise, usage is best-effort
|
|
509
|
+
pass
|
|
@@ -6,11 +6,10 @@
|
|
|
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.21 13:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import base64
|
|
13
|
-
import json
|
|
14
13
|
import time
|
|
15
14
|
from typing import Optional, Dict, Any, List, Tuple
|
|
16
15
|
|
|
@@ -92,9 +91,11 @@ class Responses:
|
|
|
92
91
|
user_name = ctx.input_name # from ctx
|
|
93
92
|
ai_name = ctx.output_name # from ctx
|
|
94
93
|
|
|
95
|
-
|
|
94
|
+
api = self.window.core.api.openai
|
|
95
|
+
client = api.get_client(mode, model)
|
|
96
96
|
|
|
97
97
|
# build chat messages
|
|
98
|
+
self.reset_tokens()
|
|
98
99
|
messages = self.build(
|
|
99
100
|
prompt=prompt,
|
|
100
101
|
system_prompt=system_prompt,
|
|
@@ -106,11 +107,12 @@ class Responses:
|
|
|
106
107
|
multimodal_ctx=multimodal_ctx,
|
|
107
108
|
is_expert_call=is_expert_call, # use separated previous response ID for expert calls
|
|
108
109
|
)
|
|
109
|
-
|
|
110
110
|
msg_tokens = self.window.core.tokens.from_messages(
|
|
111
111
|
messages,
|
|
112
112
|
model.id,
|
|
113
113
|
)
|
|
114
|
+
self.input_tokens += msg_tokens
|
|
115
|
+
|
|
114
116
|
# check if max tokens not exceeded
|
|
115
117
|
if max_tokens > 0 and model.ctx > 0:
|
|
116
118
|
if msg_tokens + int(max_tokens) > model.ctx:
|
|
@@ -121,16 +123,16 @@ class Responses:
|
|
|
121
123
|
# extra API kwargs
|
|
122
124
|
response_kwargs = {}
|
|
123
125
|
|
|
124
|
-
# tools
|
|
125
|
-
tools =
|
|
126
|
+
# tools prepare
|
|
127
|
+
tools = api.tools.prepare_responses_api(model, functions)
|
|
126
128
|
|
|
127
|
-
# extra arguments,
|
|
129
|
+
# extra arguments, reasoning models only
|
|
128
130
|
if model.extra and "reasoning_effort" in model.extra:
|
|
129
131
|
response_kwargs['reasoning'] = {}
|
|
130
132
|
response_kwargs['reasoning']['effort'] = model.extra["reasoning_effort"]
|
|
131
133
|
|
|
132
134
|
# append remote tools
|
|
133
|
-
tools =
|
|
135
|
+
tools = api.remote_tools.append_to_tools(
|
|
134
136
|
mode=mode,
|
|
135
137
|
model=model,
|
|
136
138
|
stream=stream,
|
|
@@ -217,11 +219,6 @@ class Responses:
|
|
|
217
219
|
# tokens config
|
|
218
220
|
mode = MODE_CHAT
|
|
219
221
|
tool_call_native_enabled = self.window.core.config.get('func_call.native', False)
|
|
220
|
-
allowed_system = True
|
|
221
|
-
if (model.id is not None
|
|
222
|
-
and model.id in ["o1-mini", "o1-preview"]):
|
|
223
|
-
allowed_system = False
|
|
224
|
-
|
|
225
222
|
used_tokens = self.window.core.tokens.from_user(
|
|
226
223
|
prompt,
|
|
227
224
|
system_prompt,
|
|
@@ -232,17 +229,6 @@ class Responses:
|
|
|
232
229
|
if max_ctx_tokens > model.ctx > 0:
|
|
233
230
|
max_ctx_tokens = model.ctx
|
|
234
231
|
|
|
235
|
-
# input tokens: reset
|
|
236
|
-
self.reset_tokens()
|
|
237
|
-
|
|
238
|
-
# append system prompt
|
|
239
|
-
if allowed_system:
|
|
240
|
-
pass
|
|
241
|
-
'''
|
|
242
|
-
if system_prompt is not None and system_prompt != "":
|
|
243
|
-
messages.append({"role": "developer", "content": system_prompt})
|
|
244
|
-
'''
|
|
245
|
-
|
|
246
232
|
# append messages from context (memory)
|
|
247
233
|
if self.window.core.config.get('use_context'):
|
|
248
234
|
items = self.window.core.ctx.get_history(
|
|
@@ -384,7 +370,7 @@ class Responses:
|
|
|
384
370
|
break
|
|
385
371
|
|
|
386
372
|
# --- previous message ID ---
|
|
387
|
-
if (item.msg_id
|
|
373
|
+
if (item.msg_id
|
|
388
374
|
and ((item.cmds is None or len(item.cmds) == 0) or is_tool_output)): # if no cmds before or tool output
|
|
389
375
|
if is_expert_call:
|
|
390
376
|
self.prev_internal_response_id = item.msg_id
|
|
@@ -414,12 +400,6 @@ class Responses:
|
|
|
414
400
|
"content": content,
|
|
415
401
|
})
|
|
416
402
|
|
|
417
|
-
# input tokens: update
|
|
418
|
-
self.input_tokens += self.window.core.tokens.from_messages(
|
|
419
|
-
messages,
|
|
420
|
-
model.id,
|
|
421
|
-
)
|
|
422
|
-
|
|
423
403
|
return messages
|
|
424
404
|
|
|
425
405
|
def reset_tokens(self):
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
7
|
# MIT License #
|
|
8
8
|
# Created By : Marcin Szczygliński #
|
|
9
|
-
# Updated Date:
|
|
9
|
+
# Updated Date: 2026.01.23 23:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import datetime
|
|
@@ -286,7 +286,7 @@ class VideoWorker(QRunnable):
|
|
|
286
286
|
# Poll until completed (or failed/canceled)
|
|
287
287
|
if not isinstance(self.ctx.extra, dict):
|
|
288
288
|
self.ctx.extra = {}
|
|
289
|
-
self.ctx.extra['video_id'] = video_id # store video_id in ctx extra
|
|
289
|
+
self.ctx.extra['video_id'] = self.window.core.filesystem.make_local(video_id) # store video_id in ctx extra
|
|
290
290
|
self.window.core.ctx.update_item(self.ctx)
|
|
291
291
|
last_progress = None
|
|
292
292
|
last_status = None
|
|
@@ -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: 2026.01.
|
|
9
|
+
# Updated Date: 2026.01.21 13:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from typing import Optional, Dict, Any
|
|
@@ -203,6 +203,25 @@ class ApiXAI:
|
|
|
203
203
|
pass
|
|
204
204
|
return True
|
|
205
205
|
|
|
206
|
+
def redirect_call(
|
|
207
|
+
self,
|
|
208
|
+
context: BridgeContext,
|
|
209
|
+
extra: dict = None
|
|
210
|
+
) -> str:
|
|
211
|
+
"""
|
|
212
|
+
Redirect quick call to standard call and return the output text
|
|
213
|
+
|
|
214
|
+
:param context: BridgeContext
|
|
215
|
+
:param extra: Extra parameters
|
|
216
|
+
:return: Output text
|
|
217
|
+
"""
|
|
218
|
+
context.stream = False
|
|
219
|
+
context.mode = MODE_CHAT
|
|
220
|
+
self.locked = True
|
|
221
|
+
self.call(context, extra)
|
|
222
|
+
self.locked = False
|
|
223
|
+
return context.ctx.output
|
|
224
|
+
|
|
206
225
|
def quick_call(
|
|
207
226
|
self,
|
|
208
227
|
context: BridgeContext,
|
|
@@ -222,12 +241,7 @@ class ApiXAI:
|
|
|
222
241
|
return self.quick_call_old(context, extra) # grok-3 uses old path
|
|
223
242
|
|
|
224
243
|
if context.request:
|
|
225
|
-
context
|
|
226
|
-
context.mode = MODE_CHAT
|
|
227
|
-
self.locked = True
|
|
228
|
-
self.call(context, extra)
|
|
229
|
-
self.locked = False
|
|
230
|
-
return context.ctx.output
|
|
244
|
+
return self.redirect_call(context, extra)
|
|
231
245
|
|
|
232
246
|
self.locked = True
|
|
233
247
|
try:
|
|
@@ -6,9 +6,9 @@
|
|
|
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.22 16:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
|
-
|
|
11
|
+
import json
|
|
12
12
|
import time
|
|
13
13
|
from typing import Dict, Any
|
|
14
14
|
|
|
@@ -116,14 +116,18 @@ class Storage:
|
|
|
116
116
|
title = :title,
|
|
117
117
|
content = :content,
|
|
118
118
|
is_initialized = :is_initialized,
|
|
119
|
-
updated_ts = :updated_ts
|
|
119
|
+
updated_ts = :updated_ts,
|
|
120
|
+
highlights_json = :highlights_json,
|
|
121
|
+
scroll_pos = :scroll_pos
|
|
120
122
|
WHERE idx = :idx
|
|
121
123
|
""").bindparams(
|
|
122
124
|
idx=int(notepad.idx or 0),
|
|
123
125
|
title=notepad.title,
|
|
124
126
|
content=notepad.content,
|
|
125
127
|
is_initialized=int(notepad.initialized),
|
|
126
|
-
updated_ts=ts
|
|
128
|
+
updated_ts=ts,
|
|
129
|
+
highlights_json=self.pack_item_value(notepad.highlights),
|
|
130
|
+
scroll_pos=int(notepad.scroll_pos)
|
|
127
131
|
)
|
|
128
132
|
else:
|
|
129
133
|
stmt = text("""
|
|
@@ -136,7 +140,9 @@ class Storage:
|
|
|
136
140
|
created_ts,
|
|
137
141
|
updated_ts,
|
|
138
142
|
is_deleted,
|
|
139
|
-
is_initialized
|
|
143
|
+
is_initialized,
|
|
144
|
+
highlights_json,
|
|
145
|
+
scroll_pos
|
|
140
146
|
)
|
|
141
147
|
VALUES
|
|
142
148
|
(
|
|
@@ -147,7 +153,9 @@ class Storage:
|
|
|
147
153
|
:created_ts,
|
|
148
154
|
:updated_ts,
|
|
149
155
|
:is_deleted,
|
|
150
|
-
:is_initialized
|
|
156
|
+
:is_initialized,
|
|
157
|
+
:highlights_json,
|
|
158
|
+
:scroll_pos
|
|
151
159
|
)
|
|
152
160
|
""").bindparams(
|
|
153
161
|
idx=notepad.idx,
|
|
@@ -157,7 +165,9 @@ class Storage:
|
|
|
157
165
|
created_ts=ts,
|
|
158
166
|
updated_ts=ts,
|
|
159
167
|
is_deleted=int(notepad.deleted),
|
|
160
|
-
is_initialized=int(notepad.initialized)
|
|
168
|
+
is_initialized=int(notepad.initialized),
|
|
169
|
+
highlights_json=self.pack_item_value(notepad.highlights),
|
|
170
|
+
scroll_pos=int(notepad.scroll_pos)
|
|
161
171
|
)
|
|
162
172
|
conn.execute(stmt)
|
|
163
173
|
|
|
@@ -180,7 +190,9 @@ class Storage:
|
|
|
180
190
|
created_ts,
|
|
181
191
|
updated_ts,
|
|
182
192
|
is_deleted,
|
|
183
|
-
is_initialized
|
|
193
|
+
is_initialized,
|
|
194
|
+
highlights_json,
|
|
195
|
+
scroll_pos
|
|
184
196
|
)
|
|
185
197
|
VALUES
|
|
186
198
|
(
|
|
@@ -191,7 +203,9 @@ class Storage:
|
|
|
191
203
|
:created_ts,
|
|
192
204
|
:updated_ts,
|
|
193
205
|
:is_deleted,
|
|
194
|
-
:is_initialized
|
|
206
|
+
:is_initialized,
|
|
207
|
+
:highlights_json,
|
|
208
|
+
:scroll_pos
|
|
195
209
|
)
|
|
196
210
|
""").bindparams(
|
|
197
211
|
idx=int(notepad.idx or 0),
|
|
@@ -201,7 +215,9 @@ class Storage:
|
|
|
201
215
|
created_ts=ts,
|
|
202
216
|
updated_ts=ts,
|
|
203
217
|
is_deleted=int(notepad.deleted),
|
|
204
|
-
is_initialized=int(notepad.initialized)
|
|
218
|
+
is_initialized=int(notepad.initialized),
|
|
219
|
+
highlights_json=self.pack_item_value(notepad.highlights),
|
|
220
|
+
scroll_pos=int(notepad.scroll_pos)
|
|
205
221
|
)
|
|
206
222
|
with db.begin() as conn:
|
|
207
223
|
result = conn.execute(stmt)
|
|
@@ -225,4 +241,31 @@ class Storage:
|
|
|
225
241
|
notepad.content = row['content']
|
|
226
242
|
notepad.deleted = bool(row['is_deleted'])
|
|
227
243
|
notepad.initialized = bool(row['is_initialized'])
|
|
244
|
+
notepad.highlights = self.unpack_item_value(row['highlights_json'])
|
|
245
|
+
notepad.scroll_pos = int(row.get('scroll_pos', -1))
|
|
228
246
|
return notepad
|
|
247
|
+
|
|
248
|
+
def pack_item_value(self, value: Any) -> str:
|
|
249
|
+
"""
|
|
250
|
+
Pack item value to JSON
|
|
251
|
+
|
|
252
|
+
:param value: Value to pack
|
|
253
|
+
:return: JSON string or value itself
|
|
254
|
+
"""
|
|
255
|
+
if isinstance(value, (list, dict)):
|
|
256
|
+
return json.dumps(value)
|
|
257
|
+
return value
|
|
258
|
+
|
|
259
|
+
def unpack_item_value(self, value: Any) -> Any:
|
|
260
|
+
"""
|
|
261
|
+
Unpack item value from JSON
|
|
262
|
+
|
|
263
|
+
:param value: Value to unpack
|
|
264
|
+
:return: Unpacked value
|
|
265
|
+
"""
|
|
266
|
+
if value is None:
|
|
267
|
+
return []
|
|
268
|
+
try:
|
|
269
|
+
return json.loads(value)
|
|
270
|
+
except:
|
|
271
|
+
return value
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
7
|
# MIT License #
|
|
8
8
|
# Created By : Marcin Szczygliński #
|
|
9
|
-
# Updated Date:
|
|
9
|
+
# Updated Date: 2026.01.22 17:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from PySide6.QtCore import Qt, QEvent
|
|
@@ -38,6 +38,7 @@ class Builder:
|
|
|
38
38
|
def setup_menu(self, parent=None) -> QMenuBar:
|
|
39
39
|
"""Setup agent_builder dialog menu"""
|
|
40
40
|
self.menu_bar = QMenuBar(parent)
|
|
41
|
+
self.menu_bar.setNativeMenuBar(False)
|
|
41
42
|
self.file_menu = self.menu_bar.addMenu(trans("menu.file"))
|
|
42
43
|
t = self.tool
|
|
43
44
|
|