From 4507b312decda4f8756ce9385a2bb79192bf5be1 Mon Sep 17 00:00:00 2001 From: Dmytro Skarzhynets Date: Thu, 26 Jun 2025 15:02:05 +0300 Subject: [PATCH] AI rule node: add support for GitHub Models --- application/pom.xml | 4 ++ .../Langchain4jChatModelConfigurerImpl.java | 17 +++++- .../data/ai/model/AiModelTypeIdResolver.java | 4 ++ .../data/ai/model/chat/AiChatModel.java | 2 +- .../data/ai/model/chat/AiChatModelConfig.java | 2 +- .../ai/model/chat/GitHubModelsChatModel.java | 60 +++++++++++++++++++ .../chat/Langchain4jChatModelConfigurer.java | 2 + .../common/data/ai/provider/AiProvider.java | 3 +- .../data/ai/provider/AiProviderConfig.java | 5 +- .../provider/GithubModelsProviderConfig.java | 25 ++++++++ 10 files changed, 116 insertions(+), 8 deletions(-) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/GitHubModelsChatModel.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/GithubModelsProviderConfig.java diff --git a/application/pom.xml b/application/pom.xml index 6a71074171..543724fa10 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -409,6 +409,10 @@ dev.langchain4j langchain4j-bedrock + + dev.langchain4j + langchain4j-github-models + diff --git a/application/src/main/java/org/thingsboard/server/service/ai/Langchain4jChatModelConfigurerImpl.java b/application/src/main/java/org/thingsboard/server/service/ai/Langchain4jChatModelConfigurerImpl.java index 404f9eac91..72e9506fdc 100644 --- a/application/src/main/java/org/thingsboard/server/service/ai/Langchain4jChatModelConfigurerImpl.java +++ b/application/src/main/java/org/thingsboard/server/service/ai/Langchain4jChatModelConfigurerImpl.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.service.ai; -import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.auth.oauth2.ServiceAccountCredentials; import com.google.cloud.vertexai.Transport; import com.google.cloud.vertexai.VertexAI; @@ -30,6 +29,7 @@ import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.ai.model.chat.AmazonBedrockChatModel; import org.thingsboard.server.common.data.ai.model.chat.AnthropicChatModel; import org.thingsboard.server.common.data.ai.model.chat.AzureOpenAiChatModel; +import org.thingsboard.server.common.data.ai.model.chat.GitHubModelsChatModel; import org.thingsboard.server.common.data.ai.model.chat.GoogleAiGeminiChatModel; import org.thingsboard.server.common.data.ai.model.chat.GoogleVertexAiGeminiChatModel; import org.thingsboard.server.common.data.ai.model.chat.Langchain4jChatModelConfigurer; @@ -91,11 +91,10 @@ class Langchain4jChatModelConfigurerImpl implements Langchain4jChatModelConfigur GoogleVertexAiGeminiChatModel.Config modelConfig = chatModel.modelConfig(); // construct service account credentials using service account key JSON - ObjectNode serviceAccountKeyJson = providerConfig.serviceAccountKey(); ServiceAccountCredentials serviceAccountCredentials; try { serviceAccountCredentials = ServiceAccountCredentials - .fromStream(new ByteArrayInputStream(JacksonUtil.writeValueAsBytes(serviceAccountKeyJson))); + .fromStream(new ByteArrayInputStream(JacksonUtil.writeValueAsBytes(providerConfig.serviceAccountKey()))); } catch (IOException e) { throw new RuntimeException("Failed to parse service account key JSON", e); } @@ -173,6 +172,18 @@ class Langchain4jChatModelConfigurerImpl implements Langchain4jChatModelConfigur .build(); } + @Override + public ChatModel configureChatModel(GitHubModelsChatModel chatModel) { + GitHubModelsChatModel.Config modelConfig = chatModel.modelConfig(); + return dev.langchain4j.model.github.GitHubModelsChatModel.builder() + .gitHubToken(chatModel.providerConfig().personalAccessToken()) + .modelName(modelConfig.modelId()) + .temperature(modelConfig.temperature()) + .timeout(toDuration(modelConfig.timeoutSeconds())) + .maxRetries(modelConfig.maxRetries()) + .build(); + } + private static Duration toDuration(Integer timeoutSeconds) { return timeoutSeconds != null ? Duration.ofSeconds(timeoutSeconds) : null; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/ai/model/AiModelTypeIdResolver.java b/common/data/src/main/java/org/thingsboard/server/common/data/ai/model/AiModelTypeIdResolver.java index 3c7766c4ea..d4339ccd7b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/ai/model/AiModelTypeIdResolver.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ai/model/AiModelTypeIdResolver.java @@ -21,6 +21,7 @@ import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.jsontype.impl.TypeIdResolverBase; import org.thingsboard.server.common.data.ai.model.chat.AmazonBedrockChatModel; import org.thingsboard.server.common.data.ai.model.chat.AnthropicChatModel; +import org.thingsboard.server.common.data.ai.model.chat.GitHubModelsChatModel; import org.thingsboard.server.common.data.ai.model.chat.GoogleAiGeminiChatModel; import org.thingsboard.server.common.data.ai.model.chat.GoogleVertexAiGeminiChatModel; import org.thingsboard.server.common.data.ai.model.chat.MistralAiChatModel; @@ -90,6 +91,9 @@ public final class AiModelTypeIdResolver extends TypeIdResolverBase { // Amazon Bedrock models map.put("AMAZON_BEDROCK::amazon.nova-lite-v1:0", AmazonBedrockChatModel.class); + // GitHub Models models + map.put("GITHUB_MODELS::gpt-4o", GitHubModelsChatModel.class); + typeIdToModelClass = Collections.unmodifiableMap(map); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/AiChatModel.java b/common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/AiChatModel.java index ac0f2226f9..b9a2737b20 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/AiChatModel.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/AiChatModel.java @@ -23,7 +23,7 @@ public sealed interface AiChatModel> extends AiMo permits OpenAiChatModel, AzureOpenAiChatModel, GoogleAiGeminiChatModel, GoogleVertexAiGeminiChatModel, MistralAiChatModel, AnthropicChatModel, - AmazonBedrockChatModel { + AmazonBedrockChatModel, GitHubModelsChatModel { ChatModel configure(Langchain4jChatModelConfigurer configurer); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/AiChatModelConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/AiChatModelConfig.java index e0d7232fa6..d5e794cec0 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/AiChatModelConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/AiChatModelConfig.java @@ -21,7 +21,7 @@ public sealed interface AiChatModelConfig> extend permits OpenAiChatModel.Config, AzureOpenAiChatModel.Config, GoogleAiGeminiChatModel.Config, GoogleVertexAiGeminiChatModel.Config, MistralAiChatModel.Config, AnthropicChatModel.Config, - AmazonBedrockChatModel.Config { + AmazonBedrockChatModel.Config, GitHubModelsChatModel.Config { Double temperature(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/GitHubModelsChatModel.java b/common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/GitHubModelsChatModel.java new file mode 100644 index 0000000000..165873e6e3 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/GitHubModelsChatModel.java @@ -0,0 +1,60 @@ +/** + * Copyright © 2016-2025 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.ai.model.chat; + +import dev.langchain4j.model.chat.ChatModel; +import org.thingsboard.server.common.data.ai.provider.GithubModelsProviderConfig; + +public record GitHubModelsChatModel( + GithubModelsProviderConfig providerConfig, + Config modelConfig +) implements AiChatModel { + + public record Config( + String modelId, + Double temperature, + Integer timeoutSeconds, + Integer maxRetries + ) implements AiChatModelConfig { + + @Override + public GitHubModelsChatModel.Config withTemperature(Double temperature) { + return new Config(modelId, temperature, timeoutSeconds, maxRetries); + } + + @Override + public GitHubModelsChatModel.Config withTimeoutSeconds(Integer timeoutSeconds) { + return new Config(modelId, temperature, timeoutSeconds, maxRetries); + } + + @Override + public GitHubModelsChatModel.Config withMaxRetries(Integer maxRetries) { + return new Config(modelId, temperature, timeoutSeconds, maxRetries); + } + + } + + @Override + public ChatModel configure(Langchain4jChatModelConfigurer configurer) { + return configurer.configureChatModel(this); + } + + @Override + public GitHubModelsChatModel withModelConfig(GitHubModelsChatModel.Config config) { + return new GitHubModelsChatModel(providerConfig, config); + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/Langchain4jChatModelConfigurer.java b/common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/Langchain4jChatModelConfigurer.java index d9d4c1dbb6..ea9fb80e73 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/Langchain4jChatModelConfigurer.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/Langchain4jChatModelConfigurer.java @@ -33,4 +33,6 @@ public interface Langchain4jChatModelConfigurer { ChatModel configureChatModel(AmazonBedrockChatModel chatModel); + ChatModel configureChatModel(GitHubModelsChatModel chatModel); + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/AiProvider.java b/common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/AiProvider.java index 55b3cbe7f4..d0a5bd0510 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/AiProvider.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/AiProvider.java @@ -23,6 +23,7 @@ public enum AiProvider { GOOGLE_VERTEX_AI_GEMINI, MISTRAL_AI, ANTHROPIC, - AMAZON_BEDROCK + AMAZON_BEDROCK, + GITHUB_MODELS } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/AiProviderConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/AiProviderConfig.java index 890ea197a0..04ca775f7f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/AiProviderConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/AiProviderConfig.java @@ -30,13 +30,14 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; @JsonSubTypes.Type(value = GoogleVertexAiGeminiProviderConfig.class, name = "GOOGLE_VERTEX_AI_GEMINI"), @JsonSubTypes.Type(value = MistralAiProviderConfig.class, name = "MISTRAL_AI"), @JsonSubTypes.Type(value = AnthropicProviderConfig.class, name = "ANTHROPIC"), - @JsonSubTypes.Type(value = AmazonBedrockProviderConfig.class, name = "AMAZON_BEDROCK") + @JsonSubTypes.Type(value = AmazonBedrockProviderConfig.class, name = "AMAZON_BEDROCK"), + @JsonSubTypes.Type(value = GithubModelsProviderConfig.class, name = "GITHUB_MODELS") }) public sealed interface AiProviderConfig permits OpenAiProviderConfig, AzureOpenAiProviderConfig, GoogleAiGeminiProviderConfig, GoogleVertexAiGeminiProviderConfig, MistralAiProviderConfig, AnthropicProviderConfig, - AmazonBedrockProviderConfig { + AmazonBedrockProviderConfig, GithubModelsProviderConfig { AiProvider provider(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/GithubModelsProviderConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/GithubModelsProviderConfig.java new file mode 100644 index 0000000000..afbac79dd1 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/GithubModelsProviderConfig.java @@ -0,0 +1,25 @@ +/** + * Copyright © 2016-2025 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.common.data.ai.provider; + +public record GithubModelsProviderConfig(String personalAccessToken) implements AiProviderConfig { + + @Override + public AiProvider provider() { + return AiProvider.GITHUB_MODELS; + } + +}