pygpt-net 2.7.6__py3-none-any.whl → 2.7.8__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 +13 -0
- pygpt_net/__init__.py +3 -3
- pygpt_net/app.py +5 -1
- pygpt_net/controller/assistant/batch.py +2 -2
- pygpt_net/controller/assistant/files.py +7 -6
- pygpt_net/controller/assistant/threads.py +0 -0
- pygpt_net/controller/chat/command.py +0 -0
- pygpt_net/controller/chat/remote_tools.py +3 -9
- pygpt_net/controller/chat/stream.py +2 -2
- pygpt_net/controller/chat/{handler/worker.py → stream_worker.py} +13 -35
- pygpt_net/controller/dialogs/confirm.py +35 -58
- pygpt_net/controller/lang/mapping.py +9 -9
- pygpt_net/controller/remote_store/{google/batch.py → batch.py} +209 -252
- pygpt_net/controller/remote_store/remote_store.py +982 -13
- pygpt_net/core/command/command.py +0 -0
- pygpt_net/core/db/viewer.py +1 -1
- pygpt_net/core/debug/models.py +2 -2
- pygpt_net/core/realtime/worker.py +3 -1
- pygpt_net/{controller/remote_store/google → core/remote_store/anthropic}/__init__.py +0 -1
- pygpt_net/core/remote_store/anthropic/files.py +211 -0
- pygpt_net/core/remote_store/anthropic/store.py +208 -0
- pygpt_net/core/remote_store/openai/store.py +5 -4
- pygpt_net/core/remote_store/remote_store.py +5 -1
- pygpt_net/{controller/remote_store/openai → core/remote_store/xai}/__init__.py +0 -1
- pygpt_net/core/remote_store/xai/files.py +225 -0
- pygpt_net/core/remote_store/xai/store.py +219 -0
- pygpt_net/data/config/config.json +18 -5
- pygpt_net/data/config/models.json +193 -4
- pygpt_net/data/config/settings.json +179 -36
- pygpt_net/data/icons/folder_eye.svg +1 -0
- pygpt_net/data/icons/folder_eye_filled.svg +1 -0
- pygpt_net/data/icons/folder_open.svg +1 -0
- pygpt_net/data/icons/folder_open_filled.svg +1 -0
- pygpt_net/data/locale/locale.de.ini +6 -3
- pygpt_net/data/locale/locale.en.ini +46 -12
- pygpt_net/data/locale/locale.es.ini +6 -3
- pygpt_net/data/locale/locale.fr.ini +6 -3
- pygpt_net/data/locale/locale.it.ini +6 -3
- pygpt_net/data/locale/locale.pl.ini +7 -4
- pygpt_net/data/locale/locale.uk.ini +6 -3
- pygpt_net/data/locale/locale.zh.ini +6 -3
- pygpt_net/icons.qrc +4 -0
- pygpt_net/icons_rc.py +282 -138
- pygpt_net/plugin/cmd_mouse_control/worker.py +2 -1
- pygpt_net/plugin/cmd_mouse_control/worker_sandbox.py +2 -1
- pygpt_net/provider/api/anthropic/__init__.py +10 -3
- pygpt_net/provider/api/anthropic/chat.py +342 -11
- pygpt_net/provider/api/anthropic/computer.py +844 -0
- pygpt_net/provider/api/anthropic/remote_tools.py +172 -0
- pygpt_net/provider/api/anthropic/store.py +307 -0
- pygpt_net/{controller/chat/handler/anthropic_stream.py → provider/api/anthropic/stream.py} +99 -10
- pygpt_net/provider/api/anthropic/tools.py +32 -77
- pygpt_net/provider/api/anthropic/utils.py +30 -0
- pygpt_net/{controller/chat/handler → provider/api/anthropic/worker}/__init__.py +0 -0
- pygpt_net/provider/api/anthropic/worker/importer.py +278 -0
- pygpt_net/provider/api/google/chat.py +62 -9
- pygpt_net/provider/api/google/store.py +124 -3
- pygpt_net/{controller/chat/handler/google_stream.py → provider/api/google/stream.py} +92 -25
- pygpt_net/provider/api/google/utils.py +185 -0
- pygpt_net/provider/api/google/worker/importer.py +16 -28
- pygpt_net/provider/api/langchain/__init__.py +0 -0
- pygpt_net/{controller/chat/handler/langchain_stream.py → provider/api/langchain/stream.py} +1 -1
- pygpt_net/provider/api/llama_index/__init__.py +0 -0
- pygpt_net/{controller/chat/handler/llamaindex_stream.py → provider/api/llama_index/stream.py} +1 -1
- pygpt_net/provider/api/openai/assistants.py +2 -2
- pygpt_net/provider/api/openai/image.py +2 -2
- pygpt_net/provider/api/openai/store.py +4 -1
- pygpt_net/{controller/chat/handler/openai_stream.py → provider/api/openai/stream.py} +1 -1
- pygpt_net/provider/api/openai/utils.py +69 -3
- pygpt_net/provider/api/openai/worker/importer.py +19 -61
- pygpt_net/provider/api/openai/worker/importer_assistants.py +230 -0
- pygpt_net/provider/api/x_ai/__init__.py +138 -15
- pygpt_net/provider/api/x_ai/audio.py +43 -11
- pygpt_net/provider/api/x_ai/chat.py +92 -4
- pygpt_net/provider/api/x_ai/image.py +149 -47
- pygpt_net/provider/api/x_ai/realtime/__init__.py +12 -0
- pygpt_net/provider/api/x_ai/realtime/client.py +1825 -0
- pygpt_net/provider/api/x_ai/realtime/realtime.py +198 -0
- pygpt_net/provider/api/x_ai/{remote.py → remote_tools.py} +183 -70
- pygpt_net/provider/api/x_ai/responses.py +507 -0
- pygpt_net/provider/api/x_ai/store.py +610 -0
- pygpt_net/{controller/chat/handler/xai_stream.py → provider/api/x_ai/stream.py} +42 -10
- pygpt_net/provider/api/x_ai/tools.py +59 -8
- pygpt_net/{controller/chat/handler → provider/api/x_ai}/utils.py +1 -2
- pygpt_net/provider/api/x_ai/vision.py +1 -4
- pygpt_net/provider/api/x_ai/worker/importer.py +308 -0
- pygpt_net/provider/audio_input/xai_grok_voice.py +390 -0
- pygpt_net/provider/audio_output/xai_tts.py +325 -0
- pygpt_net/provider/core/config/patch.py +39 -3
- pygpt_net/provider/core/config/patches/patch_before_2_6_42.py +2 -2
- pygpt_net/provider/core/model/patch.py +39 -1
- pygpt_net/tools/image_viewer/tool.py +334 -34
- pygpt_net/tools/image_viewer/ui/dialogs.py +319 -22
- pygpt_net/tools/text_editor/ui/dialogs.py +3 -2
- pygpt_net/tools/text_editor/ui/widgets.py +0 -0
- pygpt_net/ui/dialog/assistant.py +1 -1
- pygpt_net/ui/dialog/plugins.py +13 -5
- pygpt_net/ui/dialog/remote_store.py +552 -0
- pygpt_net/ui/dialogs.py +3 -5
- pygpt_net/ui/layout/ctx/ctx_list.py +58 -7
- pygpt_net/ui/menu/tools.py +6 -13
- pygpt_net/ui/widget/dialog/base.py +16 -5
- pygpt_net/ui/widget/dialog/{remote_store_google.py → remote_store.py} +10 -10
- pygpt_net/ui/widget/element/button.py +4 -4
- pygpt_net/ui/widget/image/display.py +2 -2
- pygpt_net/ui/widget/lists/context.py +2 -2
- pygpt_net/ui/widget/textarea/editor.py +0 -0
- {pygpt_net-2.7.6.dist-info → pygpt_net-2.7.8.dist-info}/METADATA +15 -2
- {pygpt_net-2.7.6.dist-info → pygpt_net-2.7.8.dist-info}/RECORD +107 -89
- pygpt_net/controller/remote_store/google/store.py +0 -615
- pygpt_net/controller/remote_store/openai/batch.py +0 -524
- pygpt_net/controller/remote_store/openai/store.py +0 -699
- pygpt_net/ui/dialog/remote_store_google.py +0 -539
- pygpt_net/ui/dialog/remote_store_openai.py +0 -539
- pygpt_net/ui/widget/dialog/remote_store_openai.py +0 -56
- pygpt_net/ui/widget/lists/remote_store_google.py +0 -248
- pygpt_net/ui/widget/lists/remote_store_openai.py +0 -317
- {pygpt_net-2.7.6.dist-info → pygpt_net-2.7.8.dist-info}/LICENSE +0 -0
- {pygpt_net-2.7.6.dist-info → pygpt_net-2.7.8.dist-info}/WHEEL +0 -0
- {pygpt_net-2.7.6.dist-info → pygpt_net-2.7.8.dist-info}/entry_points.txt +0 -0
|
File without changes
|
pygpt_net/core/db/viewer.py
CHANGED
pygpt_net/core/debug/models.py
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
7
|
# MIT License #
|
|
8
8
|
# Created By : Marcin Szczygliński #
|
|
9
|
-
# Updated Date:
|
|
9
|
+
# Updated Date: 2026.01.05 20:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import os
|
|
@@ -24,7 +24,7 @@ class ModelsDebug:
|
|
|
24
24
|
def update(self):
|
|
25
25
|
"""Update debug window."""
|
|
26
26
|
debug = self.window.core.debug
|
|
27
|
-
models_controller = self.window.controller.
|
|
27
|
+
models_controller = self.window.controller.model
|
|
28
28
|
models_core = self.window.core.models
|
|
29
29
|
command_core = self.window.core.command
|
|
30
30
|
config_core = self.window.core.config
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
7
|
# MIT License #
|
|
8
8
|
# Created By : Marcin Szczygliński #
|
|
9
|
-
# Updated Date:
|
|
9
|
+
# Updated Date: 2026.01.06 20:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import asyncio
|
|
@@ -63,6 +63,8 @@ class RealtimeWorker(QRunnable):
|
|
|
63
63
|
return self.window.core.api.google.realtime.handler
|
|
64
64
|
elif provider == "openai":
|
|
65
65
|
return self.window.core.api.openai.realtime.handler
|
|
66
|
+
elif provider == "x_ai":
|
|
67
|
+
return self.window.core.api.xai.realtime.handler
|
|
66
68
|
else:
|
|
67
69
|
raise RuntimeError(f"Unsupported realtime provider: {provider}")
|
|
68
70
|
|
|
@@ -0,0 +1,211 @@
|
|
|
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.05 17: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 = "anthropic"
|
|
24
|
+
DEFAULT_STORE_ID = "files"
|
|
25
|
+
|
|
26
|
+
def __init__(self, window=None):
|
|
27
|
+
"""
|
|
28
|
+
Anthropic remote files core (workspace Files API)
|
|
29
|
+
|
|
30
|
+
:param window: Window instance
|
|
31
|
+
"""
|
|
32
|
+
self.window = window
|
|
33
|
+
self.provider = DbSqliteProvider(window)
|
|
34
|
+
self.items: Dict[str, RemoteFileItem] = {}
|
|
35
|
+
|
|
36
|
+
def install(self):
|
|
37
|
+
self.provider.install()
|
|
38
|
+
|
|
39
|
+
def patch(self, app_version: Version) -> bool:
|
|
40
|
+
return self.provider.patch(app_version)
|
|
41
|
+
|
|
42
|
+
def get(self, id: str) -> Optional[RemoteFileItem]:
|
|
43
|
+
if id in self.items:
|
|
44
|
+
return self.items[id]
|
|
45
|
+
return None
|
|
46
|
+
|
|
47
|
+
def get_ids(self) -> List[str]:
|
|
48
|
+
return list(self.items.keys())
|
|
49
|
+
|
|
50
|
+
def get_all(self) -> Dict[str, RemoteFileItem]:
|
|
51
|
+
return self.items
|
|
52
|
+
|
|
53
|
+
def get_id_by_idx_all(self, idx: int) -> str:
|
|
54
|
+
return list(self.items.keys())[idx]
|
|
55
|
+
|
|
56
|
+
def get_by_idx(self, idx: int) -> str:
|
|
57
|
+
items = self.items
|
|
58
|
+
return list(items.keys())[idx]
|
|
59
|
+
|
|
60
|
+
def has(self, id: str) -> bool:
|
|
61
|
+
return id in self.items
|
|
62
|
+
|
|
63
|
+
def create(
|
|
64
|
+
self,
|
|
65
|
+
assistant: AssistantItem,
|
|
66
|
+
thread_id: str,
|
|
67
|
+
file_id: str,
|
|
68
|
+
name: str,
|
|
69
|
+
path: str,
|
|
70
|
+
size: int) -> Optional[RemoteFileItem]:
|
|
71
|
+
"""
|
|
72
|
+
Not used in the Anthropic path (kept for parity).
|
|
73
|
+
"""
|
|
74
|
+
file = RemoteFileItem()
|
|
75
|
+
file.id = file_id
|
|
76
|
+
file.file_id = file_id
|
|
77
|
+
file.thread_id = thread_id or ""
|
|
78
|
+
file.provider = self.PROVIDER_NAME
|
|
79
|
+
file.name = name
|
|
80
|
+
file.path = path
|
|
81
|
+
file.size = size or 0
|
|
82
|
+
file.store_id = self.DEFAULT_STORE_ID
|
|
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
|
|
124
|
+
try:
|
|
125
|
+
self.window.core.api.anthropic.store.remove_file(file_id)
|
|
126
|
+
except Exception as e:
|
|
127
|
+
self.window.core.debug.log("Failed to delete remote file: " + str(e))
|
|
128
|
+
self.provider.delete_by_id(f.record_id)
|
|
129
|
+
if f.record_id in self.items:
|
|
130
|
+
del self.items[f.record_id]
|
|
131
|
+
return True
|
|
132
|
+
|
|
133
|
+
def delete_by_file_id(self, file_id: str) -> bool:
|
|
134
|
+
res = self.provider.delete_by_file_id(file_id)
|
|
135
|
+
if res:
|
|
136
|
+
to_delete = []
|
|
137
|
+
for id in self.items:
|
|
138
|
+
if self.items[id].file_id == file_id:
|
|
139
|
+
to_delete.append(id)
|
|
140
|
+
for id in to_delete:
|
|
141
|
+
del self.items[id]
|
|
142
|
+
return res
|
|
143
|
+
|
|
144
|
+
def on_store_deleted(self, store_id: str):
|
|
145
|
+
self.provider.clear_store_from_files(store_id)
|
|
146
|
+
|
|
147
|
+
def on_all_stores_deleted(self):
|
|
148
|
+
self.provider.clear_all_stores_from_files(self.PROVIDER_NAME)
|
|
149
|
+
|
|
150
|
+
def rename(self, record_id: int, name: str) -> bool:
|
|
151
|
+
self.provider.rename_file(record_id, name)
|
|
152
|
+
if record_id in self.items:
|
|
153
|
+
self.items[record_id].name = name
|
|
154
|
+
return True
|
|
155
|
+
|
|
156
|
+
def truncate(self, store_id: Optional[str] = None) -> bool:
|
|
157
|
+
self.window.core.api.anthropic.store.remove_files()
|
|
158
|
+
return self.truncate_local(store_id)
|
|
159
|
+
|
|
160
|
+
def truncate_local(self, store_id: Optional[str] = None) -> bool:
|
|
161
|
+
if store_id is not None:
|
|
162
|
+
self.provider.truncate_by_store(store_id)
|
|
163
|
+
else:
|
|
164
|
+
self.provider.truncate_all(self.PROVIDER_NAME)
|
|
165
|
+
self.items = {}
|
|
166
|
+
return True
|
|
167
|
+
|
|
168
|
+
def import_from_store(self, store_id: str) -> bool:
|
|
169
|
+
files = self.window.core.api.anthropic.store.import_files()
|
|
170
|
+
for f in files:
|
|
171
|
+
pass
|
|
172
|
+
return True
|
|
173
|
+
|
|
174
|
+
def insert(self, store_id: str, data) -> RemoteFileItem:
|
|
175
|
+
"""
|
|
176
|
+
Insert a File into local DB
|
|
177
|
+
|
|
178
|
+
:param store_id: pseudo store id ('files')
|
|
179
|
+
:param data: file object from API
|
|
180
|
+
"""
|
|
181
|
+
file = RemoteFileItem()
|
|
182
|
+
file.id = getattr(data, "id", None) or getattr(data, "name", None)
|
|
183
|
+
file.file_id = getattr(data, "id", None) or getattr(data, "name", None)
|
|
184
|
+
file.thread_id = ""
|
|
185
|
+
file.name = getattr(data, "filename", None) or getattr(data, "name", "") or file.file_id
|
|
186
|
+
file.provider = self.PROVIDER_NAME
|
|
187
|
+
file.path = file.name
|
|
188
|
+
try:
|
|
189
|
+
size = getattr(data, "size_bytes", None)
|
|
190
|
+
if size is None and hasattr(data, "size"):
|
|
191
|
+
size = getattr(data, "size")
|
|
192
|
+
file.size = int(size or 0)
|
|
193
|
+
except Exception:
|
|
194
|
+
file.size = 0
|
|
195
|
+
file.store_id = self.DEFAULT_STORE_ID
|
|
196
|
+
file.record_id = self.provider.create(file)
|
|
197
|
+
self.items[file.id] = file
|
|
198
|
+
return file
|
|
199
|
+
|
|
200
|
+
def load(self):
|
|
201
|
+
self.items = self.provider.load_all(self.PROVIDER_NAME)
|
|
202
|
+
self.sort_items()
|
|
203
|
+
|
|
204
|
+
def sort_items(self):
|
|
205
|
+
return
|
|
206
|
+
|
|
207
|
+
def save(self):
|
|
208
|
+
self.provider.save_all(self.items)
|
|
209
|
+
|
|
210
|
+
def get_version(self) -> str:
|
|
211
|
+
return self.provider.get_version()
|
|
@@ -0,0 +1,208 @@
|
|
|
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.05 17: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
|
+
Anthropic Files API "store" adapter.
|
|
26
|
+
Anthropic does not have vector stores; we expose a single workspace-level pseudo store.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
PROVIDER_NAME = "anthropic"
|
|
30
|
+
DEFAULT_STORE_ID = "files"
|
|
31
|
+
DEFAULT_STORE_NAME = "Files"
|
|
32
|
+
|
|
33
|
+
def __init__(self, window=None):
|
|
34
|
+
"""
|
|
35
|
+
Anthropic Files workspace core
|
|
36
|
+
|
|
37
|
+
:param window: Window instance
|
|
38
|
+
"""
|
|
39
|
+
self.window = window
|
|
40
|
+
self.provider = DbSqliteProvider(window)
|
|
41
|
+
self.files = Files(window)
|
|
42
|
+
self.items: Dict[str, RemoteStoreItem] = {}
|
|
43
|
+
|
|
44
|
+
def install(self):
|
|
45
|
+
"""Install provider data"""
|
|
46
|
+
self.provider.install()
|
|
47
|
+
self.files.install()
|
|
48
|
+
|
|
49
|
+
def patch(self, app_version: Version) -> bool:
|
|
50
|
+
"""Patch provider data"""
|
|
51
|
+
res1 = self.files.patch(app_version)
|
|
52
|
+
res2 = self.provider.patch(app_version)
|
|
53
|
+
return res1 or res2
|
|
54
|
+
|
|
55
|
+
def get(self, id: str) -> RemoteStoreItem:
|
|
56
|
+
if id in self.items:
|
|
57
|
+
return self.items[id]
|
|
58
|
+
|
|
59
|
+
def get_ids(self) -> List[str]:
|
|
60
|
+
return list(self.items.keys())
|
|
61
|
+
|
|
62
|
+
def get_all(self) -> Dict[str, RemoteStoreItem]:
|
|
63
|
+
return self.items
|
|
64
|
+
|
|
65
|
+
def get_id_by_idx_all(self, idx: int) -> str:
|
|
66
|
+
return list(self.items.keys())[idx]
|
|
67
|
+
|
|
68
|
+
def get_by_idx(self, idx: int) -> str:
|
|
69
|
+
items = self.items
|
|
70
|
+
return list(items.keys())[idx]
|
|
71
|
+
|
|
72
|
+
def has(self, id: str) -> bool:
|
|
73
|
+
return id in self.items
|
|
74
|
+
|
|
75
|
+
def _ensure_default(self) -> RemoteStoreItem:
|
|
76
|
+
"""Ensure default pseudo-store exists locally."""
|
|
77
|
+
if self.DEFAULT_STORE_ID in self.items:
|
|
78
|
+
return self.items[self.DEFAULT_STORE_ID]
|
|
79
|
+
store = RemoteStoreItem()
|
|
80
|
+
store.id = self.DEFAULT_STORE_ID
|
|
81
|
+
store.name = self.DEFAULT_STORE_NAME
|
|
82
|
+
store.provider = self.PROVIDER_NAME
|
|
83
|
+
store.is_thread = False
|
|
84
|
+
store.record_id = self.provider.create(store)
|
|
85
|
+
self.items[store.id] = store
|
|
86
|
+
return store
|
|
87
|
+
|
|
88
|
+
def create(self, name: Optional[str] = None) -> Optional[RemoteStoreItem]:
|
|
89
|
+
"""
|
|
90
|
+
Create workspace-level pseudo-store (idempotent).
|
|
91
|
+
|
|
92
|
+
:param name: local alias
|
|
93
|
+
:return: store item
|
|
94
|
+
"""
|
|
95
|
+
store = self._ensure_default()
|
|
96
|
+
if name:
|
|
97
|
+
store.name = name
|
|
98
|
+
self.provider.save(store)
|
|
99
|
+
return store
|
|
100
|
+
|
|
101
|
+
def update(self, store: RemoteStoreItem) -> Optional[RemoteStoreItem]:
|
|
102
|
+
"""
|
|
103
|
+
Update local store metadata (no remote update).
|
|
104
|
+
"""
|
|
105
|
+
self.items[store.id] = store
|
|
106
|
+
self.provider.save(store)
|
|
107
|
+
return store
|
|
108
|
+
|
|
109
|
+
def get_status_data(self, id: str):
|
|
110
|
+
"""
|
|
111
|
+
Compute workspace status from Files API
|
|
112
|
+
"""
|
|
113
|
+
status = {}
|
|
114
|
+
stats = self.window.core.api.anthropic.store.get_files_stats()
|
|
115
|
+
if stats is not None:
|
|
116
|
+
status = {
|
|
117
|
+
"status": "ready",
|
|
118
|
+
"usage_bytes": int(stats.get("total_bytes", 0) or 0),
|
|
119
|
+
"expires_at": None,
|
|
120
|
+
"last_active_at": int(datetime.datetime.now().timestamp()),
|
|
121
|
+
"file_counts": {
|
|
122
|
+
"in_progress": 0,
|
|
123
|
+
"completed": int(stats.get("count", 0) or 0),
|
|
124
|
+
"cancelled": 0,
|
|
125
|
+
"failed": 0,
|
|
126
|
+
"total": int(stats.get("count", 0) or 0),
|
|
127
|
+
},
|
|
128
|
+
"expires_after": None,
|
|
129
|
+
"remote_display_name": self.items.get(id, RemoteStoreItem()).name if id in self.items else self.DEFAULT_STORE_NAME,
|
|
130
|
+
}
|
|
131
|
+
return status, None
|
|
132
|
+
|
|
133
|
+
def update_status(self, id: str):
|
|
134
|
+
"""
|
|
135
|
+
Update pseudo-store status
|
|
136
|
+
"""
|
|
137
|
+
self._ensure_default()
|
|
138
|
+
store = self.items[id]
|
|
139
|
+
status, _ = self.get_status_data(id)
|
|
140
|
+
self.append_status(store, status)
|
|
141
|
+
self.update(store)
|
|
142
|
+
|
|
143
|
+
def append_status(self, store: RemoteStoreItem, status: Dict[str, Any]):
|
|
144
|
+
now = datetime.datetime.now()
|
|
145
|
+
ts = int(now.timestamp())
|
|
146
|
+
status["__last_refresh__"] = now.strftime("%Y-%m-%d %H:%M:%S")
|
|
147
|
+
store.status = status
|
|
148
|
+
store.last_sync = ts
|
|
149
|
+
if "status" in status:
|
|
150
|
+
store.last_status = status["status"]
|
|
151
|
+
if "usage_bytes" in status:
|
|
152
|
+
store.usage_bytes = status["usage_bytes"]
|
|
153
|
+
if "file_counts" in status:
|
|
154
|
+
store.num_files = status["file_counts"]["total"]
|
|
155
|
+
if "last_active_at" in status and status["last_active_at"]:
|
|
156
|
+
store.last_active = int(status["last_active_at"])
|
|
157
|
+
|
|
158
|
+
def get_names(self) -> Dict[str, str]:
|
|
159
|
+
names = {}
|
|
160
|
+
for id in self.items:
|
|
161
|
+
store = self.items[id]
|
|
162
|
+
names[id] = store.name
|
|
163
|
+
return names
|
|
164
|
+
|
|
165
|
+
def delete(self, id: str) -> bool:
|
|
166
|
+
if id in self.items:
|
|
167
|
+
store = self.items[id]
|
|
168
|
+
self.provider.delete_by_id(store.record_id)
|
|
169
|
+
del self.items[id]
|
|
170
|
+
return True
|
|
171
|
+
return False
|
|
172
|
+
|
|
173
|
+
def import_items(self, items: Dict[str, RemoteStoreItem]):
|
|
174
|
+
self.items = items
|
|
175
|
+
for item in items.values():
|
|
176
|
+
item.provider = self.PROVIDER_NAME
|
|
177
|
+
item.record_id = self.provider.create(item)
|
|
178
|
+
|
|
179
|
+
def clear(self):
|
|
180
|
+
self.truncate()
|
|
181
|
+
|
|
182
|
+
def is_hidden(self, id: str) -> bool:
|
|
183
|
+
return False
|
|
184
|
+
|
|
185
|
+
def truncate(self) -> bool:
|
|
186
|
+
self.provider.truncate(self.PROVIDER_NAME)
|
|
187
|
+
self.items = {}
|
|
188
|
+
return True
|
|
189
|
+
|
|
190
|
+
def load(self):
|
|
191
|
+
self.items = self.provider.load_all(self.PROVIDER_NAME)
|
|
192
|
+
if self.DEFAULT_STORE_ID not in self.items:
|
|
193
|
+
# Ensure default exists
|
|
194
|
+
self._ensure_default()
|
|
195
|
+
self.sort_items()
|
|
196
|
+
|
|
197
|
+
def load_all(self):
|
|
198
|
+
self.load()
|
|
199
|
+
self.files.load()
|
|
200
|
+
|
|
201
|
+
def sort_items(self):
|
|
202
|
+
pass
|
|
203
|
+
|
|
204
|
+
def save(self):
|
|
205
|
+
self.provider.save_all(self.items)
|
|
206
|
+
|
|
207
|
+
def get_version(self) -> str:
|
|
208
|
+
return self.provider.get_version()
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
7
|
# MIT License #
|
|
8
8
|
# Created By : Marcin Szczygliński #
|
|
9
|
-
# Updated Date: 2026.01.
|
|
9
|
+
# Updated Date: 2026.01.05 17:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import datetime
|
|
@@ -108,13 +108,14 @@ class Store:
|
|
|
108
108
|
"""
|
|
109
109
|
return id in self.items
|
|
110
110
|
|
|
111
|
-
def create(self) -> Optional[RemoteStoreItem]:
|
|
111
|
+
def create(self, name: Optional[str] = None) -> Optional[RemoteStoreItem]:
|
|
112
112
|
"""
|
|
113
113
|
Create new store
|
|
114
114
|
|
|
115
|
+
:param name: store name
|
|
115
116
|
:return: store item
|
|
116
117
|
"""
|
|
117
|
-
name = "New vector store"
|
|
118
|
+
# name = "New vector store"
|
|
118
119
|
vector_store = self.window.core.api.openai.store.create_store(name, 0)
|
|
119
120
|
if vector_store is None:
|
|
120
121
|
return None
|
|
@@ -275,7 +276,7 @@ class Store:
|
|
|
275
276
|
:return: True if store is hidden
|
|
276
277
|
"""
|
|
277
278
|
if id in self.items:
|
|
278
|
-
if (self.window.core.config.get("remote_store.
|
|
279
|
+
if (self.window.core.config.get("remote_store.hide_threads")
|
|
279
280
|
and (self.items[id].name is None or self.items[id].name == "")):
|
|
280
281
|
return True
|
|
281
282
|
return False
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
|
|
12
12
|
from .openai import Store as OpenAIStore
|
|
13
13
|
from .google import Store as GoogleStore
|
|
14
|
+
from .anthropic import Store as AnthropicStore
|
|
15
|
+
from .xai import Store as XAIStore
|
|
14
16
|
|
|
15
17
|
class RemoteStore:
|
|
16
18
|
def __init__(self, window=None):
|
|
@@ -21,4 +23,6 @@ class RemoteStore:
|
|
|
21
23
|
"""
|
|
22
24
|
self.window = window
|
|
23
25
|
self.openai = OpenAIStore(self.window)
|
|
24
|
-
self.google = GoogleStore(self.window)
|
|
26
|
+
self.google = GoogleStore(self.window)
|
|
27
|
+
self.anthropic = AnthropicStore(self.window)
|
|
28
|
+
self.xai = XAIStore(self.window)
|