pygpt-net 2.4.37__py3-none-any.whl → 2.4.42__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.
- CHANGELOG.md +37 -0
- README.md +176 -182
- pygpt_net/CHANGELOG.txt +37 -0
- pygpt_net/__init__.py +3 -3
- pygpt_net/app.py +3 -1
- pygpt_net/controller/__init__.py +7 -3
- pygpt_net/controller/access/control.py +1 -1
- pygpt_net/controller/access/voice.py +11 -5
- pygpt_net/controller/agent/experts.py +11 -6
- pygpt_net/controller/agent/legacy.py +8 -6
- pygpt_net/controller/agent/llama.py +4 -2
- pygpt_net/controller/assistant/__init__.py +9 -4
- pygpt_net/controller/assistant/batch.py +38 -21
- pygpt_net/controller/assistant/editor.py +7 -6
- pygpt_net/controller/assistant/files.py +23 -7
- pygpt_net/controller/assistant/store.py +20 -7
- pygpt_net/controller/assistant/threads.py +34 -8
- pygpt_net/controller/attachment.py +29 -10
- pygpt_net/controller/audio/__init__.py +26 -5
- pygpt_net/controller/calendar/__init__.py +23 -4
- pygpt_net/controller/calendar/note.py +57 -11
- pygpt_net/controller/camera.py +4 -12
- pygpt_net/controller/chat/__init__.py +5 -3
- pygpt_net/controller/chat/attachment.py +34 -7
- pygpt_net/controller/chat/audio.py +2 -2
- pygpt_net/controller/chat/command.py +4 -2
- pygpt_net/controller/chat/common.py +11 -4
- pygpt_net/controller/chat/files.py +10 -3
- pygpt_net/controller/chat/image.py +17 -5
- pygpt_net/controller/chat/input.py +10 -7
- pygpt_net/controller/chat/output.py +21 -6
- pygpt_net/controller/chat/render.py +100 -21
- pygpt_net/controller/chat/response.py +34 -7
- pygpt_net/controller/chat/stream.py +4 -2
- pygpt_net/controller/chat/text.py +6 -4
- pygpt_net/controller/command.py +11 -3
- pygpt_net/controller/config/__init__.py +34 -6
- pygpt_net/controller/config/field/checkbox.py +7 -4
- pygpt_net/controller/config/field/cmd.py +7 -5
- pygpt_net/controller/config/field/combo.py +14 -6
- pygpt_net/controller/config/field/dictionary.py +14 -11
- pygpt_net/controller/config/field/input.py +9 -6
- pygpt_net/controller/config/field/slider.py +11 -8
- pygpt_net/controller/config/field/textarea.py +8 -5
- pygpt_net/controller/config/placeholder.py +66 -21
- pygpt_net/controller/ctx/__init__.py +138 -49
- pygpt_net/controller/ctx/common.py +15 -4
- pygpt_net/controller/ctx/extra.py +11 -3
- pygpt_net/controller/ctx/summarizer.py +24 -5
- pygpt_net/controller/debug/__init__.py +27 -6
- pygpt_net/controller/dialogs/confirm.py +34 -7
- pygpt_net/controller/dialogs/debug.py +4 -2
- pygpt_net/controller/dialogs/info.py +7 -2
- pygpt_net/controller/files.py +48 -10
- pygpt_net/controller/finder.py +11 -5
- pygpt_net/controller/idx/__init__.py +10 -3
- pygpt_net/controller/idx/common.py +4 -2
- pygpt_net/controller/idx/indexer.py +25 -17
- pygpt_net/controller/idx/settings.py +9 -3
- pygpt_net/controller/kernel/__init__.py +34 -8
- pygpt_net/controller/kernel/reply.py +12 -3
- pygpt_net/controller/kernel/stack.py +5 -3
- pygpt_net/controller/lang/custom.py +2 -7
- pygpt_net/controller/lang/mapping.py +5 -3
- pygpt_net/controller/layout.py +2 -2
- pygpt_net/controller/mode.py +16 -4
- pygpt_net/controller/model/__init__.py +14 -3
- pygpt_net/controller/model/editor.py +8 -3
- pygpt_net/controller/notepad.py +26 -12
- pygpt_net/controller/painter/capture.py +23 -4
- pygpt_net/controller/painter/common.py +9 -7
- pygpt_net/controller/plugins/__init__.py +19 -5
- pygpt_net/controller/plugins/presets.py +15 -6
- pygpt_net/controller/plugins/settings.py +9 -3
- pygpt_net/controller/presets/__init__.py +55 -16
- pygpt_net/controller/presets/editor.py +26 -10
- pygpt_net/controller/settings/__init__.py +3 -2
- pygpt_net/controller/settings/editor.py +29 -7
- pygpt_net/controller/settings/profile.py +22 -5
- pygpt_net/controller/theme/__init__.py +54 -12
- pygpt_net/controller/theme/common.py +24 -2
- pygpt_net/controller/theme/markdown.py +32 -16
- pygpt_net/controller/theme/menu.py +26 -5
- pygpt_net/controller/theme/nodes.py +2 -5
- pygpt_net/controller/tools/__init__.py +40 -2
- pygpt_net/controller/ui/__init__.py +4 -6
- pygpt_net/controller/ui/mode.py +16 -21
- pygpt_net/controller/ui/tabs.py +363 -65
- pygpt_net/core/access/actions.py +6 -4
- pygpt_net/core/access/shortcuts.py +4 -3
- pygpt_net/core/access/voice.py +6 -5
- pygpt_net/core/agents/legacy.py +4 -2
- pygpt_net/core/agents/memory.py +7 -2
- pygpt_net/core/agents/observer/evaluation.py +15 -7
- pygpt_net/core/agents/provider.py +9 -4
- pygpt_net/core/agents/runner.py +61 -15
- pygpt_net/core/agents/tools.py +23 -5
- pygpt_net/core/assistants/__init__.py +6 -4
- pygpt_net/core/assistants/files.py +35 -12
- pygpt_net/core/assistants/store.py +20 -10
- pygpt_net/core/attachments/__init__.py +54 -15
- pygpt_net/core/attachments/context.py +92 -29
- pygpt_net/core/audio/__init__.py +74 -3
- pygpt_net/core/audio/context.py +7 -2
- pygpt_net/core/audio/whisper.py +37 -0
- pygpt_net/core/bridge/__init__.py +22 -6
- pygpt_net/core/bridge/context.py +5 -3
- pygpt_net/core/bridge/worker.py +2 -2
- pygpt_net/core/calendar/__init__.py +57 -11
- pygpt_net/core/chain/__init__.py +8 -2
- pygpt_net/core/chain/chat.py +10 -8
- pygpt_net/core/chain/completion.py +10 -7
- pygpt_net/core/command.py +62 -17
- pygpt_net/core/ctx/__init__.py +260 -58
- pygpt_net/core/ctx/bag.py +25 -4
- pygpt_net/core/ctx/container.py +28 -17
- pygpt_net/core/ctx/idx.py +45 -8
- pygpt_net/core/ctx/output.py +95 -74
- pygpt_net/core/ctx/reply.py +5 -2
- pygpt_net/core/db/__init__.py +8 -7
- pygpt_net/core/db/viewer.py +17 -11
- pygpt_net/core/debug/__init__.py +10 -9
- pygpt_net/core/debug/events.py +22 -10
- pygpt_net/core/debug/tabs.py +9 -3
- pygpt_net/core/docker/__init__.py +11 -5
- pygpt_net/core/docker/builder.py +11 -3
- pygpt_net/core/events/app.py +5 -3
- pygpt_net/core/events/base.py +11 -5
- pygpt_net/core/events/control.py +5 -3
- pygpt_net/core/events/event.py +17 -7
- pygpt_net/core/events/kernel.py +5 -3
- pygpt_net/core/events/render.py +5 -3
- pygpt_net/core/experts/__init__.py +5 -4
- pygpt_net/core/filesystem/__init__.py +52 -34
- pygpt_net/core/filesystem/actions.py +8 -5
- pygpt_net/core/filesystem/editor.py +13 -3
- pygpt_net/core/filesystem/types.py +12 -7
- pygpt_net/core/filesystem/url.py +7 -3
- pygpt_net/core/history.py +3 -2
- pygpt_net/core/idx/__init__.py +48 -27
- pygpt_net/core/idx/chat.py +51 -17
- pygpt_net/core/idx/context.py +6 -2
- pygpt_net/core/idx/indexing.py +107 -42
- pygpt_net/core/idx/llm.py +11 -3
- pygpt_net/core/idx/metadata.py +13 -3
- pygpt_net/core/idx/types/ctx.py +32 -6
- pygpt_net/core/idx/types/external.py +41 -7
- pygpt_net/core/idx/types/files.py +31 -6
- pygpt_net/core/image.py +15 -4
- pygpt_net/core/installer.py +2 -4
- pygpt_net/core/llm/__init__.py +13 -3
- pygpt_net/core/locale.py +34 -8
- pygpt_net/core/models.py +63 -17
- pygpt_net/core/modes.py +11 -13
- pygpt_net/core/notepad.py +10 -5
- pygpt_net/core/plugins.py +31 -19
- pygpt_net/core/presets.py +37 -17
- pygpt_net/core/profile.py +21 -7
- pygpt_net/core/prompt/__init__.py +10 -3
- pygpt_net/core/prompt/custom.py +7 -6
- pygpt_net/core/prompt/template.py +9 -3
- pygpt_net/core/render/base.py +117 -22
- pygpt_net/core/render/markdown/body.py +27 -7
- pygpt_net/core/render/markdown/renderer.py +119 -22
- pygpt_net/core/render/plain/body.py +22 -5
- pygpt_net/core/render/plain/renderer.py +97 -21
- pygpt_net/core/render/web/body.py +75 -25
- pygpt_net/core/render/web/parser.py +3 -1
- pygpt_net/core/render/web/renderer.py +313 -63
- pygpt_net/core/settings.py +10 -5
- pygpt_net/core/tabs/__init__.py +290 -103
- pygpt_net/core/tabs/tab.py +26 -5
- pygpt_net/core/tokens.py +47 -12
- pygpt_net/core/updater/__init__.py +20 -7
- pygpt_net/core/vision/analyzer.py +29 -6
- pygpt_net/core/{web.py → web/__init__.py} +29 -7
- pygpt_net/core/web/helpers.py +237 -0
- pygpt_net/data/config/config.json +15 -4
- pygpt_net/data/config/models.json +3 -3
- pygpt_net/data/config/modes.json +3 -3
- pygpt_net/data/config/settings.json +55 -10
- pygpt_net/data/config/settings_section.json +3 -0
- pygpt_net/data/css/style.light.css +1 -0
- pygpt_net/data/css/{web.css → web-blocks.css} +162 -133
- pygpt_net/data/css/{web.light.css → web-blocks.light.css} +7 -0
- pygpt_net/data/css/web-chatgpt.css +350 -0
- pygpt_net/data/css/web-chatgpt.dark.css +64 -0
- pygpt_net/data/css/web-chatgpt.light.css +75 -0
- pygpt_net/data/css/web-chatgpt_wide.css +350 -0
- pygpt_net/data/css/web-chatgpt_wide.dark.css +64 -0
- pygpt_net/data/css/web-chatgpt_wide.light.css +75 -0
- pygpt_net/data/icons/split_screen.svg +1 -0
- pygpt_net/data/locale/locale.de.ini +12 -0
- pygpt_net/data/locale/locale.en.ini +18 -2
- pygpt_net/data/locale/locale.es.ini +12 -0
- pygpt_net/data/locale/locale.fr.ini +12 -0
- pygpt_net/data/locale/locale.it.ini +12 -0
- pygpt_net/data/locale/locale.pl.ini +12 -0
- pygpt_net/data/locale/locale.uk.ini +12 -0
- pygpt_net/data/locale/locale.zh.ini +12 -0
- pygpt_net/data/locale/plugin.cmd_web.de.ini +2 -0
- pygpt_net/data/locale/plugin.cmd_web.en.ini +22 -10
- pygpt_net/data/locale/plugin.cmd_web.es.ini +2 -0
- pygpt_net/data/locale/plugin.cmd_web.fr.ini +2 -0
- pygpt_net/data/locale/plugin.cmd_web.it.ini +2 -0
- pygpt_net/data/locale/plugin.cmd_web.pl.ini +2 -0
- pygpt_net/data/locale/plugin.cmd_web.uk.ini +2 -0
- pygpt_net/data/locale/plugin.cmd_web.zh.ini +2 -0
- pygpt_net/data/locale/plugin.mailer.en.ini +21 -0
- pygpt_net/icons.qrc +1 -0
- pygpt_net/icons_rc.py +165 -136
- pygpt_net/item/ctx.py +58 -25
- pygpt_net/plugin/agent/__init__.py +7 -2
- pygpt_net/plugin/audio_input/simple.py +21 -5
- pygpt_net/plugin/audio_output/__init__.py +9 -1
- pygpt_net/plugin/base/config.py +4 -2
- pygpt_net/plugin/base/plugin.py +75 -23
- pygpt_net/plugin/base/worker.py +42 -11
- pygpt_net/plugin/cmd_code_interpreter/__init__.py +39 -37
- pygpt_net/plugin/cmd_code_interpreter/runner.py +25 -12
- pygpt_net/plugin/cmd_history/config.py +2 -2
- pygpt_net/plugin/cmd_web/__init__.py +48 -9
- pygpt_net/plugin/cmd_web/config.py +135 -41
- pygpt_net/plugin/cmd_web/websearch.py +74 -33
- pygpt_net/plugin/cmd_web/worker.py +142 -13
- pygpt_net/plugin/idx_llama_index/config.py +3 -3
- pygpt_net/plugin/mailer/__init__.py +123 -0
- pygpt_net/plugin/mailer/config.py +149 -0
- pygpt_net/plugin/mailer/runner.py +285 -0
- pygpt_net/plugin/mailer/worker.py +123 -0
- pygpt_net/provider/agents/base.py +5 -2
- pygpt_net/provider/agents/openai.py +4 -2
- pygpt_net/provider/agents/openai_assistant.py +4 -2
- pygpt_net/provider/agents/planner.py +4 -2
- pygpt_net/provider/agents/react.py +4 -2
- pygpt_net/provider/audio_output/openai_tts.py +5 -11
- pygpt_net/provider/core/assistant/base.py +5 -3
- pygpt_net/provider/core/assistant/json_file.py +8 -5
- pygpt_net/provider/core/assistant_file/base.py +4 -3
- pygpt_net/provider/core/assistant_file/db_sqlite/__init__.py +4 -3
- pygpt_net/provider/core/assistant_file/db_sqlite/storage.py +3 -2
- pygpt_net/provider/core/assistant_store/base.py +6 -4
- pygpt_net/provider/core/assistant_store/db_sqlite/__init__.py +5 -4
- pygpt_net/provider/core/assistant_store/db_sqlite/storage.py +5 -3
- pygpt_net/provider/core/attachment/base.py +5 -3
- pygpt_net/provider/core/attachment/json_file.py +4 -3
- pygpt_net/provider/core/calendar/base.py +5 -3
- pygpt_net/provider/core/calendar/db_sqlite/__init__.py +6 -5
- pygpt_net/provider/core/calendar/db_sqlite/storage.py +5 -4
- pygpt_net/provider/core/config/base.py +8 -6
- pygpt_net/provider/core/config/json_file.py +9 -7
- pygpt_net/provider/core/config/patch.py +43 -1
- pygpt_net/provider/core/ctx/base.py +30 -25
- pygpt_net/provider/core/ctx/db_sqlite/__init__.py +59 -34
- pygpt_net/provider/core/ctx/db_sqlite/storage.py +62 -30
- pygpt_net/provider/core/ctx/db_sqlite/utils.py +11 -9
- pygpt_net/provider/core/index/base.py +129 -23
- pygpt_net/provider/core/index/db_sqlite/__init__.py +130 -23
- pygpt_net/provider/core/index/db_sqlite/storage.py +130 -23
- pygpt_net/provider/core/index/db_sqlite/utils.py +4 -2
- pygpt_net/provider/core/mode/base.py +5 -3
- pygpt_net/provider/core/mode/json_file.py +7 -6
- pygpt_net/provider/core/model/base.py +6 -4
- pygpt_net/provider/core/model/json_file.py +9 -7
- pygpt_net/provider/core/notepad/base.py +5 -3
- pygpt_net/provider/core/notepad/db_sqlite/__init__.py +5 -4
- pygpt_net/provider/core/notepad/db_sqlite/storage.py +4 -3
- pygpt_net/provider/core/plugin_preset/base.py +4 -2
- pygpt_net/provider/core/plugin_preset/json_file.py +5 -3
- pygpt_net/provider/core/preset/base.py +6 -4
- pygpt_net/provider/core/preset/json_file.py +9 -9
- pygpt_net/provider/core/prompt/base.py +6 -3
- pygpt_net/provider/core/prompt/json_file.py +11 -6
- pygpt_net/provider/gpt/assistants.py +21 -11
- pygpt_net/provider/gpt/audio.py +6 -5
- pygpt_net/provider/gpt/chat.py +10 -7
- pygpt_net/provider/gpt/completion.py +11 -5
- pygpt_net/provider/gpt/image.py +9 -2
- pygpt_net/provider/gpt/store.py +53 -18
- pygpt_net/provider/gpt/vision.py +17 -11
- pygpt_net/provider/llms/anthropic.py +7 -2
- pygpt_net/provider/llms/azure_openai.py +26 -5
- pygpt_net/provider/llms/base.py +47 -9
- pygpt_net/provider/llms/google.py +7 -2
- pygpt_net/provider/llms/hugging_face.py +13 -3
- pygpt_net/provider/llms/hugging_face_api.py +18 -4
- pygpt_net/provider/llms/local.py +7 -2
- pygpt_net/provider/llms/ollama.py +30 -6
- pygpt_net/provider/llms/openai.py +32 -6
- pygpt_net/provider/vector_stores/__init__.py +45 -14
- pygpt_net/provider/vector_stores/base.py +35 -8
- pygpt_net/provider/vector_stores/chroma.py +13 -3
- pygpt_net/provider/vector_stores/ctx_attachment.py +31 -12
- pygpt_net/provider/vector_stores/elasticsearch.py +12 -3
- pygpt_net/provider/vector_stores/pinecode.py +12 -3
- pygpt_net/provider/vector_stores/redis.py +12 -3
- pygpt_net/provider/vector_stores/simple.py +12 -3
- pygpt_net/provider/vector_stores/temp.py +16 -4
- pygpt_net/provider/web/base.py +10 -3
- pygpt_net/provider/web/google_custom_search.py +9 -3
- pygpt_net/provider/web/microsoft_bing.py +9 -3
- pygpt_net/tools/__init__.py +20 -4
- pygpt_net/tools/audio_transcriber/__init__.py +4 -3
- pygpt_net/tools/base.py +28 -7
- pygpt_net/tools/code_interpreter/__init__.py +177 -77
- pygpt_net/tools/code_interpreter/ui/dialogs.py +21 -103
- pygpt_net/tools/code_interpreter/ui/widgets.py +284 -9
- pygpt_net/tools/html_canvas/__init__.py +81 -25
- pygpt_net/tools/html_canvas/ui/dialogs.py +46 -62
- pygpt_net/tools/html_canvas/ui/widgets.py +96 -3
- pygpt_net/tools/image_viewer/__init__.py +10 -4
- pygpt_net/tools/indexer/__init__.py +8 -7
- pygpt_net/tools/media_player/__init__.py +4 -3
- pygpt_net/tools/text_editor/__init__.py +36 -10
- pygpt_net/ui/base/context_menu.py +2 -2
- pygpt_net/ui/layout/chat/input.py +10 -18
- pygpt_net/ui/layout/chat/output.py +27 -45
- pygpt_net/ui/layout/ctx/ctx_list.py +13 -4
- pygpt_net/ui/layout/toolbox/footer.py +18 -2
- pygpt_net/ui/main.py +2 -2
- pygpt_net/ui/menu/audio.py +12 -1
- pygpt_net/ui/menu/config.py +7 -11
- pygpt_net/ui/menu/debug.py +11 -1
- pygpt_net/ui/menu/theme.py +9 -2
- pygpt_net/ui/widget/filesystem/explorer.py +2 -2
- pygpt_net/ui/widget/lists/context.py +27 -5
- pygpt_net/ui/widget/tabs/Input.py +2 -2
- pygpt_net/ui/widget/tabs/body.py +2 -1
- pygpt_net/ui/widget/tabs/layout.py +195 -0
- pygpt_net/ui/widget/tabs/output.py +218 -55
- pygpt_net/ui/widget/textarea/html.py +11 -1
- pygpt_net/ui/widget/textarea/output.py +10 -1
- pygpt_net/ui/widget/textarea/search_input.py +4 -1
- pygpt_net/ui/widget/textarea/web.py +49 -9
- {pygpt_net-2.4.37.dist-info → pygpt_net-2.4.42.dist-info}/METADATA +177 -183
- {pygpt_net-2.4.37.dist-info → pygpt_net-2.4.42.dist-info}/RECORD +340 -325
- /pygpt_net/data/css/{web.dark.css → web-blocks.dark.css} +0 -0
- {pygpt_net-2.4.37.dist-info → pygpt_net-2.4.42.dist-info}/LICENSE +0 -0
- {pygpt_net-2.4.37.dist-info → pygpt_net-2.4.42.dist-info}/WHEEL +0 -0
- {pygpt_net-2.4.37.dist-info → pygpt_net-2.4.42.dist-info}/entry_points.txt +0 -0
@@ -6,10 +6,11 @@
|
|
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.
|
9
|
+
# Updated Date: 2024.12.14 08:00:00 #
|
10
10
|
# ================================================== #
|
11
11
|
|
12
12
|
import os
|
13
|
+
from typing import Optional, Dict, List
|
13
14
|
|
14
15
|
from PySide6.QtWidgets import QApplication
|
15
16
|
|
@@ -50,7 +51,10 @@ class Files:
|
|
50
51
|
file_id = self.window.core.assistants.files.get_file_id_by_idx(idx, assistant.vector_store, thread_id)
|
51
52
|
self.window.core.assistants.current_file = file_id
|
52
53
|
|
53
|
-
def count_upload(
|
54
|
+
def count_upload(
|
55
|
+
self,
|
56
|
+
attachments: Dict[str, AttachmentItem]
|
57
|
+
) -> int:
|
54
58
|
"""
|
55
59
|
Count attachments for upload
|
56
60
|
|
@@ -64,7 +68,7 @@ class Files:
|
|
64
68
|
num += 1 # increment uploaded files counter if file is not uploaded yet
|
65
69
|
return num
|
66
70
|
|
67
|
-
def import_files(self, store_id: str = None):
|
71
|
+
def import_files(self, store_id: Optional[str] = None):
|
68
72
|
"""
|
69
73
|
Import assistant files from API
|
70
74
|
|
@@ -239,11 +243,15 @@ class Files:
|
|
239
243
|
self.window.core.assistants.save()
|
240
244
|
self.update()
|
241
245
|
|
242
|
-
def upload(
|
246
|
+
def upload(
|
247
|
+
self,
|
248
|
+
mode: str,
|
249
|
+
attachments: Dict[str, AttachmentItem]
|
250
|
+
) -> int:
|
243
251
|
"""
|
244
252
|
Upload attachments to assistant
|
245
253
|
|
246
|
-
:param mode: mode
|
254
|
+
:param mode: work mode
|
247
255
|
:param attachments: attachments dict
|
248
256
|
:return: number of uploaded files
|
249
257
|
"""
|
@@ -334,7 +342,11 @@ class Files:
|
|
334
342
|
|
335
343
|
return num
|
336
344
|
|
337
|
-
def append(
|
345
|
+
def append(
|
346
|
+
self,
|
347
|
+
assistant: AssistantItem,
|
348
|
+
attachment: AttachmentItem
|
349
|
+
):
|
338
350
|
"""
|
339
351
|
Append attachment to assistant
|
340
352
|
|
@@ -375,7 +387,11 @@ class Files:
|
|
375
387
|
suffix = f' ({num_files})'
|
376
388
|
self.window.ui.tabs['input'].setTabText(2, trans('attachments_uploaded.tab') + suffix)
|
377
389
|
|
378
|
-
def handle_received_ids(
|
390
|
+
def handle_received_ids(
|
391
|
+
self,
|
392
|
+
ids: List[str],
|
393
|
+
ext: Optional[str] = None
|
394
|
+
) -> list:
|
379
395
|
"""
|
380
396
|
Handle (download) received message files
|
381
397
|
|
@@ -6,11 +6,12 @@
|
|
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.
|
9
|
+
# Updated Date: 2024.12.14 08:00:00 #
|
10
10
|
# ================================================== #
|
11
11
|
|
12
12
|
import copy
|
13
13
|
import json
|
14
|
+
from typing import Optional
|
14
15
|
|
15
16
|
from PySide6.QtWidgets import QApplication
|
16
17
|
|
@@ -65,7 +66,7 @@ class VectorStore:
|
|
65
66
|
"""
|
66
67
|
return self.options
|
67
68
|
|
68
|
-
def get_option(self, key: str) -> dict
|
69
|
+
def get_option(self, key: str) -> Optional[dict]:
|
69
70
|
"""
|
70
71
|
Get option by key
|
71
72
|
|
@@ -157,7 +158,11 @@ class VectorStore:
|
|
157
158
|
self.window.update_status(trans('status.assistant.saved'))
|
158
159
|
self.update() # update stores list in assistant dialog
|
159
160
|
|
160
|
-
def refresh_store(
|
161
|
+
def refresh_store(
|
162
|
+
self,
|
163
|
+
store: AssistantStoreItem,
|
164
|
+
update: bool = True
|
165
|
+
):
|
161
166
|
"""
|
162
167
|
Refresh store by ID
|
163
168
|
|
@@ -301,7 +306,11 @@ class VectorStore:
|
|
301
306
|
self.restore_selection()
|
302
307
|
self.refresh_by_store_id(store.id)
|
303
308
|
|
304
|
-
def delete_by_idx(
|
309
|
+
def delete_by_idx(
|
310
|
+
self,
|
311
|
+
idx: int,
|
312
|
+
force: bool = False
|
313
|
+
):
|
305
314
|
"""
|
306
315
|
Delete store by idx
|
307
316
|
|
@@ -311,7 +320,11 @@ class VectorStore:
|
|
311
320
|
store_id = self.get_by_tab_idx(idx)
|
312
321
|
self.delete(store_id, force=force)
|
313
322
|
|
314
|
-
def delete(
|
323
|
+
def delete(
|
324
|
+
self,
|
325
|
+
store_id: Optional[str] = None,
|
326
|
+
force: bool = False
|
327
|
+
):
|
315
328
|
"""
|
316
329
|
Delete store by idx
|
317
330
|
|
@@ -413,7 +426,7 @@ class VectorStore:
|
|
413
426
|
i += 1
|
414
427
|
return idx
|
415
428
|
|
416
|
-
def get_by_tab_idx(self, idx: int) -> str
|
429
|
+
def get_by_tab_idx(self, idx: int) -> Optional[str]:
|
417
430
|
"""
|
418
431
|
Get key by list index (including hidden)
|
419
432
|
|
@@ -429,7 +442,7 @@ class VectorStore:
|
|
429
442
|
store_idx += 1
|
430
443
|
return None
|
431
444
|
|
432
|
-
def get_first_visible(self) -> str
|
445
|
+
def get_first_visible(self) -> Optional[str]:
|
433
446
|
"""
|
434
447
|
Get first visible store ID (including hidden)
|
435
448
|
|
@@ -6,11 +6,12 @@
|
|
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.
|
9
|
+
# Updated Date: 2024.12.14 08:00:00 #
|
10
10
|
# ================================================== #
|
11
11
|
|
12
12
|
import json
|
13
13
|
import time
|
14
|
+
from typing import Optional, Any, List, Dict
|
14
15
|
|
15
16
|
from PySide6.QtCore import QObject, Signal, Slot, QRunnable
|
16
17
|
|
@@ -46,7 +47,11 @@ class Threads(QObject):
|
|
46
47
|
self.window.core.ctx.append_thread(thread_id)
|
47
48
|
return thread_id
|
48
49
|
|
49
|
-
def handle_output_message(
|
50
|
+
def handle_output_message(
|
51
|
+
self,
|
52
|
+
ctx: CtxItem,
|
53
|
+
stream: bool = False
|
54
|
+
):
|
50
55
|
"""
|
51
56
|
Handle output message (not stream ONLY)
|
52
57
|
|
@@ -92,7 +97,12 @@ class Threads(QObject):
|
|
92
97
|
|
93
98
|
self.log("Handled output message.")
|
94
99
|
|
95
|
-
def handle_message_data(
|
100
|
+
def handle_message_data(
|
101
|
+
self,
|
102
|
+
ctx: CtxItem,
|
103
|
+
msg,
|
104
|
+
stream: bool = False
|
105
|
+
):
|
96
106
|
"""
|
97
107
|
Handle message data (files, images, text) - stream and not-stream
|
98
108
|
|
@@ -286,7 +296,10 @@ class Threads(QObject):
|
|
286
296
|
})
|
287
297
|
self.window.dispatch(event)
|
288
298
|
|
289
|
-
def handle_tool_outputs(
|
299
|
+
def handle_tool_outputs(
|
300
|
+
self,
|
301
|
+
ctx: CtxItem
|
302
|
+
) -> List[Dict[str, Any]]:
|
290
303
|
"""
|
291
304
|
Handle tool outputs
|
292
305
|
|
@@ -304,7 +317,10 @@ class Threads(QObject):
|
|
304
317
|
return tools_outputs
|
305
318
|
|
306
319
|
|
307
|
-
def apply_outputs(
|
320
|
+
def apply_outputs(
|
321
|
+
self,
|
322
|
+
ctx: CtxItem
|
323
|
+
) -> List[Dict[str, Any]]:
|
308
324
|
"""
|
309
325
|
Apply tool call outputs
|
310
326
|
|
@@ -316,7 +332,12 @@ class Threads(QObject):
|
|
316
332
|
ctx.tool_calls = self.tool_calls # set previous tool calls
|
317
333
|
return self.window.core.command.get_tool_calls_outputs(ctx)
|
318
334
|
|
319
|
-
def handle_run_created(
|
335
|
+
def handle_run_created(
|
336
|
+
self,
|
337
|
+
ctx: CtxItem,
|
338
|
+
run,
|
339
|
+
stream: bool = False
|
340
|
+
):
|
320
341
|
"""
|
321
342
|
Handle run created (stream and not stream)
|
322
343
|
|
@@ -330,7 +351,7 @@ class Threads(QObject):
|
|
330
351
|
self.window.core.ctx.append_run(ctx.run_id) # get run ID and store in ctx
|
331
352
|
self.handle_run(ctx, run, stream) # handle assistant run
|
332
353
|
|
333
|
-
def handle_run_error(self, ctx: CtxItem, err):
|
354
|
+
def handle_run_error(self, ctx: CtxItem, err: Any):
|
334
355
|
"""
|
335
356
|
Handle run created (stream and not stream)
|
336
357
|
|
@@ -354,7 +375,12 @@ class Threads(QObject):
|
|
354
375
|
self.window.controller.chat.common.unlock_input() # unlock input
|
355
376
|
# self.handle_stream_end(ctx)
|
356
377
|
|
357
|
-
def handle_run(
|
378
|
+
def handle_run(
|
379
|
+
self,
|
380
|
+
ctx: CtxItem,
|
381
|
+
run,
|
382
|
+
stream: bool = False
|
383
|
+
):
|
358
384
|
"""
|
359
385
|
Handle assistant's run (not stream ONLY)
|
360
386
|
|
@@ -6,11 +6,12 @@
|
|
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.
|
9
|
+
# Updated Date: 2024.12.14 08:00:00 #
|
10
10
|
# ================================================== #
|
11
11
|
|
12
12
|
import os
|
13
13
|
from datetime import datetime
|
14
|
+
from typing import Optional
|
14
15
|
from urllib.parse import urlparse
|
15
16
|
|
16
17
|
from PySide6.QtGui import QImage
|
@@ -112,7 +113,12 @@ class Attachment:
|
|
112
113
|
# TODO: implement this
|
113
114
|
pass
|
114
115
|
|
115
|
-
def delete(
|
116
|
+
def delete(
|
117
|
+
self,
|
118
|
+
idx: int,
|
119
|
+
force: bool = False,
|
120
|
+
remove_local: bool = False
|
121
|
+
):
|
116
122
|
"""
|
117
123
|
Delete attachment
|
118
124
|
|
@@ -196,7 +202,11 @@ class Attachment:
|
|
196
202
|
self.window.ui.dialog['rename'].close()
|
197
203
|
self.update()
|
198
204
|
|
199
|
-
def add(
|
205
|
+
def add(
|
206
|
+
self,
|
207
|
+
mode: str,
|
208
|
+
attachment: AttachmentItem
|
209
|
+
):
|
200
210
|
"""
|
201
211
|
Add attachment item to list
|
202
212
|
|
@@ -210,7 +220,12 @@ class Attachment:
|
|
210
220
|
self.is_consumed = False # reset consumed flag
|
211
221
|
self.update()
|
212
222
|
|
213
|
-
def clear(
|
223
|
+
def clear(
|
224
|
+
self,
|
225
|
+
force: bool = False,
|
226
|
+
remove_local: bool = False,
|
227
|
+
auto: bool = False
|
228
|
+
):
|
214
229
|
"""
|
215
230
|
Clear attachments list
|
216
231
|
|
@@ -240,9 +255,7 @@ class Attachment:
|
|
240
255
|
self.window.dispatch(AppEvent(AppEvent.CTX_ATTACHMENTS_CLEAR))
|
241
256
|
|
242
257
|
def clear_silent(self):
|
243
|
-
"""
|
244
|
-
Clear attachments list
|
245
|
-
"""
|
258
|
+
"""Clear attachments list without confirmation"""
|
246
259
|
# delete all from attachments for current mode
|
247
260
|
mode = self.window.core.config.get('mode')
|
248
261
|
self.window.core.attachments.delete_all(
|
@@ -407,9 +420,13 @@ class Attachment:
|
|
407
420
|
self.window.core.config.get_user_dir('data'),
|
408
421
|
file_name,
|
409
422
|
)
|
410
|
-
return path
|
423
|
+
return str(path)
|
411
424
|
|
412
|
-
def download(
|
425
|
+
def download(
|
426
|
+
self,
|
427
|
+
file_id: str,
|
428
|
+
ext: Optional[str] = None
|
429
|
+
) -> Optional[str]:
|
413
430
|
"""
|
414
431
|
Download file
|
415
432
|
|
@@ -501,6 +518,8 @@ class Attachment:
|
|
501
518
|
def from_clipboard_image(self, image):
|
502
519
|
"""
|
503
520
|
Handle image from clipboard
|
521
|
+
|
522
|
+
:param image: image data
|
504
523
|
"""
|
505
524
|
now = datetime.now()
|
506
525
|
dt = now.strftime("%Y-%m-%d_%H-%M-%S")
|
@@ -509,7 +528,7 @@ class Attachment:
|
|
509
528
|
image.save(path, "PNG")
|
510
529
|
self.from_clipboard_url(path)
|
511
530
|
|
512
|
-
def from_clipboard_url(self, url, all: bool = False):
|
531
|
+
def from_clipboard_url(self, url: str, all: bool = False):
|
513
532
|
"""
|
514
533
|
Handle image from clipboard url
|
515
534
|
|
@@ -6,10 +6,11 @@
|
|
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.
|
9
|
+
# Updated Date: 2024.12.14 18:00:00 #
|
10
10
|
# ================================================== #
|
11
11
|
|
12
12
|
import os
|
13
|
+
from typing import Optional
|
13
14
|
|
14
15
|
from pygpt_net.core.events import Event, BaseEvent
|
15
16
|
from pygpt_net.item.ctx import CtxItem
|
@@ -29,7 +30,11 @@ class Audio:
|
|
29
30
|
"""Setup controller"""
|
30
31
|
self.update()
|
31
32
|
|
32
|
-
def toggle_input(
|
33
|
+
def toggle_input(
|
34
|
+
self,
|
35
|
+
state: bool,
|
36
|
+
btn: bool = True
|
37
|
+
):
|
33
38
|
"""
|
34
39
|
Toggle audio input
|
35
40
|
|
@@ -155,7 +160,11 @@ class Audio:
|
|
155
160
|
else:
|
156
161
|
self.window.ui.menu['audio.control.global'].setChecked(False)
|
157
162
|
|
158
|
-
def read_text(
|
163
|
+
def read_text(
|
164
|
+
self,
|
165
|
+
text: str,
|
166
|
+
cache_file: Optional[str] = None
|
167
|
+
):
|
159
168
|
"""
|
160
169
|
Read text using audio output plugins
|
161
170
|
|
@@ -200,17 +209,29 @@ class Audio:
|
|
200
209
|
}
|
201
210
|
self.window.dispatch(event, all=True)
|
202
211
|
|
212
|
+
def stop_audio(self):
|
213
|
+
"""Stop audio playback"""
|
214
|
+
ctx = CtxItem()
|
215
|
+
event = Event(Event.AUDIO_OUTPUT_STOP)
|
216
|
+
event.ctx = ctx
|
217
|
+
event.data = {}
|
218
|
+
self.window.dispatch(event, all=True)
|
219
|
+
|
203
220
|
def play_sound(self, filename: str):
|
204
221
|
"""
|
205
222
|
Play sound
|
206
223
|
|
207
|
-
:param filename
|
224
|
+
:param filename: sound file name
|
208
225
|
"""
|
209
226
|
path = os.path.join(self.window.core.config.get_app_path(), "data", "audio", filename)
|
210
227
|
if path:
|
211
228
|
self.play_audio(path)
|
212
229
|
|
213
|
-
def play_event(
|
230
|
+
def play_event(
|
231
|
+
self,
|
232
|
+
text: str,
|
233
|
+
event: Optional[BaseEvent] = None
|
234
|
+
):
|
214
235
|
"""
|
215
236
|
Play event (read text or play cached audio file)
|
216
237
|
|
@@ -6,9 +6,11 @@
|
|
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.
|
9
|
+
# Updated Date: 2024.12.14 08:00:00 #
|
10
10
|
# ================================================== #
|
11
11
|
|
12
|
+
from typing import Optional
|
13
|
+
|
12
14
|
from pygpt_net.core.tabs.tab import Tab
|
13
15
|
|
14
16
|
from .note import Note
|
@@ -48,6 +50,8 @@ class Calendar:
|
|
48
50
|
|
49
51
|
:param all: reload all notes
|
50
52
|
"""
|
53
|
+
if not self.is_loaded():
|
54
|
+
return
|
51
55
|
year = self.window.ui.calendar['select'].currentYear
|
52
56
|
month = self.window.ui.calendar['select'].currentMonth
|
53
57
|
self.on_page_changed(year, month, all=all) # load notes for current month
|
@@ -77,7 +81,12 @@ class Calendar:
|
|
77
81
|
month = self.window.ui.calendar['select'].currentMonth
|
78
82
|
self.window.core.calendar.load_by_month(year, month)
|
79
83
|
|
80
|
-
def on_page_changed(
|
84
|
+
def on_page_changed(
|
85
|
+
self,
|
86
|
+
year: int,
|
87
|
+
month: int,
|
88
|
+
all: bool = True
|
89
|
+
):
|
81
90
|
"""
|
82
91
|
On calendar page changed
|
83
92
|
|
@@ -90,7 +99,12 @@ class Calendar:
|
|
90
99
|
self.note.refresh_ctx(year, month)
|
91
100
|
self.note.refresh_num(year, month)
|
92
101
|
|
93
|
-
def on_day_select(
|
102
|
+
def on_day_select(
|
103
|
+
self,
|
104
|
+
year: int,
|
105
|
+
month: int,
|
106
|
+
day: int
|
107
|
+
):
|
94
108
|
"""
|
95
109
|
On day select
|
96
110
|
|
@@ -104,7 +118,12 @@ class Calendar:
|
|
104
118
|
self.note.update_content(year, month, day)
|
105
119
|
self.note.update_label(year, month, day)
|
106
120
|
|
107
|
-
def on_ctx_select(
|
121
|
+
def on_ctx_select(
|
122
|
+
self,
|
123
|
+
year: int,
|
124
|
+
month: int,
|
125
|
+
day: int
|
126
|
+
):
|
108
127
|
"""
|
109
128
|
On ctx select
|
110
129
|
|
@@ -6,10 +6,11 @@
|
|
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.
|
9
|
+
# Updated Date: 2024.12.14 08:00:00 #
|
10
10
|
# ================================================== #
|
11
11
|
|
12
12
|
import datetime
|
13
|
+
from typing import Dict
|
13
14
|
|
14
15
|
from PySide6.QtGui import QTextCursor
|
15
16
|
|
@@ -55,7 +56,12 @@ class Note:
|
|
55
56
|
|
56
57
|
self.refresh_num(year, month) # update note cells when note is changed
|
57
58
|
|
58
|
-
def update_content(
|
59
|
+
def update_content(
|
60
|
+
self,
|
61
|
+
year: int,
|
62
|
+
month: int,
|
63
|
+
day: int
|
64
|
+
):
|
59
65
|
"""
|
60
66
|
Update content
|
61
67
|
|
@@ -71,7 +77,12 @@ class Note:
|
|
71
77
|
self.window.ui.calendar['note'].setPlainText(note.content)
|
72
78
|
self.window.ui.calendar['note'].on_update()
|
73
79
|
|
74
|
-
def update_label(
|
80
|
+
def update_label(
|
81
|
+
self,
|
82
|
+
year: int,
|
83
|
+
month: int,
|
84
|
+
day: int
|
85
|
+
):
|
75
86
|
"""
|
76
87
|
Update label
|
77
88
|
|
@@ -89,7 +100,13 @@ class Note:
|
|
89
100
|
day = self.window.ui.calendar['select'].currentDay
|
90
101
|
self.update_label(year, month, day)
|
91
102
|
|
92
|
-
def update_status(
|
103
|
+
def update_status(
|
104
|
+
self,
|
105
|
+
status: str,
|
106
|
+
year: int,
|
107
|
+
month: int,
|
108
|
+
day: int
|
109
|
+
):
|
93
110
|
"""
|
94
111
|
Update status label
|
95
112
|
|
@@ -109,7 +126,11 @@ class Note:
|
|
109
126
|
|
110
127
|
self.refresh_num(year, month) # update note cells when note is changed
|
111
128
|
|
112
|
-
def get_counts_around_month(
|
129
|
+
def get_counts_around_month(
|
130
|
+
self,
|
131
|
+
year: int,
|
132
|
+
month: int
|
133
|
+
) -> Dict[str, int]:
|
113
134
|
"""
|
114
135
|
Get counts around month
|
115
136
|
|
@@ -139,7 +160,11 @@ class Note:
|
|
139
160
|
)
|
140
161
|
return {**last, **current, **next} # combine counters
|
141
162
|
|
142
|
-
def get_labels_counts_around_month(
|
163
|
+
def get_labels_counts_around_month(
|
164
|
+
self,
|
165
|
+
year: int,
|
166
|
+
month: int
|
167
|
+
) -> Dict[str, Dict[int, int]]:
|
143
168
|
"""
|
144
169
|
Get counts around month
|
145
170
|
|
@@ -169,7 +194,11 @@ class Note:
|
|
169
194
|
)
|
170
195
|
return {**last, **current, **next} # combine counters
|
171
196
|
|
172
|
-
def get_ctx_counters(
|
197
|
+
def get_ctx_counters(
|
198
|
+
self,
|
199
|
+
year: int,
|
200
|
+
month: int
|
201
|
+
) -> Dict[str, int]:
|
173
202
|
"""
|
174
203
|
Get ctx counters
|
175
204
|
|
@@ -197,7 +226,11 @@ class Note:
|
|
197
226
|
search_content=search_content,
|
198
227
|
)
|
199
228
|
|
200
|
-
def get_ctx_labels_counters(
|
229
|
+
def get_ctx_labels_counters(
|
230
|
+
self,
|
231
|
+
year: int,
|
232
|
+
month: int
|
233
|
+
) -> Dict[str, Dict[int, int]]:
|
201
234
|
"""
|
202
235
|
Get ctx labels counters
|
203
236
|
|
@@ -225,7 +258,11 @@ class Note:
|
|
225
258
|
search_content=search_content,
|
226
259
|
)
|
227
260
|
|
228
|
-
def refresh_ctx(
|
261
|
+
def refresh_ctx(
|
262
|
+
self,
|
263
|
+
year: int,
|
264
|
+
month: int
|
265
|
+
):
|
229
266
|
"""
|
230
267
|
Update calendar ctx cells
|
231
268
|
|
@@ -236,7 +273,12 @@ class Note:
|
|
236
273
|
labels = self.get_labels_counts_around_month(year, month)
|
237
274
|
self.window.ui.calendar['select'].update_ctx(count, labels)
|
238
275
|
|
239
|
-
def create(
|
276
|
+
def create(
|
277
|
+
self,
|
278
|
+
year: int,
|
279
|
+
month: int,
|
280
|
+
day: int
|
281
|
+
) -> CalendarNoteItem:
|
240
282
|
"""
|
241
283
|
Create empty note
|
242
284
|
|
@@ -251,7 +293,11 @@ class Note:
|
|
251
293
|
note.day = day
|
252
294
|
return note
|
253
295
|
|
254
|
-
def get_notes_existence_around_month(
|
296
|
+
def get_notes_existence_around_month(
|
297
|
+
self,
|
298
|
+
year: int,
|
299
|
+
month: int
|
300
|
+
) -> Dict[str, Dict[int, int]]:
|
255
301
|
"""
|
256
302
|
Get notes existence around month
|
257
303
|
|
pygpt_net/controller/camera.py
CHANGED
@@ -6,21 +6,19 @@
|
|
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.
|
9
|
+
# Updated Date: 2024.12.14 00:00:00 #
|
10
10
|
# ================================================== #
|
11
11
|
|
12
12
|
import datetime
|
13
13
|
import os
|
14
14
|
import time
|
15
|
+
from typing import Any
|
15
16
|
|
16
17
|
import cv2
|
17
18
|
|
18
19
|
from PySide6.QtCore import Slot, QObject
|
19
20
|
from PySide6.QtGui import QImage, QPixmap, Qt
|
20
21
|
|
21
|
-
from pygpt_net.core.types import (
|
22
|
-
MODE_VISION,
|
23
|
-
)
|
24
22
|
from pygpt_net.core.events import AppEvent, KernelEvent
|
25
23
|
from pygpt_net.core.camera import CaptureWorker
|
26
24
|
from pygpt_net.utils import trans
|
@@ -464,7 +462,7 @@ class Camera(QObject):
|
|
464
462
|
self.window.dispatch(AppEvent(AppEvent.CAMERA_DISABLED)) # app event
|
465
463
|
|
466
464
|
@Slot(object)
|
467
|
-
def handle_error(self, err):
|
465
|
+
def handle_error(self, err: Any):
|
468
466
|
"""
|
469
467
|
Handle thread error signal
|
470
468
|
|
@@ -507,15 +505,9 @@ class Camera(QObject):
|
|
507
505
|
|
508
506
|
:return: True if capture is allowed
|
509
507
|
"""
|
510
|
-
mode = self.window.core.config.get('mode')
|
511
508
|
if self.window.controller.painter.is_active():
|
512
509
|
return True
|
513
|
-
if (
|
514
|
-
and mode not in self.window.controller.chat.vision.allowed_modes):
|
515
|
-
return False
|
516
|
-
if self.window.controller.plugins.is_type_enabled('vision'):
|
517
|
-
return True
|
518
|
-
if self.window.controller.ui.vision.is_vision_model():
|
510
|
+
if self.window.controller.ui.vision.has_vision():
|
519
511
|
return True
|
520
512
|
return False
|
521
513
|
|