pygpt-net 2.7.7__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 +7 -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/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/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 +9 -6
- pygpt_net/data/config/models.json +5 -4
- pygpt_net/data/config/settings.json +54 -1
- 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 +4 -3
- pygpt_net/data/locale/locale.en.ini +14 -4
- pygpt_net/data/locale/locale.es.ini +4 -3
- pygpt_net/data/locale/locale.fr.ini +4 -3
- pygpt_net/data/locale/locale.it.ini +4 -3
- pygpt_net/data/locale/locale.pl.ini +5 -4
- pygpt_net/data/locale/locale.uk.ini +4 -3
- pygpt_net/data/locale/locale.zh.ini +4 -3
- pygpt_net/icons.qrc +4 -0
- pygpt_net/icons_rc.py +282 -138
- pygpt_net/provider/api/anthropic/__init__.py +2 -0
- pygpt_net/provider/api/anthropic/chat.py +84 -1
- pygpt_net/provider/api/anthropic/store.py +307 -0
- pygpt_net/provider/api/anthropic/stream.py +75 -0
- pygpt_net/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 +59 -2
- pygpt_net/provider/api/google/store.py +124 -3
- pygpt_net/provider/api/google/stream.py +91 -24
- pygpt_net/provider/api/google/worker/importer.py +16 -28
- pygpt_net/provider/api/openai/assistants.py +2 -2
- pygpt_net/provider/api/openai/store.py +4 -1
- 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 +30 -6
- 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/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_tools.py +19 -1
- pygpt_net/provider/api/x_ai/store.py +610 -0
- pygpt_net/provider/api/x_ai/stream.py +30 -9
- 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 +18 -3
- pygpt_net/provider/core/config/patches/patch_before_2_6_42.py +2 -2
- pygpt_net/provider/core/model/patch.py +13 -0
- pygpt_net/tools/image_viewer/tool.py +334 -34
- pygpt_net/tools/image_viewer/ui/dialogs.py +317 -21
- 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/{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-2.7.7.dist-info → pygpt_net-2.7.8.dist-info}/METADATA +9 -2
- {pygpt_net-2.7.7.dist-info → pygpt_net-2.7.8.dist-info}/RECORD +82 -70
- 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.7.dist-info → pygpt_net-2.7.8.dist-info}/LICENSE +0 -0
- {pygpt_net-2.7.7.dist-info → pygpt_net-2.7.8.dist-info}/WHEEL +0 -0
- {pygpt_net-2.7.7.dist-info → pygpt_net-2.7.8.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,225 @@
|
|
|
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.06 06: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 = "xai"
|
|
24
|
+
|
|
25
|
+
def __init__(self, window=None):
|
|
26
|
+
"""
|
|
27
|
+
xAI remote files core (Collections)
|
|
28
|
+
"""
|
|
29
|
+
self.window = window
|
|
30
|
+
self.provider = DbSqliteProvider(window)
|
|
31
|
+
self.items: Dict[str, RemoteFileItem] = {}
|
|
32
|
+
|
|
33
|
+
def install(self):
|
|
34
|
+
self.provider.install()
|
|
35
|
+
|
|
36
|
+
def patch(self, app_version: Version) -> bool:
|
|
37
|
+
return self.provider.patch(app_version)
|
|
38
|
+
|
|
39
|
+
def get(self, id: str) -> Optional[RemoteFileItem]:
|
|
40
|
+
if id in self.items:
|
|
41
|
+
return self.items[id]
|
|
42
|
+
return None
|
|
43
|
+
|
|
44
|
+
def get_ids(self) -> List[str]:
|
|
45
|
+
return list(self.items.keys())
|
|
46
|
+
|
|
47
|
+
def get_all(self) -> Dict[str, RemoteFileItem]:
|
|
48
|
+
return self.items
|
|
49
|
+
|
|
50
|
+
def get_id_by_idx_all(self, idx: int) -> str:
|
|
51
|
+
return list(self.items.keys())[idx]
|
|
52
|
+
|
|
53
|
+
def get_by_idx(self, idx: int) -> str:
|
|
54
|
+
items = self.items
|
|
55
|
+
return list(items.keys())[idx]
|
|
56
|
+
|
|
57
|
+
def has(self, id: str) -> bool:
|
|
58
|
+
return id in self.items
|
|
59
|
+
|
|
60
|
+
def create(
|
|
61
|
+
self,
|
|
62
|
+
assistant: AssistantItem,
|
|
63
|
+
thread_id: str,
|
|
64
|
+
file_id: str,
|
|
65
|
+
name: str,
|
|
66
|
+
path: str,
|
|
67
|
+
size: int) -> Optional[RemoteFileItem]:
|
|
68
|
+
"""
|
|
69
|
+
Not used in xAI Collections path (kept for parity).
|
|
70
|
+
"""
|
|
71
|
+
file = RemoteFileItem()
|
|
72
|
+
file.id = file_id
|
|
73
|
+
file.file_id = file_id
|
|
74
|
+
file.thread_id = thread_id or ""
|
|
75
|
+
file.provider = self.PROVIDER_NAME
|
|
76
|
+
file.name = name
|
|
77
|
+
file.path = path
|
|
78
|
+
file.size = size or 0
|
|
79
|
+
file.store_id = "" # store_id resolved by caller when needed
|
|
80
|
+
file.record_id = self.provider.create(file)
|
|
81
|
+
self.items[file.id] = file
|
|
82
|
+
return file
|
|
83
|
+
|
|
84
|
+
def update(self, file: RemoteFileItem) -> Optional[RemoteFileItem]:
|
|
85
|
+
self.items[file.id] = file
|
|
86
|
+
self.provider.save(file)
|
|
87
|
+
return file
|
|
88
|
+
|
|
89
|
+
def get_names(self) -> Dict[str, str]:
|
|
90
|
+
names = {}
|
|
91
|
+
for id in self.items:
|
|
92
|
+
file = self.items[id]
|
|
93
|
+
names[id] = file.name
|
|
94
|
+
return names
|
|
95
|
+
|
|
96
|
+
def get_by_store_or_thread(self, store_id: str, thread_id: str) -> Dict[str, RemoteFileItem]:
|
|
97
|
+
return self.provider.get_by_store_or_thread(store_id, thread_id)
|
|
98
|
+
|
|
99
|
+
def count_by_store_or_thread(self, store_id: str, thread_id: str) -> int:
|
|
100
|
+
return self.provider.count_by_store_or_thread(store_id, thread_id)
|
|
101
|
+
|
|
102
|
+
def get_file_by_idx(self, idx: int, store_id: str, thread_id: str) -> Optional[RemoteFileItem]:
|
|
103
|
+
files = self.get_by_store_or_thread(store_id, thread_id)
|
|
104
|
+
if idx >= len(files):
|
|
105
|
+
return None
|
|
106
|
+
return list(files.values())[idx]
|
|
107
|
+
|
|
108
|
+
def get_file_id_by_idx(self, idx: int, store_id: str, thread_id: str) -> Optional[str]:
|
|
109
|
+
file = self.get_file_by_idx(idx, store_id, thread_id)
|
|
110
|
+
if file is None:
|
|
111
|
+
return None
|
|
112
|
+
return file.file_id
|
|
113
|
+
|
|
114
|
+
def get_all_by_file_id(self, file_id: str) -> dict:
|
|
115
|
+
return self.provider.get_all_by_file_id(file_id)
|
|
116
|
+
|
|
117
|
+
def delete(self, file: Union[RemoteFileItem, list]) -> bool:
|
|
118
|
+
"""
|
|
119
|
+
Delete a file entry and remove from its collections if possible.
|
|
120
|
+
"""
|
|
121
|
+
files = file if isinstance(file, list) else [file]
|
|
122
|
+
for f in files:
|
|
123
|
+
file_id = f.file_id
|
|
124
|
+
items = self.get_all_by_file_id(file_id)
|
|
125
|
+
# remove from linked collections first
|
|
126
|
+
for rid in items:
|
|
127
|
+
store_id = items[rid].store_id
|
|
128
|
+
if not store_id:
|
|
129
|
+
continue
|
|
130
|
+
try:
|
|
131
|
+
self.window.core.api.xai.store.delete_collection_file_collections(store_id, file_id)
|
|
132
|
+
except Exception as e:
|
|
133
|
+
self.window.core.debug.log("Failed to detach from collection: " + str(e))
|
|
134
|
+
# delete in DB entry
|
|
135
|
+
self.provider.delete_by_id(f.record_id)
|
|
136
|
+
if f.record_id in self.items:
|
|
137
|
+
del self.items[f.record_id]
|
|
138
|
+
return True
|
|
139
|
+
|
|
140
|
+
def delete_by_file_id(self, file_id: str) -> bool:
|
|
141
|
+
res = self.provider.delete_by_file_id(file_id)
|
|
142
|
+
if res:
|
|
143
|
+
to_delete = []
|
|
144
|
+
for id in self.items:
|
|
145
|
+
if self.items[id].file_id == file_id:
|
|
146
|
+
to_delete.append(id)
|
|
147
|
+
for id in to_delete:
|
|
148
|
+
del self.items[id]
|
|
149
|
+
return res
|
|
150
|
+
|
|
151
|
+
def on_store_deleted(self, store_id: str):
|
|
152
|
+
self.provider.clear_store_from_files(store_id)
|
|
153
|
+
|
|
154
|
+
def on_all_stores_deleted(self):
|
|
155
|
+
self.provider.clear_all_stores_from_files(self.PROVIDER_NAME)
|
|
156
|
+
|
|
157
|
+
def rename(self, record_id: int, name: str) -> bool:
|
|
158
|
+
self.provider.rename_file(record_id, name)
|
|
159
|
+
if record_id in self.items:
|
|
160
|
+
self.items[record_id].name = name
|
|
161
|
+
return True
|
|
162
|
+
|
|
163
|
+
def truncate(self, store_id: Optional[str] = None) -> bool:
|
|
164
|
+
"""
|
|
165
|
+
Detach documents from one/all collections (remote) and clear local DB entries.
|
|
166
|
+
"""
|
|
167
|
+
if store_id is not None:
|
|
168
|
+
self.window.core.api.xai.store.remove_from_collection_collections(store_id)
|
|
169
|
+
else:
|
|
170
|
+
self.window.core.api.xai.store.remove_from_collections_collections()
|
|
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
|
+
"""
|
|
183
|
+
Import collection documents into local DB.
|
|
184
|
+
"""
|
|
185
|
+
self.window.core.api.xai.store.import_collection_files_collections(store_id, [])
|
|
186
|
+
return True
|
|
187
|
+
|
|
188
|
+
def insert(self, store_id: str, data) -> RemoteFileItem:
|
|
189
|
+
"""
|
|
190
|
+
Insert a File into local DB, linked to given collection (store_id).
|
|
191
|
+
|
|
192
|
+
:param store_id: collection id
|
|
193
|
+
:param data: file/document object from API (Files metadata)
|
|
194
|
+
"""
|
|
195
|
+
file = RemoteFileItem()
|
|
196
|
+
file.id = getattr(data, "id", None) or getattr(data, "name", None)
|
|
197
|
+
file.file_id = getattr(data, "id", None) or getattr(data, "name", None)
|
|
198
|
+
file.thread_id = ""
|
|
199
|
+
file.name = getattr(data, "filename", None) or getattr(data, "name", "") or file.file_id
|
|
200
|
+
file.provider = self.PROVIDER_NAME
|
|
201
|
+
file.path = file.name
|
|
202
|
+
try:
|
|
203
|
+
size = getattr(data, "size_bytes", None)
|
|
204
|
+
if size is None and hasattr(data, "size"):
|
|
205
|
+
size = getattr(data, "size")
|
|
206
|
+
file.size = int(size or 0)
|
|
207
|
+
except Exception:
|
|
208
|
+
file.size = 0
|
|
209
|
+
file.store_id = store_id
|
|
210
|
+
file.record_id = self.provider.create(file)
|
|
211
|
+
self.items[file.id] = file
|
|
212
|
+
return file
|
|
213
|
+
|
|
214
|
+
def load(self):
|
|
215
|
+
self.items = self.provider.load_all(self.PROVIDER_NAME)
|
|
216
|
+
self.sort_items()
|
|
217
|
+
|
|
218
|
+
def sort_items(self):
|
|
219
|
+
return
|
|
220
|
+
|
|
221
|
+
def save(self):
|
|
222
|
+
self.provider.save_all(self.items)
|
|
223
|
+
|
|
224
|
+
def get_version(self) -> str:
|
|
225
|
+
return self.provider.get_version()
|
|
@@ -0,0 +1,219 @@
|
|
|
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.06 06: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
|
+
xAI Collections API store adapter.
|
|
26
|
+
|
|
27
|
+
Each xAI Collection is mapped to a RemoteStoreItem (Vector Store analogue).
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
PROVIDER_NAME = "xai"
|
|
31
|
+
|
|
32
|
+
def __init__(self, window=None):
|
|
33
|
+
"""
|
|
34
|
+
xAI Collections core
|
|
35
|
+
|
|
36
|
+
:param window: Window instance
|
|
37
|
+
"""
|
|
38
|
+
self.window = window
|
|
39
|
+
self.provider = DbSqliteProvider(window)
|
|
40
|
+
self.files = Files(window)
|
|
41
|
+
self.items: Dict[str, RemoteStoreItem] = {}
|
|
42
|
+
|
|
43
|
+
def install(self):
|
|
44
|
+
"""Install provider data"""
|
|
45
|
+
self.provider.install()
|
|
46
|
+
self.files.install()
|
|
47
|
+
|
|
48
|
+
def patch(self, app_version: Version) -> bool:
|
|
49
|
+
"""Patch provider data"""
|
|
50
|
+
res1 = self.files.patch(app_version)
|
|
51
|
+
res2 = self.provider.patch(app_version)
|
|
52
|
+
return res1 or res2
|
|
53
|
+
|
|
54
|
+
def get(self, id: str) -> RemoteStoreItem:
|
|
55
|
+
if id in self.items:
|
|
56
|
+
return self.items[id]
|
|
57
|
+
|
|
58
|
+
def get_ids(self) -> List[str]:
|
|
59
|
+
return list(self.items.keys())
|
|
60
|
+
|
|
61
|
+
def get_all(self) -> Dict[str, RemoteStoreItem]:
|
|
62
|
+
return self.items
|
|
63
|
+
|
|
64
|
+
def get_id_by_idx_all(self, idx: int) -> str:
|
|
65
|
+
return list(self.items.keys())[idx]
|
|
66
|
+
|
|
67
|
+
def get_by_idx(self, idx: int) -> str:
|
|
68
|
+
items = self.items
|
|
69
|
+
return list(items.keys())[idx]
|
|
70
|
+
|
|
71
|
+
def has(self, id: str) -> bool:
|
|
72
|
+
return id in self.items
|
|
73
|
+
|
|
74
|
+
# ---------- CRUD ----------
|
|
75
|
+
|
|
76
|
+
def create(self, name: Optional[str] = None) -> Optional[RemoteStoreItem]:
|
|
77
|
+
"""
|
|
78
|
+
Create a new collection.
|
|
79
|
+
"""
|
|
80
|
+
name = name or "New collection"
|
|
81
|
+
collection = self.window.core.api.xai.store.create_collection_collections(name)
|
|
82
|
+
if collection is None:
|
|
83
|
+
return None
|
|
84
|
+
store = RemoteStoreItem()
|
|
85
|
+
store.id = getattr(collection, "collection_id", None)
|
|
86
|
+
store.name = getattr(collection, "collection_name", None) or name
|
|
87
|
+
store.provider = self.PROVIDER_NAME
|
|
88
|
+
store.is_thread = False
|
|
89
|
+
store.record_id = self.provider.create(store)
|
|
90
|
+
self.items[store.id] = store
|
|
91
|
+
return store
|
|
92
|
+
|
|
93
|
+
def update(self, store: RemoteStoreItem) -> Optional[RemoteStoreItem]:
|
|
94
|
+
"""
|
|
95
|
+
Update collection (rename).
|
|
96
|
+
"""
|
|
97
|
+
collection = self.window.core.api.xai.store.update_collection_collections(store.id, store.name)
|
|
98
|
+
if collection is None:
|
|
99
|
+
return None
|
|
100
|
+
self.items[store.id] = store
|
|
101
|
+
self.provider.save(store)
|
|
102
|
+
return store
|
|
103
|
+
|
|
104
|
+
def get_status_data(self, id: str):
|
|
105
|
+
"""
|
|
106
|
+
Obtain status data for a collection.
|
|
107
|
+
"""
|
|
108
|
+
status = {}
|
|
109
|
+
data = self.window.core.api.xai.store.get_collection_collections(id)
|
|
110
|
+
if data is not None:
|
|
111
|
+
stats = self.window.core.api.xai.store.get_collection_stats_collections(id)
|
|
112
|
+
status = {
|
|
113
|
+
"status": "ready",
|
|
114
|
+
"usage_bytes": int(stats.get("total_bytes", 0) or 0),
|
|
115
|
+
"expires_at": None,
|
|
116
|
+
"last_active_at": int(datetime.datetime.now().timestamp()),
|
|
117
|
+
"file_counts": {
|
|
118
|
+
"in_progress": 0,
|
|
119
|
+
"completed": int(stats.get("count", 0) or 0),
|
|
120
|
+
"cancelled": 0,
|
|
121
|
+
"failed": 0,
|
|
122
|
+
"total": int(stats.get("count", 0) or 0),
|
|
123
|
+
},
|
|
124
|
+
"expires_after": None,
|
|
125
|
+
"remote_display_name": getattr(data, "name", "") or "",
|
|
126
|
+
}
|
|
127
|
+
return status, data
|
|
128
|
+
|
|
129
|
+
def update_status(self, id: str):
|
|
130
|
+
"""
|
|
131
|
+
Refresh store status from remote collection.
|
|
132
|
+
"""
|
|
133
|
+
if id not in self.items:
|
|
134
|
+
return
|
|
135
|
+
store = self.items[id]
|
|
136
|
+
status, data = self.get_status_data(id)
|
|
137
|
+
if data is not None:
|
|
138
|
+
tmp_name = getattr(data, "collection_name", None) or ""
|
|
139
|
+
store.name = tmp_name
|
|
140
|
+
store.provider = self.PROVIDER_NAME
|
|
141
|
+
self.append_status(store, status)
|
|
142
|
+
self.update(store)
|
|
143
|
+
|
|
144
|
+
def append_status(self, store: RemoteStoreItem, status: Dict[str, Any]):
|
|
145
|
+
import datetime as _dt
|
|
146
|
+
now = _dt.datetime.now()
|
|
147
|
+
ts = int(now.timestamp())
|
|
148
|
+
status["__last_refresh__"] = now.strftime("%Y-%m-%d %H:%M:%S")
|
|
149
|
+
store.status = status
|
|
150
|
+
store.last_sync = ts
|
|
151
|
+
if "status" in status:
|
|
152
|
+
store.last_status = status["status"]
|
|
153
|
+
if "usage_bytes" in status:
|
|
154
|
+
store.usage_bytes = status["usage_bytes"]
|
|
155
|
+
if "file_counts" in status:
|
|
156
|
+
store.num_files = status["file_counts"]["total"]
|
|
157
|
+
if "last_active_at" in status and status["last_active_at"]:
|
|
158
|
+
store.last_active = int(status["last_active_at"])
|
|
159
|
+
|
|
160
|
+
def get_names(self) -> Dict[str, str]:
|
|
161
|
+
names = {}
|
|
162
|
+
for id in self.items:
|
|
163
|
+
store = self.items[id]
|
|
164
|
+
names[id] = store.name
|
|
165
|
+
return names
|
|
166
|
+
|
|
167
|
+
def delete(self, id: str) -> bool:
|
|
168
|
+
"""
|
|
169
|
+
Delete collection.
|
|
170
|
+
"""
|
|
171
|
+
if id in self.items:
|
|
172
|
+
store = self.items[id]
|
|
173
|
+
self.provider.delete_by_id(store.record_id)
|
|
174
|
+
try:
|
|
175
|
+
self.window.core.api.xai.store.remove_collection_collections(id)
|
|
176
|
+
except Exception as e:
|
|
177
|
+
self.window.core.debug.log(e)
|
|
178
|
+
del self.items[id]
|
|
179
|
+
return True
|
|
180
|
+
return False
|
|
181
|
+
|
|
182
|
+
def import_items(self, items: Dict[str, RemoteStoreItem]):
|
|
183
|
+
"""
|
|
184
|
+
Insert items (collections).
|
|
185
|
+
"""
|
|
186
|
+
self.items = items
|
|
187
|
+
for item in items.values():
|
|
188
|
+
item.provider = self.PROVIDER_NAME
|
|
189
|
+
item.record_id = self.provider.create(item)
|
|
190
|
+
|
|
191
|
+
def clear(self):
|
|
192
|
+
self.truncate()
|
|
193
|
+
|
|
194
|
+
def is_hidden(self, id: str) -> bool:
|
|
195
|
+
return False
|
|
196
|
+
|
|
197
|
+
def truncate(self) -> bool:
|
|
198
|
+
self.provider.truncate(self.PROVIDER_NAME)
|
|
199
|
+
self.items = {}
|
|
200
|
+
return True
|
|
201
|
+
|
|
202
|
+
# ---------- Load/Save ----------
|
|
203
|
+
|
|
204
|
+
def load(self):
|
|
205
|
+
self.items = self.provider.load_all(self.PROVIDER_NAME)
|
|
206
|
+
self.sort_items()
|
|
207
|
+
|
|
208
|
+
def load_all(self):
|
|
209
|
+
self.load()
|
|
210
|
+
self.files.load()
|
|
211
|
+
|
|
212
|
+
def sort_items(self):
|
|
213
|
+
pass
|
|
214
|
+
|
|
215
|
+
def save(self):
|
|
216
|
+
self.provider.save_all(self.items)
|
|
217
|
+
|
|
218
|
+
def get_version(self) -> str:
|
|
219
|
+
return self.provider.get_version()
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"__meta__": {
|
|
3
|
-
"version": "2.7.
|
|
4
|
-
"app.version": "2.7.
|
|
5
|
-
"updated_at": "2026-01-
|
|
3
|
+
"version": "2.7.8",
|
|
4
|
+
"app.version": "2.7.8",
|
|
5
|
+
"updated_at": "2026-01-06T00:00:00"
|
|
6
6
|
},
|
|
7
7
|
"access.audio.event.speech": false,
|
|
8
8
|
"access.audio.event.speech.disabled": [],
|
|
@@ -106,7 +106,6 @@
|
|
|
106
106
|
}
|
|
107
107
|
],
|
|
108
108
|
"assistant": "",
|
|
109
|
-
"remote_store.openai.hide_threads": true,
|
|
110
109
|
"assistant_thread": "",
|
|
111
110
|
"attachments_auto_index": true,
|
|
112
111
|
"attachments_capture_clear": true,
|
|
@@ -416,6 +415,8 @@
|
|
|
416
415
|
"prompt.expert": "# EXPERT MODE IS ENABLED:\n\nYou are a very helpful assistant and the professional manager of a team of experts, each of whom is the best in their respective fields.\n\n## Rules:\n\n- Instead of directly answering questions, you will manage these experts by forwarding queries to them, collecting their responses, controlling the flow of information between them, and sending requests to them.\n- Experts are distinct, specialized AI models running on my local machine.\n- You will serve as a bridge between them.\n- Each expert has their own conversation context, allowing you to communicate with them normally.\n- The experts can communicate with you but not with each other, making you responsible for relaying information between them.\n- Each expert can use tools and commands, for example, reading files or searching the Web. You can ask experts to call tools and commands.\n- After consulting with the experts, your task is to prepare the best possible answer based on the assistance received.\n- To send a request to an expert, prepare the \"expert_call\" tool. Remember to format the JSON tool as shown, surrounding the tool with `<tool></tool>` tags, for example: `<tool>{\"cmd\": \"expert_call\", \"params\": {\"id\": \"expert_id\", \"query\": \"some query\"}}</tool>`. Make sure the JSON object is syntactically correct, without any redundand parenthness.\n- DO NOT call any remote tools when calling the experts.\n- Remember that the call must contain a query and the ID of the expert to whom the query is being forwarded.\n- Use ONLY the experts listed below; DO NOT TRY to ask any non-existent experts\u2014they will not respond.\n- Do not respond to the expert's replies or use the JSON tool again if replying; the expert only has the ability to listen to your queries sent via the JSON command and to respond to them.\n- Always follow ALL user commands, even if you think the expert can't do something. Experts are allowed to execute commands just as you can.\n- After querying an expert, always wait for the expert's response before sending another query.\n- Do not try to query experts in the middle of the conversation; put your queries and commands at the end of the text.\n- Always call only one expert at a time.\n- Always execute only one command at a time.\n- Always use the language used by the user in the conversation.\n- Never write as a called expert and do not imagine a response from them; instead, always wait for the response from the expert.\n- Every message sent to you directly after your query is always a response from the asked expert, not from me.\n- Always send a direct, clear, and detailed prompt to the expert, without any \"Please...\", \"Could you...\", etc.\n- Always forward all the messages that the user wants to send to the expert, without any exceptions.\n-- If a user command appears outside an expert's scope, fulfill the request regardless, without refusal.\n\n## List of allowed experts, along with their IDs:\n\n{presets}",
|
|
417
416
|
"prompt.img": "Convert the user's request into a single, production\u2011ready description for generating one high\u2011quality still image. Output only the description text, nothing else.\n\nWrite in concise, vivid, present\u2011tense language. Do not use commands (no \u201cplease generate\u201d), model names, parameters, or metadata. Do not mention aspect ratio, resolution, steps, seed, or negative prompts. Avoid on\u2011image text, captions, watermarks, logos, and UI elements. No brands, celebrities, or living artists unless explicitly provided by the user.\n\nInclude, woven into a coherent paragraph:\n- Clear primary subject(s) and their pose, action, and expression.\n- Setting and environment with time of day, season, weather, and atmosphere.\n- Composition and camera viewpoint (e.g., close\u2011up portrait, wide establishing, eye\u2011level, low\u2011angle, top\u2011down), framing (rule of thirds, centered symmetry), and background/foreground separation.\n- Lens and focus behavior (e.g., 85\u202fmm portrait, macro, shallow depth of field, smooth bokeh, gentle focus falloff).\n- Lighting style and quality (e.g., soft diffused daylight, golden hour rim light, dramatic chiaroscuro, studio three\u2011point) and how it shapes forms and shadows.\n- Color palette and grading (e.g., warm cinematic teal\u2011and\u2011orange, muted earth tones, cool monochrome with a single accent color).\n- Visual style or medium (e.g., photorealistic photography, watercolor illustration, oil painting, pencil sketch, anime cel\u2011shading, 3D render, isometric).\n- Material and surface detail (e.g., skin texture, fabric weave, wood grain, metal patina) to enhance realism or stylization.\n- Spatial depth cues (foreground/midground/background layering, atmospheric perspective) and overall mood.\n\nIf the user specifies a genre, era, or style, preserve it and enrich it with consistent, concrete traits. If the request is vague, infer specific but reasonable details that enhance clarity without contradicting the user\u2019s intent.\n\nReturn only the final visual description.",
|
|
418
417
|
"prompt.video": "Convert the user's request into a single, production-ready description for generating one continuous video clip. Output only the description text, nothing else.\n\nWrite in concise, vivid, present-tense language. Do not use commands (no \u201cplease generate\u201d), model names, parameters, or metadata. Do not mention duration, aspect ratio, FPS, resolution, shot numbers, cuts, or lists. Focus on visuals only; no dialogue, captions, on\u2011screen text, watermarks, logos, or UI.\n\nInclude, in a coherent way:\n- Clear subject(s) and what they are doing.\n- Setting, time of day, atmosphere, and weather.\n- Camera perspective and motion (e.g., wide establishing, low\u2011angle tracking, slow dolly in, aerial, handheld), framing and composition.\n- Lens and focus behavior (e.g., 24\u202fmm wide, shallow depth of field, gentle rack focus).\n- Lighting style and quality (e.g., soft golden hour rim light, moody volumetric shafts).\n- Color palette and grading (e.g., warm cinematic teal\u2011and\u2011orange, desaturated documentary).\n- Visual style or medium (e.g., photoreal live\u2011action, stylized anime, stop\u2011motion clay, watercolor animation).\n- Material and surface details that reinforce realism or the chosen style.\n- Temporal progression within one shot (use cues like \u201cas\u2026\u201d, \u201cthen\u2026\u201d, \u201cwhile\u2026\u201d), maintaining physical plausibility and continuity.\n\nIf the user specifies a genre or style (e.g., cyberpunk, nature documentary), keep it and expand with consistent, concrete visual traits. If the request is vague, infer specific but reasonable details that enhance clarity without contradicting the user\u2019s intent.\n\nReturn only the final visual description.",
|
|
418
|
+
"remote_store.hide_threads": true,
|
|
419
|
+
"remote_store.provider": "openai",
|
|
419
420
|
"remote_tools.anthropic.code_execution": false,
|
|
420
421
|
"remote_tools.anthropic.mcp": false,
|
|
421
422
|
"remote_tools.anthropic.mcp.mcp_servers": "[\n {\n \"type\": \"url\",\n \"url\": \"https://mcp.example.com/sse\",\n \"name\": \"example-mcp\",\n \"authorization_token\": \"YOUR_TOKEN\"\n }\n]",
|
|
@@ -438,10 +439,12 @@
|
|
|
438
439
|
"remote_tools.mcp": false,
|
|
439
440
|
"remote_tools.mcp.args": "{\n \"type\": \"mcp\",\n \"server_label\": \"deepwiki\",\n \"server_url\": \"https://mcp.deepwiki.com/mcp\",\n \"require_approval\": \"never\",\n \"allowed_tools\": [\"ask_question\"]\n}",
|
|
440
441
|
"remote_tools.web_search": true,
|
|
441
|
-
"remote_tools.xai.
|
|
442
|
+
"remote_tools.xai.collections": false,
|
|
443
|
+
"remote_tools.xai.collections.args": "",
|
|
442
444
|
"remote_tools.xai.code_execution": false,
|
|
443
445
|
"remote_tools.xai.mcp": false,
|
|
444
|
-
"remote_tools.xai.mcp.args": "{\n \"server_url\": \"https://mcp.deepwiki.com/mcp\n}",
|
|
446
|
+
"remote_tools.xai.mcp.args": "{\n \"server_url\": \"https://mcp.deepwiki.com/mcp\n}",
|
|
447
|
+
"remote_tools.xai.mode": "auto",
|
|
445
448
|
"remote_tools.xai.sources.web": true,
|
|
446
449
|
"remote_tools.xai.sources.x": true,
|
|
447
450
|
"remote_tools.xai.sources.news": false,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"__meta__": {
|
|
3
|
-
"version": "2.7.
|
|
4
|
-
"app.version": "2.7.
|
|
5
|
-
"updated_at": "2026-01-
|
|
3
|
+
"version": "2.7.8",
|
|
4
|
+
"app.version": "2.7.8",
|
|
5
|
+
"updated_at": "2026-01-06T00:00:00"
|
|
6
6
|
},
|
|
7
7
|
"items": {
|
|
8
8
|
"SpeakLeash/bielik-11b-v2.3-instruct:Q4_K_M": {
|
|
@@ -3195,7 +3195,8 @@
|
|
|
3195
3195
|
"tokens": 0,
|
|
3196
3196
|
"default": false,
|
|
3197
3197
|
"input": [
|
|
3198
|
-
"text"
|
|
3198
|
+
"text",
|
|
3199
|
+
"image"
|
|
3199
3200
|
],
|
|
3200
3201
|
"output": [
|
|
3201
3202
|
"text"
|
|
@@ -344,6 +344,28 @@
|
|
|
344
344
|
"advanced": false,
|
|
345
345
|
"tab": "xAI"
|
|
346
346
|
},
|
|
347
|
+
"api_key_management_xai": {
|
|
348
|
+
"section": "api_keys",
|
|
349
|
+
"type": "text",
|
|
350
|
+
"slider": false,
|
|
351
|
+
"label": "settings.api_key_management.xai",
|
|
352
|
+
"description": "settings.api_key_management.xai.desc",
|
|
353
|
+
"value": "",
|
|
354
|
+
"min": null,
|
|
355
|
+
"max": null,
|
|
356
|
+
"multiplier": null,
|
|
357
|
+
"step": null,
|
|
358
|
+
"extra": {
|
|
359
|
+
"bold": true
|
|
360
|
+
},
|
|
361
|
+
"urls": {
|
|
362
|
+
"API Keys": "https://console.x.ai"
|
|
363
|
+
},
|
|
364
|
+
"secret": true,
|
|
365
|
+
"persist": true,
|
|
366
|
+
"advanced": false,
|
|
367
|
+
"tab": "xAI"
|
|
368
|
+
},
|
|
347
369
|
"api_endpoint_xai": {
|
|
348
370
|
"section": "api_keys",
|
|
349
371
|
"type": "text",
|
|
@@ -2034,7 +2056,7 @@
|
|
|
2034
2056
|
"advanced": false,
|
|
2035
2057
|
"tab": "Google",
|
|
2036
2058
|
"urls": {
|
|
2037
|
-
"Google Docs": "https://ai.google.dev/gemini-api/docs/file-search
|
|
2059
|
+
"Google Docs": "https://ai.google.dev/gemini-api/docs/file-search"
|
|
2038
2060
|
}
|
|
2039
2061
|
},
|
|
2040
2062
|
"remote_tools.anthropic.web_search": {
|
|
@@ -2200,6 +2222,37 @@
|
|
|
2200
2222
|
"xAI Docs": "https://docs.x.ai/docs/guides/tools/remote-mcp-tools"
|
|
2201
2223
|
}
|
|
2202
2224
|
},
|
|
2225
|
+
"remote_tools.xai.collections": {
|
|
2226
|
+
"section": "remote_tools",
|
|
2227
|
+
"type": "bool",
|
|
2228
|
+
"slider": false,
|
|
2229
|
+
"label": "settings.remote_tools.xai.collections",
|
|
2230
|
+
"description": "settings.remote_tools.xai.collections.desc",
|
|
2231
|
+
"value": true,
|
|
2232
|
+
"min": null,
|
|
2233
|
+
"max": null,
|
|
2234
|
+
"multiplier": null,
|
|
2235
|
+
"step": null,
|
|
2236
|
+
"advanced": false,
|
|
2237
|
+
"tab": "xAI"
|
|
2238
|
+
},
|
|
2239
|
+
"remote_tools.xai.collections.args": {
|
|
2240
|
+
"section": "remote_tools",
|
|
2241
|
+
"type": "text",
|
|
2242
|
+
"slider": false,
|
|
2243
|
+
"label": "settings.remote_tools.xai.collections.args",
|
|
2244
|
+
"description": "settings.remote_tools.xai.collections.args.desc",
|
|
2245
|
+
"value": "",
|
|
2246
|
+
"min": null,
|
|
2247
|
+
"max": null,
|
|
2248
|
+
"multiplier": null,
|
|
2249
|
+
"step": null,
|
|
2250
|
+
"advanced": false,
|
|
2251
|
+
"tab": "xAI",
|
|
2252
|
+
"urls": {
|
|
2253
|
+
"xAI Docs": "https://docs.x.ai/docs/guides/tools/collections-search-tool"
|
|
2254
|
+
}
|
|
2255
|
+
},
|
|
2203
2256
|
"llama.idx.list": {
|
|
2204
2257
|
"section": "llama-index",
|
|
2205
2258
|
"type": "dict",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#686868"><path d="M160-160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800h240l80 80h320q33 0 56.5 23.5T880-640v242q-18-14-38-23t-42-19v-200H447l-80-80H160v480h120v80H160ZM640-40q-91 0-168-48T360-220q35-84 112-132t168-48q91 0 168 48t112 132q-35 84-112 132T640-40Zm0-80q57 0 107.5-26t82.5-74q-32-48-82.5-74T640-320q-57 0-107.5 26T450-220q32 48 82.5 74T640-120Zm0-40q-25 0-42.5-17.5T580-220q0-25 17.5-42.5T640-280q25 0 42.5 17.5T700-220q0 25-17.5 42.5T640-160Zm-480-80v-480 277-37 240Z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#686868"><path d="M160-160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800h240l80 80h320q33 0 56.5 23.5T880-640v249q-51-42-112.5-65.5T640-480q-143 0-247.5 94T252-160h-92ZM640-40q-91 0-168-48T360-220q35-84 112-132t168-48q91 0 168 48t112 132q-35 84-112 132T640-40Zm0-80q42 0 71-29t29-71q0-42-29-71t-71-29q-42 0-71 29t-29 71q0 42 29 71t71 29Zm0-40q-25 0-42.5-17.5T580-220q0-25 17.5-42.5T640-280q25 0 42.5 17.5T700-220q0 25-17.5 42.5T640-160Z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#686868"><path d="M160-160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800h240l80 80h320q33 0 56.5 23.5T880-640H447l-80-80H160v480l96-320h684L837-217q-8 26-29.5 41.5T760-160H160Zm84-80h516l72-240H316l-72 240Zm0 0 72-240-72 240Zm-84-400v-80 80Z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#686868"><path d="M160-160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800h240l80 80h320q33 0 56.5 23.5T880-640H160v400l96-320h684L837-217q-8 26-29.5 41.5T760-160H160Z"/></svg>
|
|
@@ -291,11 +291,11 @@ confirm.profile.delete = Sind Sie sicher, dass Sie das Profil löschen möchten?
|
|
|
291
291
|
confirm.profile.delete_all = Sind Sie sicher, dass Sie das Profil samt aller Konfigurationsdateien und Benutzerdateien im Profil-Arbeitsverzeichnis löschen möchten?
|
|
292
292
|
confirm.profile.reset = Sind Sie sicher, dass Sie das Profil auf die Standardeinstellungen zurücksetzen möchten? WARNUNG: Alle Konfigurationsdateien und die Kontext-Datenbank werden entfernt! Das Datenverzeichnis wird beibehalten - um es zu entfernen, müssen Sie es manuell löschen oder die Lösch-Option verwenden.
|
|
293
293
|
confirm.remote_store.clear = Vektor-Speicher leeren (nur lokal)?
|
|
294
|
+
confirm.remote_store.files.truncate = Sind Sie sicher, dass Sie alle Dateien von allen Speichern entfernen möchten?
|
|
295
|
+
confirm.remote_store.files.truncate.store = Sind Sie sicher, dass Sie alle Dateien aus dem ausgewählten Speicher entfernen möchten?
|
|
294
296
|
confirm.remote_store.import = Alle Vektor-Speicher von API importieren?
|
|
295
297
|
confirm.remote_store.import_files = Alle Dateien von API importieren?
|
|
296
298
|
confirm.remote_store.import_files.store = Aktuelle Speicherdateien von API importieren?
|
|
297
|
-
confirm.remote_store.openai.files.truncate = Sind Sie sicher, dass Sie alle Dateien von allen Speichern entfernen möchten?
|
|
298
|
-
confirm.remote_store.openai.files.truncate.store = Sind Sie sicher, dass Sie alle Dateien aus dem ausgewählten Speicher entfernen möchten?
|
|
299
299
|
confirm.remote_store.refresh = Alle Speicher aktualisieren?
|
|
300
300
|
confirm.remote_store.truncate = Alle Vektor-Speicher in der API löschen?
|
|
301
301
|
context.btn.clear = Erinnerung löschen
|
|
@@ -439,6 +439,7 @@ dialog.profile.status.removed = Profil von der Liste entfernt
|
|
|
439
439
|
dialog.profile.status.updated = Profil aktualisiert.
|
|
440
440
|
dialog.profile.tip = Durch das Erstellen neuer Profile können Sie separate Einstellungen, unterschiedliche Kontexthistorien und Benutzerdaten verwenden und schnell zwischen ihnen wechseln.
|
|
441
441
|
dialog.profile.workdir.label = Arbeitsverzeichnis (Verzeichnis für Konfigurationsdateien und Benutzerdaten)
|
|
442
|
+
dialog.remote_store = Externe Vektorbasen (OpenAI, Google, etc.)
|
|
442
443
|
dialog.remote_store.alert.assign = Bitte zuerst einen Vektor-Speicher dem Assistenten zuweisen.
|
|
443
444
|
dialog.remote_store.alert.select = Bitte zuerst Vektor-Speicher auswählen.
|
|
444
445
|
dialog.remote_store.btn.close = Abbrechen
|
|
@@ -1140,9 +1141,9 @@ profile.current.suffix = (aktuell)
|
|
|
1140
1141
|
remote_store.expire_days = Ablauftage
|
|
1141
1142
|
remote_store.expire_days.desc = 0 = Niemals
|
|
1142
1143
|
remote_store.files.suffix = Dateien
|
|
1144
|
+
remote_store.hide_threads = Thread-Vektor-Speicher verbergen
|
|
1143
1145
|
remote_store.id = ID
|
|
1144
1146
|
remote_store.name = Name
|
|
1145
|
-
remote_store.openai.hide_threads = Thread-Vektor-Speicher verbergen
|
|
1146
1147
|
remote_store.status = Status
|
|
1147
1148
|
remote_store.thread_only = (nur aktueller Thread)
|
|
1148
1149
|
reset.description = Beschreibung
|