pygpt-net 2.7.4__py3-none-any.whl → 2.7.5__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 (133) hide show
  1. pygpt_net/CHANGELOG.txt +7 -0
  2. pygpt_net/__init__.py +3 -3
  3. pygpt_net/app_core.py +4 -2
  4. pygpt_net/controller/__init__.py +5 -1
  5. pygpt_net/controller/assistant/assistant.py +1 -4
  6. pygpt_net/controller/assistant/batch.py +5 -504
  7. pygpt_net/controller/assistant/editor.py +5 -5
  8. pygpt_net/controller/assistant/files.py +16 -16
  9. pygpt_net/controller/chat/handler/google_stream.py +307 -1
  10. pygpt_net/controller/chat/handler/worker.py +8 -1
  11. pygpt_net/controller/chat/image.py +2 -2
  12. pygpt_net/controller/dialogs/confirm.py +73 -101
  13. pygpt_net/controller/lang/mapping.py +9 -9
  14. pygpt_net/controller/painter/capture.py +50 -1
  15. pygpt_net/controller/presets/presets.py +2 -1
  16. pygpt_net/controller/remote_store/__init__.py +12 -0
  17. pygpt_net/{provider/core/assistant_file/db_sqlite → controller/remote_store/google}/__init__.py +2 -2
  18. pygpt_net/controller/remote_store/google/batch.py +402 -0
  19. pygpt_net/controller/remote_store/google/store.py +615 -0
  20. pygpt_net/controller/remote_store/openai/__init__.py +12 -0
  21. pygpt_net/controller/remote_store/openai/batch.py +524 -0
  22. pygpt_net/controller/{assistant → remote_store/openai}/store.py +63 -60
  23. pygpt_net/controller/remote_store/remote_store.py +35 -0
  24. pygpt_net/controller/ui/ui.py +20 -1
  25. pygpt_net/core/assistants/assistants.py +3 -15
  26. pygpt_net/core/db/database.py +5 -3
  27. pygpt_net/core/locale/placeholder.py +35 -0
  28. pygpt_net/core/remote_store/__init__.py +12 -0
  29. pygpt_net/core/remote_store/google/__init__.py +11 -0
  30. pygpt_net/core/remote_store/google/files.py +224 -0
  31. pygpt_net/core/remote_store/google/store.py +248 -0
  32. pygpt_net/core/remote_store/openai/__init__.py +11 -0
  33. pygpt_net/core/{assistants → remote_store/openai}/files.py +26 -19
  34. pygpt_net/core/{assistants → remote_store/openai}/store.py +32 -15
  35. pygpt_net/core/remote_store/remote_store.py +24 -0
  36. pygpt_net/data/config/config.json +8 -4
  37. pygpt_net/data/config/models.json +77 -3
  38. pygpt_net/data/config/settings.json +45 -0
  39. pygpt_net/data/locale/locale.de.ini +41 -41
  40. pygpt_net/data/locale/locale.en.ini +53 -43
  41. pygpt_net/data/locale/locale.es.ini +41 -41
  42. pygpt_net/data/locale/locale.fr.ini +41 -41
  43. pygpt_net/data/locale/locale.it.ini +41 -41
  44. pygpt_net/data/locale/locale.pl.ini +42 -42
  45. pygpt_net/data/locale/locale.uk.ini +41 -41
  46. pygpt_net/data/locale/locale.zh.ini +41 -41
  47. pygpt_net/data/locale/plugin.cmd_history.de.ini +1 -1
  48. pygpt_net/data/locale/plugin.cmd_history.en.ini +1 -1
  49. pygpt_net/data/locale/plugin.cmd_history.es.ini +1 -1
  50. pygpt_net/data/locale/plugin.cmd_history.fr.ini +1 -1
  51. pygpt_net/data/locale/plugin.cmd_history.it.ini +1 -1
  52. pygpt_net/data/locale/plugin.cmd_history.pl.ini +1 -1
  53. pygpt_net/data/locale/plugin.cmd_history.uk.ini +1 -1
  54. pygpt_net/data/locale/plugin.cmd_history.zh.ini +1 -1
  55. pygpt_net/data/locale/plugin.cmd_mouse_control.en.ini +14 -0
  56. pygpt_net/data/locale/plugin.cmd_web.de.ini +1 -1
  57. pygpt_net/data/locale/plugin.cmd_web.en.ini +1 -1
  58. pygpt_net/data/locale/plugin.cmd_web.es.ini +1 -1
  59. pygpt_net/data/locale/plugin.cmd_web.fr.ini +1 -1
  60. pygpt_net/data/locale/plugin.cmd_web.it.ini +1 -1
  61. pygpt_net/data/locale/plugin.cmd_web.pl.ini +1 -1
  62. pygpt_net/data/locale/plugin.cmd_web.uk.ini +1 -1
  63. pygpt_net/data/locale/plugin.cmd_web.zh.ini +1 -1
  64. pygpt_net/data/locale/plugin.idx_llama_index.de.ini +2 -2
  65. pygpt_net/data/locale/plugin.idx_llama_index.en.ini +2 -2
  66. pygpt_net/data/locale/plugin.idx_llama_index.es.ini +2 -2
  67. pygpt_net/data/locale/plugin.idx_llama_index.fr.ini +2 -2
  68. pygpt_net/data/locale/plugin.idx_llama_index.it.ini +2 -2
  69. pygpt_net/data/locale/plugin.idx_llama_index.pl.ini +2 -2
  70. pygpt_net/data/locale/plugin.idx_llama_index.uk.ini +2 -2
  71. pygpt_net/data/locale/plugin.idx_llama_index.zh.ini +2 -2
  72. pygpt_net/item/assistant.py +1 -211
  73. pygpt_net/item/ctx.py +3 -1
  74. pygpt_net/item/store.py +238 -0
  75. pygpt_net/migrations/Version20260102190000.py +35 -0
  76. pygpt_net/migrations/__init__.py +3 -1
  77. pygpt_net/plugin/cmd_mouse_control/config.py +470 -1
  78. pygpt_net/plugin/cmd_mouse_control/plugin.py +488 -22
  79. pygpt_net/plugin/cmd_mouse_control/worker.py +464 -87
  80. pygpt_net/plugin/cmd_mouse_control/worker_sandbox.py +729 -0
  81. pygpt_net/plugin/idx_llama_index/config.py +2 -2
  82. pygpt_net/provider/api/google/__init__.py +16 -54
  83. pygpt_net/provider/api/google/chat.py +546 -129
  84. pygpt_net/provider/api/google/computer.py +190 -0
  85. pygpt_net/provider/api/google/realtime/realtime.py +2 -2
  86. pygpt_net/provider/api/google/remote_tools.py +93 -0
  87. pygpt_net/provider/api/google/store.py +546 -0
  88. pygpt_net/provider/api/google/worker/__init__.py +0 -0
  89. pygpt_net/provider/api/google/worker/importer.py +392 -0
  90. pygpt_net/provider/api/openai/computer.py +10 -1
  91. pygpt_net/provider/api/openai/store.py +6 -6
  92. pygpt_net/provider/api/openai/worker/importer.py +24 -24
  93. pygpt_net/provider/core/config/patch.py +16 -1
  94. pygpt_net/provider/core/config/patches/patch_before_2_6_42.py +3 -3
  95. pygpt_net/provider/core/model/patch.py +17 -3
  96. pygpt_net/provider/core/preset/json_file.py +13 -7
  97. pygpt_net/provider/core/{assistant_file → remote_file}/__init__.py +1 -1
  98. pygpt_net/provider/core/{assistant_file → remote_file}/base.py +9 -9
  99. pygpt_net/provider/core/remote_file/db_sqlite/__init__.py +12 -0
  100. pygpt_net/provider/core/{assistant_file → remote_file}/db_sqlite/patch.py +1 -1
  101. pygpt_net/provider/core/{assistant_file → remote_file}/db_sqlite/provider.py +23 -20
  102. pygpt_net/provider/core/{assistant_file → remote_file}/db_sqlite/storage.py +35 -27
  103. pygpt_net/provider/core/{assistant_file → remote_file}/db_sqlite/utils.py +5 -4
  104. pygpt_net/provider/core/{assistant_store → remote_store}/__init__.py +1 -1
  105. pygpt_net/provider/core/{assistant_store → remote_store}/base.py +10 -10
  106. pygpt_net/provider/core/{assistant_store → remote_store}/db_sqlite/__init__.py +1 -1
  107. pygpt_net/provider/core/{assistant_store → remote_store}/db_sqlite/patch.py +1 -1
  108. pygpt_net/provider/core/{assistant_store → remote_store}/db_sqlite/provider.py +16 -15
  109. pygpt_net/provider/core/{assistant_store → remote_store}/db_sqlite/storage.py +30 -23
  110. pygpt_net/provider/core/{assistant_store → remote_store}/db_sqlite/utils.py +5 -4
  111. pygpt_net/provider/core/{assistant_store → remote_store}/json_file.py +9 -9
  112. pygpt_net/provider/llms/google.py +2 -2
  113. pygpt_net/ui/base/config_dialog.py +3 -2
  114. pygpt_net/ui/dialog/assistant.py +3 -3
  115. pygpt_net/ui/dialog/plugins.py +3 -1
  116. pygpt_net/ui/dialog/remote_store_google.py +539 -0
  117. pygpt_net/ui/dialog/{assistant_store.py → remote_store_openai.py} +95 -95
  118. pygpt_net/ui/dialogs.py +5 -3
  119. pygpt_net/ui/layout/chat/attachments_uploaded.py +3 -3
  120. pygpt_net/ui/layout/toolbox/computer_env.py +26 -8
  121. pygpt_net/ui/menu/tools.py +13 -5
  122. pygpt_net/ui/widget/dialog/remote_store_google.py +56 -0
  123. pygpt_net/ui/widget/dialog/{assistant_store.py → remote_store_openai.py} +9 -9
  124. pygpt_net/ui/widget/element/button.py +4 -4
  125. pygpt_net/ui/widget/lists/remote_store_google.py +248 -0
  126. pygpt_net/ui/widget/lists/{assistant_store.py → remote_store_openai.py} +21 -21
  127. pygpt_net/ui/widget/option/checkbox_list.py +47 -9
  128. pygpt_net/ui/widget/option/combo.py +39 -3
  129. {pygpt_net-2.7.4.dist-info → pygpt_net-2.7.5.dist-info}/METADATA +33 -2
  130. {pygpt_net-2.7.4.dist-info → pygpt_net-2.7.5.dist-info}/RECORD +133 -108
  131. {pygpt_net-2.7.4.dist-info → pygpt_net-2.7.5.dist-info}/LICENSE +0 -0
  132. {pygpt_net-2.7.4.dist-info → pygpt_net-2.7.5.dist-info}/WHEEL +0 -0
  133. {pygpt_net-2.7.4.dist-info → pygpt_net-2.7.5.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,190 @@
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 Szczyglinski #
9
+ # Updated Date: 2026.01.02 02:00:00 #
10
+ # ================================================== #
11
+
12
+ import json
13
+ import time
14
+ from typing import Dict, Any, List, Tuple, Optional
15
+
16
+ from google.genai import types as gtypes
17
+ from pygpt_net.item.ctx import CtxItem
18
+
19
+
20
+ class Computer:
21
+ def __init__(self, window=None):
22
+ """
23
+ Google Gemini Computer Use adapter
24
+
25
+ This adapter passes Google Gemini function calls and action events
26
+ directly to the plugin worker without translating names to legacy
27
+ commands. The workers (host and sandbox) implement the full set
28
+ of functions and handle any coordinate conversions themselves.
29
+ """
30
+ self.window = window
31
+ self._seq = 0
32
+
33
+ # --------------- Tool spec --------------- #
34
+
35
+ def get_current_env(self) -> Dict[str, Any]:
36
+ idx = self.window.ui.nodes["computer_env"].currentIndex()
37
+ return self.window.ui.nodes["computer_env"].itemData(idx)
38
+
39
+ def _map_env(self) -> gtypes.Environment:
40
+ return gtypes.Environment.ENVIRONMENT_BROWSER
41
+ env = self.get_current_env()
42
+ val = ""
43
+ if isinstance(env, str):
44
+ val = env.lower()
45
+ elif isinstance(env, dict):
46
+ val = str(env.get("value") or env.get("name") or env).lower()
47
+ if "mac" in val:
48
+ return gtypes.Environment.ENVIRONMENT_MAC
49
+ if "windows" in val or "win" in val:
50
+ return gtypes.Environment.ENVIRONMENT_WINDOWS
51
+ if "linux" in val:
52
+ return gtypes.Environment.ENVIRONMENT_LINUX
53
+ return gtypes.Environment.ENVIRONMENT_BROWSER
54
+
55
+ def get_tool(self) -> gtypes.Tool:
56
+ return gtypes.Tool(
57
+ computer_use=gtypes.ComputerUse(
58
+ environment=self._map_env(),
59
+ )
60
+ )
61
+
62
+ # --------------- Streaming handling --------------- #
63
+
64
+ def _next_id(self) -> str:
65
+ self._seq += 1
66
+ return f"gc-{int(time.time()*1000)}-{self._seq}"
67
+
68
+ def _append_call(self, tool_calls: list, id_: str, call_id: str, name: str, args: dict) -> None:
69
+ tool_calls.append({
70
+ "id": id_,
71
+ "call_id": call_id,
72
+ "type": "computer_call",
73
+ "function": {
74
+ "name": name,
75
+ "arguments": json.dumps(args or {}),
76
+ }
77
+ })
78
+
79
+ def _append_screenshot(self, tool_calls: list, id_: str, call_id: str) -> None:
80
+ self._append_call(tool_calls, id_, call_id, "get_screenshot", {})
81
+
82
+ def _record_pending_checks(self, ctx: CtxItem, pending: Optional[list]) -> None:
83
+ if not pending:
84
+ return
85
+ ctx.extra["pending_safety_checks"] = []
86
+ for item in pending:
87
+ try:
88
+ ctx.extra["pending_safety_checks"].append({
89
+ "id": getattr(item, "id", None),
90
+ "code": getattr(item, "code", None),
91
+ "message": getattr(item, "message", None),
92
+ })
93
+ except Exception:
94
+ pass
95
+
96
+ def handle_stream_chunk(self, ctx: CtxItem, chunk, tool_calls: list) -> Tuple[List, bool]:
97
+ """
98
+ Handle function_call parts (Gemini) and older action-shaped events.
99
+ All functions are passed through unchanged to the worker.
100
+
101
+ Returns: updated tool_calls and a boolean indicating if there were calls.
102
+ """
103
+ has_calls = False
104
+
105
+ # Case A: Google SDK function_call parts (recommended)
106
+ for fname, fargs in self._iter_function_calls(chunk):
107
+ if not fname:
108
+ continue
109
+ id_ = self._next_id()
110
+ call_id = id_
111
+ try:
112
+ self._append_call(tool_calls, id_, call_id, fname, fargs or {})
113
+ has_calls = True
114
+ except Exception as e:
115
+ print(f"Gemini pass-through error for function '{fname}': {e}")
116
+
117
+ # Case B: Older/OpenAI-shaped events embedded as chunk.item.action
118
+ try:
119
+ item = getattr(chunk, "item", None)
120
+ if item and getattr(item, "type", "") == "computer_call":
121
+ id_ = getattr(item, "id", None) or self._next_id()
122
+ call_id = getattr(item, "call_id", None) or id_
123
+ action = getattr(item, "action", None)
124
+ if action:
125
+ name, args = self._pass_action(action)
126
+ if name:
127
+ self._append_call(tool_calls, id_, call_id, name, args)
128
+ has_calls = True
129
+ # optional pending safety checks
130
+ if getattr(item, "pending_safety_checks", None):
131
+ self._record_pending_checks(ctx, item.pending_safety_checks)
132
+ except Exception as e:
133
+ print(f"Gemini action stream parse error: {e}")
134
+
135
+ return tool_calls, has_calls
136
+
137
+ # --------------- Parsers --------------- #
138
+
139
+ def _iter_function_calls(self, resp) -> List[tuple]:
140
+ calls = []
141
+ try:
142
+ candidates = getattr(resp, "candidates", None)
143
+ if candidates:
144
+ for cand in candidates:
145
+ content = getattr(cand, "content", None)
146
+ if content:
147
+ parts = getattr(content, "parts", None)
148
+ if parts:
149
+ for part in parts:
150
+ fc = getattr(part, "function_call", None)
151
+ if fc:
152
+ name = getattr(fc, "name", None)
153
+ args = getattr(fc, "args", {}) or {}
154
+ calls.append((name, args))
155
+ else:
156
+ if isinstance(resp, dict):
157
+ content = resp.get("content", {})
158
+ parts = content.get("parts", [])
159
+ for part in parts:
160
+ if "function_call" in part:
161
+ fc = part["function_call"]
162
+ calls.append((fc.get("name"), fc.get("args", {})))
163
+ except Exception as e:
164
+ print(f"Gemini: failed to parse function_call: {e}")
165
+ return calls
166
+
167
+ def _pass_action(self, action) -> Tuple[Optional[str], dict]:
168
+ """
169
+ Convert old-style action object into a direct function call name + args,
170
+ without changing the semantic names (workers handle details).
171
+ """
172
+ try:
173
+ atype = getattr(action, "type", None)
174
+ if not atype:
175
+ return None, {}
176
+ atype = str(atype)
177
+
178
+ # Build args by introspection; workers know how to interpret them
179
+ args = {}
180
+ for attr in ("x", "y", "button", "scroll_x", "scroll_y", "keys", "text", "path"):
181
+ if hasattr(action, attr):
182
+ args[attr] = getattr(action, attr)
183
+
184
+ if atype == "double_click":
185
+ args["num_clicks"] = 2
186
+
187
+ return atype, args
188
+ except Exception as e:
189
+ print(f"Gemini: pass_action error: {e}")
190
+ return 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: 2025.08.31 23:00:00 #
9
+ # Updated Date: 2026.01.02 19:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import json
@@ -97,7 +97,7 @@ class Realtime:
97
97
 
98
98
  # Tools
99
99
  tools = self.window.core.api.google.tools.prepare(model, context.external_functions)
100
- remote_tools = self.window.core.api.google.build_remote_tools(model)
100
+ remote_tools = self.window.core.api.google.remote_tools.build_remote_tools(model)
101
101
  if tools:
102
102
  remote_tools = [] # in Google, remote tools are not allowed if function calling is used
103
103
 
@@ -0,0 +1,93 @@
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: 2026.01.02 19:00:00 #
10
+ # ================================================== #
11
+
12
+ from google.genai import types as gtypes
13
+
14
+ from pygpt_net.item.model import ModelItem
15
+
16
+
17
+ class RemoteTools:
18
+ def __init__(self, window=None):
19
+ """
20
+ Remote Tools helpers for Google GenAI.
21
+
22
+ :param window: Window instance
23
+ """
24
+ self.window = window
25
+
26
+ def build_remote_tools(self, model: ModelItem = None) -> list:
27
+ """
28
+ Build Google GenAI remote tools based on config flags.
29
+ - remote_tools.google.web_search: enables grounding via Google Search (Gemini 2.x)
30
+ or GoogleSearchRetrieval (Gemini 1.5 fallback).
31
+ - remote_tools.google.code_interpreter: enables code execution tool.
32
+
33
+ Returns a list of gtypes.Tool objects (can be empty).
34
+
35
+ :param model: ModelItem
36
+ :return: list of gtypes.Tool
37
+ """
38
+ tools: list = []
39
+ cfg = self.window.core.config
40
+ model_id = (model.id if model and getattr(model, "id", None) else "").lower()
41
+ is_web = self.window.controller.chat.remote_tools.enabled(model, "web_search") # get global config
42
+
43
+ # Google Search tool
44
+ if is_web and "image" not in model.id:
45
+ try:
46
+ if not model_id.startswith("gemini-1.5") and not model_id.startswith("models/gemini-1.5"):
47
+ # Gemini 2.x uses GoogleSearch
48
+ tools.append(gtypes.Tool(google_search=gtypes.GoogleSearch()))
49
+ else:
50
+ # Gemini 1.5 fallback uses GoogleSearchRetrieval
51
+ # Note: Supported only for 1.5 models.
52
+ tools.append(gtypes.Tool(
53
+ google_search_retrieval=gtypes.GoogleSearchRetrieval()
54
+ ))
55
+ except Exception as e:
56
+ # Do not break the request if tool construction fails
57
+ self.window.core.debug.log(e)
58
+
59
+ # Code Execution tool
60
+ if cfg.get("remote_tools.google.code_interpreter") and "image" not in model.id:
61
+ try:
62
+ tools.append(gtypes.Tool(code_execution=gtypes.ToolCodeExecution))
63
+ except Exception as e:
64
+ self.window.core.debug.log(e)
65
+
66
+ # URL Context tool
67
+ if cfg.get("remote_tools.google.url_ctx") and "image" not in model.id:
68
+ try:
69
+ # Supported on Gemini 2.x+ models (not on 1.5)
70
+ if not model_id.startswith("gemini-1.5") and not model_id.startswith("models/gemini-1.5"):
71
+ tools.append(gtypes.Tool(url_context=gtypes.UrlContext))
72
+ except Exception as e:
73
+ self.window.core.debug.log(e)
74
+
75
+ # Google Maps
76
+ if cfg.get("remote_tools.google.maps") and "image" not in model.id:
77
+ try:
78
+ tools.append(gtypes.Tool(google_maps=gtypes.GoogleMaps()))
79
+ except Exception as e:
80
+ self.window.core.debug.log(e)
81
+
82
+ # File search
83
+ if cfg.get("remote_tools.google.file_search") and "image" not in model.id:
84
+ store_ids = cfg.get("remote_tools.google.file_search.args", "")
85
+ file_search_store_names = [s.strip() for s in store_ids.split(",") if s.strip()]
86
+ try:
87
+ tools.append(gtypes.Tool(file_search=gtypes.FileSearch(
88
+ file_search_store_names=file_search_store_names,
89
+ )))
90
+ except Exception as e:
91
+ self.window.core.debug.log(e)
92
+
93
+ return tools