pygpt-net 2.7.7__py3-none-any.whl → 2.7.9__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 (98) hide show
  1. pygpt_net/CHANGELOG.txt +12 -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/realtime/realtime.py +13 -1
  11. pygpt_net/controller/remote_store/{google/batch.py → batch.py} +209 -252
  12. pygpt_net/controller/remote_store/remote_store.py +982 -13
  13. pygpt_net/core/command/command.py +0 -0
  14. pygpt_net/core/db/viewer.py +1 -1
  15. pygpt_net/core/realtime/worker.py +3 -1
  16. pygpt_net/{controller/remote_store/google → core/remote_store/anthropic}/__init__.py +0 -1
  17. pygpt_net/core/remote_store/anthropic/files.py +211 -0
  18. pygpt_net/core/remote_store/anthropic/store.py +208 -0
  19. pygpt_net/core/remote_store/openai/store.py +5 -4
  20. pygpt_net/core/remote_store/remote_store.py +5 -1
  21. pygpt_net/{controller/remote_store/openai → core/remote_store/xai}/__init__.py +0 -1
  22. pygpt_net/core/remote_store/xai/files.py +225 -0
  23. pygpt_net/core/remote_store/xai/store.py +219 -0
  24. pygpt_net/data/config/config.json +10 -6
  25. pygpt_net/data/config/models.json +38 -22
  26. pygpt_net/data/config/settings.json +54 -1
  27. pygpt_net/data/icons/folder_eye.svg +1 -0
  28. pygpt_net/data/icons/folder_eye_filled.svg +1 -0
  29. pygpt_net/data/icons/folder_open.svg +1 -0
  30. pygpt_net/data/icons/folder_open_filled.svg +1 -0
  31. pygpt_net/data/locale/locale.de.ini +4 -3
  32. pygpt_net/data/locale/locale.en.ini +14 -4
  33. pygpt_net/data/locale/locale.es.ini +4 -3
  34. pygpt_net/data/locale/locale.fr.ini +4 -3
  35. pygpt_net/data/locale/locale.it.ini +4 -3
  36. pygpt_net/data/locale/locale.pl.ini +5 -4
  37. pygpt_net/data/locale/locale.uk.ini +4 -3
  38. pygpt_net/data/locale/locale.zh.ini +4 -3
  39. pygpt_net/icons.qrc +4 -0
  40. pygpt_net/icons_rc.py +282 -138
  41. pygpt_net/provider/api/anthropic/__init__.py +2 -0
  42. pygpt_net/provider/api/anthropic/chat.py +84 -1
  43. pygpt_net/provider/api/anthropic/store.py +307 -0
  44. pygpt_net/provider/api/anthropic/stream.py +75 -0
  45. pygpt_net/provider/api/anthropic/worker/__init__.py +0 -0
  46. pygpt_net/provider/api/anthropic/worker/importer.py +278 -0
  47. pygpt_net/provider/api/google/chat.py +59 -2
  48. pygpt_net/provider/api/google/realtime/client.py +70 -24
  49. pygpt_net/provider/api/google/realtime/realtime.py +48 -12
  50. pygpt_net/provider/api/google/store.py +124 -3
  51. pygpt_net/provider/api/google/stream.py +91 -24
  52. pygpt_net/provider/api/google/worker/importer.py +16 -28
  53. pygpt_net/provider/api/openai/assistants.py +2 -2
  54. pygpt_net/provider/api/openai/realtime/realtime.py +26 -6
  55. pygpt_net/provider/api/openai/store.py +4 -1
  56. pygpt_net/provider/api/openai/worker/importer.py +19 -61
  57. pygpt_net/provider/api/openai/worker/importer_assistants.py +230 -0
  58. pygpt_net/provider/api/x_ai/__init__.py +27 -6
  59. pygpt_net/provider/api/x_ai/audio.py +43 -11
  60. pygpt_net/provider/api/x_ai/chat.py +92 -4
  61. pygpt_net/provider/api/x_ai/realtime/__init__.py +12 -0
  62. pygpt_net/provider/api/x_ai/realtime/client.py +1864 -0
  63. pygpt_net/provider/api/x_ai/realtime/realtime.py +213 -0
  64. pygpt_net/provider/api/x_ai/remote_tools.py +102 -1
  65. pygpt_net/provider/api/x_ai/store.py +610 -0
  66. pygpt_net/provider/api/x_ai/stream.py +30 -9
  67. pygpt_net/provider/api/x_ai/tools.py +51 -0
  68. pygpt_net/provider/api/x_ai/worker/importer.py +308 -0
  69. pygpt_net/provider/audio_input/xai_grok_voice.py +390 -0
  70. pygpt_net/provider/audio_output/xai_tts.py +325 -0
  71. pygpt_net/provider/core/config/patch.py +29 -3
  72. pygpt_net/provider/core/config/patches/patch_before_2_6_42.py +2 -2
  73. pygpt_net/provider/core/model/patch.py +49 -1
  74. pygpt_net/tools/image_viewer/tool.py +334 -34
  75. pygpt_net/tools/image_viewer/ui/dialogs.py +317 -21
  76. pygpt_net/ui/dialog/assistant.py +1 -1
  77. pygpt_net/ui/dialog/plugins.py +13 -5
  78. pygpt_net/ui/dialog/remote_store.py +552 -0
  79. pygpt_net/ui/dialogs.py +3 -5
  80. pygpt_net/ui/layout/ctx/ctx_list.py +58 -7
  81. pygpt_net/ui/menu/tools.py +6 -13
  82. pygpt_net/ui/widget/dialog/{remote_store_google.py → remote_store.py} +10 -10
  83. pygpt_net/ui/widget/element/button.py +4 -4
  84. pygpt_net/ui/widget/image/display.py +2 -2
  85. pygpt_net/ui/widget/lists/context.py +2 -2
  86. {pygpt_net-2.7.7.dist-info → pygpt_net-2.7.9.dist-info}/METADATA +14 -2
  87. {pygpt_net-2.7.7.dist-info → pygpt_net-2.7.9.dist-info}/RECORD +87 -75
  88. pygpt_net/controller/remote_store/google/store.py +0 -615
  89. pygpt_net/controller/remote_store/openai/batch.py +0 -524
  90. pygpt_net/controller/remote_store/openai/store.py +0 -699
  91. pygpt_net/ui/dialog/remote_store_google.py +0 -539
  92. pygpt_net/ui/dialog/remote_store_openai.py +0 -539
  93. pygpt_net/ui/widget/dialog/remote_store_openai.py +0 -56
  94. pygpt_net/ui/widget/lists/remote_store_google.py +0 -248
  95. pygpt_net/ui/widget/lists/remote_store_openai.py +0 -317
  96. {pygpt_net-2.7.7.dist-info → pygpt_net-2.7.9.dist-info}/LICENSE +0 -0
  97. {pygpt_net-2.7.7.dist-info → pygpt_net-2.7.9.dist-info}/WHEEL +0 -0
  98. {pygpt_net-2.7.7.dist-info → pygpt_net-2.7.9.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.9",
4
+ "app.version": "2.7.9",
5
+ "updated_at": "2026-01-08T00:00:00"
6
6
  },
7
7
  "access.audio.event.speech": false,
8
8
  "access.audio.event.speech.disabled": [],
@@ -88,6 +88,7 @@
88
88
  "api_key_perplexity": "",
89
89
  "api_key_voyage": "",
90
90
  "api_key_xai": "",
91
+ "api_key_management_xai": "",
91
92
  "api_native_anthropic": true,
92
93
  "api_native_google": true,
93
94
  "api_native_google.app_credentials": "",
@@ -106,7 +107,6 @@
106
107
  }
107
108
  ],
108
109
  "assistant": "",
109
- "remote_store.openai.hide_threads": true,
110
110
  "assistant_thread": "",
111
111
  "attachments_auto_index": true,
112
112
  "attachments_capture_clear": true,
@@ -416,6 +416,8 @@
416
416
  "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
417
  "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
418
  "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.",
419
+ "remote_store.hide_threads": true,
420
+ "remote_store.provider": "openai",
419
421
  "remote_tools.anthropic.code_execution": false,
420
422
  "remote_tools.anthropic.mcp": false,
421
423
  "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 +440,12 @@
438
440
  "remote_tools.mcp": false,
439
441
  "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
442
  "remote_tools.web_search": true,
441
- "remote_tools.xai.mode": "auto",
443
+ "remote_tools.xai.collections": false,
444
+ "remote_tools.xai.collections.args": "",
442
445
  "remote_tools.xai.code_execution": false,
443
446
  "remote_tools.xai.mcp": false,
444
- "remote_tools.xai.mcp.args": "{\n \"server_url\": \"https://mcp.deepwiki.com/mcp\n}",
447
+ "remote_tools.xai.mcp.args": "{\n \"server_url\": \"https://mcp.deepwiki.com/mcp\n}",
448
+ "remote_tools.xai.mode": "auto",
445
449
  "remote_tools.xai.sources.web": true,
446
450
  "remote_tools.xai.sources.x": true,
447
451
  "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.9",
4
+ "app.version": "2.7.9",
5
+ "updated_at": "2026-01-08T00:00:00"
6
6
  },
7
7
  "items": {
8
8
  "SpeakLeash/bielik-11b-v2.3-instruct:Q4_K_M": {
@@ -1073,9 +1073,9 @@
1073
1073
  "provider": "google",
1074
1074
  "tool_calls": true
1075
1075
  },
1076
- "gemini-2.5-flash-preview-native-audio-dialog": {
1077
- "id": "gemini-2.5-flash-preview-native-audio-dialog",
1078
- "name": "gemini-2.5-flash-preview-native-audio-dialog",
1076
+ "gemini-2.5-flash-native-audio-latest": {
1077
+ "id": "gemini-2.5-flash-native-audio-latest",
1078
+ "name": "gemini-2.5-flash-native-audio-latest",
1079
1079
  "mode": [
1080
1080
  "audio"
1081
1081
  ],
@@ -1083,7 +1083,7 @@
1083
1083
  "args": [
1084
1084
  {
1085
1085
  "name": "model",
1086
- "value": "models/gemini-2.5-flash-preview-native-audio-dialog",
1086
+ "value": "models/gemini-2.5-flash-native-audio-latest",
1087
1087
  "type": "str"
1088
1088
  }
1089
1089
  ],
@@ -3170,7 +3170,8 @@
3170
3170
  "agent",
3171
3171
  "agent_llama",
3172
3172
  "expert",
3173
- "agent_openai"
3173
+ "agent_openai",
3174
+ "audio"
3174
3175
  ],
3175
3176
  "llama_index": {
3176
3177
  "args": [
@@ -3195,10 +3196,13 @@
3195
3196
  "tokens": 0,
3196
3197
  "default": false,
3197
3198
  "input": [
3198
- "text"
3199
+ "text",
3200
+ "image",
3201
+ "audio"
3199
3202
  ],
3200
3203
  "output": [
3201
- "text"
3204
+ "text",
3205
+ "audio"
3202
3206
  ],
3203
3207
  "extra": {},
3204
3208
  "imported": false,
@@ -3214,7 +3218,8 @@
3214
3218
  "agent_llama",
3215
3219
  "agent_openai",
3216
3220
  "agent",
3217
- "expert"
3221
+ "expert",
3222
+ "audio"
3218
3223
  ],
3219
3224
  "llama_index": {
3220
3225
  "args": [
@@ -3242,10 +3247,12 @@
3242
3247
  "default": false,
3243
3248
  "input": [
3244
3249
  "text",
3245
- "image"
3250
+ "image",
3251
+ "audio"
3246
3252
  ],
3247
3253
  "output": [
3248
- "text"
3254
+ "text",
3255
+ "audio"
3249
3256
  ],
3250
3257
  "extra": {},
3251
3258
  "imported": false,
@@ -3261,7 +3268,8 @@
3261
3268
  "agent_llama",
3262
3269
  "agent_openai",
3263
3270
  "agent",
3264
- "expert"
3271
+ "expert",
3272
+ "audio"
3265
3273
  ],
3266
3274
  "llama_index": {
3267
3275
  "args": [
@@ -3289,10 +3297,12 @@
3289
3297
  "default": false,
3290
3298
  "input": [
3291
3299
  "text",
3292
- "image"
3300
+ "image",
3301
+ "audio"
3293
3302
  ],
3294
3303
  "output": [
3295
- "text"
3304
+ "text",
3305
+ "audio"
3296
3306
  ],
3297
3307
  "extra": {},
3298
3308
  "imported": false,
@@ -3308,7 +3318,8 @@
3308
3318
  "agent_llama",
3309
3319
  "agent_openai",
3310
3320
  "agent",
3311
- "expert"
3321
+ "expert",
3322
+ "audio"
3312
3323
  ],
3313
3324
  "llama_index": {
3314
3325
  "args": [
@@ -3336,10 +3347,12 @@
3336
3347
  "default": false,
3337
3348
  "input": [
3338
3349
  "text",
3339
- "image"
3350
+ "image",
3351
+ "audio"
3340
3352
  ],
3341
3353
  "output": [
3342
- "text"
3354
+ "text",
3355
+ "audio"
3343
3356
  ],
3344
3357
  "extra": {},
3345
3358
  "imported": false,
@@ -3355,7 +3368,8 @@
3355
3368
  "agent_llama",
3356
3369
  "agent_openai",
3357
3370
  "agent",
3358
- "expert"
3371
+ "expert",
3372
+ "audio"
3359
3373
  ],
3360
3374
  "llama_index": {
3361
3375
  "args": [
@@ -3383,10 +3397,12 @@
3383
3397
  "default": false,
3384
3398
  "input": [
3385
3399
  "text",
3386
- "image"
3400
+ "image",
3401
+ "audio"
3387
3402
  ],
3388
3403
  "output": [
3389
- "text"
3404
+ "text",
3405
+ "audio"
3390
3406
  ],
3391
3407
  "extra": {},
3392
3408
  "imported": false,