pygpt-net 2.7.3__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.
- pygpt_net/CHANGELOG.txt +15 -0
- pygpt_net/__init__.py +3 -3
- pygpt_net/app.py +382 -350
- 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/attachment.py +5 -1
- pygpt_net/controller/chat/handler/google_stream.py +307 -1
- pygpt_net/controller/chat/handler/worker.py +8 -1
- pygpt_net/controller/chat/image.py +15 -3
- pygpt_net/controller/dialogs/confirm.py +73 -101
- pygpt_net/controller/files/files.py +3 -1
- pygpt_net/controller/lang/mapping.py +9 -9
- pygpt_net/controller/layout/layout.py +2 -2
- 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/theme/nodes.py +2 -1
- pygpt_net/controller/ui/mode.py +5 -1
- pygpt_net/controller/ui/ui.py +36 -2
- 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/helpers.py +5 -0
- pygpt_net/data/config/config.json +8 -5
- pygpt_net/data/config/models.json +77 -3
- pygpt_net/data/config/settings.json +45 -14
- pygpt_net/data/css/web-blocks.css +3 -0
- pygpt_net/data/css/web-chatgpt.css +3 -0
- pygpt_net/data/locale/locale.de.ini +43 -41
- pygpt_net/data/locale/locale.en.ini +56 -44
- pygpt_net/data/locale/locale.es.ini +43 -41
- pygpt_net/data/locale/locale.fr.ini +43 -41
- pygpt_net/data/locale/locale.it.ini +43 -41
- pygpt_net/data/locale/locale.pl.ini +43 -41
- pygpt_net/data/locale/locale.uk.ini +43 -41
- pygpt_net/data/locale/locale.zh.ini +43 -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 -1
- pygpt_net/item/store.py +238 -0
- pygpt_net/launcher.py +115 -55
- pygpt_net/migrations/Version20260102190000.py +35 -0
- pygpt_net/migrations/__init__.py +3 -1
- pygpt_net/plugin/cmd_mouse_control/config.py +470 -1
- pygpt_net/plugin/cmd_mouse_control/plugin.py +488 -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/preload.py +243 -0
- pygpt_net/provider/api/google/__init__.py +16 -54
- pygpt_net/provider/api/google/chat.py +546 -129
- pygpt_net/provider/api/google/computer.py +190 -0
- pygpt_net/provider/api/google/image.py +74 -6
- 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/video.py +9 -4
- 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/computer.py +10 -1
- pygpt_net/provider/api/openai/image.py +42 -19
- pygpt_net/provider/api/openai/store.py +6 -6
- pygpt_net/provider/api/openai/video.py +27 -2
- pygpt_net/provider/api/openai/worker/importer.py +24 -24
- pygpt_net/provider/api/x_ai/image.py +25 -2
- pygpt_net/provider/core/config/patch.py +23 -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/ui/base/config_dialog.py +3 -2
- 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/chat/input.py +20 -2
- pygpt_net/ui/layout/chat/painter.py +6 -4
- pygpt_net/ui/layout/toolbox/computer_env.py +26 -8
- pygpt_net/ui/layout/toolbox/image.py +5 -5
- pygpt_net/ui/layout/toolbox/video.py +5 -4
- pygpt_net/ui/main.py +84 -3
- pygpt_net/ui/menu/tools.py +13 -5
- pygpt_net/ui/widget/dialog/base.py +3 -10
- 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/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 +158 -4
- pygpt_net/ui/widget/textarea/input_extra.py +664 -0
- {pygpt_net-2.7.3.dist-info → pygpt_net-2.7.5.dist-info}/METADATA +48 -9
- {pygpt_net-2.7.3.dist-info → pygpt_net-2.7.5.dist-info}/RECORD +157 -130
- {pygpt_net-2.7.3.dist-info → pygpt_net-2.7.5.dist-info}/LICENSE +0 -0
- {pygpt_net-2.7.3.dist-info → pygpt_net-2.7.5.dist-info}/WHEEL +0 -0
- {pygpt_net-2.7.3.dist-info → pygpt_net-2.7.5.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,35 @@
|
|
|
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.01 15:00:00 #
|
|
10
|
+
# ================================================== #
|
|
11
|
+
from typing import Optional
|
|
12
|
+
|
|
13
|
+
from pygpt_net.core.types import (
|
|
14
|
+
MODEL_DEFAULT,
|
|
15
|
+
MODEL_DEFAULT_MINI
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def apply(text: Optional[str]) -> str:
|
|
20
|
+
"""
|
|
21
|
+
Apply placeholders to the given text.
|
|
22
|
+
|
|
23
|
+
:param text: input text with placeholders
|
|
24
|
+
:return: text with applied placeholders
|
|
25
|
+
"""
|
|
26
|
+
if text is None:
|
|
27
|
+
return ""
|
|
28
|
+
placeholders = {
|
|
29
|
+
"%MODEL_DEFAULT_MINI%": MODEL_DEFAULT_MINI,
|
|
30
|
+
"%MODEL_DEFAULT%": MODEL_DEFAULT,
|
|
31
|
+
}
|
|
32
|
+
for placeholder, value in placeholders.items():
|
|
33
|
+
if placeholder in text:
|
|
34
|
+
text = text.replace(placeholder, value)
|
|
35
|
+
return text
|
|
@@ -0,0 +1,12 @@
|
|
|
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
|
+
from .remote_store import *
|
|
@@ -0,0 +1,11 @@
|
|
|
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
|
+
from .store import *
|
|
@@ -0,0 +1,224 @@
|
|
|
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
|
+
from typing import Optional, List, Dict, Union
|
|
13
|
+
|
|
14
|
+
from packaging.version import Version
|
|
15
|
+
|
|
16
|
+
from pygpt_net.item.assistant import AssistantItem
|
|
17
|
+
from pygpt_net.item.store import RemoteFileItem
|
|
18
|
+
from pygpt_net.provider.core.remote_file.db_sqlite import DbSqliteProvider
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class Files:
|
|
22
|
+
|
|
23
|
+
PROVIDER_NAME = "google"
|
|
24
|
+
|
|
25
|
+
def __init__(self, window=None):
|
|
26
|
+
"""
|
|
27
|
+
Google remote files core (File Search documents)
|
|
28
|
+
|
|
29
|
+
:param window: Window instance
|
|
30
|
+
"""
|
|
31
|
+
self.window = window
|
|
32
|
+
self.provider = DbSqliteProvider(window)
|
|
33
|
+
self.items = {}
|
|
34
|
+
|
|
35
|
+
def install(self):
|
|
36
|
+
self.provider.install()
|
|
37
|
+
|
|
38
|
+
def patch(self, app_version: Version) -> bool:
|
|
39
|
+
return self.provider.patch(app_version)
|
|
40
|
+
|
|
41
|
+
def get(self, id: str) -> Optional[RemoteFileItem]:
|
|
42
|
+
if id in self.items:
|
|
43
|
+
return self.items[id]
|
|
44
|
+
return None
|
|
45
|
+
|
|
46
|
+
def get_ids(self) -> List[str]:
|
|
47
|
+
return list(self.items.keys())
|
|
48
|
+
|
|
49
|
+
def get_all(self) -> Dict[str, RemoteFileItem]:
|
|
50
|
+
return self.items
|
|
51
|
+
|
|
52
|
+
def get_id_by_idx_all(self, idx: int) -> str:
|
|
53
|
+
return list(self.items.keys())[idx]
|
|
54
|
+
|
|
55
|
+
def get_by_idx(self, idx: int) -> str:
|
|
56
|
+
items = self.items
|
|
57
|
+
return list(items.keys())[idx]
|
|
58
|
+
|
|
59
|
+
def has(self, id: str) -> bool:
|
|
60
|
+
return id in self.items
|
|
61
|
+
|
|
62
|
+
def create(
|
|
63
|
+
self,
|
|
64
|
+
assistant: AssistantItem,
|
|
65
|
+
thread_id: str,
|
|
66
|
+
file_id: str,
|
|
67
|
+
name: str,
|
|
68
|
+
path: str,
|
|
69
|
+
size: int) -> Optional[RemoteFileItem]:
|
|
70
|
+
"""
|
|
71
|
+
Not used in Google path (kept for parity). Use insert() instead.
|
|
72
|
+
"""
|
|
73
|
+
file = RemoteFileItem()
|
|
74
|
+
file.id = file_id
|
|
75
|
+
file.file_id = file_id
|
|
76
|
+
file.thread_id = thread_id
|
|
77
|
+
file.provider = self.PROVIDER_NAME
|
|
78
|
+
file.name = name
|
|
79
|
+
file.path = path
|
|
80
|
+
file.size = size
|
|
81
|
+
if assistant.vector_store is not None and assistant.vector_store != "":
|
|
82
|
+
file.store_id = assistant.vector_store
|
|
83
|
+
file.record_id = self.provider.create(file)
|
|
84
|
+
self.items[file.id] = file
|
|
85
|
+
return file
|
|
86
|
+
|
|
87
|
+
def update(self, file: RemoteFileItem) -> Optional[RemoteFileItem]:
|
|
88
|
+
self.items[file.id] = file
|
|
89
|
+
self.provider.save(file)
|
|
90
|
+
return file
|
|
91
|
+
|
|
92
|
+
def get_names(self) -> Dict[str, str]:
|
|
93
|
+
names = {}
|
|
94
|
+
for id in self.items:
|
|
95
|
+
file = self.items[id]
|
|
96
|
+
names[id] = file.name
|
|
97
|
+
return names
|
|
98
|
+
|
|
99
|
+
def get_by_store_or_thread(self, store_id: str, thread_id: str) -> Dict[str, RemoteFileItem]:
|
|
100
|
+
return self.provider.get_by_store_or_thread(store_id, thread_id)
|
|
101
|
+
|
|
102
|
+
def count_by_store_or_thread(self, store_id: str, thread_id: str) -> int:
|
|
103
|
+
return self.provider.count_by_store_or_thread(store_id, thread_id)
|
|
104
|
+
|
|
105
|
+
def get_file_by_idx(self, idx: int, store_id: str, thread_id: str) -> Optional[RemoteFileItem]:
|
|
106
|
+
files = self.get_by_store_or_thread(store_id, thread_id)
|
|
107
|
+
if idx >= len(files):
|
|
108
|
+
return None
|
|
109
|
+
return list(files.values())[idx]
|
|
110
|
+
|
|
111
|
+
def get_file_id_by_idx(self, idx: int, store_id: str, thread_id: str) -> Optional[str]:
|
|
112
|
+
file = self.get_file_by_idx(idx, store_id, thread_id)
|
|
113
|
+
if file is None:
|
|
114
|
+
return None
|
|
115
|
+
return file.file_id
|
|
116
|
+
|
|
117
|
+
def get_all_by_file_id(self, file_id: str) -> dict:
|
|
118
|
+
return self.provider.get_all_by_file_id(file_id)
|
|
119
|
+
|
|
120
|
+
def delete(self, file: Union[RemoteFileItem, list]) -> bool:
|
|
121
|
+
files = file if isinstance(file, list) else [file]
|
|
122
|
+
for f in files:
|
|
123
|
+
file_id = f.file_id # document name (fileSearchStores/.../documents/..)
|
|
124
|
+
items = self.get_all_by_file_id(file_id)
|
|
125
|
+
for id in items:
|
|
126
|
+
store_id = items[id].store_id
|
|
127
|
+
if store_id is None or store_id == "":
|
|
128
|
+
continue
|
|
129
|
+
try:
|
|
130
|
+
self.window.core.api.google.store.delete_store_file(store_id, file_id)
|
|
131
|
+
except Exception as e:
|
|
132
|
+
self.window.core.debug.log("Failed to delete document from store: " + str(e))
|
|
133
|
+
self.provider.delete_by_id(f.record_id)
|
|
134
|
+
try:
|
|
135
|
+
# There is no Files API deletion here; this is store document
|
|
136
|
+
pass
|
|
137
|
+
except Exception as e:
|
|
138
|
+
self.window.core.debug.log("Failed to delete remote document: " + str(e))
|
|
139
|
+
if f.record_id in self.items:
|
|
140
|
+
del self.items[f.record_id]
|
|
141
|
+
return True
|
|
142
|
+
|
|
143
|
+
def delete_by_file_id(self, file_id: str) -> bool:
|
|
144
|
+
res = self.provider.delete_by_file_id(file_id)
|
|
145
|
+
if res:
|
|
146
|
+
to_delete = []
|
|
147
|
+
for id in self.items:
|
|
148
|
+
if self.items[id].file_id == file_id:
|
|
149
|
+
to_delete.append(id)
|
|
150
|
+
for id in to_delete:
|
|
151
|
+
del self.items[id]
|
|
152
|
+
return res
|
|
153
|
+
|
|
154
|
+
def on_store_deleted(self, store_id: str):
|
|
155
|
+
self.provider.clear_store_from_files(store_id)
|
|
156
|
+
|
|
157
|
+
def on_all_stores_deleted(self):
|
|
158
|
+
self.provider.clear_all_stores_from_files(self.PROVIDER_NAME)
|
|
159
|
+
|
|
160
|
+
def rename(self, record_id: int, name: str) -> bool:
|
|
161
|
+
self.provider.rename_file(record_id, name)
|
|
162
|
+
if record_id in self.items:
|
|
163
|
+
self.items[record_id].name = name
|
|
164
|
+
return True
|
|
165
|
+
|
|
166
|
+
def truncate(self, store_id: Optional[str] = None) -> bool:
|
|
167
|
+
if store_id is not None:
|
|
168
|
+
self.window.core.api.google.store.remove_from_store(store_id)
|
|
169
|
+
else:
|
|
170
|
+
self.window.core.api.google.store.remove_from_stores()
|
|
171
|
+
return self.truncate_local(store_id)
|
|
172
|
+
|
|
173
|
+
def truncate_local(self, store_id: Optional[str] = None) -> bool:
|
|
174
|
+
if store_id is not None:
|
|
175
|
+
self.provider.truncate_by_store(store_id)
|
|
176
|
+
else:
|
|
177
|
+
self.provider.truncate_all(self.PROVIDER_NAME)
|
|
178
|
+
self.items = {}
|
|
179
|
+
return True
|
|
180
|
+
|
|
181
|
+
def import_from_store(self, store_id: str) -> bool:
|
|
182
|
+
files = self.window.core.api.google.store.import_store_files(store_id)
|
|
183
|
+
for file in files:
|
|
184
|
+
# Not used in this path
|
|
185
|
+
pass
|
|
186
|
+
return True
|
|
187
|
+
|
|
188
|
+
def insert(self, store_id: str, data) -> RemoteFileItem:
|
|
189
|
+
"""
|
|
190
|
+
Insert a Document into local DB
|
|
191
|
+
|
|
192
|
+
:param store_id: store name ('fileSearchStores/...').
|
|
193
|
+
:param data: document object from API
|
|
194
|
+
"""
|
|
195
|
+
file = RemoteFileItem()
|
|
196
|
+
# Use document name as unique id and file_id
|
|
197
|
+
file.id = getattr(data, "name", None)
|
|
198
|
+
file.file_id = getattr(data, "name", None)
|
|
199
|
+
file.thread_id = ""
|
|
200
|
+
file.name = getattr(data, "display_name", None) or getattr(data, "name", "")
|
|
201
|
+
file.provider = self.PROVIDER_NAME
|
|
202
|
+
file.path = getattr(data, "display_name", None) or getattr(data, "name", "")
|
|
203
|
+
# sizeBytes is a string (int64), convert if possible
|
|
204
|
+
try:
|
|
205
|
+
file.size = int(getattr(data, "size_bytes", 0) or 0)
|
|
206
|
+
except Exception:
|
|
207
|
+
file.size = 0
|
|
208
|
+
file.store_id = store_id
|
|
209
|
+
file.record_id = self.provider.create(file)
|
|
210
|
+
self.items[file.id] = file
|
|
211
|
+
return file
|
|
212
|
+
|
|
213
|
+
def load(self):
|
|
214
|
+
self.items = self.provider.load_all(self.PROVIDER_NAME)
|
|
215
|
+
self.sort_items()
|
|
216
|
+
|
|
217
|
+
def sort_items(self):
|
|
218
|
+
return
|
|
219
|
+
|
|
220
|
+
def save(self):
|
|
221
|
+
self.provider.save_all(self.items)
|
|
222
|
+
|
|
223
|
+
def get_version(self) -> str:
|
|
224
|
+
return self.provider.get_version()
|
|
@@ -0,0 +1,248 @@
|
|
|
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 datetime
|
|
13
|
+
from typing import Optional, List, Dict, Any
|
|
14
|
+
|
|
15
|
+
from packaging.version import Version
|
|
16
|
+
|
|
17
|
+
from pygpt_net.item.store import RemoteStoreItem
|
|
18
|
+
from pygpt_net.provider.core.remote_store.db_sqlite import DbSqliteProvider
|
|
19
|
+
|
|
20
|
+
from .files import Files
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Store:
|
|
24
|
+
|
|
25
|
+
PROVIDER_NAME = "google"
|
|
26
|
+
|
|
27
|
+
def __init__(self, window=None):
|
|
28
|
+
"""
|
|
29
|
+
Google File Search store core
|
|
30
|
+
|
|
31
|
+
:param window: Window instance
|
|
32
|
+
"""
|
|
33
|
+
self.window = window
|
|
34
|
+
self.provider = DbSqliteProvider(window)
|
|
35
|
+
self.files = Files(window)
|
|
36
|
+
self.items = {}
|
|
37
|
+
|
|
38
|
+
def install(self):
|
|
39
|
+
"""Install provider data"""
|
|
40
|
+
self.provider.install()
|
|
41
|
+
self.files.install()
|
|
42
|
+
|
|
43
|
+
def patch(self, app_version: Version) -> bool:
|
|
44
|
+
"""Patch provider data"""
|
|
45
|
+
res1 = self.files.patch(app_version)
|
|
46
|
+
res2 = self.provider.patch(app_version)
|
|
47
|
+
return res1 or res2
|
|
48
|
+
|
|
49
|
+
def get(self, id: str) -> RemoteStoreItem:
|
|
50
|
+
if id in self.items:
|
|
51
|
+
return self.items[id]
|
|
52
|
+
|
|
53
|
+
def get_ids(self) -> List[str]:
|
|
54
|
+
return list(self.items.keys())
|
|
55
|
+
|
|
56
|
+
def get_all(self) -> Dict[str, RemoteStoreItem]:
|
|
57
|
+
return self.items
|
|
58
|
+
|
|
59
|
+
def get_id_by_idx_all(self, idx: int) -> str:
|
|
60
|
+
return list(self.items.keys())[idx]
|
|
61
|
+
|
|
62
|
+
def get_by_idx(self, idx: int) -> str:
|
|
63
|
+
items = self.items
|
|
64
|
+
return list(items.keys())[idx]
|
|
65
|
+
|
|
66
|
+
def has(self, id: str) -> bool:
|
|
67
|
+
return id in self.items
|
|
68
|
+
|
|
69
|
+
def create(self, name: Optional[str] = None) -> Optional[RemoteStoreItem]:
|
|
70
|
+
"""
|
|
71
|
+
Create new File Search store
|
|
72
|
+
|
|
73
|
+
:param name: display name for the store (also used as local alias)
|
|
74
|
+
:return: store item
|
|
75
|
+
"""
|
|
76
|
+
# Use provided name or fallback
|
|
77
|
+
display_name = name or "New file search store"
|
|
78
|
+
|
|
79
|
+
# Create remote store with a display name
|
|
80
|
+
vector_store = self.window.core.api.google.store.create_store(display_name, 0)
|
|
81
|
+
if vector_store is None:
|
|
82
|
+
return None
|
|
83
|
+
|
|
84
|
+
# Build local item; set local alias to the provided name
|
|
85
|
+
store = RemoteStoreItem()
|
|
86
|
+
store.id = getattr(vector_store, "name", None) # 'fileSearchStores/...'
|
|
87
|
+
# Keep local alias as the passed name; fallback to remote display_name if missing
|
|
88
|
+
store.name = display_name or (getattr(vector_store, "display_name", None) or "")
|
|
89
|
+
store.provider = self.PROVIDER_NAME
|
|
90
|
+
store.is_thread = False
|
|
91
|
+
store.record_id = self.provider.create(store)
|
|
92
|
+
self.items[store.id] = store
|
|
93
|
+
return store
|
|
94
|
+
|
|
95
|
+
def update(self, store: RemoteStoreItem) -> Optional[RemoteStoreItem]:
|
|
96
|
+
"""
|
|
97
|
+
Update store (local persist; remote update is not supported by Google)
|
|
98
|
+
"""
|
|
99
|
+
vector_store = self.window.core.api.google.store.update_store(store.id, store.name, store.expire_days)
|
|
100
|
+
# Always persist local metadata (alias)
|
|
101
|
+
self.items[store.id] = store
|
|
102
|
+
self.provider.save(store)
|
|
103
|
+
return store
|
|
104
|
+
|
|
105
|
+
def get_status_data(self, id: str):
|
|
106
|
+
"""
|
|
107
|
+
Get store status data
|
|
108
|
+
|
|
109
|
+
:param id: store id
|
|
110
|
+
:return: status data, store data
|
|
111
|
+
"""
|
|
112
|
+
status = {}
|
|
113
|
+
data = self.window.core.api.google.store.get_store(id)
|
|
114
|
+
if data is not None:
|
|
115
|
+
status = self.parse_status(data)
|
|
116
|
+
return status, data
|
|
117
|
+
|
|
118
|
+
def parse_status(self, store) -> Dict[str, Any]:
|
|
119
|
+
"""
|
|
120
|
+
Map Google File Search store status to local fields
|
|
121
|
+
"""
|
|
122
|
+
def _to_int(x):
|
|
123
|
+
try:
|
|
124
|
+
return int(x or 0)
|
|
125
|
+
except Exception:
|
|
126
|
+
return 0
|
|
127
|
+
|
|
128
|
+
active = _to_int(getattr(store, "active_documents_count", 0))
|
|
129
|
+
pending = _to_int(getattr(store, "pending_documents_count", 0))
|
|
130
|
+
failed = _to_int(getattr(store, "failed_documents_count", 0))
|
|
131
|
+
size_bytes = _to_int(getattr(store, "size_bytes", 0))
|
|
132
|
+
update_time = getattr(store, "update_time", None)
|
|
133
|
+
display_name = getattr(store, "display_name", None) or ""
|
|
134
|
+
|
|
135
|
+
status_str = "ready"
|
|
136
|
+
if pending > 0:
|
|
137
|
+
status_str = "indexing"
|
|
138
|
+
if failed > 0 and active == 0 and pending == 0:
|
|
139
|
+
status_str = "failed"
|
|
140
|
+
|
|
141
|
+
status = {
|
|
142
|
+
"status": status_str,
|
|
143
|
+
"usage_bytes": size_bytes,
|
|
144
|
+
"expires_at": None,
|
|
145
|
+
"last_active_at": self._parse_rfc3339_to_epoch(update_time) if update_time else None,
|
|
146
|
+
"file_counts": {
|
|
147
|
+
"in_progress": pending,
|
|
148
|
+
"completed": active,
|
|
149
|
+
"cancelled": 0,
|
|
150
|
+
"failed": failed,
|
|
151
|
+
"total": active + pending + failed,
|
|
152
|
+
},
|
|
153
|
+
"expires_after": None,
|
|
154
|
+
"remote_display_name": display_name,
|
|
155
|
+
}
|
|
156
|
+
return status
|
|
157
|
+
|
|
158
|
+
def _parse_rfc3339_to_epoch(self, s: str) -> int:
|
|
159
|
+
"""Convert RFC3339 to epoch seconds if possible"""
|
|
160
|
+
try:
|
|
161
|
+
s2 = s.replace('Z', '+00:00')
|
|
162
|
+
ts = datetime.datetime.fromisoformat(s2)
|
|
163
|
+
return int(ts.timestamp())
|
|
164
|
+
except Exception:
|
|
165
|
+
return int(datetime.datetime.now().timestamp())
|
|
166
|
+
|
|
167
|
+
def update_status(self, id: str):
|
|
168
|
+
"""
|
|
169
|
+
Update store status and keep local alias if set
|
|
170
|
+
"""
|
|
171
|
+
store = self.items[id]
|
|
172
|
+
status, data = self.get_status_data(id)
|
|
173
|
+
remote_name = getattr(data, "display_name", None) if data else None
|
|
174
|
+
# Keep local alias if user set it; otherwise adopt remote display name
|
|
175
|
+
if not store.name or store.name.strip() == "":
|
|
176
|
+
store.name = remote_name or ""
|
|
177
|
+
store.provider = self.PROVIDER_NAME
|
|
178
|
+
self.append_status(store, status)
|
|
179
|
+
self.update(store)
|
|
180
|
+
|
|
181
|
+
def append_status(self, store: RemoteStoreItem, status: Dict[str, Any]):
|
|
182
|
+
now = datetime.datetime.now()
|
|
183
|
+
ts = int(now.timestamp())
|
|
184
|
+
status["__last_refresh__"] = now.strftime("%Y-%m-%d %H:%M:%S")
|
|
185
|
+
store.status = status
|
|
186
|
+
store.last_sync = ts
|
|
187
|
+
if "status" in status:
|
|
188
|
+
store.last_status = status["status"]
|
|
189
|
+
if "usage_bytes" in status:
|
|
190
|
+
store.usage_bytes = status["usage_bytes"]
|
|
191
|
+
if "file_counts" in status:
|
|
192
|
+
store.num_files = status["file_counts"]["total"]
|
|
193
|
+
if "last_active_at" in status and status["last_active_at"]:
|
|
194
|
+
store.last_active = int(status["last_active_at"])
|
|
195
|
+
|
|
196
|
+
def get_names(self) -> Dict[str, str]:
|
|
197
|
+
names = {}
|
|
198
|
+
for id in self.items:
|
|
199
|
+
store = self.items[id]
|
|
200
|
+
names[id] = store.name
|
|
201
|
+
return names
|
|
202
|
+
|
|
203
|
+
def delete(self, id: str) -> bool:
|
|
204
|
+
if id in self.items:
|
|
205
|
+
store = self.items[id]
|
|
206
|
+
self.provider.delete_by_id(store.record_id)
|
|
207
|
+
self.window.core.api.google.store.remove_store(id)
|
|
208
|
+
del self.items[id]
|
|
209
|
+
return True
|
|
210
|
+
return False
|
|
211
|
+
|
|
212
|
+
def import_items(self, items: Dict[str, RemoteStoreItem]):
|
|
213
|
+
self.items = items
|
|
214
|
+
for item in items.values():
|
|
215
|
+
item.provider = self.PROVIDER_NAME
|
|
216
|
+
item.record_id = self.provider.create(item)
|
|
217
|
+
|
|
218
|
+
def clear(self):
|
|
219
|
+
self.truncate()
|
|
220
|
+
|
|
221
|
+
def is_hidden(self, id: str) -> bool:
|
|
222
|
+
if id in self.items:
|
|
223
|
+
if (self.window.core.config.get("remote_store.google.hide_threads")
|
|
224
|
+
and (self.items[id].name is None or self.items[id].name == "")):
|
|
225
|
+
return True
|
|
226
|
+
return False
|
|
227
|
+
|
|
228
|
+
def truncate(self) -> bool:
|
|
229
|
+
self.provider.truncate(self.PROVIDER_NAME)
|
|
230
|
+
self.items = {}
|
|
231
|
+
return True
|
|
232
|
+
|
|
233
|
+
def load(self):
|
|
234
|
+
self.items = self.provider.load_all(self.PROVIDER_NAME)
|
|
235
|
+
self.sort_items()
|
|
236
|
+
|
|
237
|
+
def load_all(self):
|
|
238
|
+
self.load()
|
|
239
|
+
self.files.load()
|
|
240
|
+
|
|
241
|
+
def sort_items(self):
|
|
242
|
+
pass
|
|
243
|
+
|
|
244
|
+
def save(self):
|
|
245
|
+
self.provider.save_all(self.items)
|
|
246
|
+
|
|
247
|
+
def get_version(self) -> str:
|
|
248
|
+
return self.provider.get_version()
|
|
@@ -0,0 +1,11 @@
|
|
|
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
|
+
from .store import *
|