pygpt-net 2.6.0.post2__py3-none-any.whl → 2.6.2__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.
- pygpt_net/CHANGELOG.txt +8 -0
- pygpt_net/__init__.py +3 -3
- pygpt_net/app.py +27 -9
- pygpt_net/controller/chat/response.py +10 -4
- pygpt_net/controller/chat/stream.py +40 -2
- pygpt_net/controller/model/editor.py +45 -4
- pygpt_net/controller/plugins/plugins.py +25 -0
- pygpt_net/controller/presets/editor.py +100 -100
- pygpt_net/controller/presets/experts.py +20 -1
- pygpt_net/controller/presets/presets.py +5 -4
- pygpt_net/controller/ui/mode.py +17 -66
- pygpt_net/core/agents/provider.py +2 -1
- pygpt_net/core/agents/runner.py +123 -9
- pygpt_net/core/agents/runners/helpers.py +3 -2
- pygpt_net/core/agents/runners/llama_workflow.py +176 -22
- pygpt_net/core/agents/runners/loop.py +22 -13
- pygpt_net/core/experts/experts.py +19 -25
- pygpt_net/core/idx/chat.py +24 -34
- pygpt_net/core/idx/response.py +5 -2
- pygpt_net/core/locale/locale.py +73 -45
- pygpt_net/core/render/web/body.py +152 -207
- pygpt_net/core/render/web/renderer.py +4 -2
- pygpt_net/data/config/config.json +3 -3
- pygpt_net/data/config/models.json +3 -3
- pygpt_net/data/locale/locale.de.ini +12 -8
- pygpt_net/data/locale/locale.en.ini +12 -8
- pygpt_net/data/locale/locale.es.ini +12 -8
- pygpt_net/data/locale/locale.fr.ini +12 -8
- pygpt_net/data/locale/locale.it.ini +12 -8
- pygpt_net/data/locale/locale.pl.ini +12 -8
- pygpt_net/data/locale/locale.uk.ini +12 -8
- pygpt_net/data/locale/locale.zh.ini +12 -8
- pygpt_net/item/ctx.py +2 -1
- pygpt_net/plugin/base/plugin.py +35 -3
- pygpt_net/plugin/bitbucket/__init__.py +12 -0
- pygpt_net/plugin/bitbucket/config.py +267 -0
- pygpt_net/plugin/bitbucket/plugin.py +125 -0
- pygpt_net/plugin/bitbucket/worker.py +569 -0
- pygpt_net/plugin/cmd_files/worker.py +19 -16
- pygpt_net/plugin/facebook/__init__.py +12 -0
- pygpt_net/plugin/facebook/config.py +359 -0
- pygpt_net/plugin/facebook/plugin.py +114 -0
- pygpt_net/plugin/facebook/worker.py +698 -0
- pygpt_net/plugin/github/__init__.py +12 -0
- pygpt_net/plugin/github/config.py +441 -0
- pygpt_net/plugin/github/plugin.py +124 -0
- pygpt_net/plugin/github/worker.py +674 -0
- pygpt_net/plugin/google/__init__.py +12 -0
- pygpt_net/plugin/google/config.py +367 -0
- pygpt_net/plugin/google/plugin.py +126 -0
- pygpt_net/plugin/google/worker.py +826 -0
- pygpt_net/plugin/slack/__init__.py +12 -0
- pygpt_net/plugin/slack/config.py +349 -0
- pygpt_net/plugin/slack/plugin.py +116 -0
- pygpt_net/plugin/slack/worker.py +639 -0
- pygpt_net/plugin/telegram/__init__.py +12 -0
- pygpt_net/plugin/telegram/config.py +308 -0
- pygpt_net/plugin/telegram/plugin.py +118 -0
- pygpt_net/plugin/telegram/worker.py +563 -0
- pygpt_net/plugin/twitter/__init__.py +12 -0
- pygpt_net/plugin/twitter/config.py +491 -0
- pygpt_net/plugin/twitter/plugin.py +126 -0
- pygpt_net/plugin/twitter/worker.py +837 -0
- pygpt_net/provider/agents/base.py +4 -1
- pygpt_net/provider/agents/llama_index/codeact_workflow.py +95 -0
- pygpt_net/provider/agents/llama_index/legacy/__init__.py +0 -0
- pygpt_net/provider/agents/llama_index/{openai.py → legacy/openai.py} +2 -2
- pygpt_net/provider/agents/llama_index/{openai_assistant.py → legacy/openai_assistant.py} +37 -5
- pygpt_net/provider/agents/llama_index/{planner.py → legacy/planner.py} +3 -3
- pygpt_net/provider/agents/llama_index/{react.py → legacy/react.py} +3 -3
- pygpt_net/provider/agents/llama_index/openai_workflow.py +52 -0
- pygpt_net/provider/agents/llama_index/planner_workflow.py +115 -0
- pygpt_net/provider/agents/llama_index/react_workflow.py +6 -4
- pygpt_net/provider/agents/llama_index/workflow/__init__.py +0 -0
- pygpt_net/provider/agents/llama_index/{codeact_agent_custom.py → workflow/codeact.py} +124 -8
- pygpt_net/provider/agents/llama_index/workflow/events.py +24 -0
- pygpt_net/provider/agents/llama_index/workflow/openai.py +634 -0
- pygpt_net/provider/agents/llama_index/workflow/planner.py +601 -0
- pygpt_net/provider/agents/openai/agent.py +1 -0
- pygpt_net/provider/agents/openai/agent_b2b.py +2 -0
- pygpt_net/provider/agents/openai/agent_planner.py +1 -0
- pygpt_net/provider/agents/openai/agent_with_experts.py +1 -0
- pygpt_net/provider/agents/openai/agent_with_experts_feedback.py +1 -0
- pygpt_net/provider/agents/openai/agent_with_feedback.py +1 -0
- pygpt_net/provider/agents/openai/evolve.py +1 -0
- pygpt_net/provider/core/preset/patch.py +11 -17
- pygpt_net/ui/base/config_dialog.py +4 -0
- pygpt_net/ui/dialog/preset.py +34 -77
- pygpt_net/ui/layout/toolbox/presets.py +2 -2
- pygpt_net/ui/main.py +3 -1
- pygpt_net/ui/widget/lists/experts.py +3 -2
- {pygpt_net-2.6.0.post2.dist-info → pygpt_net-2.6.2.dist-info}/METADATA +155 -4
- {pygpt_net-2.6.0.post2.dist-info → pygpt_net-2.6.2.dist-info}/RECORD +96 -62
- pygpt_net/data/config/presets/agent_react_workflow.json +0 -34
- pygpt_net/provider/agents/llama_index/code_act.py +0 -58
- {pygpt_net-2.6.0.post2.dist-info → pygpt_net-2.6.2.dist-info}/LICENSE +0 -0
- {pygpt_net-2.6.0.post2.dist-info → pygpt_net-2.6.2.dist-info}/WHEEL +0 -0
- {pygpt_net-2.6.0.post2.dist-info → pygpt_net-2.6.2.dist-info}/entry_points.txt +0 -0
pygpt_net/CHANGELOG.txt
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
2.6.2 (2025-08-15)
|
|
2
|
+
|
|
3
|
+
- Added plugins (beta): Google, Facebook, X/Twitter, Telegram, Slack, GitHub, Bitbucket.
|
|
4
|
+
|
|
5
|
+
2.6.1 (2025-08-14)
|
|
6
|
+
|
|
7
|
+
- LlamaIndex Agents refactored to Workflows.
|
|
8
|
+
|
|
1
9
|
2.6.0 (2025-08-13)
|
|
2
10
|
|
|
3
11
|
- Added split responses to the OpenAI Agents in non-streaming mode.
|
pygpt_net/__init__.py
CHANGED
|
@@ -6,15 +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.
|
|
9
|
+
# Updated Date: 2025.08.15 00:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
__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.
|
|
17
|
-
__build__ = "2025-08-
|
|
16
|
+
__version__ = "2.6.2"
|
|
17
|
+
__build__ = "2025-08-15"
|
|
18
18
|
__maintainer__ = "Marcin Szczygliński"
|
|
19
19
|
__github__ = "https://github.com/szczyglis-dev/py-gpt"
|
|
20
20
|
__report__ = "https://github.com/szczyglis-dev/py-gpt/issues"
|
pygpt_net/app.py
CHANGED
|
@@ -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.
|
|
9
|
+
# Updated Date: 2025.08.15 00:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import os
|
|
@@ -70,14 +70,23 @@ from pygpt_net.plugin.openai_vision import Plugin as OpenAIVisionPlugin
|
|
|
70
70
|
from pygpt_net.plugin.real_time import Plugin as RealTimePlugin
|
|
71
71
|
from pygpt_net.plugin.agent import Plugin as AgentPlugin
|
|
72
72
|
from pygpt_net.plugin.mailer import Plugin as MailerPlugin
|
|
73
|
+
from pygpt_net.plugin.google import Plugin as GooglePlugin
|
|
74
|
+
from pygpt_net.plugin.twitter import Plugin as TwitterPlugin
|
|
75
|
+
from pygpt_net.plugin.facebook import Plugin as FacebookPlugin
|
|
76
|
+
from pygpt_net.plugin.telegram import Plugin as TelegramPlugin
|
|
77
|
+
from pygpt_net.plugin.slack import Plugin as SlackPlugin
|
|
78
|
+
from pygpt_net.plugin.github import Plugin as GithubPlugin
|
|
79
|
+
from pygpt_net.plugin.bitbucket import Plugin as BitbucketPlugin
|
|
73
80
|
|
|
74
81
|
# agents (Llama-index)
|
|
75
|
-
from pygpt_net.provider.agents.llama_index.openai import OpenAIAgent
|
|
76
|
-
from pygpt_net.provider.agents.llama_index.openai_assistant import OpenAIAssistantAgent
|
|
77
|
-
from pygpt_net.provider.agents.llama_index.planner import PlannerAgent
|
|
78
|
-
from pygpt_net.provider.agents.llama_index.
|
|
82
|
+
# from pygpt_net.provider.agents.llama_index.legacy.openai import OpenAIAgent
|
|
83
|
+
from pygpt_net.provider.agents.llama_index.legacy.openai_assistant import OpenAIAssistantAgent
|
|
84
|
+
# from pygpt_net.provider.agents.llama_index.legacy.planner import PlannerAgent
|
|
85
|
+
from pygpt_net.provider.agents.llama_index.planner_workflow import PlannerAgent as PlannerWorkflowAgent
|
|
86
|
+
from pygpt_net.provider.agents.llama_index.openai_workflow import OpenAIAgent as OpenAIWorkflowAgent
|
|
87
|
+
# from pygpt_net.provider.agents.llama_index.legacy.react import ReactAgent
|
|
79
88
|
from pygpt_net.provider.agents.llama_index.react_workflow import ReactWorkflowAgent
|
|
80
|
-
from pygpt_net.provider.agents.llama_index.
|
|
89
|
+
from pygpt_net.provider.agents.llama_index.codeact_workflow import CodeActAgent
|
|
81
90
|
from pygpt_net.provider.agents.openai.agent import Agent as OpenAIAgentsBase
|
|
82
91
|
from pygpt_net.provider.agents.openai.agent_with_experts import Agent as OpenAIAgentsExperts
|
|
83
92
|
from pygpt_net.provider.agents.openai.agent_with_experts_feedback import Agent as OpenAIAgentsExpertsFeedback
|
|
@@ -378,6 +387,13 @@ def run(**kwargs):
|
|
|
378
387
|
launcher.add_plugin(IdxLlamaIndexPlugin())
|
|
379
388
|
launcher.add_plugin(MailerPlugin())
|
|
380
389
|
launcher.add_plugin(CrontabPlugin())
|
|
390
|
+
launcher.add_plugin(GooglePlugin())
|
|
391
|
+
launcher.add_plugin(TwitterPlugin())
|
|
392
|
+
launcher.add_plugin(FacebookPlugin())
|
|
393
|
+
launcher.add_plugin(TelegramPlugin())
|
|
394
|
+
launcher.add_plugin(SlackPlugin())
|
|
395
|
+
launcher.add_plugin(GithubPlugin())
|
|
396
|
+
launcher.add_plugin(BitbucketPlugin())
|
|
381
397
|
|
|
382
398
|
# register custom plugins
|
|
383
399
|
plugins = kwargs.get('plugins', None)
|
|
@@ -420,10 +436,12 @@ def run(**kwargs):
|
|
|
420
436
|
launcher.add_vector_store(store)
|
|
421
437
|
|
|
422
438
|
# register base agents
|
|
423
|
-
launcher.add_agent(OpenAIAgent()) # llama-index
|
|
439
|
+
# launcher.add_agent(OpenAIAgent()) # llama-index
|
|
440
|
+
launcher.add_agent(OpenAIWorkflowAgent()) # llama-index
|
|
424
441
|
launcher.add_agent(OpenAIAssistantAgent()) # llama-index
|
|
425
|
-
launcher.add_agent(PlannerAgent()) # llama-index
|
|
426
|
-
launcher.add_agent(
|
|
442
|
+
# launcher.add_agent(PlannerAgent()) # llama-index
|
|
443
|
+
launcher.add_agent(PlannerWorkflowAgent()) # llama-index
|
|
444
|
+
# launcher.add_agent(ReactAgent()) # llama-index
|
|
427
445
|
launcher.add_agent(ReactWorkflowAgent()) # llama-index
|
|
428
446
|
launcher.add_agent(CodeActAgent()) # llama-index
|
|
429
447
|
launcher.add_agent(OpenAIAgentsBase()) # openai-agents
|
|
@@ -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.
|
|
9
|
+
# Updated Date: 2025.08.14 01:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from typing import Dict, Any
|
|
@@ -164,14 +164,16 @@ class Response:
|
|
|
164
164
|
:param context: BridgeContext
|
|
165
165
|
:param extra: Extra data
|
|
166
166
|
"""
|
|
167
|
+
global_mode = self.window.core.config.get('mode', MODE_AGENT_LLAMA)
|
|
167
168
|
ctx = context.ctx
|
|
168
169
|
if self.window.controller.kernel.stopped():
|
|
169
170
|
output = ctx.output
|
|
170
171
|
if output and has_unclosed_code_tag(output):
|
|
171
172
|
ctx.output += "\n```"
|
|
172
173
|
ctx.msg_id = None
|
|
173
|
-
|
|
174
|
-
|
|
174
|
+
if ctx.id is None:
|
|
175
|
+
self.window.core.ctx.add(ctx) # store context to prevent current output from being lost
|
|
176
|
+
self.window.controller.ctx.prepare_name(ctx) # summarize if not yet
|
|
175
177
|
|
|
176
178
|
# finish render
|
|
177
179
|
self.window.dispatch(AppEvent(AppEvent.CTX_END)) # app event
|
|
@@ -227,7 +229,8 @@ class Response:
|
|
|
227
229
|
}
|
|
228
230
|
event = RenderEvent(RenderEvent.INPUT_APPEND, data)
|
|
229
231
|
self.window.dispatch(event)
|
|
230
|
-
|
|
232
|
+
if ctx.id is None:
|
|
233
|
+
self.window.core.ctx.add(ctx)
|
|
231
234
|
self.window.controller.ctx.update(
|
|
232
235
|
reload=True,
|
|
233
236
|
all=False,
|
|
@@ -266,6 +269,9 @@ class Response:
|
|
|
266
269
|
self.window.dispatch(event)
|
|
267
270
|
|
|
268
271
|
# if continue reasoning
|
|
272
|
+
if global_mode not in [MODE_AGENT_LLAMA, MODE_AGENT_OPENAI]:
|
|
273
|
+
return # no agent mode, nothing to do
|
|
274
|
+
|
|
269
275
|
if ctx.extra is None or (type(ctx.extra) == dict and "agent_finish" not in ctx.extra):
|
|
270
276
|
self.window.update_status(trans("status.agent.reasoning"))
|
|
271
277
|
self.window.controller.chat.common.lock_input() # lock input, re-enable stop button
|
|
@@ -77,6 +77,21 @@ class StreamWorker(QRunnable):
|
|
|
77
77
|
if generator is not None:
|
|
78
78
|
for chunk in generator:
|
|
79
79
|
if ctrl.kernel.stopped():
|
|
80
|
+
if hasattr(generator, 'close'):
|
|
81
|
+
try:
|
|
82
|
+
generator.close()
|
|
83
|
+
except Exception:
|
|
84
|
+
pass
|
|
85
|
+
elif hasattr(generator, 'cancel'):
|
|
86
|
+
try:
|
|
87
|
+
generator.cancel()
|
|
88
|
+
except Exception:
|
|
89
|
+
pass
|
|
90
|
+
elif hasattr(generator, 'stop'):
|
|
91
|
+
try:
|
|
92
|
+
generator.stop()
|
|
93
|
+
except Exception:
|
|
94
|
+
pass
|
|
80
95
|
ctx.msg_id = None
|
|
81
96
|
stopped = True
|
|
82
97
|
break
|
|
@@ -239,7 +254,6 @@ class StreamWorker(QRunnable):
|
|
|
239
254
|
elif etype == "response.image_generation_call.partial_image":
|
|
240
255
|
image_base64 = chunk.partial_image_b64
|
|
241
256
|
image_bytes = base64.b64decode(image_base64)
|
|
242
|
-
# prosty i bezpieczny overwrite (jak w oryginale)
|
|
243
257
|
with open(img_path, "wb") as f:
|
|
244
258
|
f.write(image_bytes)
|
|
245
259
|
is_image = True
|
|
@@ -249,7 +263,6 @@ class StreamWorker(QRunnable):
|
|
|
249
263
|
ctx.msg_id = str(chunk.response.id)
|
|
250
264
|
core.ctx.update_item(ctx)
|
|
251
265
|
|
|
252
|
-
# end/error etype – nic nie robimy
|
|
253
266
|
elif etype in {"response.done", "response.failed", "error"}:
|
|
254
267
|
pass
|
|
255
268
|
|
|
@@ -407,6 +420,14 @@ class Stream:
|
|
|
407
420
|
):
|
|
408
421
|
"""
|
|
409
422
|
Asynchronous append of stream worker to the thread.
|
|
423
|
+
|
|
424
|
+
:param ctx: Context item
|
|
425
|
+
:param mode: Mode of operation (e.g., MODE_ASSISTANT)
|
|
426
|
+
:param is_response: Whether this is a response stream
|
|
427
|
+
:param reply: Reply identifier
|
|
428
|
+
:param internal: Whether this is an internal stream
|
|
429
|
+
:param context: Optional BridgeContext for additional context
|
|
430
|
+
:param extra: Additional data to pass to the stream
|
|
410
431
|
"""
|
|
411
432
|
self.ctx = ctx
|
|
412
433
|
self.mode = mode
|
|
@@ -430,6 +451,8 @@ class Stream:
|
|
|
430
451
|
def handleEnd(self, ctx: CtxItem):
|
|
431
452
|
"""
|
|
432
453
|
Slot for handling end of stream
|
|
454
|
+
|
|
455
|
+
:param ctx: Context item
|
|
433
456
|
"""
|
|
434
457
|
self.window.controller.ui.update_tokens()
|
|
435
458
|
|
|
@@ -457,9 +480,19 @@ class Stream:
|
|
|
457
480
|
self.worker = None
|
|
458
481
|
|
|
459
482
|
def handleEvent(self, event):
|
|
483
|
+
"""
|
|
484
|
+
Slot for handling stream events
|
|
485
|
+
|
|
486
|
+
:param event: RenderEvent
|
|
487
|
+
"""
|
|
460
488
|
self.window.dispatch(event)
|
|
461
489
|
|
|
462
490
|
def handleError(self, error):
|
|
491
|
+
"""
|
|
492
|
+
Slot for handling stream errors
|
|
493
|
+
|
|
494
|
+
:param error: Exception or error message
|
|
495
|
+
"""
|
|
463
496
|
self.window.core.debug.log(error)
|
|
464
497
|
if self.is_response:
|
|
465
498
|
if not isinstance(self.extra, dict):
|
|
@@ -475,4 +508,9 @@ class Stream:
|
|
|
475
508
|
)
|
|
476
509
|
|
|
477
510
|
def log(self, data: object):
|
|
511
|
+
"""
|
|
512
|
+
Log data to the debug console
|
|
513
|
+
|
|
514
|
+
:param data: object to log
|
|
515
|
+
"""
|
|
478
516
|
self.window.core.debug.info(data)
|
|
@@ -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: 2025.
|
|
9
|
+
# Updated Date: 2025.08.14 01:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import copy
|
|
13
|
+
import json
|
|
13
14
|
from typing import Optional, Any
|
|
14
15
|
|
|
15
16
|
from pygpt_net.core.events import Event
|
|
@@ -107,7 +108,14 @@ class Editor:
|
|
|
107
108
|
"description": "model.llama_index.env.desc",
|
|
108
109
|
"advanced": True,
|
|
109
110
|
},
|
|
111
|
+
"extra_json": {
|
|
112
|
+
"type": "textarea",
|
|
113
|
+
"label": "model.extra",
|
|
114
|
+
"description": "model.extra.desc",
|
|
115
|
+
"advanced": True,
|
|
116
|
+
},
|
|
110
117
|
}
|
|
118
|
+
self.custom_fields = ["extra_json"]
|
|
111
119
|
|
|
112
120
|
def get_options(self):
|
|
113
121
|
"""
|
|
@@ -220,8 +228,12 @@ class Editor:
|
|
|
220
228
|
model = self.window.core.models.items[self.current]
|
|
221
229
|
data_dict = model.to_dict()
|
|
222
230
|
for key in options:
|
|
223
|
-
|
|
224
|
-
|
|
231
|
+
if key in data_dict:
|
|
232
|
+
value = data_dict[key]
|
|
233
|
+
options[key]["value"] = value
|
|
234
|
+
|
|
235
|
+
# custom fields
|
|
236
|
+
options["extra_json"]["value"] = json.dumps(model.extra, indent=4) if model.extra else ""
|
|
225
237
|
|
|
226
238
|
if self.current is not None and self.current in self.window.core.models.items:
|
|
227
239
|
self.set_tab_by_id(self.current)
|
|
@@ -229,15 +241,24 @@ class Editor:
|
|
|
229
241
|
# load and apply options to config dialog
|
|
230
242
|
self.window.controller.config.load_options("model", options)
|
|
231
243
|
|
|
232
|
-
def save(
|
|
244
|
+
def save(
|
|
245
|
+
self,
|
|
246
|
+
persist: bool = True,
|
|
247
|
+
force: bool = False
|
|
248
|
+
):
|
|
233
249
|
"""
|
|
234
250
|
Save models editor
|
|
235
251
|
|
|
236
252
|
:param persist: persist to file and close dialog
|
|
253
|
+
:param force: force save without validation
|
|
237
254
|
"""
|
|
238
255
|
options = copy.deepcopy(self.get_options()) # copy options
|
|
239
256
|
data_dict = {}
|
|
257
|
+
|
|
258
|
+
# base fields
|
|
240
259
|
for key in options:
|
|
260
|
+
if key in self.custom_fields:
|
|
261
|
+
continue
|
|
241
262
|
value = self.window.controller.config.get_value(
|
|
242
263
|
parent_id="model",
|
|
243
264
|
key=key,
|
|
@@ -245,6 +266,26 @@ class Editor:
|
|
|
245
266
|
)
|
|
246
267
|
data_dict[key] = value
|
|
247
268
|
|
|
269
|
+
# custom fields
|
|
270
|
+
if "extra_json" in options:
|
|
271
|
+
extra_json = self.window.controller.config.get_value(
|
|
272
|
+
parent_id="model",
|
|
273
|
+
key="extra_json",
|
|
274
|
+
option=options["extra_json"],
|
|
275
|
+
)
|
|
276
|
+
try:
|
|
277
|
+
if extra_json:
|
|
278
|
+
decoded = json.loads(extra_json)
|
|
279
|
+
data_dict["extra"] = decoded
|
|
280
|
+
else:
|
|
281
|
+
data_dict["extra"] = {}
|
|
282
|
+
except json.JSONDecodeError as error:
|
|
283
|
+
self.window.ui.dialogs.alert(
|
|
284
|
+
"JSON decoding error in 'extra' field. Please check the syntax:\n\n{}".format(error)
|
|
285
|
+
)
|
|
286
|
+
if not force:
|
|
287
|
+
return # if JSON is invalid, do not save
|
|
288
|
+
|
|
248
289
|
# update current model
|
|
249
290
|
if self.current in self.window.core.models.items:
|
|
250
291
|
self.window.core.models.items[self.current].from_dict(data_dict)
|
|
@@ -458,6 +458,31 @@ class Plugins:
|
|
|
458
458
|
self.settings.setup()
|
|
459
459
|
self.update()
|
|
460
460
|
|
|
461
|
+
def save_all(self):
|
|
462
|
+
"""Save plugin settings"""
|
|
463
|
+
for id in self.window.core.plugins.plugins.keys():
|
|
464
|
+
plugin = self.window.core.plugins.plugins[id]
|
|
465
|
+
options = plugin.setup() # get plugin options
|
|
466
|
+
|
|
467
|
+
# add plugin to global config data if not exists
|
|
468
|
+
if id not in self.window.core.config.get('plugins'):
|
|
469
|
+
self.window.core.config.data['plugins'][id] = {}
|
|
470
|
+
|
|
471
|
+
# update config with current values
|
|
472
|
+
for key in options:
|
|
473
|
+
self.window.core.config.data['plugins'][id][key] = self.window.core.plugins.plugins[id].options[key]['value']
|
|
474
|
+
|
|
475
|
+
# remove key from config if plugin option not exists
|
|
476
|
+
for key in list(self.window.core.config.data['plugins'].keys()):
|
|
477
|
+
if key not in self.window.core.plugins.plugins:
|
|
478
|
+
self.window.core.config.data['plugins'].pop(key)
|
|
479
|
+
|
|
480
|
+
# save preset
|
|
481
|
+
self.window.controller.plugins.presets.save_current()
|
|
482
|
+
|
|
483
|
+
# save config
|
|
484
|
+
self.window.core.config.save()
|
|
485
|
+
|
|
461
486
|
def log(self, data: Any):
|
|
462
487
|
"""
|
|
463
488
|
Log data to debug
|