pygpt-net 2.7.4__py3-none-any.whl → 2.7.6__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 +15 -0
- pygpt_net/__init__.py +4 -4
- pygpt_net/app_core.py +4 -2
- pygpt_net/controller/__init__.py +5 -1
- pygpt_net/controller/assistant/assistant.py +1 -4
- pygpt_net/controller/assistant/batch.py +5 -504
- pygpt_net/controller/assistant/editor.py +5 -5
- pygpt_net/controller/assistant/files.py +16 -16
- pygpt_net/controller/chat/handler/google_stream.py +307 -1
- pygpt_net/controller/chat/handler/worker.py +10 -25
- pygpt_net/controller/chat/handler/xai_stream.py +621 -52
- pygpt_net/controller/chat/image.py +2 -2
- pygpt_net/controller/debug/fixtures.py +3 -2
- pygpt_net/controller/dialogs/confirm.py +73 -101
- pygpt_net/controller/files/files.py +65 -4
- pygpt_net/controller/lang/mapping.py +9 -9
- pygpt_net/controller/painter/capture.py +50 -1
- pygpt_net/controller/presets/presets.py +2 -1
- pygpt_net/controller/remote_store/__init__.py +12 -0
- pygpt_net/{provider/core/assistant_file/db_sqlite → controller/remote_store/google}/__init__.py +2 -2
- pygpt_net/controller/remote_store/google/batch.py +402 -0
- pygpt_net/controller/remote_store/google/store.py +615 -0
- pygpt_net/controller/remote_store/openai/__init__.py +12 -0
- pygpt_net/controller/remote_store/openai/batch.py +524 -0
- pygpt_net/controller/{assistant → remote_store/openai}/store.py +63 -60
- pygpt_net/controller/remote_store/remote_store.py +35 -0
- pygpt_net/controller/ui/ui.py +20 -1
- pygpt_net/core/assistants/assistants.py +3 -15
- pygpt_net/core/db/database.py +5 -3
- pygpt_net/core/filesystem/url.py +4 -1
- pygpt_net/core/locale/placeholder.py +35 -0
- pygpt_net/core/remote_store/__init__.py +12 -0
- pygpt_net/core/remote_store/google/__init__.py +11 -0
- pygpt_net/core/remote_store/google/files.py +224 -0
- pygpt_net/core/remote_store/google/store.py +248 -0
- pygpt_net/core/remote_store/openai/__init__.py +11 -0
- pygpt_net/core/{assistants → remote_store/openai}/files.py +26 -19
- pygpt_net/core/{assistants → remote_store/openai}/store.py +32 -15
- pygpt_net/core/remote_store/remote_store.py +24 -0
- pygpt_net/core/render/web/body.py +3 -2
- pygpt_net/core/types/chunk.py +27 -0
- pygpt_net/data/config/config.json +8 -4
- pygpt_net/data/config/models.json +77 -3
- pygpt_net/data/config/settings.json +45 -0
- pygpt_net/data/js/app/template.js +1 -1
- pygpt_net/data/js/app.min.js +2 -2
- pygpt_net/data/locale/locale.de.ini +44 -41
- pygpt_net/data/locale/locale.en.ini +56 -43
- pygpt_net/data/locale/locale.es.ini +44 -41
- pygpt_net/data/locale/locale.fr.ini +44 -41
- pygpt_net/data/locale/locale.it.ini +44 -41
- pygpt_net/data/locale/locale.pl.ini +45 -42
- pygpt_net/data/locale/locale.uk.ini +44 -41
- pygpt_net/data/locale/locale.zh.ini +44 -41
- pygpt_net/data/locale/plugin.cmd_history.de.ini +1 -1
- pygpt_net/data/locale/plugin.cmd_history.en.ini +1 -1
- pygpt_net/data/locale/plugin.cmd_history.es.ini +1 -1
- pygpt_net/data/locale/plugin.cmd_history.fr.ini +1 -1
- pygpt_net/data/locale/plugin.cmd_history.it.ini +1 -1
- pygpt_net/data/locale/plugin.cmd_history.pl.ini +1 -1
- pygpt_net/data/locale/plugin.cmd_history.uk.ini +1 -1
- pygpt_net/data/locale/plugin.cmd_history.zh.ini +1 -1
- pygpt_net/data/locale/plugin.cmd_mouse_control.en.ini +14 -0
- pygpt_net/data/locale/plugin.cmd_web.de.ini +1 -1
- pygpt_net/data/locale/plugin.cmd_web.en.ini +1 -1
- pygpt_net/data/locale/plugin.cmd_web.es.ini +1 -1
- pygpt_net/data/locale/plugin.cmd_web.fr.ini +1 -1
- pygpt_net/data/locale/plugin.cmd_web.it.ini +1 -1
- pygpt_net/data/locale/plugin.cmd_web.pl.ini +1 -1
- pygpt_net/data/locale/plugin.cmd_web.uk.ini +1 -1
- pygpt_net/data/locale/plugin.cmd_web.zh.ini +1 -1
- pygpt_net/data/locale/plugin.idx_llama_index.de.ini +2 -2
- pygpt_net/data/locale/plugin.idx_llama_index.en.ini +2 -2
- pygpt_net/data/locale/plugin.idx_llama_index.es.ini +2 -2
- pygpt_net/data/locale/plugin.idx_llama_index.fr.ini +2 -2
- pygpt_net/data/locale/plugin.idx_llama_index.it.ini +2 -2
- pygpt_net/data/locale/plugin.idx_llama_index.pl.ini +2 -2
- pygpt_net/data/locale/plugin.idx_llama_index.uk.ini +2 -2
- pygpt_net/data/locale/plugin.idx_llama_index.zh.ini +2 -2
- pygpt_net/item/assistant.py +1 -211
- pygpt_net/item/ctx.py +3 -3
- pygpt_net/item/store.py +238 -0
- pygpt_net/js_rc.py +2449 -2447
- pygpt_net/migrations/Version20260102190000.py +35 -0
- pygpt_net/migrations/__init__.py +3 -1
- pygpt_net/plugin/cmd_mouse_control/config.py +471 -1
- pygpt_net/plugin/cmd_mouse_control/plugin.py +487 -22
- pygpt_net/plugin/cmd_mouse_control/worker.py +464 -87
- pygpt_net/plugin/cmd_mouse_control/worker_sandbox.py +729 -0
- pygpt_net/plugin/idx_llama_index/config.py +2 -2
- pygpt_net/provider/api/anthropic/__init__.py +10 -8
- pygpt_net/provider/api/google/__init__.py +21 -58
- pygpt_net/provider/api/google/chat.py +545 -129
- pygpt_net/provider/api/google/computer.py +190 -0
- pygpt_net/provider/api/google/realtime/realtime.py +2 -2
- pygpt_net/provider/api/google/remote_tools.py +93 -0
- pygpt_net/provider/api/google/store.py +546 -0
- pygpt_net/provider/api/google/worker/__init__.py +0 -0
- pygpt_net/provider/api/google/worker/importer.py +392 -0
- pygpt_net/provider/api/openai/__init__.py +7 -3
- pygpt_net/provider/api/openai/computer.py +10 -1
- pygpt_net/provider/api/openai/responses.py +0 -0
- pygpt_net/provider/api/openai/store.py +6 -6
- pygpt_net/provider/api/openai/worker/importer.py +24 -24
- pygpt_net/provider/api/x_ai/__init__.py +10 -9
- pygpt_net/provider/api/x_ai/chat.py +272 -102
- pygpt_net/provider/core/config/patch.py +16 -1
- pygpt_net/provider/core/config/patches/patch_before_2_6_42.py +3 -3
- pygpt_net/provider/core/model/patch.py +17 -3
- pygpt_net/provider/core/preset/json_file.py +13 -7
- pygpt_net/provider/core/{assistant_file → remote_file}/__init__.py +1 -1
- pygpt_net/provider/core/{assistant_file → remote_file}/base.py +9 -9
- pygpt_net/provider/core/remote_file/db_sqlite/__init__.py +12 -0
- pygpt_net/provider/core/{assistant_file → remote_file}/db_sqlite/patch.py +1 -1
- pygpt_net/provider/core/{assistant_file → remote_file}/db_sqlite/provider.py +23 -20
- pygpt_net/provider/core/{assistant_file → remote_file}/db_sqlite/storage.py +35 -27
- pygpt_net/provider/core/{assistant_file → remote_file}/db_sqlite/utils.py +5 -4
- pygpt_net/provider/core/{assistant_store → remote_store}/__init__.py +1 -1
- pygpt_net/provider/core/{assistant_store → remote_store}/base.py +10 -10
- pygpt_net/provider/core/{assistant_store → remote_store}/db_sqlite/__init__.py +1 -1
- pygpt_net/provider/core/{assistant_store → remote_store}/db_sqlite/patch.py +1 -1
- pygpt_net/provider/core/{assistant_store → remote_store}/db_sqlite/provider.py +16 -15
- pygpt_net/provider/core/{assistant_store → remote_store}/db_sqlite/storage.py +30 -23
- pygpt_net/provider/core/{assistant_store → remote_store}/db_sqlite/utils.py +5 -4
- pygpt_net/provider/core/{assistant_store → remote_store}/json_file.py +9 -9
- pygpt_net/provider/llms/google.py +2 -2
- pygpt_net/tools/image_viewer/ui/dialogs.py +298 -12
- pygpt_net/tools/text_editor/ui/widgets.py +5 -1
- pygpt_net/ui/base/config_dialog.py +3 -2
- pygpt_net/ui/base/context_menu.py +44 -1
- pygpt_net/ui/dialog/assistant.py +3 -3
- pygpt_net/ui/dialog/plugins.py +3 -1
- pygpt_net/ui/dialog/remote_store_google.py +539 -0
- pygpt_net/ui/dialog/{assistant_store.py → remote_store_openai.py} +95 -95
- pygpt_net/ui/dialogs.py +5 -3
- pygpt_net/ui/layout/chat/attachments_uploaded.py +3 -3
- pygpt_net/ui/layout/toolbox/computer_env.py +26 -8
- pygpt_net/ui/layout/toolbox/indexes.py +22 -19
- pygpt_net/ui/layout/toolbox/model.py +28 -5
- pygpt_net/ui/menu/tools.py +13 -5
- pygpt_net/ui/widget/dialog/remote_store_google.py +56 -0
- pygpt_net/ui/widget/dialog/{assistant_store.py → remote_store_openai.py} +9 -9
- pygpt_net/ui/widget/element/button.py +4 -4
- pygpt_net/ui/widget/image/display.py +25 -8
- pygpt_net/ui/widget/lists/remote_store_google.py +248 -0
- pygpt_net/ui/widget/lists/{assistant_store.py → remote_store_openai.py} +21 -21
- pygpt_net/ui/widget/option/checkbox_list.py +47 -9
- pygpt_net/ui/widget/option/combo.py +39 -3
- pygpt_net/ui/widget/tabs/output.py +9 -1
- pygpt_net/ui/widget/textarea/editor.py +14 -1
- pygpt_net/ui/widget/textarea/input.py +20 -7
- pygpt_net/ui/widget/textarea/notepad.py +24 -1
- pygpt_net/ui/widget/textarea/output.py +23 -1
- pygpt_net/ui/widget/textarea/web.py +16 -1
- {pygpt_net-2.7.4.dist-info → pygpt_net-2.7.6.dist-info}/METADATA +41 -2
- {pygpt_net-2.7.4.dist-info → pygpt_net-2.7.6.dist-info}/RECORD +158 -132
- {pygpt_net-2.7.4.dist-info → pygpt_net-2.7.6.dist-info}/LICENSE +0 -0
- {pygpt_net-2.7.4.dist-info → pygpt_net-2.7.6.dist-info}/WHEEL +0 -0
- {pygpt_net-2.7.4.dist-info → pygpt_net-2.7.6.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,546 @@
|
|
|
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 20:00:00 #
|
|
10
|
+
# ================================================== #
|
|
11
|
+
|
|
12
|
+
import os
|
|
13
|
+
import time
|
|
14
|
+
from typing import Optional, List, Dict, Any
|
|
15
|
+
|
|
16
|
+
from pygpt_net.item.store import RemoteStoreItem
|
|
17
|
+
|
|
18
|
+
from .worker.importer import Importer
|
|
19
|
+
|
|
20
|
+
class Store:
|
|
21
|
+
def __init__(self, window=None):
|
|
22
|
+
"""
|
|
23
|
+
Google (Gemini) File Search stores API wrapper
|
|
24
|
+
|
|
25
|
+
:param window: Window instance
|
|
26
|
+
"""
|
|
27
|
+
self.window = window
|
|
28
|
+
self.importer = Importer(window)
|
|
29
|
+
|
|
30
|
+
def get_client(self):
|
|
31
|
+
"""
|
|
32
|
+
Get Google GenAI client
|
|
33
|
+
|
|
34
|
+
:return: google.genai.Client
|
|
35
|
+
"""
|
|
36
|
+
return self.window.core.api.google.get_client()
|
|
37
|
+
|
|
38
|
+
def log(self, msg: str, callback: Optional[callable] = None):
|
|
39
|
+
"""
|
|
40
|
+
Log message
|
|
41
|
+
|
|
42
|
+
:param msg: message to log
|
|
43
|
+
:param callback: callback log function
|
|
44
|
+
"""
|
|
45
|
+
if callback is not None:
|
|
46
|
+
callback(msg)
|
|
47
|
+
else:
|
|
48
|
+
print(msg)
|
|
49
|
+
|
|
50
|
+
# -----------------------------
|
|
51
|
+
# helpers
|
|
52
|
+
# -----------------------------
|
|
53
|
+
|
|
54
|
+
def _clamp(self, val: int, lo: int, hi: int) -> int:
|
|
55
|
+
"""Clamp integer value into range [lo, hi]."""
|
|
56
|
+
try:
|
|
57
|
+
v = int(val or 0)
|
|
58
|
+
except Exception:
|
|
59
|
+
v = lo
|
|
60
|
+
if v < lo:
|
|
61
|
+
v = lo
|
|
62
|
+
if v > hi:
|
|
63
|
+
v = hi
|
|
64
|
+
return v
|
|
65
|
+
|
|
66
|
+
# -----------------------------
|
|
67
|
+
# Files service (global)
|
|
68
|
+
# -----------------------------
|
|
69
|
+
|
|
70
|
+
def get_file(self, file_name: str):
|
|
71
|
+
"""
|
|
72
|
+
Get Files API file metadata by name (e.g. 'files/abc-123').
|
|
73
|
+
|
|
74
|
+
:param file_name: Files API resource name
|
|
75
|
+
:return: file metadata
|
|
76
|
+
"""
|
|
77
|
+
client = self.get_client()
|
|
78
|
+
return client.files.get(name=file_name)
|
|
79
|
+
|
|
80
|
+
def upload(self, path: str) -> Optional[str]:
|
|
81
|
+
"""
|
|
82
|
+
Upload file to Files API (not to a File Search Store). Use when you want to import later.
|
|
83
|
+
|
|
84
|
+
:param path: file path
|
|
85
|
+
:return: Files API resource name (e.g., 'files/abc-123') or None
|
|
86
|
+
"""
|
|
87
|
+
client = self.get_client()
|
|
88
|
+
if not os.path.exists(path):
|
|
89
|
+
return None
|
|
90
|
+
|
|
91
|
+
cfg = {'name': os.path.basename(path)}
|
|
92
|
+
res = client.files.upload(file=path, config=cfg)
|
|
93
|
+
if res is not None and hasattr(res, "name"):
|
|
94
|
+
return res.name
|
|
95
|
+
|
|
96
|
+
def delete_file(self, file_name: str) -> Optional[str]:
|
|
97
|
+
"""
|
|
98
|
+
Delete file from Files API
|
|
99
|
+
|
|
100
|
+
:param file_name: Files API resource name ('files/...').
|
|
101
|
+
:return: file_name if removed
|
|
102
|
+
"""
|
|
103
|
+
client = self.get_client()
|
|
104
|
+
res = client.files.delete(name=file_name)
|
|
105
|
+
if res is not None:
|
|
106
|
+
return file_name
|
|
107
|
+
|
|
108
|
+
def get_files_ids_all(
|
|
109
|
+
self,
|
|
110
|
+
items: list,
|
|
111
|
+
order: str = "asc",
|
|
112
|
+
limit: int = 100,
|
|
113
|
+
after: Optional[str] = None,
|
|
114
|
+
) -> list:
|
|
115
|
+
"""
|
|
116
|
+
Get all Files API file names (paginated)
|
|
117
|
+
|
|
118
|
+
:param items: items accumulator
|
|
119
|
+
:param order: unused, for compatibility
|
|
120
|
+
:param limit: page size (max 100 for Files API)
|
|
121
|
+
:param after: page token
|
|
122
|
+
:return: file names list
|
|
123
|
+
"""
|
|
124
|
+
client = self.get_client()
|
|
125
|
+
limit = self._clamp(limit, 1, 100) # Files API allows up to 100
|
|
126
|
+
cfg: Dict[str, Any] = {'page_size': limit}
|
|
127
|
+
if after:
|
|
128
|
+
cfg['page_token'] = after
|
|
129
|
+
|
|
130
|
+
pager = client.files.list(config=cfg)
|
|
131
|
+
for f in pager:
|
|
132
|
+
name = getattr(f, "name", None)
|
|
133
|
+
if name and name not in items:
|
|
134
|
+
items.append(name)
|
|
135
|
+
|
|
136
|
+
# The SDK pager auto-iterates pages; explicit tokens are optional
|
|
137
|
+
return items
|
|
138
|
+
|
|
139
|
+
def get_files_ids(self) -> List[str]:
|
|
140
|
+
"""
|
|
141
|
+
Get Files API file names (single iterator over all pages)
|
|
142
|
+
|
|
143
|
+
:return: list of file names
|
|
144
|
+
"""
|
|
145
|
+
client = self.get_client()
|
|
146
|
+
items = []
|
|
147
|
+
pager = client.files.list()
|
|
148
|
+
for f in pager:
|
|
149
|
+
name = getattr(f, "name", None)
|
|
150
|
+
if name and name not in items:
|
|
151
|
+
items.append(name)
|
|
152
|
+
return items
|
|
153
|
+
|
|
154
|
+
def remove_files(self, callback: Optional[callable] = None) -> int:
|
|
155
|
+
"""
|
|
156
|
+
Remove all Files API files
|
|
157
|
+
|
|
158
|
+
:param callback: callback function
|
|
159
|
+
:return: number of deleted files
|
|
160
|
+
"""
|
|
161
|
+
num = 0
|
|
162
|
+
files = self.get_files_ids()
|
|
163
|
+
for file_name in files:
|
|
164
|
+
self.log("Removing file: " + file_name, callback)
|
|
165
|
+
try:
|
|
166
|
+
res = self.delete_file(file_name)
|
|
167
|
+
if res:
|
|
168
|
+
num += 1
|
|
169
|
+
except Exception as e:
|
|
170
|
+
msg = "Error removing file {}: {}".format(file_name, str(e))
|
|
171
|
+
self.log(msg, callback)
|
|
172
|
+
return num
|
|
173
|
+
|
|
174
|
+
def remove_file(self, file_name: str, callback: Optional[callable] = None) -> bool:
|
|
175
|
+
"""
|
|
176
|
+
Remove a single Files API file
|
|
177
|
+
|
|
178
|
+
:param file_name: Files API resource name ('files/...').
|
|
179
|
+
:param callback: callback function
|
|
180
|
+
:return: True if removed
|
|
181
|
+
"""
|
|
182
|
+
self.log("Removing file: " + file_name, callback)
|
|
183
|
+
try:
|
|
184
|
+
res = self.delete_file(file_name)
|
|
185
|
+
return res is not None
|
|
186
|
+
except Exception as e:
|
|
187
|
+
msg = "Error removing file {}: {}".format(file_name, str(e))
|
|
188
|
+
self.log(msg, callback)
|
|
189
|
+
raise
|
|
190
|
+
|
|
191
|
+
# -----------------------------
|
|
192
|
+
# File Search stores
|
|
193
|
+
# -----------------------------
|
|
194
|
+
|
|
195
|
+
def import_stores(
|
|
196
|
+
self,
|
|
197
|
+
items: dict,
|
|
198
|
+
order: str = "asc",
|
|
199
|
+
limit: int = 100,
|
|
200
|
+
after: Optional[str] = None,
|
|
201
|
+
callback: Optional[callable] = None
|
|
202
|
+
) -> dict:
|
|
203
|
+
"""
|
|
204
|
+
Import File Search stores
|
|
205
|
+
|
|
206
|
+
:param items: items dict accumulator
|
|
207
|
+
:param order: unused
|
|
208
|
+
:param limit: page size (Google limit: 20)
|
|
209
|
+
:param after: page token
|
|
210
|
+
:param callback: callback
|
|
211
|
+
:return: items dict of RemoteStoreItem
|
|
212
|
+
"""
|
|
213
|
+
client = self.get_client()
|
|
214
|
+
limit = self._clamp(limit, 1, 20) # API limit = 20
|
|
215
|
+
cfg: Dict[str, Any] = {'page_size': limit}
|
|
216
|
+
if after:
|
|
217
|
+
cfg['page_token'] = after
|
|
218
|
+
|
|
219
|
+
pager = client.file_search_stores.list(config=cfg)
|
|
220
|
+
for remote in pager:
|
|
221
|
+
name = getattr(remote, "name", None) # 'fileSearchStores/...'
|
|
222
|
+
if not name:
|
|
223
|
+
continue
|
|
224
|
+
if name not in items:
|
|
225
|
+
items[name] = RemoteStoreItem()
|
|
226
|
+
items[name].id = name
|
|
227
|
+
items[name].name = getattr(remote, "display_name", "") or ""
|
|
228
|
+
items[name].file_ids = []
|
|
229
|
+
items[name].provider = "google"
|
|
230
|
+
|
|
231
|
+
status = self.window.core.remote_store.google.parse_status(remote)
|
|
232
|
+
self.window.core.remote_store.google.append_status(items[name], status)
|
|
233
|
+
self.log("Imported file search store: " + name, callback)
|
|
234
|
+
|
|
235
|
+
return items
|
|
236
|
+
|
|
237
|
+
def create_store(self, name: str, expire_days: int = 0):
|
|
238
|
+
"""
|
|
239
|
+
Create File Search store
|
|
240
|
+
|
|
241
|
+
:param name: display name
|
|
242
|
+
:param expire_days: ignored (not supported)
|
|
243
|
+
:return: store object
|
|
244
|
+
"""
|
|
245
|
+
client = self.get_client()
|
|
246
|
+
store = client.file_search_stores.create(config={'display_name': name})
|
|
247
|
+
if store is not None:
|
|
248
|
+
return store
|
|
249
|
+
|
|
250
|
+
def update_store(self, id: str, name: str, expire_days: int = 0):
|
|
251
|
+
"""
|
|
252
|
+
Update File Search store (no remote update available). Returns the current remote store.
|
|
253
|
+
|
|
254
|
+
:param id: store name ('fileSearchStores/...').
|
|
255
|
+
:param name: local alias (persisted locally)
|
|
256
|
+
:param expire_days: ignored
|
|
257
|
+
:return: store object (fresh get) or None if not found
|
|
258
|
+
"""
|
|
259
|
+
try:
|
|
260
|
+
return self.get_store(id)
|
|
261
|
+
except Exception:
|
|
262
|
+
return None
|
|
263
|
+
|
|
264
|
+
def get_store(self, id: str):
|
|
265
|
+
"""
|
|
266
|
+
Get File Search store
|
|
267
|
+
|
|
268
|
+
:param id: store name ('fileSearchStores/...').
|
|
269
|
+
:return: store object
|
|
270
|
+
"""
|
|
271
|
+
client = self.get_client()
|
|
272
|
+
return client.file_search_stores.get(name=id)
|
|
273
|
+
|
|
274
|
+
def remove_store(self, id: str):
|
|
275
|
+
"""
|
|
276
|
+
Delete File Search store (force delete related documents)
|
|
277
|
+
|
|
278
|
+
:param id: store name ('fileSearchStores/...').
|
|
279
|
+
:return: delete response
|
|
280
|
+
"""
|
|
281
|
+
client = self.get_client()
|
|
282
|
+
return client.file_search_stores.delete(name=id, config={'force': True})
|
|
283
|
+
|
|
284
|
+
def get_stores_ids(
|
|
285
|
+
self,
|
|
286
|
+
items: list,
|
|
287
|
+
order: str = "asc",
|
|
288
|
+
limit: int = 100,
|
|
289
|
+
after: Optional[str] = None,
|
|
290
|
+
) -> list:
|
|
291
|
+
"""
|
|
292
|
+
Get all File Search store names
|
|
293
|
+
|
|
294
|
+
:param items: items accumulator
|
|
295
|
+
:param order: unused
|
|
296
|
+
:param limit: page size (Google limit 20)
|
|
297
|
+
:param after: page token
|
|
298
|
+
:return: list of store names
|
|
299
|
+
"""
|
|
300
|
+
client = self.get_client()
|
|
301
|
+
limit = self._clamp(limit, 1, 20)
|
|
302
|
+
cfg: Dict[str, Any] = {'page_size': limit}
|
|
303
|
+
if after:
|
|
304
|
+
cfg['page_token'] = after
|
|
305
|
+
|
|
306
|
+
pager = client.file_search_stores.list(config=cfg)
|
|
307
|
+
for remote in pager:
|
|
308
|
+
name = getattr(remote, "name", None)
|
|
309
|
+
if name and name not in items:
|
|
310
|
+
items.append(name)
|
|
311
|
+
return items
|
|
312
|
+
|
|
313
|
+
# -----------------------------
|
|
314
|
+
# Documents within a store
|
|
315
|
+
# -----------------------------
|
|
316
|
+
|
|
317
|
+
def get_store_files_ids(
|
|
318
|
+
self,
|
|
319
|
+
store_id: str,
|
|
320
|
+
items: list,
|
|
321
|
+
order: str = "asc",
|
|
322
|
+
limit: int = 100,
|
|
323
|
+
after: Optional[str] = None,
|
|
324
|
+
) -> list:
|
|
325
|
+
"""
|
|
326
|
+
Get all Document names for a File Search store
|
|
327
|
+
|
|
328
|
+
:param store_id: store name ('fileSearchStores/...').
|
|
329
|
+
:param items: items accumulator
|
|
330
|
+
:param order: unused
|
|
331
|
+
:param limit: page size (Google limit 20)
|
|
332
|
+
:param after: page token
|
|
333
|
+
:return: list of Document names ('fileSearchStores/.../documents/...').
|
|
334
|
+
"""
|
|
335
|
+
client = self.get_client()
|
|
336
|
+
limit = self._clamp(limit, 1, 20)
|
|
337
|
+
cfg: Dict[str, Any] = {'page_size': limit}
|
|
338
|
+
if after:
|
|
339
|
+
cfg['page_token'] = after
|
|
340
|
+
|
|
341
|
+
try:
|
|
342
|
+
pager = client.file_search_stores.documents.list(parent=store_id, config=cfg)
|
|
343
|
+
except Exception:
|
|
344
|
+
pager = client.documents.list(parent=store_id, config=cfg)
|
|
345
|
+
|
|
346
|
+
for remote in pager:
|
|
347
|
+
name = getattr(remote, "name", None)
|
|
348
|
+
if name and name not in items:
|
|
349
|
+
items.append(name)
|
|
350
|
+
return items
|
|
351
|
+
|
|
352
|
+
def remove_from_stores(self) -> int:
|
|
353
|
+
"""
|
|
354
|
+
Remove all documents from all File Search stores
|
|
355
|
+
|
|
356
|
+
:return: number of deleted documents
|
|
357
|
+
"""
|
|
358
|
+
stores = self.get_stores_ids([])
|
|
359
|
+
num = 0
|
|
360
|
+
for store_id in stores:
|
|
361
|
+
files = self.get_store_files_ids(store_id, [])
|
|
362
|
+
for doc_name in files:
|
|
363
|
+
self.log("Removing document from store [{}]:{} ".format(store_id, doc_name))
|
|
364
|
+
self.delete_store_file(store_id, doc_name)
|
|
365
|
+
num += 1
|
|
366
|
+
return num
|
|
367
|
+
|
|
368
|
+
def remove_from_store(self, store_id: str) -> int:
|
|
369
|
+
"""
|
|
370
|
+
Remove all documents from a specific File Search store
|
|
371
|
+
|
|
372
|
+
:param store_id: store name ('fileSearchStores/...').
|
|
373
|
+
:return: number of deleted documents
|
|
374
|
+
"""
|
|
375
|
+
files = self.get_store_files_ids(store_id, [])
|
|
376
|
+
num = 0
|
|
377
|
+
for doc_name in files:
|
|
378
|
+
self.log("Removing document from store [{}]:{} ".format(store_id, doc_name))
|
|
379
|
+
self.delete_store_file(store_id, doc_name)
|
|
380
|
+
num += 1
|
|
381
|
+
return num
|
|
382
|
+
|
|
383
|
+
def remove_all(self, callback: Optional[callable] = None) -> int:
|
|
384
|
+
"""
|
|
385
|
+
Remove all File Search stores
|
|
386
|
+
|
|
387
|
+
:param callback: callback function
|
|
388
|
+
:return: number of deleted stores
|
|
389
|
+
"""
|
|
390
|
+
num = 0
|
|
391
|
+
stores = self.get_stores_ids([])
|
|
392
|
+
for store_id in stores:
|
|
393
|
+
self.log("Removing file search store: " + store_id, callback)
|
|
394
|
+
try:
|
|
395
|
+
self.remove_store(store_id)
|
|
396
|
+
num += 1
|
|
397
|
+
except Exception as e:
|
|
398
|
+
msg = "Error removing file search store {}: {}".format(store_id, str(e))
|
|
399
|
+
self.log(msg, callback)
|
|
400
|
+
return num
|
|
401
|
+
|
|
402
|
+
def add_file(self, store_id: str, file_name: str):
|
|
403
|
+
"""
|
|
404
|
+
Import a Files API file into a File Search store.
|
|
405
|
+
|
|
406
|
+
:param store_id: store name ('fileSearchStores/...').
|
|
407
|
+
:param file_name: Files API file name ('files/...').
|
|
408
|
+
:return: operation (long running)
|
|
409
|
+
"""
|
|
410
|
+
client = self.get_client()
|
|
411
|
+
op = client.file_search_stores.import_file(
|
|
412
|
+
file_search_store_name=store_id,
|
|
413
|
+
file_name=file_name,
|
|
414
|
+
)
|
|
415
|
+
return op
|
|
416
|
+
|
|
417
|
+
def delete_store_file(self, store_id: str, document_name: str):
|
|
418
|
+
"""
|
|
419
|
+
Delete a Document from a File Search store.
|
|
420
|
+
|
|
421
|
+
:param store_id: store name ('fileSearchStores/...').
|
|
422
|
+
:param document_name: document resource name ('fileSearchStores/.../documents/...').
|
|
423
|
+
:return: delete response
|
|
424
|
+
"""
|
|
425
|
+
client = self.get_client()
|
|
426
|
+
return client.file_search_stores.documents.delete(name=document_name, config={'force': True})
|
|
427
|
+
|
|
428
|
+
def remove_store_file(self, store_id: str, document_name: str):
|
|
429
|
+
"""
|
|
430
|
+
Compatibility alias used by controller: remove a document from a File Search store.
|
|
431
|
+
"""
|
|
432
|
+
return self.delete_store_file(store_id, document_name)
|
|
433
|
+
|
|
434
|
+
def import_stores_files(self, callback: Optional[callable] = None) -> int:
|
|
435
|
+
"""
|
|
436
|
+
Import all documents (files) from all File Search stores
|
|
437
|
+
|
|
438
|
+
:param callback: callback function
|
|
439
|
+
:return: number of imported documents
|
|
440
|
+
"""
|
|
441
|
+
store_ids = self.get_stores_ids([])
|
|
442
|
+
num = 0
|
|
443
|
+
for store_id in store_ids:
|
|
444
|
+
items = []
|
|
445
|
+
try:
|
|
446
|
+
items = self.import_store_files(store_id, items, callback=callback)
|
|
447
|
+
except Exception as e:
|
|
448
|
+
msg = "Error importing store {} documents list: {}".format(store_id, str(e))
|
|
449
|
+
self.log(msg, callback)
|
|
450
|
+
num += len(items)
|
|
451
|
+
return num
|
|
452
|
+
|
|
453
|
+
def import_store_files(
|
|
454
|
+
self,
|
|
455
|
+
store_id: str,
|
|
456
|
+
items: list,
|
|
457
|
+
order: str = "asc",
|
|
458
|
+
limit: int = 100,
|
|
459
|
+
after: Optional[str] = None,
|
|
460
|
+
callback: Optional[callable] = None
|
|
461
|
+
) -> list:
|
|
462
|
+
"""
|
|
463
|
+
Import a store's documents and insert into local DB
|
|
464
|
+
|
|
465
|
+
:param store_id: store name ('fileSearchStores/...').
|
|
466
|
+
:param items: accumulator
|
|
467
|
+
:param order: unused
|
|
468
|
+
:param limit: page size (Google limit 20)
|
|
469
|
+
:param after: page token
|
|
470
|
+
:param callback: log callback
|
|
471
|
+
:return: list of imported document names
|
|
472
|
+
"""
|
|
473
|
+
client = self.get_client()
|
|
474
|
+
limit = self._clamp(limit, 1, 20)
|
|
475
|
+
cfg: Dict[str, Any] = {'page_size': limit}
|
|
476
|
+
if after:
|
|
477
|
+
cfg['page_token'] = after
|
|
478
|
+
|
|
479
|
+
try:
|
|
480
|
+
pager = client.file_search_stores.documents.list(parent=store_id, config=cfg)
|
|
481
|
+
except Exception:
|
|
482
|
+
pager = client.documents.list(parent=store_id, config=cfg)
|
|
483
|
+
|
|
484
|
+
for remote in pager:
|
|
485
|
+
try:
|
|
486
|
+
doc_name = getattr(remote, "name", None)
|
|
487
|
+
if not doc_name:
|
|
488
|
+
continue
|
|
489
|
+
if doc_name not in items:
|
|
490
|
+
items.append(doc_name)
|
|
491
|
+
try:
|
|
492
|
+
doc = client.file_search_stores.documents.get(name=doc_name)
|
|
493
|
+
except Exception:
|
|
494
|
+
doc = remote
|
|
495
|
+
self.window.core.remote_store.google.files.insert(store_id, doc)
|
|
496
|
+
msg = "Imported document {} to store {}".format(doc_name, store_id)
|
|
497
|
+
self.log(msg, callback)
|
|
498
|
+
except Exception as e:
|
|
499
|
+
msg = "Error importing document {} to store {}: {}".format(
|
|
500
|
+
getattr(remote, "name", "?"), store_id, str(e)
|
|
501
|
+
)
|
|
502
|
+
self.log(msg, callback)
|
|
503
|
+
return items
|
|
504
|
+
|
|
505
|
+
# -----------------------------
|
|
506
|
+
# Convenience: direct upload to a store
|
|
507
|
+
# -----------------------------
|
|
508
|
+
|
|
509
|
+
def upload_to_store(self, store_id: str, path: str):
|
|
510
|
+
"""
|
|
511
|
+
Directly upload a local file to a File Search store. This creates a Document.
|
|
512
|
+
|
|
513
|
+
:param store_id: store name ('fileSearchStores/...').
|
|
514
|
+
:param path: file path
|
|
515
|
+
:return: created document object or None
|
|
516
|
+
"""
|
|
517
|
+
client = self.get_client()
|
|
518
|
+
if not os.path.exists(path):
|
|
519
|
+
return None
|
|
520
|
+
|
|
521
|
+
basename = os.path.basename(path)
|
|
522
|
+
op = client.file_search_stores.upload_to_file_search_store(
|
|
523
|
+
file=path,
|
|
524
|
+
file_search_store_name=store_id,
|
|
525
|
+
config={'display_name': basename},
|
|
526
|
+
)
|
|
527
|
+
|
|
528
|
+
for _ in range(180):
|
|
529
|
+
if getattr(op, "done", False):
|
|
530
|
+
break
|
|
531
|
+
time.sleep(2)
|
|
532
|
+
op = client.operations.get(op)
|
|
533
|
+
|
|
534
|
+
try:
|
|
535
|
+
docs = self.get_store_files_ids(store_id, [])
|
|
536
|
+
for doc_name in docs:
|
|
537
|
+
try:
|
|
538
|
+
d = client.file_search_stores.documents.get(name=doc_name)
|
|
539
|
+
except Exception:
|
|
540
|
+
continue
|
|
541
|
+
if getattr(d, "display_name", None) == basename:
|
|
542
|
+
return d
|
|
543
|
+
except Exception:
|
|
544
|
+
pass
|
|
545
|
+
|
|
546
|
+
return None
|
|
File without changes
|