pygpt-net 2.6.33__py3-none-any.whl → 2.6.35__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.
Files changed (64) hide show
  1. pygpt_net/CHANGELOG.txt +14 -0
  2. pygpt_net/__init__.py +3 -3
  3. pygpt_net/controller/assistant/batch.py +14 -4
  4. pygpt_net/controller/assistant/files.py +1 -0
  5. pygpt_net/controller/assistant/store.py +195 -1
  6. pygpt_net/controller/camera/camera.py +1 -1
  7. pygpt_net/controller/chat/common.py +58 -48
  8. pygpt_net/controller/chat/handler/stream_worker.py +55 -43
  9. pygpt_net/controller/config/placeholder.py +95 -75
  10. pygpt_net/controller/dialogs/confirm.py +3 -1
  11. pygpt_net/controller/media/media.py +11 -3
  12. pygpt_net/controller/painter/common.py +243 -13
  13. pygpt_net/controller/painter/painter.py +11 -2
  14. pygpt_net/core/assistants/files.py +18 -0
  15. pygpt_net/core/bridge/bridge.py +1 -5
  16. pygpt_net/core/bridge/context.py +81 -36
  17. pygpt_net/core/bridge/worker.py +3 -1
  18. pygpt_net/core/camera/camera.py +31 -402
  19. pygpt_net/core/camera/worker.py +430 -0
  20. pygpt_net/core/ctx/bag.py +4 -0
  21. pygpt_net/core/events/app.py +10 -17
  22. pygpt_net/core/events/base.py +17 -25
  23. pygpt_net/core/events/control.py +9 -17
  24. pygpt_net/core/events/event.py +9 -62
  25. pygpt_net/core/events/kernel.py +8 -17
  26. pygpt_net/core/events/realtime.py +8 -17
  27. pygpt_net/core/events/render.py +9 -17
  28. pygpt_net/core/filesystem/url.py +3 -0
  29. pygpt_net/core/render/web/body.py +454 -40
  30. pygpt_net/core/render/web/pid.py +39 -24
  31. pygpt_net/core/render/web/renderer.py +146 -40
  32. pygpt_net/core/text/utils.py +3 -0
  33. pygpt_net/data/config/config.json +4 -3
  34. pygpt_net/data/config/models.json +3 -3
  35. pygpt_net/data/config/settings.json +10 -5
  36. pygpt_net/data/css/web-blocks.css +3 -2
  37. pygpt_net/data/css/web-chatgpt.css +3 -1
  38. pygpt_net/data/css/web-chatgpt_wide.css +3 -1
  39. pygpt_net/data/locale/locale.de.ini +9 -7
  40. pygpt_net/data/locale/locale.en.ini +10 -6
  41. pygpt_net/data/locale/locale.es.ini +9 -7
  42. pygpt_net/data/locale/locale.fr.ini +9 -7
  43. pygpt_net/data/locale/locale.it.ini +9 -7
  44. pygpt_net/data/locale/locale.pl.ini +9 -7
  45. pygpt_net/data/locale/locale.uk.ini +9 -7
  46. pygpt_net/data/locale/locale.zh.ini +9 -7
  47. pygpt_net/item/assistant.py +13 -1
  48. pygpt_net/provider/api/google/__init__.py +46 -28
  49. pygpt_net/provider/api/openai/__init__.py +13 -10
  50. pygpt_net/provider/api/openai/store.py +45 -1
  51. pygpt_net/provider/core/config/patch.py +9 -0
  52. pygpt_net/provider/llms/google.py +4 -0
  53. pygpt_net/ui/dialog/assistant_store.py +213 -203
  54. pygpt_net/ui/layout/chat/input.py +3 -3
  55. pygpt_net/ui/layout/chat/painter.py +63 -4
  56. pygpt_net/ui/widget/draw/painter.py +715 -104
  57. pygpt_net/ui/widget/option/combo.py +5 -1
  58. pygpt_net/ui/widget/textarea/input.py +273 -3
  59. pygpt_net/ui/widget/textarea/web.py +2 -0
  60. {pygpt_net-2.6.33.dist-info → pygpt_net-2.6.35.dist-info}/METADATA +16 -2
  61. {pygpt_net-2.6.33.dist-info → pygpt_net-2.6.35.dist-info}/RECORD +64 -63
  62. {pygpt_net-2.6.33.dist-info → pygpt_net-2.6.35.dist-info}/LICENSE +0 -0
  63. {pygpt_net-2.6.33.dist-info → pygpt_net-2.6.35.dist-info}/WHEEL +0 -0
  64. {pygpt_net-2.6.33.dist-info → pygpt_net-2.6.35.dist-info}/entry_points.txt +0 -0
@@ -6,37 +6,52 @@
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.19 07:00:00 #
9
+ # Updated Date: 2025.09.04 00:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import io
13
+ from dataclasses import dataclass, field
14
+ from typing import Any, Optional
15
+
13
16
  from pygpt_net.utils import trans
14
17
 
15
18
 
19
+ @dataclass(slots=True)
16
20
  class PidData:
17
-
18
- def __init__(self, pid, meta=None):
19
- """Pid Data"""
20
- self.pid = pid
21
- self.meta = meta
22
- self.images_appended = []
23
- self.urls_appended = []
24
- self.files_appended = []
25
- self._buffer = io.StringIO()
26
- self._live_buffer = io.StringIO()
27
- self._html = io.StringIO()
28
- self._document = io.StringIO()
29
- self.is_cmd = False
30
- self.initialized = False
31
- self.loaded = False
32
- self.item = None
33
- self.use_buffer = False
34
- self.name_user = trans("chat.name.user")
35
- self.name_bot = trans("chat.name.bot")
36
- self.last_time_called = 0
37
- self.cooldown = 1 / 6
38
- self.throttling_min_chars = 5000
39
- self.header = None
21
+ """Pid Data"""
22
+ # Required/primary data
23
+ pid: Any
24
+ meta: Optional[Any] = None
25
+
26
+ # Collections
27
+ images_appended: list = field(default_factory=list)
28
+ urls_appended: list = field(default_factory=list)
29
+ files_appended: list = field(default_factory=list)
30
+
31
+ # Internal buffers (excluded from repr to avoid large dumps)
32
+ _buffer: io.StringIO = field(default_factory=io.StringIO, repr=False)
33
+ _live_buffer: io.StringIO = field(default_factory=io.StringIO, repr=False)
34
+ _html: io.StringIO = field(default_factory=io.StringIO, repr=False)
35
+ _document: io.StringIO = field(default_factory=io.StringIO, repr=False)
36
+
37
+ # Flags/state
38
+ is_cmd: bool = False
39
+ initialized: bool = False
40
+ loaded: bool = False
41
+ item: Optional[Any] = None
42
+ use_buffer: bool = False
43
+
44
+ # Names
45
+ name_user: str = field(default_factory=lambda: trans("chat.name.user"))
46
+ name_bot: str = field(default_factory=lambda: trans("chat.name.bot"))
47
+
48
+ # Throttling / timing
49
+ last_time_called: float = 0.0
50
+ cooldown: float = 1 / 6
51
+ throttling_min_chars: int = 5000
52
+
53
+ # Misc
54
+ header: Optional[Any] = None
40
55
 
41
56
  @property
42
57
  def buffer(self) -> str:
@@ -6,16 +6,19 @@
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.24 02:00:00 #
9
+ # Updated Date: 2025.09.04 00:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import json
13
13
  import os
14
14
  import re
15
+ import gc
16
+ from dataclasses import dataclass, field
15
17
 
16
18
  from datetime import datetime
17
19
  from typing import Optional, List, Any
18
20
  from time import monotonic
21
+ from io import StringIO
19
22
 
20
23
  from pygpt_net.core.render.base import BaseRenderer
21
24
  from pygpt_net.core.text.utils import has_unclosed_code_tag
@@ -48,6 +51,46 @@ class Renderer(BaseRenderer):
48
51
  )
49
52
  RE_AMP_LT_GT = re.compile(r'&(lt|gt);')
50
53
 
54
+ @dataclass(slots=True)
55
+ class _AppendBuffer:
56
+ """Small, allocation-friendly buffer for throttled appends."""
57
+ _buf: StringIO = field(default_factory=StringIO, repr=False)
58
+ _size: int = 0
59
+
60
+ def append(self, s: str) -> None:
61
+ if not s:
62
+ return
63
+ self._buf.write(s)
64
+ self._size += len(s)
65
+
66
+ def is_empty(self) -> bool:
67
+ return self._size == 0
68
+
69
+ def get_and_clear(self) -> str:
70
+ """Return content and replace underlying buffer to release memory eagerly."""
71
+ if self._size == 0:
72
+ return ""
73
+ data = self._buf.getvalue()
74
+ old = self._buf
75
+ # Replace the internal buffer instance to drop capacity immediately
76
+ self._buf = StringIO()
77
+ self._size = 0
78
+ try:
79
+ old.close()
80
+ except Exception:
81
+ pass
82
+ return data
83
+
84
+ def clear(self) -> None:
85
+ """Clear content and drop buffer capacity."""
86
+ old = self._buf
87
+ self._buf = StringIO()
88
+ self._size = 0
89
+ try:
90
+ old.close()
91
+ except Exception:
92
+ pass
93
+
51
94
  def __init__(self, window=None):
52
95
  super(Renderer, self).__init__(window)
53
96
  """
@@ -69,7 +112,7 @@ class Renderer(BaseRenderer):
69
112
  self._file_prefix = 'file:///' if self.window and self.window.core.platforms.is_windows() else 'file://'
70
113
 
71
114
  self._thr = {}
72
- self._throttle_interval = 0.01 # 10 ms delay
115
+ self._throttle_interval = 0.03 # 30 ms delay
73
116
 
74
117
  def prepare(self):
75
118
  """
@@ -328,6 +371,9 @@ class Renderer(BaseRenderer):
328
371
  except Exception:
329
372
  pass
330
373
 
374
+ # release strings
375
+ gc.collect()
376
+
331
377
  def append_context(
332
378
  self,
333
379
  meta: CtxMeta,
@@ -597,7 +643,7 @@ class Renderer(BaseRenderer):
597
643
  if self.window.core.config.get("agent.output.render.all", False):
598
644
  output = ctx.output # full agent output
599
645
  else:
600
- output = ctx.extra["output"] # final output only
646
+ output = ctx.extra["output"] # final output only
601
647
  else:
602
648
  if not output:
603
649
  return
@@ -656,6 +702,7 @@ class Renderer(BaseRenderer):
656
702
  pid = self.get_or_create_pid(meta)
657
703
  pctx = self.pids[pid]
658
704
  pctx.item = ctx
705
+
659
706
  if not text_chunk:
660
707
  if begin:
661
708
  pctx.clear()
@@ -663,13 +710,16 @@ class Renderer(BaseRenderer):
663
710
  self._throttle_reset(pid)
664
711
  return
665
712
 
666
- if begin: # prepare name and avatar header only at the beginning to avoid unnecessary checks
713
+ if begin:
714
+ # Prepare name header once per streaming session
667
715
  pctx.header = self.get_name_header(ctx, stream=True)
668
716
  self.update_names(meta, ctx)
669
717
 
670
718
  name_header_str = pctx.header
671
719
  text_chunk = text_chunk if isinstance(text_chunk, str) else str(text_chunk)
672
- text_chunk = text_chunk.translate({ord('<'): '&lt;', ord('>'): '&gt;'})
720
+ # Escape angle brackets only if present to avoid unnecessary allocations
721
+ if ('<' in text_chunk) or ('>' in text_chunk):
722
+ text_chunk = text_chunk.translate({ord('<'): '&lt;', ord('>'): '&gt;'})
673
723
 
674
724
  if begin:
675
725
  if self.is_debug():
@@ -683,42 +733,69 @@ class Renderer(BaseRenderer):
683
733
  self.clear_chunks_output(pid)
684
734
  self.prev_chunk_replace = False
685
735
 
736
+ # Append to the logical buffer (owned by pid)
686
737
  pctx.append_buffer(text_chunk)
687
-
688
738
  buffer = pctx.buffer
689
- if has_unclosed_code_tag(buffer):
690
- buffer_to_parse = "".join((buffer, "\n```"))
691
- else:
692
- buffer_to_parse = buffer
693
739
 
694
- html = self.parser.parse(buffer_to_parse)
695
- del buffer_to_parse
696
- is_code_block = html.endswith(self.ENDINGS_CODE)
697
- is_list = html.endswith(self.ENDINGS_LIST)
740
+ # Cheap detection of open code fence without full parse
741
+ open_code = has_unclosed_code_tag(buffer)
742
+
743
+ # Newline/flow state
698
744
  is_n = "\n" in text_chunk
699
- is_newline = is_n or buffer.endswith("\n") or is_code_block
700
- force_replace = False
701
- if self.prev_chunk_newline:
702
- force_replace = True
703
- if is_n:
704
- self.prev_chunk_newline = True
705
- else:
706
- self.prev_chunk_newline = False
745
+ is_newline = is_n or buffer.endswith("\n") or open_code
746
+ force_replace = self.prev_chunk_newline
747
+ self.prev_chunk_newline = bool(is_n)
707
748
 
708
749
  replace = False
709
- if is_newline or force_replace or is_list:
750
+ if is_newline or force_replace:
710
751
  replace = True
711
- if is_code_block:
712
- if not is_n:
713
- replace = False
752
+ # Do not replace for an open code block unless a newline arrived
753
+ if open_code and not is_n:
754
+ replace = False
755
+
756
+ thr = self._throttle_get(pid)
757
+ html = None
714
758
 
759
+ # Only parse when required:
760
+ # - a replace is needed now, or
761
+ # - a replace is pending in the throttle and must be refreshed, or
762
+ # - rarely: we must detect list termination without a newline
763
+ need_parse_for_pending_replace = (thr["op"] == 1)
764
+ need_parse_for_list = False
765
+
766
+ if not replace:
767
+ # Very rare case: list closing without newline. Check on a short tail only.
768
+ # This keeps behavior intact while avoiding full-buffer parse on every chunk.
769
+ tail = buffer[-4096:]
770
+ if tail:
771
+ tail_to_parse = f"{tail}\n```" if open_code else tail
772
+ tail_html = self.parser.parse(tail_to_parse)
773
+ need_parse_for_list = tail_html.endswith(self.ENDINGS_LIST)
774
+ # Tail string is short and will be collected promptly
775
+ del tail_html
776
+ if need_parse_for_list:
777
+ replace = True
778
+
779
+ if replace or need_parse_for_pending_replace:
780
+ buffer_to_parse = f"{buffer}\n```" if open_code else buffer
781
+ html = self.parser.parse(buffer_to_parse)
782
+ # Help the GC by breaking the reference as soon as possible
783
+ del buffer_to_parse
784
+
785
+ is_code_block = open_code
786
+
787
+ # Adjust output chunk formatting based on block type
715
788
  if not is_code_block:
716
789
  if is_n:
790
+ # Convert text newlines to <br/> in non-code context
717
791
  text_chunk = text_chunk.replace("\n", "<br/>")
718
792
  else:
793
+ # When previous operation replaced content and this chunk closes the fence,
794
+ # prepend a newline so the final code block renders correctly.
719
795
  if self.prev_chunk_replace and (is_code_block and not has_unclosed_code_tag(text_chunk)):
720
796
  text_chunk = "\n" + text_chunk
721
797
 
798
+ # Update replace flag for next iteration AFTER formatting decisions
722
799
  self.prev_chunk_replace = replace
723
800
 
724
801
  if begin:
@@ -727,7 +804,19 @@ class Renderer(BaseRenderer):
727
804
  except Exception:
728
805
  pass
729
806
 
730
- self._throttle_queue(pid, name_header_str or "", html, text_chunk, replace, is_code_block)
807
+ # Queue throttled emission; HTML is only provided when it is really needed
808
+ self._throttle_queue(
809
+ pid=pid,
810
+ name=name_header_str or "",
811
+ html=html if html is not None else "",
812
+ text_chunk=text_chunk,
813
+ replace=replace,
814
+ is_code_block=is_code_block,
815
+ )
816
+ # Explicitly drop local ref to large html string as early as possible
817
+ html = None
818
+
819
+ # Emit if throttle interval allows
731
820
  self._throttle_emit(pid, force=False)
732
821
 
733
822
  def next_chunk(
@@ -1276,7 +1365,7 @@ class Renderer(BaseRenderer):
1276
1365
  extra = ctx.extra["footer"]
1277
1366
  extra_style = "display:block;"
1278
1367
 
1279
- return f'<div class="msg-box msg-user" id="{msg_id}"><div class="name-header name-user">{name}</div><div class="msg">{html}<div class="msg-extra" style="{extra_style}">{extra}</div>{debug}</div></div>'
1368
+ return f'<div class="msg-box msg-user" id="{msg_id}"><div class="name-header name-user">{name}</div><div class="msg">{html}<div class="msg-extra" style="{extra_style}">{extra}</div>{debug}</div></div>'
1280
1369
 
1281
1370
  def prepare_node_output(
1282
1371
  self,
@@ -1289,7 +1378,7 @@ class Renderer(BaseRenderer):
1289
1378
  """
1290
1379
  Prepare output node
1291
1380
 
1292
- :param meta: context meta
1381
+ :param meta: CtxMeta
1293
1382
  :param ctx: CtxItem instance
1294
1383
  :param html: html text
1295
1384
  :param prev_ctx: previous context item
@@ -1399,12 +1488,12 @@ class Renderer(BaseRenderer):
1399
1488
  """
1400
1489
  try:
1401
1490
  if replace:
1402
- self.get_output_node_by_pid(pid).page().runJavaScript(
1403
- f"if (typeof window.replaceNodes !== 'undefined') replaceNodes({self.to_json(self.sanitize_html(html))});"
1491
+ self.get_output_node_by_pid(pid).page().bridge.nodeReplace.emit(
1492
+ self.sanitize_html(html)
1404
1493
  )
1405
1494
  else:
1406
- self.get_output_node_by_pid(pid).page().runJavaScript(
1407
- f"if (typeof window.appendNode !== 'undefined') appendNode({self.to_json(self.sanitize_html(html))});"
1495
+ self.get_output_node_by_pid(pid).page().bridge.node.emit(
1496
+ self.sanitize_html(html)
1408
1497
  )
1409
1498
  except Exception:
1410
1499
  pass
@@ -1772,6 +1861,9 @@ class Renderer(BaseRenderer):
1772
1861
  """
1773
1862
  if not html:
1774
1863
  return ""
1864
+ # Fast path: avoid regex work and extra allocations when not needed
1865
+ if '&amp;' not in html:
1866
+ return html
1775
1867
  return self.RE_AMP_LT_GT.sub(r'&\1;', html)
1776
1868
 
1777
1869
  def append_debug(
@@ -1819,7 +1911,14 @@ class Renderer(BaseRenderer):
1819
1911
  """
1820
1912
  thr = self._thr.get(pid)
1821
1913
  if thr is None:
1822
- thr = {"last": 0.0, "op": 0, "name": "", "replace_html": "", "append": [], "code": False}
1914
+ thr = {
1915
+ "last": 0.0,
1916
+ "op": 0,
1917
+ "name": "",
1918
+ "replace_html": "",
1919
+ "append": Renderer._AppendBuffer(),
1920
+ "code": False,
1921
+ }
1823
1922
  self._thr[pid] = thr
1824
1923
  return thr
1825
1924
 
@@ -1837,7 +1936,8 @@ class Renderer(BaseRenderer):
1837
1936
  thr["op"] = 0
1838
1937
  thr["name"] = ""
1839
1938
  thr["replace_html"] = ""
1840
- thr["append"].clear()
1939
+ # Replace append buffer instance to drop any capacity eagerly
1940
+ thr["append"] = Renderer._AppendBuffer()
1841
1941
  thr["code"] = False
1842
1942
 
1843
1943
  def _throttle_queue(
@@ -1866,10 +1966,12 @@ class Renderer(BaseRenderer):
1866
1966
  if replace:
1867
1967
  thr["op"] = 1
1868
1968
  thr["replace_html"] = html
1969
+ # Drop previous append items aggressively when a replace snapshot is available
1869
1970
  thr["append"].clear()
1870
1971
  thr["code"] = bool(is_code_block)
1871
1972
  else:
1872
1973
  if thr["op"] == 1:
1974
+ # Refresh the pending replace with the latest HTML snapshot
1873
1975
  thr["replace_html"] = html
1874
1976
  thr["code"] = bool(is_code_block)
1875
1977
  return
@@ -1895,17 +1997,21 @@ class Renderer(BaseRenderer):
1895
1997
 
1896
1998
  try:
1897
1999
  if thr["op"] == 1:
2000
+ # Replace snapshot
2001
+ replace_payload = self.sanitize_html(thr["replace_html"])
1898
2002
  node.page().bridge.chunk.emit(
1899
2003
  thr["name"],
1900
- self.sanitize_html(thr["replace_html"]),
2004
+ replace_payload,
1901
2005
  "",
1902
2006
  True,
1903
2007
  bool(thr["code"]),
1904
2008
  )
2009
+ thr["replace_html"] = "" # Cut reference ASAP
1905
2010
  thr["last"] = now
1906
2011
 
1907
- if thr["append"]:
1908
- append_str = "".join(thr["append"])
2012
+ # Append tail (if any)
2013
+ if not thr["append"].is_empty():
2014
+ append_str = thr["append"].get_and_clear()
1909
2015
  node.page().bridge.chunk.emit(
1910
2016
  thr["name"],
1911
2017
  "",
@@ -1917,8 +2023,8 @@ class Renderer(BaseRenderer):
1917
2023
 
1918
2024
  self._throttle_reset(pid)
1919
2025
 
1920
- elif thr["op"] == 2 and thr["append"]:
1921
- append_str = "".join(thr["append"])
2026
+ elif thr["op"] == 2 and not thr["append"].is_empty():
2027
+ append_str = thr["append"].get_and_clear()
1922
2028
  node.page().bridge.chunk.emit(
1923
2029
  thr["name"],
1924
2030
  "",
@@ -60,6 +60,9 @@ def output_clean_html(html: str) -> str:
60
60
  # remove action icons
61
61
  for tag in soup.select('div.action-icons'):
62
62
  tag.decompose()
63
+ # remove scripts
64
+ for tag in soup.select('script'):
65
+ tag.decompose()
63
66
  return str(soup)
64
67
  except Exception:
65
68
  pass
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "__meta__": {
3
- "version": "2.6.33",
4
- "app.version": "2.6.33",
5
- "updated_at": "2025-09-02T00:00:00"
3
+ "version": "2.6.35",
4
+ "app.version": "2.6.35",
5
+ "updated_at": "2025-09-04T00:00:00"
6
6
  },
7
7
  "access.audio.event.speech": false,
8
8
  "access.audio.event.speech.disabled": [],
@@ -354,6 +354,7 @@
354
354
  "painter.brush.mode": "brush",
355
355
  "painter.brush.size": 3,
356
356
  "painter.canvas.size": "1280x720",
357
+ "painter.zoom": 100,
357
358
  "personalize.about": "",
358
359
  "personalize.modes": "chat",
359
360
  "plugins": {},
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "__meta__": {
3
- "version": "2.6.33",
4
- "app.version": "2.6.33",
5
- "updated_at": "2025-09-02T08:03:34"
3
+ "version": "2.6.35",
4
+ "app.version": "2.6.35",
5
+ "updated_at": "2025-09-04T08:03:34"
6
6
  },
7
7
  "items": {
8
8
  "SpeakLeash/bielik-11b-v2.3-instruct:Q4_K_M": {
@@ -1520,7 +1520,8 @@
1520
1520
  },
1521
1521
  "vision.capture.idx": {
1522
1522
  "section": "vision",
1523
- "type": "int",
1523
+ "type": "combo",
1524
+ "use": "camera_devices",
1524
1525
  "slider": true,
1525
1526
  "label": "settings.vision.capture.idx",
1526
1527
  "description": "settings.vision.capture.idx.desc",
@@ -1529,7 +1530,8 @@
1529
1530
  "max": 3,
1530
1531
  "multiplier": 1,
1531
1532
  "step": 1,
1532
- "advanced": false
1533
+ "advanced": false,
1534
+ "tab": "camera"
1533
1535
  },
1534
1536
  "vision.capture.width": {
1535
1537
  "section": "vision",
@@ -1541,7 +1543,8 @@
1541
1543
  "max": 4096,
1542
1544
  "multiplier": 1,
1543
1545
  "step": 1,
1544
- "advanced": false
1546
+ "advanced": false,
1547
+ "tab": "camera"
1545
1548
  },
1546
1549
  "vision.capture.height": {
1547
1550
  "section": "vision",
@@ -1553,7 +1556,8 @@
1553
1556
  "max": 4096,
1554
1557
  "multiplier": 1,
1555
1558
  "step": 1,
1556
- "advanced": false
1559
+ "advanced": false,
1560
+ "tab": "camera"
1557
1561
  },
1558
1562
  "vision.capture.quality": {
1559
1563
  "section": "vision",
@@ -1565,7 +1569,8 @@
1565
1569
  "max": 100,
1566
1570
  "multiplier": 1,
1567
1571
  "step": 1,
1568
- "advanced": false
1572
+ "advanced": false,
1573
+ "tab": "camera"
1569
1574
  },
1570
1575
  "audio.input.backend": {
1571
1576
  "section": "audio",
@@ -13,7 +13,6 @@ body {{
13
13
  word-wrap: break-word;
14
14
  padding: 6px;
15
15
  line-height: 1.25;
16
- will-change: transform, opacity;
17
16
  margin: 4px;
18
17
  padding: 0;
19
18
  max-width: 100%;
@@ -26,10 +25,12 @@ body {{
26
25
  -webkit-border-radius: 1ex;
27
26
  -webkit-box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.75);
28
27
  }}
28
+ #container {{
29
+ will-change: transform, opacity;
30
+ }}
29
31
  .ts {{
30
32
  display: none;
31
33
  }}
32
-
33
34
  /* base */
34
35
  a {{
35
36
  text-decoration: none;
@@ -13,7 +13,6 @@ body {{
13
13
  word-wrap: break-word;
14
14
  padding: 6px;
15
15
  line-height: 1.25;
16
- will-change: transform, opacity;
17
16
  padding: 0;
18
17
  margin: auto;
19
18
  max-width: 720px;
@@ -27,6 +26,9 @@ body {{
27
26
  -webkit-border-radius: 1ex;
28
27
  -webkit-box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.75);
29
28
  }}
29
+ #container {{
30
+ will-change: transform, opacity;
31
+ }}
30
32
  .ts {{
31
33
  display: none;
32
34
  }}
@@ -13,7 +13,6 @@ body {{
13
13
  word-wrap: break-word;
14
14
  padding: 6px;
15
15
  line-height: 1.25;
16
- will-change: transform, opacity;
17
16
  padding: 0;
18
17
  margin: auto;
19
18
  max-width: 100%;
@@ -27,6 +26,9 @@ body {{
27
26
  -webkit-border-radius: 1ex;
28
27
  -webkit-box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.75);
29
28
  }}
29
+ #container {{
30
+ will-change: transform, opacity;
31
+ }}
30
32
  .ts {{
31
33
  display: none;
32
34
  }}
@@ -957,10 +957,11 @@ output.tips.7 = Für eine bessere Leistung deaktivieren Sie die Werkzeuge, wenn
957
957
  output.tips.8 = Wenn Sie lokale Modelle verwenden, denken Sie daran, das lokale Modell auch für die Kontextzusammenfassung und Einbettungen einzustellen.
958
958
  output.tips.9 = Um Audio-Ein- und Ausgangsanbieter zu konfigurieren, gehen Sie zu Plugins -> Einstellungen. Um Audiogeräte zu konfigurieren, gehen Sie zu Konfiguration -> Einstellungen -> Audio.
959
959
  output.tips.prefix = Tipp
960
- painter.btn.crop = Zuschneiden
961
960
  painter.btn.camera.capture = Von der Kamera
962
961
  painter.btn.capture = Bild erfassen
963
962
  painter.btn.clear = Löschen
963
+ painter.btn.crop = Zuschneiden
964
+ painter.btn.fit = Anpassen
964
965
  painter.capture.manual.captured.success = Bild erfasst:
965
966
  painter.capture.name.prefix = Zeichnung von
966
967
  painter.mode.erase = Radieren
@@ -1364,7 +1365,8 @@ settings.section.remote_tools = Fernwerkzeuge
1364
1365
  settings.section.remote_tools.openai = OpenAI
1365
1366
  settings.section.tab.general = Allgemein
1366
1367
  settings.section.updates = Aktualisierungen
1367
- settings.section.vision = Vision
1368
+ settings.section.vision = Vision und Kamera
1369
+ settings.section.vision.camera = Kamera
1368
1370
  settings.store_history = Verlauf speichern
1369
1371
  settings.store_history_time = Uhrzeit im Verlauf speichern
1370
1372
  settings.temperature = Temperatur
@@ -1394,11 +1396,11 @@ settings.video.seed = Startwert
1394
1396
  settings.video.seed.desc = Optionaler Zufallsstartwert für reproduzierbare Ergebnisse; leer lassen für zufällig
1395
1397
  settings.vision.capture.auto = Automatisches Erfassen
1396
1398
  settings.vision.capture.enabled = Kamera
1397
- settings.vision.capture.height = Kamerabildhöhe (px)
1398
- settings.vision.capture.idx = Kameraindex (Nummer)
1399
- settings.vision.capture.idx.desc = Kamera-Index zur Videoaufzeichnung (Index der Kamera, Standard: 0)
1400
- settings.vision.capture.quality = Bildqualität beim Erfassen (%)
1401
- settings.vision.capture.width = Kamerabildbreite (px)
1399
+ settings.vision.capture.height = Aufnahmehöhe (in Pixeln)
1400
+ settings.vision.capture.idx = Kameragerät
1401
+ settings.vision.capture.idx.desc = Wählen Sie ein Kameragerät für die Echtzeit-Videoaufnahme
1402
+ settings.vision.capture.quality = Aufnahmequalität (%)
1403
+ settings.vision.capture.width = Aufnahmewidth (in Pixeln)
1402
1404
  settings.zoom = Zoomen des Chat-Ausgabefensters
1403
1405
  speech.enable = Sprachausgabe aktivieren
1404
1406
  speech.listening = Sprechen Sie jetzt...
@@ -147,6 +147,7 @@ assistant.store.expire_days.desc = 0 = Never
147
147
  assistant.store.files.suffix = files
148
148
  assistant.store.hide_threads = Hide threads vector stores
149
149
  assistant.store.id = ID
150
+ assistant.store.menu.file.delete = Delete file
150
151
  assistant.store.name = Name
151
152
  assistant.store.status = Status
152
153
  assistant.store.thread_only = (current thread only)
@@ -250,6 +251,7 @@ confirm.assistant.import = Import all assistants from API?
250
251
  confirm.assistant.import_files = Import all files from API?
251
252
  confirm.assistant.import_files.store = Import current store files from API?
252
253
  confirm.assistant.store.clear = Clear vector stores (local only)?
254
+ confirm.assistant.store.file.delete = Delete selected file in API?
253
255
  confirm.assistant.store.import = Import all vector stores from API?
254
256
  confirm.assistant.store.refresh = Refresh all stores?
255
257
  confirm.assistant.store.truncate = Delete all vector stores in API?
@@ -958,10 +960,11 @@ output.tips.7 = For better performance, disable tools when using local models if
958
960
  output.tips.8 = When using local models, remember to set the local model for context summary and embeddings as well.
959
961
  output.tips.9 = To configure audio input and output providers, go to Plugins -> Settings. To configure audio devices, go to Config -> Settings -> Audio.
960
962
  output.tips.prefix = Tip
961
- painter.btn.crop = Crop
962
963
  painter.btn.camera.capture = From camera
963
964
  painter.btn.capture = Use image
964
965
  painter.btn.clear = Clear
966
+ painter.btn.crop = Crop
967
+ painter.btn.fit = Fit
965
968
  painter.capture.manual.captured.success = Image captured:
966
969
  painter.capture.name.prefix = Drawing from
967
970
  painter.mode.erase = Erase
@@ -1406,7 +1409,8 @@ settings.section.remote_tools.google = Google
1406
1409
  settings.section.remote_tools.openai = OpenAI
1407
1410
  settings.section.tab.general = General
1408
1411
  settings.section.updates = Updates
1409
- settings.section.vision = Vision
1412
+ settings.section.vision = Vision and camera
1413
+ settings.section.vision.camera = Camera
1410
1414
  settings.store_history = Store history
1411
1415
  settings.store_history_time = Store time in history
1412
1416
  settings.temperature = Temperature
@@ -1436,11 +1440,11 @@ settings.video.seed = Seed
1436
1440
  settings.video.seed.desc = Optional random seed for reproducible results; leave empty for random
1437
1441
  settings.vision.capture.auto = Auto capture
1438
1442
  settings.vision.capture.enabled = Camera
1439
- settings.vision.capture.height = Camera height (px)
1440
- settings.vision.capture.idx = Camera Input Device
1441
- settings.vision.capture.idx.desc = Video capture camera index (index of the camera, default: 0)
1443
+ settings.vision.capture.height = Capture height (in pixels)
1444
+ settings.vision.capture.idx = Camera Device
1445
+ settings.vision.capture.idx.desc = Select a camera device for real-time video capture
1442
1446
  settings.vision.capture.quality = Capture quality (%)
1443
- settings.vision.capture.width = Camera width (px)
1447
+ settings.vision.capture.width = Capture width (in pixels)
1444
1448
  settings.zoom = Chat output window zoom
1445
1449
  speech.enable = Speak
1446
1450
  speech.listening = Speak now...