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.
Files changed (93) hide show
  1. pygpt_net/CHANGELOG.txt +7 -0
  2. pygpt_net/__init__.py +3 -3
  3. pygpt_net/app.py +5 -1
  4. pygpt_net/controller/assistant/batch.py +2 -2
  5. pygpt_net/controller/assistant/files.py +7 -6
  6. pygpt_net/controller/assistant/threads.py +0 -0
  7. pygpt_net/controller/chat/command.py +0 -0
  8. pygpt_net/controller/dialogs/confirm.py +35 -58
  9. pygpt_net/controller/lang/mapping.py +9 -9
  10. pygpt_net/controller/remote_store/{google/batch.py → batch.py} +209 -252
  11. pygpt_net/controller/remote_store/remote_store.py +982 -13
  12. pygpt_net/core/command/command.py +0 -0
  13. pygpt_net/core/db/viewer.py +1 -1
  14. pygpt_net/core/realtime/worker.py +3 -1
  15. pygpt_net/{controller/remote_store/google → core/remote_store/anthropic}/__init__.py +0 -1
  16. pygpt_net/core/remote_store/anthropic/files.py +211 -0
  17. pygpt_net/core/remote_store/anthropic/store.py +208 -0
  18. pygpt_net/core/remote_store/openai/store.py +5 -4
  19. pygpt_net/core/remote_store/remote_store.py +5 -1
  20. pygpt_net/{controller/remote_store/openai → core/remote_store/xai}/__init__.py +0 -1
  21. pygpt_net/core/remote_store/xai/files.py +225 -0
  22. pygpt_net/core/remote_store/xai/store.py +219 -0
  23. pygpt_net/data/config/config.json +9 -6
  24. pygpt_net/data/config/models.json +5 -4
  25. pygpt_net/data/config/settings.json +54 -1
  26. pygpt_net/data/icons/folder_eye.svg +1 -0
  27. pygpt_net/data/icons/folder_eye_filled.svg +1 -0
  28. pygpt_net/data/icons/folder_open.svg +1 -0
  29. pygpt_net/data/icons/folder_open_filled.svg +1 -0
  30. pygpt_net/data/locale/locale.de.ini +4 -3
  31. pygpt_net/data/locale/locale.en.ini +14 -4
  32. pygpt_net/data/locale/locale.es.ini +4 -3
  33. pygpt_net/data/locale/locale.fr.ini +4 -3
  34. pygpt_net/data/locale/locale.it.ini +4 -3
  35. pygpt_net/data/locale/locale.pl.ini +5 -4
  36. pygpt_net/data/locale/locale.uk.ini +4 -3
  37. pygpt_net/data/locale/locale.zh.ini +4 -3
  38. pygpt_net/icons.qrc +4 -0
  39. pygpt_net/icons_rc.py +282 -138
  40. pygpt_net/provider/api/anthropic/__init__.py +2 -0
  41. pygpt_net/provider/api/anthropic/chat.py +84 -1
  42. pygpt_net/provider/api/anthropic/store.py +307 -0
  43. pygpt_net/provider/api/anthropic/stream.py +75 -0
  44. pygpt_net/provider/api/anthropic/worker/__init__.py +0 -0
  45. pygpt_net/provider/api/anthropic/worker/importer.py +278 -0
  46. pygpt_net/provider/api/google/chat.py +59 -2
  47. pygpt_net/provider/api/google/store.py +124 -3
  48. pygpt_net/provider/api/google/stream.py +91 -24
  49. pygpt_net/provider/api/google/worker/importer.py +16 -28
  50. pygpt_net/provider/api/openai/assistants.py +2 -2
  51. pygpt_net/provider/api/openai/store.py +4 -1
  52. pygpt_net/provider/api/openai/worker/importer.py +19 -61
  53. pygpt_net/provider/api/openai/worker/importer_assistants.py +230 -0
  54. pygpt_net/provider/api/x_ai/__init__.py +30 -6
  55. pygpt_net/provider/api/x_ai/audio.py +43 -11
  56. pygpt_net/provider/api/x_ai/chat.py +92 -4
  57. pygpt_net/provider/api/x_ai/realtime/__init__.py +12 -0
  58. pygpt_net/provider/api/x_ai/realtime/client.py +1825 -0
  59. pygpt_net/provider/api/x_ai/realtime/realtime.py +198 -0
  60. pygpt_net/provider/api/x_ai/remote_tools.py +19 -1
  61. pygpt_net/provider/api/x_ai/store.py +610 -0
  62. pygpt_net/provider/api/x_ai/stream.py +30 -9
  63. pygpt_net/provider/api/x_ai/worker/importer.py +308 -0
  64. pygpt_net/provider/audio_input/xai_grok_voice.py +390 -0
  65. pygpt_net/provider/audio_output/xai_tts.py +325 -0
  66. pygpt_net/provider/core/config/patch.py +18 -3
  67. pygpt_net/provider/core/config/patches/patch_before_2_6_42.py +2 -2
  68. pygpt_net/provider/core/model/patch.py +13 -0
  69. pygpt_net/tools/image_viewer/tool.py +334 -34
  70. pygpt_net/tools/image_viewer/ui/dialogs.py +317 -21
  71. pygpt_net/ui/dialog/assistant.py +1 -1
  72. pygpt_net/ui/dialog/plugins.py +13 -5
  73. pygpt_net/ui/dialog/remote_store.py +552 -0
  74. pygpt_net/ui/dialogs.py +3 -5
  75. pygpt_net/ui/layout/ctx/ctx_list.py +58 -7
  76. pygpt_net/ui/menu/tools.py +6 -13
  77. pygpt_net/ui/widget/dialog/{remote_store_google.py → remote_store.py} +10 -10
  78. pygpt_net/ui/widget/element/button.py +4 -4
  79. pygpt_net/ui/widget/image/display.py +2 -2
  80. pygpt_net/ui/widget/lists/context.py +2 -2
  81. {pygpt_net-2.7.7.dist-info → pygpt_net-2.7.8.dist-info}/METADATA +9 -2
  82. {pygpt_net-2.7.7.dist-info → pygpt_net-2.7.8.dist-info}/RECORD +82 -70
  83. pygpt_net/controller/remote_store/google/store.py +0 -615
  84. pygpt_net/controller/remote_store/openai/batch.py +0 -524
  85. pygpt_net/controller/remote_store/openai/store.py +0 -699
  86. pygpt_net/ui/dialog/remote_store_google.py +0 -539
  87. pygpt_net/ui/dialog/remote_store_openai.py +0 -539
  88. pygpt_net/ui/widget/dialog/remote_store_openai.py +0 -56
  89. pygpt_net/ui/widget/lists/remote_store_google.py +0 -248
  90. pygpt_net/ui/widget/lists/remote_store_openai.py +0 -317
  91. {pygpt_net-2.7.7.dist-info → pygpt_net-2.7.8.dist-info}/LICENSE +0 -0
  92. {pygpt_net-2.7.7.dist-info → pygpt_net-2.7.8.dist-info}/WHEEL +0 -0
  93. {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.7",
4
- "app.version": "2.7.7",
5
- "updated_at": "2026-01-05T00:00:00"
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.mode": "auto",
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.7",
4
- "app.version": "2.7.7",
5
- "updated_at": "2026-01-05T00:00:00"
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#metadata"
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