pygpt-net 2.6.37__py3-none-any.whl → 2.6.38__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 (51) hide show
  1. pygpt_net/CHANGELOG.txt +7 -0
  2. pygpt_net/__init__.py +1 -1
  3. pygpt_net/controller/chat/handler/anthropic_stream.py +0 -2
  4. pygpt_net/controller/chat/handler/worker.py +6 -2
  5. pygpt_net/controller/debug/debug.py +6 -6
  6. pygpt_net/controller/model/importer.py +9 -2
  7. pygpt_net/controller/plugins/plugins.py +11 -3
  8. pygpt_net/controller/presets/presets.py +2 -2
  9. pygpt_net/core/ctx/bag.py +7 -2
  10. pygpt_net/core/ctx/reply.py +17 -2
  11. pygpt_net/core/db/viewer.py +19 -34
  12. pygpt_net/core/render/plain/pid.py +12 -1
  13. pygpt_net/core/render/web/body.py +1 -5
  14. pygpt_net/core/tabs/tab.py +24 -1
  15. pygpt_net/data/config/config.json +2 -2
  16. pygpt_net/data/config/models.json +2 -2
  17. pygpt_net/item/assistant.py +51 -2
  18. pygpt_net/item/attachment.py +21 -20
  19. pygpt_net/item/calendar_note.py +19 -2
  20. pygpt_net/item/ctx.py +115 -2
  21. pygpt_net/item/index.py +9 -2
  22. pygpt_net/item/mode.py +9 -6
  23. pygpt_net/item/model.py +20 -3
  24. pygpt_net/item/notepad.py +14 -2
  25. pygpt_net/item/preset.py +42 -2
  26. pygpt_net/item/prompt.py +8 -2
  27. pygpt_net/plugin/cmd_files/plugin.py +2 -2
  28. pygpt_net/provider/api/anthropic/tools.py +1 -1
  29. pygpt_net/provider/api/google/realtime/client.py +2 -2
  30. pygpt_net/provider/core/attachment/json_file.py +2 -2
  31. pygpt_net/tools/text_editor/tool.py +4 -1
  32. pygpt_net/tools/text_editor/ui/dialogs.py +1 -1
  33. pygpt_net/ui/dialog/db.py +177 -59
  34. pygpt_net/ui/dialog/dictionary.py +57 -59
  35. pygpt_net/ui/dialog/editor.py +3 -2
  36. pygpt_net/ui/dialog/image.py +1 -1
  37. pygpt_net/ui/dialog/logger.py +3 -2
  38. pygpt_net/ui/dialog/models.py +14 -12
  39. pygpt_net/ui/dialog/plugins.py +26 -20
  40. pygpt_net/ui/layout/ctx/ctx_list.py +3 -4
  41. pygpt_net/ui/layout/toolbox/__init__.py +2 -2
  42. pygpt_net/ui/layout/toolbox/assistants.py +8 -9
  43. pygpt_net/ui/layout/toolbox/presets.py +2 -2
  44. pygpt_net/ui/main.py +9 -4
  45. pygpt_net/ui/widget/element/labels.py +2 -2
  46. pygpt_net/ui/widget/textarea/editor.py +0 -4
  47. {pygpt_net-2.6.37.dist-info → pygpt_net-2.6.38.dist-info}/METADATA +9 -2
  48. {pygpt_net-2.6.37.dist-info → pygpt_net-2.6.38.dist-info}/RECORD +51 -51
  49. {pygpt_net-2.6.37.dist-info → pygpt_net-2.6.38.dist-info}/LICENSE +0 -0
  50. {pygpt_net-2.6.37.dist-info → pygpt_net-2.6.38.dist-info}/WHEEL +0 -0
  51. {pygpt_net-2.6.37.dist-info → pygpt_net-2.6.38.dist-info}/entry_points.txt +0 -0
pygpt_net/CHANGELOG.txt CHANGED
@@ -1,3 +1,10 @@
1
+ 2.6.38 (2025-09-05)
2
+
3
+ - Fixed: Detection of chunk type in Ollama.
4
+ - Fixed: Import of models with existing IDs.
5
+ - Fixed: Updating Assistants UI list after create new Assistant.
6
+ - Refactor and optimization.
7
+
1
8
  2.6.37 (2025-09-05)
2
9
 
3
10
  - Fixed: Function parameters sanitization in the Google Gen AI SDK.
pygpt_net/__init__.py CHANGED
@@ -13,7 +13,7 @@ __author__ = "Marcin Szczygliński"
13
13
  __copyright__ = "Copyright 2025, Marcin Szczygliński"
14
14
  __credits__ = ["Marcin Szczygliński"]
15
15
  __license__ = "MIT"
16
- __version__ = "2.6.37"
16
+ __version__ = "2.6.38"
17
17
  __build__ = "2025-09-05"
18
18
  __maintainer__ = "Marcin Szczygliński"
19
19
  __github__ = "https://github.com/szczyglis-dev/py-gpt"
@@ -154,8 +154,6 @@ def process_anthropic_chunk(ctx, core, state, chunk) -> Optional[str]:
154
154
  state.usage_payload["out"] = out_tok
155
155
  delta = getattr(chunk, "delta", None)
156
156
  stop_reason = getattr(delta, "stop_reason", None) if delta else None
157
- if stop_reason == "tool_use":
158
- state.force_func_call = True
159
157
  except Exception:
160
158
  pass
161
159
  return None
@@ -6,7 +6,7 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2025.09.04 00:00:00 #
9
+ # Updated Date: 2025.09.05 18:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import io
@@ -16,6 +16,7 @@ from typing import Optional, Literal, Any
16
16
  from enum import Enum
17
17
 
18
18
  from PySide6.QtCore import QObject, Signal, Slot, QRunnable
19
+ from openai.types.chat import ChatCompletionChunk
19
20
 
20
21
  from pygpt_net.core.events import RenderEvent
21
22
  from pygpt_net.core.text.utils import has_unclosed_code_tag
@@ -262,6 +263,10 @@ class StreamWorker(QRunnable):
262
263
  if not hasattr(chunk, "type") and not hasattr(chunk, "candidates"):
263
264
  return ChunkType.LLAMA_CHAT
264
265
 
266
+ # fallback: OpenAI ChatCompletionChunk not caught above
267
+ if isinstance(chunk, ChatCompletionChunk):
268
+ return ChunkType.API_CHAT
269
+
265
270
  return ChunkType.RAW
266
271
 
267
272
  def _append_response(
@@ -344,7 +349,6 @@ class StreamWorker(QRunnable):
344
349
  calls = xai_stream.xai_extract_tool_calls(state.xai_last_response)
345
350
  if calls:
346
351
  state.tool_calls = calls
347
- state.force_func_call = True
348
352
  except Exception:
349
353
  pass
350
354
 
@@ -6,7 +6,7 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2025.08.18 01:00:00 #
9
+ # Updated Date: 2025.09.05 18:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from datetime import datetime
@@ -31,6 +31,7 @@ class Debug(QObject):
31
31
  self.is_logger = False # logger window opened
32
32
  self.is_app_log = False # app log window opened
33
33
  self.allow_level_change = False # allow changing log level
34
+ self._ids = None
34
35
 
35
36
  def update(self):
36
37
  """Update debug"""
@@ -100,12 +101,11 @@ class Debug(QObject):
100
101
 
101
102
  :param all: update all debug windows
102
103
  """
103
- # not_realtime = ['context']
104
- not_realtime = []
105
- for id in self.window.controller.dialogs.debug.get_ids():
104
+ if self._ids is None:
105
+ self._ids = self.window.controller.dialogs.debug.get_ids()
106
+ for id in self._ids:
106
107
  if self.window.controller.dialogs.debug.is_active(id):
107
- if all or id not in not_realtime:
108
- self.window.controller.dialogs.debug.update_worker(id)
108
+ self.window.controller.dialogs.debug.update_worker(id)
109
109
 
110
110
  def post_setup(self):
111
111
  """Post setup debug"""
@@ -6,7 +6,7 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2025.08.26 23:00:00 #
9
+ # Updated Date: 2025.09.05 18:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import copy
@@ -503,7 +503,7 @@ class Importer:
503
503
  return models
504
504
  else:
505
505
  for model in ollama_models:
506
- name = model.get('name').replace(":latest", "")
506
+ name = model.get('name')
507
507
  m = self.window.core.models.create_empty(append=False)
508
508
  m.id = name
509
509
  m.name = name
@@ -541,6 +541,13 @@ class Importer:
541
541
  base_models[key] = copy.deepcopy(self.pending[key])
542
542
  base_models[key].imported = True
543
543
  added = True
544
+ else:
545
+ # add provider suffix - to key
546
+ new_key = f"{key}-{self.provider}"
547
+ if new_key not in base_models:
548
+ base_models[new_key] = copy.deepcopy(self.pending[key])
549
+ base_models[new_key].imported = True
550
+ added = True
544
551
  for key in list(self.removed.keys()):
545
552
  if key in base_models:
546
553
  del base_models[key]
@@ -6,7 +6,7 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2025.08.15 23:00:00 #
9
+ # Updated Date: 2025.09.05 18:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from typing import List, Dict, Any, Optional
@@ -34,6 +34,8 @@ class Plugins:
34
34
  self.settings = Settings(window)
35
35
  self.presets = Presets(window)
36
36
  self.enabled = {}
37
+ self._ids = None
38
+ self._ids_with_update = None
37
39
  self._suspend_updates = 0
38
40
 
39
41
  def _begin_batch(self):
@@ -316,9 +318,15 @@ class Plugins:
316
318
  def on_post_update(self):
317
319
  """Called on post update"""
318
320
  pm = self.window.core.plugins
319
- for pid in pm.get_ids():
321
+ if self._ids is None:
322
+ self._ids = pm.get_ids()
323
+ if self._ids_with_update is None:
324
+ self._ids_with_update = [pid for pid in self._ids if hasattr(self.window.core.plugins.get(pid), "on_post_update")]
325
+ if len(self._ids_with_update) == 0:
326
+ return
327
+ for pid in self._ids_with_update:
320
328
  if self.is_enabled(pid):
321
- fn = getattr(pm.get(pid), "on_post_update", None)
329
+ fn = pm.get(pid).on_post_update
322
330
  if callable(fn):
323
331
  fn()
324
332
 
@@ -6,7 +6,7 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2025.08.15 03:00:00 #
9
+ # Updated Date: 2025.09.05 18:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import re
@@ -440,7 +440,7 @@ class Presets:
440
440
  if preset_id and preset_id in w.core.presets.items:
441
441
  preset = w.core.presets.items[preset_id]
442
442
  preset.prompt = w.core.config.get('prompt')
443
- w.core.presets.save(preset)
443
+ w.core.presets.save(preset_id)
444
444
 
445
445
  def select_model(self):
446
446
  """Select model by current preset"""
pygpt_net/core/ctx/bag.py CHANGED
@@ -6,16 +6,21 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2025.08.19 07:00:00 #
9
+ # Updated Date: 2025.09.05 18:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from typing import List
13
+ from dataclasses import dataclass, field
13
14
 
14
15
  from pygpt_net.item.ctx import CtxItem
15
16
 
16
17
 
18
+ @dataclass(slots=True)
17
19
  class Bag:
18
- __slots__ = ('window', 'meta', 'tab_id', 'items')
20
+ window: object = None
21
+ meta: object = None
22
+ tab_id: int = 0
23
+ items: List[CtxItem] = field(default_factory=list)
19
24
 
20
25
  def __init__(self, window=None):
21
26
  """
@@ -6,12 +6,14 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2025.07.26 18:00:00 #
9
+ # Updated Date: 2025.09.05 18:00:00 #
10
10
  # ================================================== #
11
11
 
12
- from typing import Dict, Any
12
+ from typing import Dict, Any, Optional
13
+ from dataclasses import dataclass, field
13
14
 
14
15
 
16
+ @dataclass(slots=True)
15
17
  class ReplyContext:
16
18
 
17
19
  AGENT_CONTINUE = "agent.continue"
@@ -21,6 +23,15 @@ class ReplyContext:
21
23
  EXPERT_CALL = "expert.call"
22
24
  EXPERT_RESPONSE = "expert.response"
23
25
 
26
+ type: Optional[object] = None
27
+ bridge_context: Optional[object] = None
28
+ ctx: Optional[object] = None
29
+ prev_ctx: Optional[object] = None
30
+ parent_id: Optional[object] = None
31
+ input: str = ""
32
+ internal: bool = False
33
+ cmds: list = field(default_factory=list)
34
+
24
35
  def __init__(self):
25
36
  """Reply context"""
26
37
  self.type = None
@@ -55,7 +66,11 @@ class ReplyContext:
55
66
  data["prev_ctx"] = self.prev_ctx.to_dict()
56
67
  return data
57
68
 
69
+
70
+ @dataclass(slots=True)
58
71
  class Reply:
72
+ window: Optional[object] = None
73
+
59
74
  def __init__(self, window=None):
60
75
  """
61
76
  Reply core
@@ -6,7 +6,7 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2024.12.14 08:00:00 #
9
+ # Updated Date: 2025.09.05 18:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import json
@@ -65,16 +65,13 @@ class Viewer:
65
65
  limit_clause = f" LIMIT {limit} OFFSET {offset}"
66
66
 
67
67
  params = {}
68
- if search_query:
69
- search_clauses = [f"{column} LIKE :search_query" for column in search_fields]
70
- where_clause = f" WHERE ({' OR '.join(search_clauses)})"
71
- params['search_query'] = f"%{search_query}%"
72
-
73
- # apply filters
74
- # filters = {
75
- # "column1": "value1",
76
- # "column2": "value2" # AND condition
77
- # }
68
+ if search_query is not None:
69
+ sq = search_query.strip()
70
+ if sq:
71
+ search_clauses = [f"{column} LIKE :search_query" for column in search_fields]
72
+ where_clause = f" WHERE ({' OR '.join(search_clauses)})"
73
+ params['search_query'] = f"%{sq}%"
74
+
78
75
  if filters:
79
76
  filter_clauses = [f"{column} = :filter_{column}" for column in filters.keys()]
80
77
  if where_clause == "":
@@ -82,13 +79,13 @@ class Viewer:
82
79
  else:
83
80
  where_clause += f" AND ({' AND '.join(filter_clauses)})"
84
81
  for column, value in filters.items():
85
- params[f"filter_{column}"] = value # filter placeholder prefixed with 'filter_'
82
+ params[f"filter_{column}"] = value
86
83
 
87
84
  query = f"{base_query}{where_clause}{order_clause}{limit_clause}"
88
85
  stmt = text(query).bindparams(**params)
89
86
  with self.database.get_db().connect() as conn:
90
87
  result = conn.execute(stmt).fetchall()
91
- return result
88
+ return [tuple(r) for r in result]
92
89
 
93
90
  def count_rows(
94
91
  self,
@@ -116,15 +113,12 @@ class Viewer:
116
113
  else:
117
114
  search_fields = tables[table]['search_fields']
118
115
 
119
- if search_query:
120
- where_clause = f" WHERE {' OR '.join([f'{column} LIKE :search_query' for column in search_fields])}"
121
- params['search_query'] = f"%{search_query}%"
116
+ if search_query is not None:
117
+ sq = search_query.strip()
118
+ if sq:
119
+ where_clause = f" WHERE {' OR '.join([f'{column} LIKE :search_query' for column in search_fields])}"
120
+ params['search_query'] = f"%{sq}%"
122
121
 
123
- # apply filters
124
- # filters = {
125
- # "column1": "value1",
126
- # "column2": "value2" # AND condition
127
- # }
128
122
  if filters:
129
123
  filter_clauses = [f"{column} = :filter_{column}" for column in filters.keys()]
130
124
  if where_clause == "":
@@ -132,13 +126,13 @@ class Viewer:
132
126
  else:
133
127
  where_clause += f" AND ({' AND '.join(filter_clauses)})"
134
128
  for column, value in filters.items():
135
- params[f"filter_{column}"] = value # filter placeholder prefixed with 'filter_'
129
+ params[f"filter_{column}"] = value
136
130
 
137
131
  query = f"{base_query}{where_clause}"
138
132
  stmt = text(query).bindparams(**params)
139
133
  with self.database.get_db().connect() as conn:
140
134
  count = conn.execute(stmt).scalar()
141
- return count
135
+ return int(count) if count is not None else 0
142
136
 
143
137
  def is_auto_backup(self) -> bool:
144
138
  """
@@ -154,14 +148,12 @@ class Viewer:
154
148
 
155
149
  :param data: Dictionary with table and row_id keys
156
150
  """
157
- # create backup
158
151
  if self.is_auto_backup():
159
152
  backup_path = self.database.make_backup()
160
153
  if backup_path:
161
154
  msg = f"[DB] Created DB backup: {backup_path}"
162
155
  self.log(msg)
163
156
 
164
- # delete row
165
157
  with self.database.get_db().begin() as conn:
166
158
  conn.execute(
167
159
  text(f"DELETE FROM {data['table']} WHERE id = :row_id")
@@ -184,35 +176,30 @@ class Viewer:
184
176
  timestamp_columns = tables[data['table']]['timestamp_columns']
185
177
  primary_key = tables[data['table']]['primary_key']
186
178
 
187
- # check JSON
188
179
  if field in json_columns or field.endswith("_json"):
189
180
  try:
190
- value = json.dumps(json.loads(value)) # validate and pack JSON
181
+ value = json.dumps(json.loads(value))
191
182
  except:
192
183
  raise ValueError(f"Invalid JSON value for column {field}")
193
184
 
194
- # check timestamp
195
185
  if field in timestamp_columns or field.endswith("_ts"):
196
186
  try:
197
187
  value = int(value)
198
188
  except:
199
189
  raise ValueError(f"Invalid timestamp value for column {field}")
200
190
 
201
- # check foreign id field
202
191
  if field.endswith("_id"):
203
192
  try:
204
193
  value = int(value)
205
194
  except:
206
195
  raise ValueError(f"Invalid _id value for column {field}")
207
196
 
208
- # create backup
209
197
  if self.is_auto_backup():
210
198
  backup_path = self.database.make_backup()
211
199
  if backup_path:
212
200
  msg = f"[DB] Created DB backup: {backup_path}"
213
201
  self.log(msg)
214
202
 
215
- # update row
216
203
  with self.database.get_db().begin() as conn:
217
204
  conn.execute(
218
205
  text(f"UPDATE {data['table']} SET {data['field']} = :value WHERE {primary_key} = :id")
@@ -229,17 +216,15 @@ class Viewer:
229
216
  :param data: Dictionary with table key
230
217
  :param reset: Reset table sequence
231
218
  """
232
- # create backup
233
219
  if self.is_auto_backup():
234
220
  backup_path = self.database.make_backup()
235
221
  if backup_path:
236
222
  msg = f"[DB] Created DB backup: {backup_path}"
237
223
  self.log(msg)
238
224
 
239
- # truncate table
240
225
  with self.database.get_db().begin() as conn:
241
226
  conn.execute(text(f"DELETE FROM {data['table']}"))
242
- if reset: # reset table sequence (autoincrement)
227
+ if reset:
243
228
  conn.execute(text(f"DELETE FROM sqlite_sequence WHERE name='{data['table']}'"))
244
229
  msg = f"[DB] Truncated table {data['table']}"
245
230
  else:
@@ -6,13 +6,24 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2025.08.12 19:00:00 #
9
+ # Updated Date: 2025.09.05 18:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import io
13
+ from dataclasses import dataclass, field
13
14
 
15
+
16
+ @dataclass(slots=True)
14
17
  class PidData:
15
18
 
19
+ pid: object = None
20
+ meta: object = None
21
+ images_appended: list = field(default_factory=list)
22
+ urls_appended: list = field(default_factory=list)
23
+ files_appended: list = field(default_factory=list)
24
+ _buffer: io.StringIO = field(default_factory=io.StringIO)
25
+ is_cmd: bool = False
26
+
16
27
  def __init__(self, pid, meta=None):
17
28
  """Pid Data"""
18
29
  self.pid = pid
@@ -1013,11 +1013,7 @@ class Body:
1013
1013
  // - Otherwise hide the FAB to prevent overlap and noise.
1014
1014
  let action = 'none'; // 'up' | 'down' | 'none'
1015
1015
  if (atBottom) {
1016
- if (loaderHidden) {
1017
- action = 'up';
1018
- } else {
1019
- action = 'none';
1020
- }
1016
+ action = 'up';
1021
1017
  } else {
1022
1018
  if (dist >= SHOW_DOWN_THRESHOLD_PX) {
1023
1019
  action = 'down';
@@ -6,13 +6,15 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2025.08.06 19:00:00 #
9
+ # Updated Date: 2025.09.05 18:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from datetime import datetime
13
13
  from typing import Dict, Any, Optional
14
+ from dataclasses import dataclass, field
14
15
 
15
16
 
17
+ @dataclass(slots=True)
16
18
  class Tab:
17
19
 
18
20
  # types
@@ -24,6 +26,27 @@ class Tab:
24
26
  TAB_TOOL_CALENDAR = 4
25
27
  TAB_TOOL = 100
26
28
 
29
+ uuid: Optional[str] = None
30
+ pid: Optional[int] = None
31
+ idx: Optional[int] = 0
32
+ type: Optional[int] = TAB_CHAT
33
+ title: Optional[str] = ""
34
+ icon: Optional[str] = None
35
+ tooltip: Optional[str] = None
36
+ data_id: Optional[str] = None
37
+ new_idx: Optional[int] = None
38
+ custom_name: Optional[bool] = False
39
+ child: Optional[Any] = None
40
+ parent: Optional[Any] = None
41
+ column_idx: Optional[int] = 0
42
+ tool_id: Optional[str] = None
43
+ on_delete: Optional[callable] = None
44
+
45
+ loaded: bool = False
46
+ refs: list = field(default_factory=list)
47
+ created_at: datetime = field(default_factory=datetime.now)
48
+ updated_at: datetime = field(default_factory=datetime.now)
49
+
27
50
  def __init__(
28
51
  self,
29
52
  uuid: Optional[str] = None,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "__meta__": {
3
- "version": "2.6.37",
4
- "app.version": "2.6.37",
3
+ "version": "2.6.38",
4
+ "app.version": "2.6.38",
5
5
  "updated_at": "2025-09-05T00:00:00"
6
6
  },
7
7
  "access.audio.event.speech": false,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "__meta__": {
3
- "version": "2.6.37",
4
- "app.version": "2.6.37",
3
+ "version": "2.6.38",
4
+ "app.version": "2.6.38",
5
5
  "updated_at": "2025-09-05T08:03:34"
6
6
  },
7
7
  "items": {
@@ -6,16 +6,33 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2025.09.02 22:00:00 #
9
+ # Updated Date: 2025.09.05 18:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import json
13
13
  import time
14
+ from dataclasses import dataclass, field
15
+ from typing import Optional
14
16
 
15
17
  from pygpt_net.item.attachment import AttachmentItem
16
18
 
17
19
 
20
+ @dataclass(slots=True)
18
21
  class AssistantItem:
22
+ id: Optional[object] = None
23
+ name: Optional[object] = None
24
+ description: Optional[object] = None
25
+ instructions: Optional[object] = None
26
+ model: Optional[object] = None
27
+ meta: dict = field(default_factory=dict)
28
+ files: dict = field(default_factory=dict)
29
+ attachments: dict = field(default_factory=dict)
30
+ vector_store: str = ""
31
+ tools: dict = field(default_factory=lambda: {
32
+ "code_interpreter": False,
33
+ "file_search": False,
34
+ "function": [],
35
+ })
19
36
 
20
37
  def __init__(self):
21
38
  """Assistant item"""
@@ -203,7 +220,26 @@ class AssistantItem:
203
220
  return self.dump()
204
221
 
205
222
 
223
+ @dataclass(slots=True)
206
224
  class AssistantStoreItem:
225
+ id: Optional[object] = None
226
+ record_id: Optional[object] = None
227
+ uuid: Optional[object] = None
228
+ name: Optional[object] = None
229
+ description: Optional[object] = None
230
+ status: dict = field(default_factory=dict)
231
+ last_status: str = ""
232
+ expire_days: int = 0
233
+ usage_bytes: int = 0
234
+ bytes: int = 0
235
+ num_files: int = 0
236
+ is_thread: bool = False
237
+ created: int = 0
238
+ updated: int = 0
239
+ last_active: int = 0
240
+ last_sync: int = 0
241
+ file_ids: list = field(default_factory=list)
242
+
207
243
  def __init__(self):
208
244
  """Assistant vector store item"""
209
245
  self.id = None
@@ -300,7 +336,20 @@ class AssistantStoreItem:
300
336
  return self.dump()
301
337
 
302
338
 
339
+ @dataclass(slots=True)
303
340
  class AssistantFileItem:
341
+ id: Optional[object] = None
342
+ record_id: Optional[object] = None
343
+ name: Optional[object] = None
344
+ path: Optional[object] = None
345
+ file_id: Optional[object] = None
346
+ store_id: Optional[object] = None
347
+ thread_id: Optional[object] = None
348
+ uuid: Optional[object] = None
349
+ size: int = 0
350
+ created: int = 0
351
+ updated: int = 0
352
+
304
353
  def __init__(self):
305
354
  """Assistant file item"""
306
355
  self.id = None
@@ -379,4 +428,4 @@ class AssistantFileItem:
379
428
 
380
429
  def __str__(self):
381
430
  """To string"""
382
- return self.dump()
431
+ return self.dump()