pygpt-net 2.6.1__py3-none-any.whl → 2.6.6__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 (131) hide show
  1. pygpt_net/CHANGELOG.txt +23 -0
  2. pygpt_net/__init__.py +3 -3
  3. pygpt_net/app.py +20 -1
  4. pygpt_net/config.py +55 -65
  5. pygpt_net/controller/__init__.py +5 -2
  6. pygpt_net/controller/calendar/note.py +101 -126
  7. pygpt_net/controller/chat/chat.py +38 -35
  8. pygpt_net/controller/chat/render.py +154 -214
  9. pygpt_net/controller/chat/response.py +5 -3
  10. pygpt_net/controller/chat/stream.py +92 -27
  11. pygpt_net/controller/config/config.py +39 -42
  12. pygpt_net/controller/config/field/checkbox.py +16 -12
  13. pygpt_net/controller/config/field/checkbox_list.py +36 -31
  14. pygpt_net/controller/config/field/cmd.py +51 -57
  15. pygpt_net/controller/config/field/combo.py +33 -16
  16. pygpt_net/controller/config/field/dictionary.py +48 -55
  17. pygpt_net/controller/config/field/input.py +50 -32
  18. pygpt_net/controller/config/field/slider.py +40 -45
  19. pygpt_net/controller/config/field/textarea.py +20 -6
  20. pygpt_net/controller/config/placeholder.py +110 -231
  21. pygpt_net/controller/ctx/common.py +48 -48
  22. pygpt_net/controller/ctx/ctx.py +91 -132
  23. pygpt_net/controller/lang/mapping.py +57 -95
  24. pygpt_net/controller/lang/plugins.py +64 -55
  25. pygpt_net/controller/lang/settings.py +39 -38
  26. pygpt_net/controller/layout/layout.py +176 -109
  27. pygpt_net/controller/mode/mode.py +88 -85
  28. pygpt_net/controller/model/model.py +73 -73
  29. pygpt_net/controller/plugins/plugins.py +209 -223
  30. pygpt_net/controller/plugins/presets.py +54 -55
  31. pygpt_net/controller/plugins/settings.py +54 -69
  32. pygpt_net/controller/presets/editor.py +33 -88
  33. pygpt_net/controller/presets/experts.py +20 -1
  34. pygpt_net/controller/presets/presets.py +293 -298
  35. pygpt_net/controller/settings/profile.py +16 -4
  36. pygpt_net/controller/theme/theme.py +72 -81
  37. pygpt_net/controller/ui/mode.py +118 -186
  38. pygpt_net/controller/ui/tabs.py +69 -90
  39. pygpt_net/controller/ui/ui.py +47 -56
  40. pygpt_net/controller/ui/vision.py +24 -23
  41. pygpt_net/core/agents/runner.py +15 -7
  42. pygpt_net/core/bridge/bridge.py +5 -5
  43. pygpt_net/core/command/command.py +149 -219
  44. pygpt_net/core/ctx/ctx.py +94 -146
  45. pygpt_net/core/debug/debug.py +48 -58
  46. pygpt_net/core/experts/experts.py +3 -3
  47. pygpt_net/core/models/models.py +74 -112
  48. pygpt_net/core/modes/modes.py +13 -21
  49. pygpt_net/core/plugins/plugins.py +154 -177
  50. pygpt_net/core/presets/presets.py +103 -176
  51. pygpt_net/core/render/web/body.py +217 -215
  52. pygpt_net/core/render/web/renderer.py +330 -474
  53. pygpt_net/core/text/utils.py +28 -44
  54. pygpt_net/core/tokens/tokens.py +104 -203
  55. pygpt_net/data/config/config.json +3 -3
  56. pygpt_net/data/config/models.json +3 -3
  57. pygpt_net/data/locale/locale.de.ini +2 -0
  58. pygpt_net/data/locale/locale.en.ini +2 -0
  59. pygpt_net/data/locale/locale.es.ini +2 -0
  60. pygpt_net/data/locale/locale.fr.ini +2 -0
  61. pygpt_net/data/locale/locale.it.ini +2 -0
  62. pygpt_net/data/locale/locale.pl.ini +3 -1
  63. pygpt_net/data/locale/locale.uk.ini +2 -0
  64. pygpt_net/data/locale/locale.zh.ini +2 -0
  65. pygpt_net/item/ctx.py +141 -139
  66. pygpt_net/plugin/agent/plugin.py +2 -1
  67. pygpt_net/plugin/audio_output/plugin.py +5 -2
  68. pygpt_net/plugin/base/plugin.py +101 -85
  69. pygpt_net/plugin/bitbucket/__init__.py +12 -0
  70. pygpt_net/plugin/bitbucket/config.py +267 -0
  71. pygpt_net/plugin/bitbucket/plugin.py +126 -0
  72. pygpt_net/plugin/bitbucket/worker.py +569 -0
  73. pygpt_net/plugin/cmd_code_interpreter/plugin.py +3 -2
  74. pygpt_net/plugin/cmd_custom/plugin.py +3 -2
  75. pygpt_net/plugin/cmd_files/plugin.py +3 -2
  76. pygpt_net/plugin/cmd_history/plugin.py +3 -2
  77. pygpt_net/plugin/cmd_mouse_control/plugin.py +5 -2
  78. pygpt_net/plugin/cmd_serial/plugin.py +3 -2
  79. pygpt_net/plugin/cmd_system/plugin.py +3 -6
  80. pygpt_net/plugin/cmd_web/plugin.py +3 -2
  81. pygpt_net/plugin/experts/plugin.py +2 -2
  82. pygpt_net/plugin/facebook/__init__.py +12 -0
  83. pygpt_net/plugin/facebook/config.py +359 -0
  84. pygpt_net/plugin/facebook/plugin.py +113 -0
  85. pygpt_net/plugin/facebook/worker.py +698 -0
  86. pygpt_net/plugin/github/__init__.py +12 -0
  87. pygpt_net/plugin/github/config.py +441 -0
  88. pygpt_net/plugin/github/plugin.py +126 -0
  89. pygpt_net/plugin/github/worker.py +674 -0
  90. pygpt_net/plugin/google/__init__.py +12 -0
  91. pygpt_net/plugin/google/config.py +367 -0
  92. pygpt_net/plugin/google/plugin.py +126 -0
  93. pygpt_net/plugin/google/worker.py +826 -0
  94. pygpt_net/plugin/idx_llama_index/plugin.py +3 -2
  95. pygpt_net/plugin/mailer/plugin.py +3 -5
  96. pygpt_net/plugin/openai_vision/plugin.py +3 -2
  97. pygpt_net/plugin/real_time/plugin.py +52 -60
  98. pygpt_net/plugin/slack/__init__.py +12 -0
  99. pygpt_net/plugin/slack/config.py +349 -0
  100. pygpt_net/plugin/slack/plugin.py +115 -0
  101. pygpt_net/plugin/slack/worker.py +639 -0
  102. pygpt_net/plugin/telegram/__init__.py +12 -0
  103. pygpt_net/plugin/telegram/config.py +308 -0
  104. pygpt_net/plugin/telegram/plugin.py +117 -0
  105. pygpt_net/plugin/telegram/worker.py +563 -0
  106. pygpt_net/plugin/twitter/__init__.py +12 -0
  107. pygpt_net/plugin/twitter/config.py +491 -0
  108. pygpt_net/plugin/twitter/plugin.py +125 -0
  109. pygpt_net/plugin/twitter/worker.py +837 -0
  110. pygpt_net/provider/agents/llama_index/legacy/openai_assistant.py +35 -3
  111. pygpt_net/tools/code_interpreter/tool.py +0 -1
  112. pygpt_net/tools/translator/tool.py +1 -1
  113. pygpt_net/ui/base/config_dialog.py +86 -100
  114. pygpt_net/ui/base/context_menu.py +48 -46
  115. pygpt_net/ui/dialog/preset.py +34 -77
  116. pygpt_net/ui/layout/ctx/ctx_list.py +10 -6
  117. pygpt_net/ui/layout/toolbox/presets.py +41 -41
  118. pygpt_net/ui/main.py +49 -31
  119. pygpt_net/ui/tray.py +61 -60
  120. pygpt_net/ui/widget/calendar/select.py +86 -70
  121. pygpt_net/ui/widget/lists/attachment.py +86 -44
  122. pygpt_net/ui/widget/lists/base_list_combo.py +85 -33
  123. pygpt_net/ui/widget/lists/context.py +135 -188
  124. pygpt_net/ui/widget/lists/preset.py +59 -61
  125. pygpt_net/ui/widget/textarea/web.py +161 -48
  126. pygpt_net/utils.py +8 -1
  127. {pygpt_net-2.6.1.dist-info → pygpt_net-2.6.6.dist-info}/METADATA +164 -2
  128. {pygpt_net-2.6.1.dist-info → pygpt_net-2.6.6.dist-info}/RECORD +131 -103
  129. {pygpt_net-2.6.1.dist-info → pygpt_net-2.6.6.dist-info}/LICENSE +0 -0
  130. {pygpt_net-2.6.1.dist-info → pygpt_net-2.6.6.dist-info}/WHEEL +0 -0
  131. {pygpt_net-2.6.1.dist-info → pygpt_net-2.6.6.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.12.14 08:00:00 #
9
+ # Updated Date: 2025.08.15 03:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import datetime
@@ -32,29 +32,47 @@ class Note:
32
32
  """Setup calendar notes"""
33
33
  self.counters_all = self.window.core.config.get("ctx.counters.all", True)
34
34
 
35
+ def _adjacent_months(self, year: int, month: int):
36
+ if month == 1:
37
+ py, pm = year - 1, 12
38
+ else:
39
+ py, pm = year, month - 1
40
+ if month == 12:
41
+ ny, nm = year + 1, 1
42
+ else:
43
+ ny, nm = year, month + 1
44
+ return (py, pm), (ny, nm)
45
+
35
46
  def update(self):
36
47
  """Update on content change"""
37
- year = self.window.controller.calendar.selected_year
38
- month = self.window.controller.calendar.selected_month
39
- day = self.window.controller.calendar.selected_day
48
+ ctrl_cal = self.window.controller.calendar
49
+ year = ctrl_cal.selected_year
50
+ month = ctrl_cal.selected_month
51
+ day = ctrl_cal.selected_day
40
52
 
41
53
  if year is None or month is None or day is None:
42
54
  return
43
55
 
44
- content = self.window.ui.calendar['note'].toPlainText()
45
- note = self.window.core.calendar.get_by_date(year, month, day)
56
+ ui_note = self.window.ui.calendar['note']
57
+ content = ui_note.toPlainText()
58
+ cal = self.window.core.calendar
59
+ note = cal.get_by_date(year, month, day)
46
60
 
47
- # update or create note
61
+ changed = False
48
62
  if note is None:
49
- if content.strip() != "":
63
+ if content.strip():
50
64
  note = self.create(year, month, day)
51
65
  note.content = content
52
- self.window.core.calendar.add(note)
66
+ cal.add(note)
67
+ changed = True
53
68
  else:
54
- note.content = content
55
- self.window.core.calendar.update(note)
69
+ if note.content != content:
70
+ note.content = content
71
+ cal.update(note)
72
+ changed = True
56
73
 
57
- self.refresh_num(year, month) # update note cells when note is changed
74
+ if changed:
75
+ self.refresh_num(year, month)
58
76
 
59
77
  def update_content(
60
78
  self,
@@ -69,13 +87,12 @@ class Note:
69
87
  :param month: month
70
88
  :param day: day
71
89
  """
90
+ ui_note = self.window.ui.calendar['note']
72
91
  note = self.window.core.calendar.get_by_date(year, month, day)
73
- if note is None:
74
- self.window.ui.calendar['note'].setPlainText("")
75
- self.window.ui.calendar['note'].on_update()
76
- else:
77
- self.window.ui.calendar['note'].setPlainText(note.content)
78
- self.window.ui.calendar['note'].on_update()
92
+ new_text = "" if note is None else note.content
93
+ if ui_note.toPlainText() != new_text:
94
+ ui_note.setPlainText(new_text)
95
+ ui_note.on_update()
79
96
 
80
97
  def update_label(
81
98
  self,
@@ -90,15 +107,13 @@ class Note:
90
107
  :param month: month
91
108
  :param day: day
92
109
  """
93
- suffix = datetime.datetime(year, month, day).strftime("%Y-%m-%d")
94
- self.window.ui.calendar['note.label'].setText(trans('calendar.note.label') + " (" + suffix + ")")
110
+ suffix = f"{year:04d}-{month:02d}-{day:02d}"
111
+ self.window.ui.calendar['note.label'].setText(f"{trans('calendar.note.label')} ({suffix})")
95
112
 
96
113
  def update_current(self):
97
114
  """Update label to current selected date"""
98
- year = self.window.ui.calendar['select'].currentYear
99
- month = self.window.ui.calendar['select'].currentMonth
100
- day = self.window.ui.calendar['select'].currentDay
101
- self.update_label(year, month, day)
115
+ select = self.window.ui.calendar['select']
116
+ self.update_label(select.currentYear, select.currentMonth, select.currentDay)
102
117
 
103
118
  def update_status(
104
119
  self,
@@ -115,16 +130,22 @@ class Note:
115
130
  :param month: month
116
131
  :param day: day
117
132
  """
118
- note = self.window.core.calendar.get_by_date(year, month, day)
133
+ cal = self.window.core.calendar
134
+ note = cal.get_by_date(year, month, day)
135
+ changed = False
119
136
  if note is None:
120
137
  note = self.create(year, month, day)
121
138
  note.status = status
122
- self.window.core.calendar.add(note)
139
+ cal.add(note)
140
+ changed = True
123
141
  else:
124
- note.status = status
125
- self.window.core.calendar.update(note)
142
+ if note.status != status:
143
+ note.status = status
144
+ cal.update(note)
145
+ changed = True
126
146
 
127
- self.refresh_num(year, month) # update note cells when note is changed
147
+ if changed:
148
+ self.refresh_num(year, month)
128
149
 
129
150
  def get_counts_around_month(
130
151
  self,
@@ -138,27 +159,12 @@ class Note:
138
159
  :param month: month
139
160
  :return: combined counters
140
161
  """
141
- current_month_start = datetime.datetime(year, month, 1)
142
- last_month_start = (current_month_start - datetime.timedelta(days=1)).replace(day=1)
143
-
144
- if month == 12:
145
- next_month_start = datetime.datetime(year + 1, 1, 1)
146
- else:
147
- next_month_start = datetime.datetime(year, month + 1, 1)
148
-
149
- current = self.get_ctx_counters(
150
- year,
151
- month,
152
- )
153
- last = self.get_ctx_counters(
154
- last_month_start.year,
155
- last_month_start.month,
156
- )
157
- next = self.get_ctx_counters(
158
- next_month_start.year,
159
- next_month_start.month,
160
- )
161
- return {**last, **current, **next} # combine counters
162
+ (ly, lm), (ny, nm) = self._adjacent_months(year, month)
163
+ result: Dict[str, int] = {}
164
+ result.update(self.get_ctx_counters(ly, lm))
165
+ result.update(self.get_ctx_counters(year, month))
166
+ result.update(self.get_ctx_counters(ny, nm))
167
+ return result
162
168
 
163
169
  def get_labels_counts_around_month(
164
170
  self,
@@ -172,27 +178,12 @@ class Note:
172
178
  :param month: month
173
179
  :return: combined counters
174
180
  """
175
- current_month_start = datetime.datetime(year, month, 1)
176
- last_month_start = (current_month_start - datetime.timedelta(days=1)).replace(day=1)
177
-
178
- if month == 12:
179
- next_month_start = datetime.datetime(year + 1, 1, 1)
180
- else:
181
- next_month_start = datetime.datetime(year, month + 1, 1)
182
-
183
- current = self.get_ctx_labels_counters(
184
- year,
185
- month,
186
- )
187
- last = self.get_ctx_labels_counters(
188
- last_month_start.year,
189
- last_month_start.month,
190
- )
191
- next = self.get_ctx_labels_counters(
192
- next_month_start.year,
193
- next_month_start.month,
194
- )
195
- return {**last, **current, **next} # combine counters
181
+ (ly, lm), (ny, nm) = self._adjacent_months(year, month)
182
+ result: Dict[str, Dict[int, int]] = {}
183
+ result.update(self.get_ctx_labels_counters(ly, lm))
184
+ result.update(self.get_ctx_labels_counters(year, month))
185
+ result.update(self.get_ctx_labels_counters(ny, nm))
186
+ return result
196
187
 
197
188
  def get_ctx_counters(
198
189
  self,
@@ -206,18 +197,17 @@ class Note:
206
197
  :param month: month
207
198
  :return: ctx counters
208
199
  """
209
- # default values (no filters)
210
- search_string = None
211
- search_content = False
212
- filters = None
213
-
214
- # + filters
215
- if not self.counters_all:
216
- search_string = self.window.core.ctx.get_search_string()
217
- search_content = self.window.core.ctx.is_search_content()
218
- filters = self.window.core.ctx.get_parsed_filters()
219
-
220
- return self.window.core.ctx.provider.get_ctx_count_by_day(
200
+ ctx = self.window.core.ctx
201
+ if self.counters_all:
202
+ search_string = None
203
+ search_content = False
204
+ filters = None
205
+ else:
206
+ search_string = ctx.get_search_string()
207
+ search_content = ctx.is_search_content()
208
+ filters = ctx.get_parsed_filters()
209
+
210
+ return ctx.provider.get_ctx_count_by_day(
221
211
  year=year,
222
212
  month=month,
223
213
  day=None,
@@ -238,18 +228,17 @@ class Note:
238
228
  :param month: month
239
229
  :return: ctx counters
240
230
  """
241
- # default values (no filters)
242
- search_string = None
243
- search_content = False
244
- filters = None
245
-
246
- # + filters
247
- if not self.counters_all:
248
- search_string = self.window.core.ctx.get_search_string()
249
- search_content = self.window.core.ctx.is_search_content()
250
- filters = self.window.core.ctx.get_parsed_filters()
251
-
252
- return self.window.core.ctx.provider.get_ctx_labels_count_by_day(
231
+ ctx = self.window.core.ctx
232
+ if self.counters_all:
233
+ search_string = None
234
+ search_content = False
235
+ filters = None
236
+ else:
237
+ search_string = ctx.get_search_string()
238
+ search_content = ctx.is_search_content()
239
+ filters = ctx.get_parsed_filters()
240
+
241
+ return ctx.provider.get_ctx_labels_count_by_day(
253
242
  year=year,
254
243
  month=month,
255
244
  day=None,
@@ -305,26 +294,13 @@ class Note:
305
294
  :param month: month
306
295
  :return: combined notes existence
307
296
  """
308
- current_month_start = datetime.datetime(year, month, 1)
309
- last_month_start = (current_month_start - datetime.timedelta(days=1)).replace(day=1)
310
- if month == 12:
311
- next_month_start = datetime.datetime(year + 1, 1, 1)
312
- else:
313
- next_month_start = datetime.datetime(year, month + 1, 1)
314
-
315
- current = self.window.core.calendar.get_notes_existence_by_day(
316
- year,
317
- month,
318
- )
319
- last = self.window.core.calendar.get_notes_existence_by_day(
320
- last_month_start.year,
321
- last_month_start.month,
322
- )
323
- next = self.window.core.calendar.get_notes_existence_by_day(
324
- next_month_start.year,
325
- next_month_start.month,
326
- )
327
- return {**last, **current, **next} # combine notes existence
297
+ (ly, lm), (ny, nm) = self._adjacent_months(year, month)
298
+ cal = self.window.core.calendar
299
+ result: Dict[str, Dict[int, int]] = {}
300
+ result.update(cal.get_notes_existence_by_day(ly, lm))
301
+ result.update(cal.get_notes_existence_by_day(year, month))
302
+ result.update(cal.get_notes_existence_by_day(ny, nm))
303
+ return result
328
304
 
329
305
  def refresh_num(self, year: int, month: int):
330
306
  """
@@ -342,6 +318,8 @@ class Note:
342
318
 
343
319
  :param state: state
344
320
  """
321
+ if self.counters_all == state:
322
+ return
345
323
  self.counters_all = state
346
324
  self.window.core.config.set("ctx.counters.all", state)
347
325
  self.window.core.config.save()
@@ -355,17 +333,14 @@ class Note:
355
333
  """
356
334
  dt = "" # TODO: add to config append date/time
357
335
  # dt = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + ":\n--------------------------\n"
358
- prev_text = self.window.ui.calendar['note'].toPlainText()
359
- if prev_text != "":
360
- prev_text += "\n\n"
361
- new_text = prev_text + dt + text.strip()
362
- self.window.ui.calendar['note'].setText(new_text)
363
- self.update()
364
-
365
- # move cursor to end
366
- cursor = self.window.ui.calendar['note'].textCursor()
336
+ editor = self.window.ui.calendar['note']
337
+ cursor = editor.textCursor()
367
338
  cursor.movePosition(QTextCursor.End)
368
- self.window.ui.calendar['note'].setTextCursor(cursor)
339
+ if not editor.document().isEmpty():
340
+ cursor.insertText("\n\n")
341
+ cursor.insertText(dt + text.strip())
342
+ editor.setTextCursor(cursor)
343
+ self.update()
369
344
 
370
345
  def clear_note(self):
371
346
  """Clear note"""
@@ -379,4 +354,4 @@ class Note:
379
354
 
380
355
  :return: notepad text
381
356
  """
382
- return self.window.ui.calendar['note'].toPlainText()
357
+ return self.window.ui.calendar['note'].toPlainText()
@@ -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 00:00:00 #
9
+ # Updated Date: 2025.08.15 23:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from typing import Any
@@ -37,68 +37,71 @@ class Chat:
37
37
  :param window: Window instance
38
38
  """
39
39
  self.window = window
40
- self.attachment = Attachment(window)
41
- self.audio = Audio(window)
42
- self.command = Command(window)
43
- self.common = Common(window)
44
- self.files = Files(window)
45
- self.image = Image(window)
46
- self.input = Input(window)
47
- self.output = Output(window)
48
- self.render = Render(window)
49
- self.response = Response(window)
50
- self.stream = Stream(window)
51
- self.text = Text(window)
52
- self.vision = Vision(window)
53
-
54
- def init(self):
40
+ w = window
41
+ self.attachment = Attachment(w)
42
+ self.audio = Audio(w)
43
+ self.command = Command(w)
44
+ self.common = Common(w)
45
+ self.files = Files(w)
46
+ self.image = Image(w)
47
+ self.input = Input(w)
48
+ self.output = Output(w)
49
+ self.render = Render(w)
50
+ self.response = Response(w)
51
+ self.stream = Stream(w)
52
+ self.text = Text(w)
53
+ self.vision = Vision(w)
54
+
55
+ def init(self) -> None:
55
56
  """Init"""
56
57
  self.render.setup() # setup render engine
57
58
 
58
- def setup(self):
59
+ def setup(self) -> None:
59
60
  """Setup"""
60
61
  self.common.setup()
61
62
  self.attachment.setup()
62
63
 
63
- def reload(self):
64
+ def reload(self) -> None:
64
65
  """Reload"""
65
66
  self.common.setup()
66
67
  self.render.reload()
67
68
  self.attachment.reload()
68
69
 
69
- def handle_error(self, err: Any):
70
+ def handle_error(self, err: Any) -> None:
70
71
  """
71
72
  Handle error
72
73
 
73
74
  :param err: Exception
74
75
  """
75
- self.window.core.debug.log(err)
76
- self.window.ui.dialogs.alert(str(err))
77
- self.window.update_status(str(err))
78
- self.window.controller.chat.common.unlock_input() # always unlock input on error
79
- self.window.stateChanged.emit(self.window.STATE_ERROR)
80
- self.window.dispatch(AppEvent(AppEvent.INPUT_ERROR)) # app event
81
-
82
- # stop agent on error
83
- if self.window.controller.agent.legacy.enabled():
84
- self.window.controller.agent.legacy.on_stop()
85
-
86
- def log_ctx(self, ctx: CtxItem, mode: str):
76
+ w = self.window
77
+ err_str = str(err)
78
+ w.core.debug.log(err)
79
+ w.ui.dialogs.alert(err_str)
80
+ w.update_status(err_str)
81
+ self.common.unlock_input() # always unlock input on error
82
+ w.stateChanged.emit(w.STATE_ERROR)
83
+ w.dispatch(AppEvent(AppEvent.INPUT_ERROR)) # app event
84
+
85
+ if w.controller.agent.legacy.enabled():
86
+ w.controller.agent.legacy.on_stop()
87
+
88
+ def log_ctx(self, ctx: CtxItem, mode: str) -> None:
87
89
  """
88
90
  Log context item
89
91
 
90
92
  :param ctx: CtxItem
91
93
  :param mode: mode (input/output)
92
94
  """
95
+ upper_mode = mode.upper()
93
96
  if self.window.core.config.get("log.ctx"):
94
- self.log("[ctx] {}: {}".format(mode.upper(), ctx.dump())) # log
97
+ self.log(f"[ctx] {upper_mode}: {ctx.dump()}") # log
95
98
  else:
96
- self.log("[ctx] {}.".format(mode.upper()))
99
+ self.log(f"[ctx] {upper_mode}.")
97
100
 
98
- def log(self, data: Any):
101
+ def log(self, data: Any) -> None:
99
102
  """
100
103
  Log data to debug
101
104
 
102
105
  :param data: Data to log
103
106
  """
104
- self.window.core.debug.info("[chat] " + str(data))
107
+ self.window.core.debug.info(f"[chat] {data}")