pygpt-net 2.4.30__py3-none-any.whl → 2.4.35__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 (135) hide show
  1. CHANGELOG.md +32 -0
  2. README.md +2105 -1892
  3. pygpt_net/CHANGELOG.txt +32 -0
  4. pygpt_net/__init__.py +3 -3
  5. pygpt_net/controller/access/__init__.py +5 -5
  6. pygpt_net/controller/access/control.py +3 -2
  7. pygpt_net/controller/attachment.py +68 -1
  8. pygpt_net/controller/audio/__init__.py +34 -6
  9. pygpt_net/controller/chat/__init__.py +3 -1
  10. pygpt_net/controller/chat/attachment.py +263 -38
  11. pygpt_net/controller/chat/audio.py +99 -0
  12. pygpt_net/controller/chat/input.py +10 -3
  13. pygpt_net/controller/chat/output.py +4 -1
  14. pygpt_net/controller/chat/text.py +7 -3
  15. pygpt_net/controller/dialogs/confirm.py +17 -1
  16. pygpt_net/controller/lang/custom.py +3 -1
  17. pygpt_net/controller/mode.py +2 -1
  18. pygpt_net/controller/painter/capture.py +2 -2
  19. pygpt_net/controller/presets/editor.py +15 -2
  20. pygpt_net/controller/ui/__init__.py +4 -1
  21. pygpt_net/core/access/voice.py +2 -2
  22. pygpt_net/core/agents/legacy.py +3 -1
  23. pygpt_net/core/attachments/__init__.py +14 -9
  24. pygpt_net/core/attachments/context.py +226 -44
  25. pygpt_net/core/{audio.py → audio/__init__.py} +1 -1
  26. pygpt_net/core/audio/context.py +34 -0
  27. pygpt_net/core/bridge/context.py +29 -1
  28. pygpt_net/core/ctx/__init__.py +4 -1
  29. pygpt_net/core/db/__init__.py +4 -2
  30. pygpt_net/core/debug/attachments.py +3 -1
  31. pygpt_net/core/debug/context.py +5 -1
  32. pygpt_net/core/debug/presets.py +3 -1
  33. pygpt_net/core/events/event.py +2 -1
  34. pygpt_net/core/experts/__init__.py +3 -1
  35. pygpt_net/core/idx/chat.py +28 -6
  36. pygpt_net/core/idx/indexing.py +123 -15
  37. pygpt_net/core/modes.py +3 -1
  38. pygpt_net/core/presets.py +13 -2
  39. pygpt_net/core/render/markdown/pid.py +2 -1
  40. pygpt_net/core/render/plain/pid.py +2 -1
  41. pygpt_net/core/render/web/body.py +34 -12
  42. pygpt_net/core/render/web/pid.py +2 -1
  43. pygpt_net/core/render/web/renderer.py +8 -3
  44. pygpt_net/core/tokens.py +4 -2
  45. pygpt_net/core/types/mode.py +2 -1
  46. pygpt_net/data/config/config.json +7 -5
  47. pygpt_net/data/config/models.json +190 -5
  48. pygpt_net/data/config/modes.json +11 -5
  49. pygpt_net/data/config/presets/current.audio.json +34 -0
  50. pygpt_net/data/config/settings.json +15 -1
  51. pygpt_net/data/css/web.css +70 -0
  52. pygpt_net/data/css/web.dark.css +4 -1
  53. pygpt_net/data/css/web.light.css +1 -1
  54. pygpt_net/data/locale/locale.de.ini +27 -14
  55. pygpt_net/data/locale/locale.en.ini +63 -47
  56. pygpt_net/data/locale/locale.es.ini +27 -14
  57. pygpt_net/data/locale/locale.fr.ini +29 -16
  58. pygpt_net/data/locale/locale.it.ini +27 -14
  59. pygpt_net/data/locale/locale.pl.ini +31 -18
  60. pygpt_net/data/locale/locale.uk.ini +27 -14
  61. pygpt_net/data/locale/locale.zh.ini +34 -21
  62. pygpt_net/data/locale/plugin.cmd_files.de.ini +4 -4
  63. pygpt_net/data/locale/plugin.cmd_files.en.ini +4 -4
  64. pygpt_net/data/locale/plugin.cmd_files.es.ini +4 -4
  65. pygpt_net/data/locale/plugin.cmd_files.fr.ini +4 -4
  66. pygpt_net/data/locale/plugin.cmd_files.it.ini +4 -4
  67. pygpt_net/data/locale/plugin.cmd_files.pl.ini +4 -4
  68. pygpt_net/data/locale/plugin.cmd_files.uk.ini +4 -4
  69. pygpt_net/data/locale/plugin.cmd_files.zh.ini +4 -4
  70. pygpt_net/data/locale/plugin.cmd_web.de.ini +5 -5
  71. pygpt_net/data/locale/plugin.cmd_web.en.ini +5 -5
  72. pygpt_net/data/locale/plugin.cmd_web.es.ini +5 -5
  73. pygpt_net/data/locale/plugin.cmd_web.fr.ini +5 -5
  74. pygpt_net/data/locale/plugin.cmd_web.it.ini +5 -5
  75. pygpt_net/data/locale/plugin.cmd_web.pl.ini +5 -5
  76. pygpt_net/data/locale/plugin.cmd_web.uk.ini +5 -5
  77. pygpt_net/data/locale/plugin.cmd_web.zh.ini +5 -5
  78. pygpt_net/data/locale/plugin.idx_llama_index.de.ini +12 -12
  79. pygpt_net/data/locale/plugin.idx_llama_index.en.ini +12 -12
  80. pygpt_net/data/locale/plugin.idx_llama_index.es.ini +12 -12
  81. pygpt_net/data/locale/plugin.idx_llama_index.fr.ini +12 -12
  82. pygpt_net/data/locale/plugin.idx_llama_index.it.ini +12 -12
  83. pygpt_net/data/locale/plugin.idx_llama_index.pl.ini +12 -12
  84. pygpt_net/data/locale/plugin.idx_llama_index.uk.ini +12 -12
  85. pygpt_net/data/locale/plugin.idx_llama_index.zh.ini +12 -12
  86. pygpt_net/data/win32/USER-LICENSE.rtf +0 -0
  87. pygpt_net/data/win32/banner.bmp +0 -0
  88. pygpt_net/data/win32/banner_welcome.bmp +0 -0
  89. pygpt_net/item/attachment.py +9 -1
  90. pygpt_net/item/ctx.py +9 -1
  91. pygpt_net/item/preset.py +5 -1
  92. pygpt_net/launcher.py +3 -1
  93. pygpt_net/migrations/Version20241126170000.py +28 -0
  94. pygpt_net/migrations/__init__.py +3 -1
  95. pygpt_net/plugin/audio_input/__init__.py +11 -1
  96. pygpt_net/plugin/audio_input/worker.py +9 -1
  97. pygpt_net/plugin/audio_output/__init__.py +37 -7
  98. pygpt_net/plugin/audio_output/worker.py +38 -41
  99. pygpt_net/plugin/cmd_code_interpreter/runner.py +2 -2
  100. pygpt_net/plugin/cmd_mouse_control/__init__.py +4 -2
  101. pygpt_net/plugin/openai_dalle/__init__.py +3 -1
  102. pygpt_net/plugin/openai_vision/__init__.py +3 -1
  103. pygpt_net/provider/core/attachment/json_file.py +4 -1
  104. pygpt_net/provider/core/config/patch.py +22 -0
  105. pygpt_net/provider/core/ctx/db_sqlite/storage.py +14 -4
  106. pygpt_net/provider/core/ctx/db_sqlite/utils.py +19 -2
  107. pygpt_net/provider/core/model/patch.py +7 -1
  108. pygpt_net/provider/core/preset/json_file.py +5 -1
  109. pygpt_net/provider/gpt/__init__.py +14 -2
  110. pygpt_net/provider/gpt/audio.py +63 -0
  111. pygpt_net/provider/gpt/chat.py +76 -44
  112. pygpt_net/provider/gpt/utils.py +27 -0
  113. pygpt_net/provider/gpt/vision.py +37 -15
  114. pygpt_net/provider/loaders/base.py +10 -1
  115. pygpt_net/provider/loaders/web_yt.py +19 -1
  116. pygpt_net/tools/image_viewer/ui/dialogs.py +3 -1
  117. pygpt_net/ui/dialog/about.py +1 -1
  118. pygpt_net/ui/dialog/preset.py +3 -1
  119. pygpt_net/ui/dialog/url.py +29 -0
  120. pygpt_net/ui/dialogs.py +5 -1
  121. pygpt_net/ui/layout/chat/attachments.py +42 -6
  122. pygpt_net/ui/layout/chat/attachments_ctx.py +14 -4
  123. pygpt_net/ui/layout/chat/attachments_uploaded.py +8 -4
  124. pygpt_net/ui/widget/anims/toggles.py +2 -2
  125. pygpt_net/ui/widget/dialog/url.py +59 -0
  126. pygpt_net/ui/widget/lists/attachment.py +22 -17
  127. pygpt_net/ui/widget/lists/attachment_ctx.py +65 -3
  128. pygpt_net/ui/widget/option/checkbox.py +1 -3
  129. pygpt_net/ui/widget/option/toggle.py +1 -0
  130. pygpt_net/ui/widget/textarea/url.py +43 -0
  131. {pygpt_net-2.4.30.dist-info → pygpt_net-2.4.35.dist-info}/METADATA +2107 -1894
  132. {pygpt_net-2.4.30.dist-info → pygpt_net-2.4.35.dist-info}/RECORD +135 -124
  133. {pygpt_net-2.4.30.dist-info → pygpt_net-2.4.35.dist-info}/LICENSE +0 -0
  134. {pygpt_net-2.4.30.dist-info → pygpt_net-2.4.35.dist-info}/WHEEL +0 -0
  135. {pygpt_net-2.4.30.dist-info → pygpt_net-2.4.35.dist-info}/entry_points.txt +0 -0
@@ -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.11.23 21:00:00 #
9
+ # Updated Date: 2024.11.26 04:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import copy
@@ -31,6 +31,11 @@ class Context:
31
31
  """
32
32
  self.window = window
33
33
  self.dir_index = "index"
34
+ self.last_used_item = None
35
+ self.last_used_content = None
36
+ self.last_used_context = None
37
+ self.last_files = []
38
+ self.last_urls = []
34
39
  self.summary_prompt = """
35
40
  Summarize the text below by extracting the most important information,
36
41
  especially those that may help answer the question:
@@ -87,16 +92,38 @@ class Context:
87
92
  context = ""
88
93
  if os.path.exists(meta_path) and os.path.isdir(meta_path):
89
94
  for file in meta.additional_ctx:
90
- if "type" not in file or file["type"] != "local_file":
95
+ if ("type" not in file
96
+ or file["type"] not in ["local_file", "url"]):
91
97
  continue
92
98
  file_id = file["uuid"]
93
99
  file_idx_path = os.path.join(meta_path, file_id)
94
100
  text_path = os.path.join(file_idx_path, file_id + ".txt")
101
+ store_path = file["path"]
102
+ if "real_path" in file:
103
+ store_path = file["real_path"]
95
104
  if filename:
96
- context += "Filename: {}\n".format(file["name"]) + "\n"
105
+ if file["type"] == "url":
106
+ context += "URL: {}\n".format(file["path"]) + "\n"
107
+ else:
108
+ context += "Filename: {}\n".format(file["name"]) + "\n"
109
+
110
+ # store used files and URLs in ctx
111
+ if file["type"] == "url":
112
+ if store_path not in self.last_urls:
113
+ self.last_urls.append(store_path)
114
+ else:
115
+ if store_path not in self.last_files:
116
+ self.last_files.append(store_path)
117
+
97
118
  if os.path.exists(text_path):
98
- with open(text_path, "r") as f:
99
- context += f.read() + "\n\n"
119
+ try:
120
+ with open(text_path, "r", encoding="utf-8") as f:
121
+ context += f.read() + "\n\n"
122
+ except Exception as e:
123
+ print("Attachments: read error: {}".format(e))
124
+
125
+ self.last_used_content = context
126
+ self.last_used_context = context
100
127
  return context
101
128
 
102
129
  def query_context(self, meta: CtxMeta, query: str) -> str:
@@ -111,14 +138,55 @@ class Context:
111
138
  if not os.path.exists(meta_path) or not os.path.isdir(meta_path):
112
139
  return ""
113
140
  idx_path = os.path.join(self.get_dir(meta), self.dir_index)
114
- model = None
115
- result = self.window.core.idx.chat.query_attachment(query, idx_path, model)
141
+
142
+ indexed = False
143
+ # index files if not indexed by auto_index
144
+ for i, file in enumerate(meta.additional_ctx):
145
+ if "indexed" not in file or not file["indexed"]:
146
+ file_id = file["uuid"]
147
+ file_idx_path = os.path.join(meta_path, file_id)
148
+ file_path = os.path.join(file_idx_path, file["name"])
149
+ type = AttachmentItem.TYPE_FILE
150
+ source = file_path
151
+ if "type" in file:
152
+ if file["type"] == "url":
153
+ type = AttachmentItem.TYPE_URL
154
+ source = file["path"] # URL
155
+ doc_ids = self.index_attachment(type, source, idx_path)
156
+ file["indexed"] = True
157
+ file["doc_ids"] = doc_ids
158
+ indexed = True
159
+
160
+ if indexed:
161
+ # update ctx in DB
162
+ self.window.core.ctx.replace(meta)
163
+ self.window.core.ctx.save(meta.id)
164
+
165
+ model, model_item = self.get_selected_model("query")
166
+ result = self.window.core.idx.chat.query_attachment(query, idx_path, model_item)
167
+ self.last_used_context = result
116
168
 
117
169
  if self.is_verbose():
118
170
  print("Attachments: query result: {}".format(result))
119
171
 
120
172
  return result
121
173
 
174
+ def get_selected_model(self, mode: str = "summary"):
175
+ """
176
+ Get selected model for attachments
177
+
178
+ :return: model name, model item
179
+ """
180
+ model_item = None
181
+ model = None
182
+ if mode == "summary":
183
+ model = self.window.core.config.get("ctx.attachment.summary.model", "gpt-4o-mini")
184
+ elif mode == "query":
185
+ model = self.window.core.config.get("ctx.attachment.query.model", "gpt-4o-mini")
186
+ if model:
187
+ model_item = self.window.core.models.get(model)
188
+ return model, model_item
189
+
122
190
  def summary_context(self, ctx: CtxItem, query: str) -> str:
123
191
  """
124
192
  Get summary of the context
@@ -127,20 +195,17 @@ class Context:
127
195
  :param query: query string
128
196
  :return: query result
129
197
  """
130
- model_item = None
131
- model = self.window.core.config.get("ctx.attachment.summary.model", "gpt-4o-mini")
132
- if model:
133
- model_item = self.window.core.models.get(model)
134
-
198
+ model, model_item = self.get_selected_model("summary")
135
199
  if model_item is None:
136
200
  raise Exception("Attachments: summary model not found: {}".format(model))
137
201
 
138
202
  if self.is_verbose():
139
203
  print("Attachments: using summary model: {}".format(model))
140
204
 
205
+ content = self.get_context_text(ctx, filename=True)
141
206
  prompt = self.summary_prompt.format(
142
207
  query=str(query).strip(),
143
- content=str(self.get_context_text(ctx, filename=True)).strip(),
208
+ content=str(content).strip(),
144
209
  )
145
210
  if self.is_verbose():
146
211
  print("Attachments: summary prompt: {}".format(prompt))
@@ -158,17 +223,27 @@ class Context:
158
223
  })
159
224
  self.window.dispatch(event)
160
225
  response = event.data.get("response")
226
+ self.last_used_context = response
161
227
  if self.is_verbose():
162
228
  print("Attachments: summary received: {}".format(response))
163
229
  return response
164
230
 
165
- def upload(self, meta: CtxMeta, attachment: AttachmentItem, prompt: str) -> dict:
231
+ def upload(
232
+ self,
233
+ meta: CtxMeta,
234
+ attachment: AttachmentItem,
235
+ prompt: str,
236
+ auto_index: bool = False,
237
+ real_path: str = None
238
+ ) -> dict:
166
239
  """
167
240
  Upload attachment for context
168
241
 
169
242
  :param meta: CtxMeta instance
170
243
  :param attachment: AttachmentItem instance
171
244
  :param prompt: user input prompt
245
+ :param auto_index: auto index
246
+ :param real_path: real path
172
247
  :return: Dict with attachment data
173
248
  """
174
249
  if self.is_verbose():
@@ -180,64 +255,147 @@ class Context:
180
255
  meta_path = self.get_dir(meta)
181
256
  file_idx_path = os.path.join(meta_path, file_id)
182
257
  index_path = os.path.join(meta_path, self.dir_index)
258
+
183
259
  os.makedirs(meta_path, exist_ok=True)
184
260
  os.makedirs(file_idx_path, exist_ok=True)
185
261
 
186
262
  if self.is_verbose():
187
263
  print("Attachments: created path: {}".format(meta_path))
188
- print("Attachments: vector index path: {}".format(index_path))
264
+ if auto_index:
265
+ print("Attachments: vector index path: {}".format(index_path))
189
266
 
190
- # copy raw file
191
- raw_path = os.path.join(file_idx_path, name)
192
- if os.path.exists(raw_path):
193
- os.remove(raw_path)
194
- copyfile(attachment.path, raw_path)
267
+ # store content to read
268
+ src_file = self.store_content(attachment, file_idx_path)
195
269
 
196
270
  # extract text content using data loader
197
- loader_kwargs = {
198
- "prompt": prompt,
199
- } # extra loader kwargs
200
- text = self.window.core.idx.indexing.read_text_content(
201
- path=raw_path,
202
- loader_kwargs=loader_kwargs,
203
- )
204
- if text:
271
+ content = self.read_content(attachment, src_file, prompt)
272
+ if content:
205
273
  text_path = os.path.join(file_idx_path, file_id + ".txt")
206
- with open(text_path, "w") as f:
207
- f.write(text)
208
-
274
+ with open(text_path, "w", encoding="utf-8") as f:
275
+ f.write(content)
209
276
  if self.is_verbose():
210
- print("Attachments: read text content: {}".format(text))
277
+ print("Attachments: read text content: {}".format(content))
211
278
 
212
279
  tokens = 0
213
- if text:
214
- tokens = self.window.core.tokens.from_str(text)
280
+ if content:
281
+ tokens = self.window.core.tokens.from_str(content)
215
282
 
216
- # index file to ctx index
217
- model = None
218
- doc_ids = self.window.core.idx.indexing.index_attachment(attachment.path, index_path, model)
283
+ type = "local_file"
284
+ size = 0
285
+ if attachment.type == AttachmentItem.TYPE_FILE:
286
+ size = os.path.getsize(attachment.path)
287
+ elif attachment.type == AttachmentItem.TYPE_URL:
288
+ size = os.path.getsize(src_file)
289
+ type = "url" # extra ctx type
219
290
 
220
- if self.is_verbose():
221
- print("Attachments: indexed. Doc IDs: {}".format(doc_ids))
291
+ # index file to ctx index
292
+ doc_ids = []
293
+ if auto_index:
294
+ source = src_file
295
+ if attachment.type == AttachmentItem.TYPE_URL:
296
+ source = attachment.path # URL
297
+ doc_ids = self.index_attachment(attachment.type, source, index_path)
222
298
 
223
299
  result = {
224
300
  "name": name,
225
301
  "path": attachment.path,
226
- "type": "local_file",
302
+ "type": type,
227
303
  "uuid": str(file_id),
228
- "doc_ids": doc_ids,
229
- "indexed": True,
230
304
  "content_type": "text",
231
- "size": os.path.getsize(attachment.path),
232
- "length": len(text),
305
+ "size": size,
306
+ "length": len(content),
233
307
  "tokens": tokens,
308
+ "indexed": False,
234
309
  }
310
+ if auto_index:
311
+ result["indexed"] = True
312
+ result["doc_ids"] = doc_ids
313
+
314
+ if real_path:
315
+ result["real_path"] = real_path
235
316
 
236
317
  if self.is_verbose():
237
318
  print("Attachments: uploaded: {}".format(result))
238
319
 
239
320
  return result
240
321
 
322
+ def read_content(self, attachment: AttachmentItem, path: str, prompt: str) -> str:
323
+ """
324
+ Read content from attachment
325
+
326
+ :param attachment: AttachmentItem instance
327
+ :param path: source file path
328
+ :param prompt: user input prompt
329
+ :return: content
330
+ """
331
+ content = ""
332
+ if attachment.type == AttachmentItem.TYPE_FILE:
333
+ loader_kwargs = {
334
+ "prompt": prompt,
335
+ } # extra loader kwargs
336
+ content = self.window.core.idx.indexing.read_text_content(
337
+ path=path,
338
+ loader_kwargs=loader_kwargs,
339
+ )
340
+ elif attachment.type == AttachmentItem.TYPE_URL:
341
+ # directly from path
342
+ with open(path, "r", encoding="utf-8") as f:
343
+ content = f.read() # already crawled
344
+
345
+ return content
346
+
347
+ def store_content(self, attachment: AttachmentItem, dir: str) -> str:
348
+ """
349
+ Prepare content for attachment
350
+
351
+ :param attachment: AttachmentItem instance
352
+ :param dir: directory to save content
353
+ :return: content
354
+ """
355
+ path = None
356
+ if attachment.type == AttachmentItem.TYPE_FILE:
357
+ # copy raw file
358
+ name = os.path.basename(attachment.path)
359
+ path = os.path.join(dir, name)
360
+ if os.path.exists(path):
361
+ os.remove(path)
362
+ copyfile(attachment.path, path)
363
+ elif attachment.type == AttachmentItem.TYPE_URL:
364
+ web_type = self.window.core.idx.indexing.get_webtype(attachment.path)
365
+ content = self.window.core.idx.indexing.read_web_content(
366
+ url=attachment.path,
367
+ type=web_type, # webpage, default, TODO: add more types
368
+ extra_args={},
369
+ )
370
+ # src file save
371
+ name = "url.txt"
372
+ path = os.path.join(dir, name)
373
+ if os.path.exists(path):
374
+ os.remove(path)
375
+ with open(path, "w", encoding="utf-8") as f:
376
+ f.write(content)
377
+ return path
378
+
379
+ def index_attachment(self, type: str, source: str, idx_path: str, documents: list = None) -> list:
380
+ """
381
+ Index attachment
382
+
383
+ :param type: attachment type
384
+ :param source: source file or URL
385
+ :param idx_path: index path
386
+ :param documents: list of documents (optional)
387
+ :return: list of doc IDs
388
+ """
389
+ model = None
390
+ doc_ids = []
391
+ if type == AttachmentItem.TYPE_FILE:
392
+ doc_ids = self.window.core.idx.indexing.index_attachment(source, idx_path, model, documents)
393
+ elif type == AttachmentItem.TYPE_URL:
394
+ doc_ids = self.window.core.idx.indexing.index_attachment_web(source, idx_path, model, documents)
395
+ if self.is_verbose():
396
+ print("Attachments: indexed. Doc IDs: {}".format(doc_ids))
397
+ return doc_ids
398
+
241
399
  def duplicate(self, from_meta_id: int, to_meta_id: int) -> bool:
242
400
  """
243
401
  Duplicate attachments from one meta to another
@@ -390,6 +548,30 @@ class Context:
390
548
  except Exception as e:
391
549
  self.window.core.debug.error("Attachment.truncate", e)
392
550
 
551
+ def reset(self):
552
+ """Reset context info"""
553
+ self.last_used_item = None
554
+ self.last_used_content = None
555
+ self.last_used_context = None
556
+ self.last_files = []
557
+ self.last_urls = []
558
+
559
+ def get_used_files(self) -> list:
560
+ """
561
+ Get last used files
562
+
563
+ :return: list of files
564
+ """
565
+ return self.last_files
566
+
567
+ def get_used_urls(self) -> list:
568
+ """
569
+ Get last used URLs
570
+
571
+ :return: list of URLs
572
+ """
573
+ return self.last_urls
574
+
393
575
  def is_verbose(self) -> bool:
394
576
  """
395
577
  Check if verbose mode is enabled
@@ -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.02.24 00:00:00 #
9
+ # Updated Date: 2024.11.26 19:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import re
@@ -0,0 +1,34 @@
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: 2024.11.21 20:00:00 #
10
+ # ================================================== #
11
+
12
+ class AudioContext:
13
+ def __init__(self, **kwargs):
14
+ """
15
+ Audio context
16
+
17
+ :param kwargs: keyword arguments
18
+ """
19
+ self.data = kwargs.get("ctx", None)
20
+ self.prev_id = kwargs.get("prev_id", None)
21
+
22
+ def to_dict(self) -> dict:
23
+ """
24
+ Return as dictionary
25
+
26
+ :return: dictionary
27
+ """
28
+ data = {
29
+ "data": self.data,
30
+ "prev_id": self.prev_id
31
+ }
32
+ # sort by keys
33
+ data = dict(sorted(data.items(), key=lambda item: item[0]))
34
+ return data
@@ -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.11.21 20:00:00 #
9
+ # Updated Date: 2024.11.26 19:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from pygpt_net.item.ctx import CtxItem
@@ -39,6 +39,7 @@ class BridgeContext:
39
39
  self.attachments = kwargs.get("attachments", [])
40
40
  self.file_ids = kwargs.get("file_ids", [])
41
41
  self.reply_context = kwargs.get("reply_ctx", None) # ReplyContext
42
+ self.multimodal_ctx = kwargs.get("multimodal_ctx", MultimodalContext()) # AudioContext
42
43
 
43
44
  # check types
44
45
  if self.ctx is not None and not isinstance(self.ctx, CtxItem):
@@ -78,6 +79,33 @@ class BridgeContext:
78
79
  if self.model is not None:
79
80
  data["model"] = self.model.to_dict()
80
81
 
82
+ # sort by keys
83
+ data = dict(sorted(data.items(), key=lambda item: item[0]))
84
+ return data
85
+
86
+ class MultimodalContext:
87
+ def __init__(self, **kwargs):
88
+ """
89
+ Multimodal context
90
+
91
+ :param kwargs: keyword arguments
92
+ """
93
+ self.is_audio_input = kwargs.get("is_audio_input", False)
94
+ self.is_audio_output = kwargs.get("is_audio_output", False)
95
+ self.audio_data = kwargs.get("audio_data", None)
96
+ self.audio_format = kwargs.get("audio_format", "wav")
97
+
98
+ def to_dict(self) -> dict:
99
+ """
100
+ Return as dictionary
101
+
102
+ :return: dictionary
103
+ """
104
+ data = {
105
+ "is_audio_input": self.is_audio_input,
106
+ "is_audio_output": self.is_audio_output,
107
+ "audio_format": self.audio_format,
108
+ }
81
109
  # sort by keys
82
110
  data = dict(sorted(data.items(), key=lambda item: item[0]))
83
111
  return data
@@ -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.11.21 20:00:00 #
9
+ # Updated Date: 2024.11.26 19:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import copy
@@ -19,6 +19,7 @@ from pygpt_net.core.types import (
19
19
  MODE_AGENT,
20
20
  MODE_AGENT_LLAMA,
21
21
  MODE_ASSISTANT,
22
+ MODE_AUDIO,
22
23
  MODE_CHAT,
23
24
  MODE_COMPLETION,
24
25
  MODE_EXPERT,
@@ -78,6 +79,7 @@ class Ctx:
78
79
  MODE_LLAMA_INDEX,
79
80
  MODE_AGENT,
80
81
  MODE_EXPERT,
82
+ MODE_AUDIO,
81
83
  ]
82
84
  self.allowed_modes = {
83
85
  MODE_CHAT: self.all_modes,
@@ -89,6 +91,7 @@ class Ctx:
89
91
  MODE_LLAMA_INDEX: self.all_modes,
90
92
  MODE_AGENT: self.all_modes,
91
93
  MODE_EXPERT: self.all_modes,
94
+ MODE_AUDIO: self.all_modes,
92
95
  MODE_AGENT_LLAMA: [MODE_AGENT_LLAMA],
93
96
  }
94
97
  self.current_sys_prompt = ""
@@ -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.11.23 00:00:00 #
9
+ # Updated Date: 2024.11.26 19:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import os
@@ -82,6 +82,8 @@ class Database:
82
82
  'is_internal',
83
83
  'docs_json',
84
84
  'external_id',
85
+ 'audio_id',
86
+ 'audio_expires_ts',
85
87
  ]
86
88
  columns["ctx_meta"] = [
87
89
  'id',
@@ -218,7 +220,7 @@ class Database:
218
220
  'columns': columns["ctx_item"],
219
221
  'sort_by': columns["ctx_item"],
220
222
  'search_fields': ['id', 'input', 'output', 'input_name', 'output_name', 'meta_id', 'hidden_input', 'hidden_output'],
221
- 'timestamp_columns': ['input_ts', 'output_ts'],
223
+ 'timestamp_columns': ['input_ts', 'output_ts', 'audio_expires_ts'],
222
224
  'json_columns': ['cmds_json', 'results_json', 'urls_json', 'images_json', 'files_json', 'attachments_json', 'docs_json', 'additional_ctx_json'],
223
225
  'default_sort': 'id',
224
226
  'default_order': 'DESC',
@@ -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.11.21 20:00:00 #
9
+ # Updated Date: 2024.11.26 02:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  class AttachmentsDebug:
@@ -36,6 +36,8 @@ class AttachmentsDebug:
36
36
  'send': attachment.send,
37
37
  'key': key,
38
38
  'mode': mode,
39
+ 'type': attachment.type,
40
+ 'consumed': attachment.consumed,
39
41
  }
40
42
  self.window.core.debug.add(self.id, attachment.name, str(data))
41
43
 
@@ -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.11.20 03:00:00 #
9
+ # Updated Date: 2024.11.26 04:00:00 #
10
10
  # ================================================== #
11
11
  import json
12
12
 
@@ -45,6 +45,10 @@ class ContextDebug:
45
45
  self.window.core.debug.add(self.id, 'CMD (current)', str(self.window.core.ctx.current_cmd))
46
46
  self.window.core.debug.add(self.id, 'CMD schema (current)', str(self.window.core.ctx.current_cmd_schema))
47
47
  self.window.core.debug.add(self.id, 'FUNCTIONS (current)', str(self.get_functions()))
48
+ self.window.core.debug.add(self.id, 'Attachments: last used content',
49
+ str(self.window.core.attachments.context.last_used_content))
50
+ self.window.core.debug.add(self.id, 'Attachments: last used context',
51
+ str(self.window.core.attachments.context.last_used_context))
48
52
 
49
53
  current = None
50
54
  if self.window.core.ctx.get_current() is not 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: 2024.11.21 20:00:00 #
9
+ # Updated Date: 2024.11.26 19:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import os
@@ -15,6 +15,7 @@ from pygpt_net.core.types import (
15
15
  MODE_AGENT,
16
16
  MODE_AGENT_LLAMA,
17
17
  MODE_ASSISTANT,
18
+ MODE_AUDIO,
18
19
  MODE_CHAT,
19
20
  MODE_COMPLETION,
20
21
  MODE_EXPERT,
@@ -64,6 +65,7 @@ class PresetsDebug:
64
65
  MODE_AGENT: preset.agent,
65
66
  MODE_AGENT_LLAMA: preset.agent_llama,
66
67
  MODE_EXPERT: preset.expert,
68
+ MODE_AUDIO: preset.audio,
67
69
  'temperature': preset.temperature,
68
70
  'version': preset.version,
69
71
  }
@@ -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.11.20 03:00:00 #
9
+ # Updated Date: 2024.11.26 19:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import json
@@ -27,6 +27,7 @@ class Event(BaseEvent):
27
27
  AUDIO_INPUT_TOGGLE = "audio.input.toggle"
28
28
  AUDIO_OUTPUT_STOP = "audio.output.stop"
29
29
  AUDIO_OUTPUT_TOGGLE = "audio.output.toggle"
30
+ AUDIO_PLAYBACK = "audio.playback"
30
31
  AUDIO_READ_TEXT = "audio.read_text"
31
32
  CMD_EXECUTE = "cmd.execute"
32
33
  CMD_INLINE = "cmd.inline"
@@ -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.11.21 20:00:00 #
9
+ # Updated Date: 2024.11.26 19:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
 
@@ -18,6 +18,7 @@ from pygpt_net.core.types import (
18
18
  MODE_LANGCHAIN,
19
19
  MODE_LLAMA_INDEX,
20
20
  MODE_VISION,
21
+ MODE_AUDIO,
21
22
  )
22
23
  from pygpt_net.core.bridge.context import BridgeContext
23
24
  from pygpt_net.core.events import Event, KernelEvent, RenderEvent
@@ -39,6 +40,7 @@ class Experts:
39
40
  MODE_VISION,
40
41
  MODE_LANGCHAIN,
41
42
  MODE_LLAMA_INDEX,
43
+ MODE_AUDIO,
42
44
  ]
43
45
  self.allowed_cmds = ["expert_call"]
44
46