node-red-contrib-knx-ultimate 6.3.22 → 6.3.25
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.
- package/CHANGELOG.md +19 -0
- package/examples/IoT Bridge - Modbus Flex Adapter.json +361 -0
- package/examples/KNX AI - Telegrambot Direct Chat.json +1 -17
- package/nodes/knxUltimate-config.js +13 -4
- package/nodes/knxUltimateAI.html +142 -15
- package/nodes/knxUltimateAI.js +734 -163
- package/nodes/knxUltimateIoTBridge.html +349 -20
- package/nodes/knxUltimateIoTBridge.js +474 -44
- package/nodes/locales/de/knxUltimateAI.html +26 -8
- package/nodes/locales/de/knxUltimateAI.json +11 -1
- package/nodes/locales/de/knxUltimateIoTBridge.html +28 -2
- package/nodes/locales/de/knxUltimateIoTBridge.json +20 -2
- package/nodes/locales/en/knxUltimateAI.html +26 -8
- package/nodes/locales/en/knxUltimateAI.json +11 -1
- package/nodes/locales/en/knxUltimateIoTBridge.html +28 -2
- package/nodes/locales/en/knxUltimateIoTBridge.json +20 -2
- package/nodes/locales/es/knxUltimateAI.html +26 -8
- package/nodes/locales/es/knxUltimateAI.json +11 -1
- package/nodes/locales/es/knxUltimateIoTBridge.html +28 -2
- package/nodes/locales/es/knxUltimateIoTBridge.json +20 -2
- package/nodes/locales/fr/knxUltimateAI.html +26 -8
- package/nodes/locales/fr/knxUltimateAI.json +11 -1
- package/nodes/locales/fr/knxUltimateIoTBridge.html +28 -2
- package/nodes/locales/fr/knxUltimateIoTBridge.json +20 -2
- package/nodes/locales/it/knxUltimateAI.html +26 -8
- package/nodes/locales/it/knxUltimateAI.json +11 -1
- package/nodes/locales/it/knxUltimateIoTBridge.html +28 -2
- package/nodes/locales/it/knxUltimateIoTBridge.json +20 -2
- package/nodes/locales/zh-CN/knxUltimateAI.html +26 -8
- package/nodes/locales/zh-CN/knxUltimateAI.json +11 -1
- package/nodes/locales/zh-CN/knxUltimateIoTBridge.html +28 -2
- package/nodes/locales/zh-CN/knxUltimateIoTBridge.json +20 -2
- package/nodes/plugins/knxUltimateAI-vue/assets/app.css +1 -1
- package/nodes/plugins/knxUltimateAI-vue/assets/app.js +4 -4
- package/nodes/utils/knxAiChatContext.js +181 -84
- package/nodes/utils/knxAiEventHistory.js +275 -0
- package/package.json +3 -3
- package/resources/KNXAIChatAdapterMappings.js +14 -12
package/nodes/knxUltimateAI.html
CHANGED
|
@@ -5,6 +5,37 @@
|
|
|
5
5
|
align-items: flex-start;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
+
#knx-ai-llm-connection-settings {
|
|
9
|
+
width: 100%;
|
|
10
|
+
max-width: 720px;
|
|
11
|
+
min-width: 0;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
#knx-ai-llm-connection-settings .form-row {
|
|
15
|
+
min-width: 0;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
#knx-ai-local-context-status,
|
|
19
|
+
.knx-ai-prompt-context-hint {
|
|
20
|
+
width: 100%;
|
|
21
|
+
max-width: 720px;
|
|
22
|
+
min-width: 0;
|
|
23
|
+
box-sizing: border-box;
|
|
24
|
+
color: var(--red-ui-secondary-text-color, #777);
|
|
25
|
+
font-size: 12px;
|
|
26
|
+
line-height: 1.4;
|
|
27
|
+
white-space: normal;
|
|
28
|
+
overflow-wrap: break-word;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
#knx-ai-local-context-status {
|
|
32
|
+
margin: -8px 0 12px;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.knx-ai-prompt-context-hint {
|
|
36
|
+
margin: 6px 0 0;
|
|
37
|
+
}
|
|
38
|
+
|
|
8
39
|
#knx-ai-ollama-install-row>label {
|
|
9
40
|
padding-top: 6px;
|
|
10
41
|
}
|
|
@@ -138,7 +169,8 @@
|
|
|
138
169
|
}
|
|
139
170
|
|
|
140
171
|
#knx-ai-detected-adapters-panel,
|
|
141
|
-
#knx-ai-chat-context-panel
|
|
172
|
+
#knx-ai-chat-context-panel,
|
|
173
|
+
#knx-ai-chat-learning-link-panel {
|
|
142
174
|
margin: 0 0 18px;
|
|
143
175
|
padding: 12px 14px;
|
|
144
176
|
border: 1px solid var(--red-ui-form-input-border-color, #ccc);
|
|
@@ -155,7 +187,8 @@
|
|
|
155
187
|
}
|
|
156
188
|
|
|
157
189
|
#knx-ai-detected-adapters-panel>h4,
|
|
158
|
-
#knx-ai-chat-context-panel>h4
|
|
190
|
+
#knx-ai-chat-context-panel>h4,
|
|
191
|
+
#knx-ai-chat-learning-link-panel>h4 {
|
|
159
192
|
margin: 0 0 9px;
|
|
160
193
|
font-size: 14px;
|
|
161
194
|
font-weight: 600;
|
|
@@ -189,6 +222,17 @@
|
|
|
189
222
|
font-size: 12px;
|
|
190
223
|
}
|
|
191
224
|
|
|
225
|
+
#knx-ai-chat-learning-link-panel {
|
|
226
|
+
margin: 18px 0 0;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
.knx-ai-chat-learning-link-copy {
|
|
230
|
+
margin: 0 0 10px;
|
|
231
|
+
color: var(--red-ui-secondary-text-color, #777);
|
|
232
|
+
font-size: 12px;
|
|
233
|
+
line-height: 1.45;
|
|
234
|
+
}
|
|
235
|
+
|
|
192
236
|
.knx-ai-chat-context-metric {
|
|
193
237
|
margin-top: 6px;
|
|
194
238
|
font-weight: 600;
|
|
@@ -273,6 +317,7 @@
|
|
|
273
317
|
llmBaseUrl: { value: "https://api.openai.com/v1/chat/completions" },
|
|
274
318
|
llmModel: { value: "gpt-5.4" },
|
|
275
319
|
llmContextLength: { value: 0 },
|
|
320
|
+
llmPromptContextTokens: { value: 16384 },
|
|
276
321
|
llmAllowKnxCommands: { value: false },
|
|
277
322
|
llmRequireCommandConfirmation: { value: true },
|
|
278
323
|
chatAdapterPreset: { value: "none" },
|
|
@@ -398,6 +443,13 @@
|
|
|
398
443
|
} catch (error) { }
|
|
399
444
|
delete node[key];
|
|
400
445
|
});
|
|
446
|
+
try {
|
|
447
|
+
const $model = $("#node-input-llmModel");
|
|
448
|
+
$model.off(".knxUltimateAiModelPicker");
|
|
449
|
+
if (typeof $model.autocomplete === "function" && $model.data("ui-autocomplete")) {
|
|
450
|
+
$model.autocomplete("destroy");
|
|
451
|
+
}
|
|
452
|
+
} catch (error) { }
|
|
401
453
|
};
|
|
402
454
|
|
|
403
455
|
try {
|
|
@@ -658,7 +710,11 @@
|
|
|
658
710
|
const currentProvider = String(contextLimit.provider || $("#node-input-llmProvider").val() || "").toLowerCase();
|
|
659
711
|
let contextLimitTokens = Math.max(0, Number(contextLimit.tokens) || 0);
|
|
660
712
|
if (!contextLimitTokens && (currentProvider === "lmstudio" || currentProvider === "ollama")) {
|
|
661
|
-
contextLimitTokens = Math.min(
|
|
713
|
+
contextLimitTokens = Math.min(
|
|
714
|
+
Math.max(0, Number($("#node-input-llmContextLength").val()) || 16384),
|
|
715
|
+
Math.max(0, Number($("#node-input-llmPromptContextTokens").val()) || 16384),
|
|
716
|
+
16384
|
|
717
|
+
);
|
|
662
718
|
}
|
|
663
719
|
const contextLimitValue = contextLimitTokens
|
|
664
720
|
? formatCompactTokens(contextLimitTokens) + " " + t("knxUltimateAI.messages.chatContextTokens", "tokens")
|
|
@@ -742,7 +798,7 @@
|
|
|
742
798
|
});
|
|
743
799
|
|
|
744
800
|
$("#knx-ai-chat-context-pattern")
|
|
745
|
-
.text(t("knxUltimateAI.messages.chatContextTelegramPattern", "Daily files") + ": " + String(overview.telegramFilePattern || "YYYY-MM-DD.
|
|
801
|
+
.text(t("knxUltimateAI.messages.chatContextTelegramPattern", "Daily files") + ": " + String(overview.telegramFilePattern || "YYYY-MM-DD.knxctx"));
|
|
746
802
|
$status.hide();
|
|
747
803
|
$content.show();
|
|
748
804
|
};
|
|
@@ -763,17 +819,30 @@
|
|
|
763
819
|
renderChatContextOverview(null);
|
|
764
820
|
});
|
|
765
821
|
|
|
766
|
-
|
|
767
|
-
evt.preventDefault();
|
|
822
|
+
const openKnxAiWebPage = function (view) {
|
|
768
823
|
const adminRoot = resolveAdminRoot();
|
|
769
824
|
const targetBase = adminRoot + "/knxUltimateAI/sidebar/page";
|
|
770
825
|
const params = new URLSearchParams();
|
|
771
826
|
if (nodeId) params.set("nodeId", nodeId);
|
|
827
|
+
if (view === "chat-learning") {
|
|
828
|
+
params.set("tab", "settings");
|
|
829
|
+
params.set("settingsTab", "learning");
|
|
830
|
+
}
|
|
772
831
|
const accessToken = resolveAccessToken();
|
|
773
832
|
if (accessToken) params.set("access_token", accessToken);
|
|
774
833
|
const target = targetBase + (params.toString() ? ("?" + params.toString()) : "");
|
|
775
834
|
const wnd = window.open(target, "_blank", "noopener,noreferrer");
|
|
776
835
|
try { if (wnd && typeof wnd.focus === "function") wnd.focus(); } catch (e) { }
|
|
836
|
+
};
|
|
837
|
+
|
|
838
|
+
$("#knx-ai-open-web-page-vue").on("click", function (evt) {
|
|
839
|
+
evt.preventDefault();
|
|
840
|
+
openKnxAiWebPage();
|
|
841
|
+
});
|
|
842
|
+
|
|
843
|
+
$("#knx-ai-open-chat-learning").on("click", function (evt) {
|
|
844
|
+
evt.preventDefault();
|
|
845
|
+
openKnxAiWebPage("chat-learning");
|
|
777
846
|
});
|
|
778
847
|
|
|
779
848
|
const setRefreshModelsBusy = (busy) => {
|
|
@@ -788,6 +857,8 @@
|
|
|
788
857
|
};
|
|
789
858
|
|
|
790
859
|
let localModelDetailsById = new Map();
|
|
860
|
+
let availableModelItems = [];
|
|
861
|
+
let modelPickerMouseDown = false;
|
|
791
862
|
let localContextRequestVersion = 0;
|
|
792
863
|
|
|
793
864
|
const formatContextTokens = (value) => {
|
|
@@ -823,28 +894,63 @@
|
|
|
823
894
|
text += " · " + t("knxUltimateAI.messages.lmStudioContextCurrentlyLoaded", "currently loaded") + ": " + loadedTokens + " token";
|
|
824
895
|
}
|
|
825
896
|
if (provider === "lmstudio" || provider === "ollama") {
|
|
826
|
-
|
|
897
|
+
const selectedBudget = Math.max(4096, Number($("#node-input-llmPromptContextTokens").val()) || 16384);
|
|
898
|
+
text += " · " + t("knxUltimateAI.messages.localContextBudget", "KNX AI context budget") + ": " + (selectedBudget / 1024) + "K";
|
|
827
899
|
}
|
|
828
900
|
$status.text(text).css("color", color).show();
|
|
829
901
|
};
|
|
830
902
|
|
|
831
903
|
const populateModels = (models, modelDetails) => {
|
|
832
|
-
const $dl = $("#knx-ai-llmModels");
|
|
833
|
-
if (!$dl || $dl.length === 0) return;
|
|
834
904
|
localModelDetailsById = new Map();
|
|
835
905
|
(modelDetails || []).forEach(function (detail) {
|
|
836
906
|
if (detail && detail.id) localModelDetailsById.set(String(detail.id), detail);
|
|
837
907
|
});
|
|
838
|
-
|
|
839
|
-
(models || []).forEach(function (m) {
|
|
908
|
+
availableModelItems = (models || []).map(function (m) {
|
|
840
909
|
const detail = localModelDetailsById.get(String(m));
|
|
841
910
|
const label = detail
|
|
842
911
|
? String(detail.displayName || detail.id) + " · " + formatContextTokens(detail.maxContextLength) + " token"
|
|
843
912
|
: String(m);
|
|
844
|
-
|
|
913
|
+
return { label: label, value: String(m) };
|
|
845
914
|
});
|
|
846
915
|
};
|
|
847
916
|
|
|
917
|
+
const configureModelPicker = () => {
|
|
918
|
+
const $model = $("#node-input-llmModel");
|
|
919
|
+
if (!$model.length || typeof $model.autocomplete !== "function") return;
|
|
920
|
+
$model.autocomplete({
|
|
921
|
+
minLength: 0,
|
|
922
|
+
source: function (request, response) {
|
|
923
|
+
const term = String(request && request.term ? request.term : "").trim().toLowerCase();
|
|
924
|
+
if (!term) {
|
|
925
|
+
response(availableModelItems.slice());
|
|
926
|
+
return;
|
|
927
|
+
}
|
|
928
|
+
response(availableModelItems.filter(function (item) {
|
|
929
|
+
return item.value.toLowerCase().includes(term) || item.label.toLowerCase().includes(term);
|
|
930
|
+
}));
|
|
931
|
+
},
|
|
932
|
+
select: function (event, ui) {
|
|
933
|
+
$(this).val(ui.item.value).trigger("change");
|
|
934
|
+
return false;
|
|
935
|
+
}
|
|
936
|
+
});
|
|
937
|
+
$model.on("mousedown.knxUltimateAiModelPicker", function () {
|
|
938
|
+
modelPickerMouseDown = true;
|
|
939
|
+
}).on("focus.knxUltimateAiModelPicker", function () {
|
|
940
|
+
if (!modelPickerMouseDown) $(this).autocomplete("search", "");
|
|
941
|
+
}).on("click.knxUltimateAiModelPicker", function () {
|
|
942
|
+
modelPickerMouseDown = false;
|
|
943
|
+
const input = this;
|
|
944
|
+
setTimeout(function () {
|
|
945
|
+
try { $(input).autocomplete("search", ""); } catch (error) { }
|
|
946
|
+
}, 0);
|
|
947
|
+
}).on("blur.knxUltimateAiModelPicker", function () {
|
|
948
|
+
modelPickerMouseDown = false;
|
|
949
|
+
});
|
|
950
|
+
};
|
|
951
|
+
|
|
952
|
+
configureModelPicker();
|
|
953
|
+
|
|
848
954
|
const normalizeUrl = (value) => String(value || "").trim().replace(/\/+$/, "").toLowerCase();
|
|
849
955
|
const isOpenAiDefaultUrl = (value) => normalizeUrl(value) === normalizeUrl(OPENAI_COMPAT_DEFAULT_CHAT_URL);
|
|
850
956
|
const isOllamaDefaultUrl = (value) => normalizeUrl(value) === normalizeUrl(OLLAMA_DEFAULT_CHAT_URL);
|
|
@@ -1044,7 +1150,9 @@
|
|
|
1044
1150
|
|
|
1045
1151
|
const toggleProvider = ({ applyDefaults = false, autoLoadModels = false } = {}) => {
|
|
1046
1152
|
const provider = $("#node-input-llmProvider").val();
|
|
1153
|
+
const localProvider = provider === "lmstudio" || provider === "ollama";
|
|
1047
1154
|
if (applyDefaults) applyProviderDefaults(provider);
|
|
1155
|
+
$("#knx-ai-prompt-context-row").toggle(localProvider);
|
|
1048
1156
|
if (provider === "ollama") {
|
|
1049
1157
|
$("#knx-ai-apikey-row").hide();
|
|
1050
1158
|
$("#knx-ai-ollama-install-row").show();
|
|
@@ -1071,6 +1179,10 @@
|
|
|
1071
1179
|
$("#node-input-llmModel").on("change", function () {
|
|
1072
1180
|
configureSelectedLocalModel();
|
|
1073
1181
|
});
|
|
1182
|
+
$("#node-input-llmPromptContextTokens").on("change", function () {
|
|
1183
|
+
const detail = localModelDetailsById.get(String($("#node-input-llmModel").val() || "").trim());
|
|
1184
|
+
if (detail) setLocalModelContextStatus(detail, detail.loadedContextLength > 0 ? "configured" : "inactive");
|
|
1185
|
+
});
|
|
1074
1186
|
$("#knx-ai-downloadOllamaModel").on("click", function (evt) {
|
|
1075
1187
|
evt.preventDefault();
|
|
1076
1188
|
openOllamaLibrary();
|
|
@@ -1171,6 +1283,13 @@
|
|
|
1171
1283
|
<h4><i class="fa fa-home"></i> <span data-i18n="knxUltimateAI.sections.homeIntelligence"></span></h4>
|
|
1172
1284
|
<div class="knx-ai-accordion-mount" id="knx-ai-mount-home-intelligence"></div>
|
|
1173
1285
|
</div>
|
|
1286
|
+
<div id="knx-ai-chat-learning-link-panel">
|
|
1287
|
+
<h4><i class="fa fa-graduation-cap"></i> <span data-i18n="knxUltimateAI.sections.chatLearning"></span></h4>
|
|
1288
|
+
<p class="knx-ai-chat-learning-link-copy" data-i18n="knxUltimateAI.messages.chatLearningOpenHint"></p>
|
|
1289
|
+
<button type="button" class="red-ui-button" id="knx-ai-open-chat-learning">
|
|
1290
|
+
<i class="fa fa-external-link"></i> <span data-i18n="knxUltimateAI.buttons.openChatLearning"></span>
|
|
1291
|
+
</button>
|
|
1292
|
+
</div>
|
|
1174
1293
|
<div id="knx-ai-detected-adapters-panel">
|
|
1175
1294
|
<h4><i class="fa fa-puzzle-piece"></i> <span data-i18n="knxUltimateAI.sections.detectedAdapters"></span></h4>
|
|
1176
1295
|
<div id="knx-ai-detected-adapters-status" data-i18n="knxUltimateAI.messages.detectedAdaptersLoading"></div>
|
|
@@ -1226,14 +1345,22 @@
|
|
|
1226
1345
|
|
|
1227
1346
|
<div class="form-row">
|
|
1228
1347
|
<label style="width:290px" for="node-input-llmModel"><i class="fa fa-cube"></i> <span data-i18n="knxUltimateAI.properties.llmModel"></span></label>
|
|
1229
|
-
<input style="width:calc(100% - 110px)" type="text" id="node-input-llmModel"
|
|
1348
|
+
<input style="width:calc(100% - 110px)" type="text" id="node-input-llmModel" autocomplete="off" data-i18n="[placeholder]knxUltimateAI.placeholder.llmModel">
|
|
1230
1349
|
<button type="button" class="red-ui-button" id="knx-ai-refreshModels" style="margin-left:6px;" title="Refresh models">
|
|
1231
1350
|
<i class="fa fa-refresh"></i> <span data-i18n="knxUltimateAI.buttons.refreshModels"></span>
|
|
1232
1351
|
</button>
|
|
1233
1352
|
</div>
|
|
1234
|
-
<datalist id="knx-ai-llmModels"></datalist>
|
|
1235
1353
|
<input type="hidden" id="node-input-llmContextLength">
|
|
1236
|
-
<div id="knx-ai-local-context-status" style="display:none;
|
|
1354
|
+
<div id="knx-ai-local-context-status" style="display:none;"></div>
|
|
1355
|
+
<div class="form-row" id="knx-ai-prompt-context-row" style="display:none;">
|
|
1356
|
+
<label style="width:290px" for="node-input-llmPromptContextTokens"><i class="fa fa-database"></i> <span data-i18n="knxUltimateAI.properties.llmPromptContextTokens"></span></label>
|
|
1357
|
+
<select style="width:100%" id="node-input-llmPromptContextTokens">
|
|
1358
|
+
<option value="4096" data-i18n="knxUltimateAI.selectlists.promptContext.small"></option>
|
|
1359
|
+
<option value="8192" data-i18n="knxUltimateAI.selectlists.promptContext.medium"></option>
|
|
1360
|
+
<option value="16384" data-i18n="knxUltimateAI.selectlists.promptContext.full"></option>
|
|
1361
|
+
</select>
|
|
1362
|
+
<div class="knx-ai-prompt-context-hint" data-i18n="knxUltimateAI.messages.promptContextHint"></div>
|
|
1363
|
+
</div>
|
|
1237
1364
|
<div class="form-row">
|
|
1238
1365
|
<input type="checkbox" id="node-input-llmAllowKnxCommands" style="display:inline-block; width:auto; vertical-align:top;">
|
|
1239
1366
|
<label style="width:auto" for="node-input-llmAllowKnxCommands"> <span data-i18n="knxUltimateAI.properties.llmAllowKnxCommands"></span></label>
|