AI rule node: add support for GitHub Models

This commit is contained in:
Dmytro Skarzhynets 2025-06-26 15:02:05 +03:00
parent 4f38422df8
commit 4507b312de
No known key found for this signature in database
GPG Key ID: 2B51652F224037DF
10 changed files with 116 additions and 8 deletions

View File

@ -409,6 +409,10 @@
<groupId>dev.langchain4j</groupId> <groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-bedrock</artifactId> <artifactId>langchain4j-bedrock</artifactId>
</dependency> </dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-github-models</artifactId>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -15,7 +15,6 @@
*/ */
package org.thingsboard.server.service.ai; package org.thingsboard.server.service.ai;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.auth.oauth2.ServiceAccountCredentials; import com.google.auth.oauth2.ServiceAccountCredentials;
import com.google.cloud.vertexai.Transport; import com.google.cloud.vertexai.Transport;
import com.google.cloud.vertexai.VertexAI; 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.AmazonBedrockChatModel;
import org.thingsboard.server.common.data.ai.model.chat.AnthropicChatModel; 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.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.GoogleAiGeminiChatModel;
import org.thingsboard.server.common.data.ai.model.chat.GoogleVertexAiGeminiChatModel; import org.thingsboard.server.common.data.ai.model.chat.GoogleVertexAiGeminiChatModel;
import org.thingsboard.server.common.data.ai.model.chat.Langchain4jChatModelConfigurer; import org.thingsboard.server.common.data.ai.model.chat.Langchain4jChatModelConfigurer;
@ -91,11 +91,10 @@ class Langchain4jChatModelConfigurerImpl implements Langchain4jChatModelConfigur
GoogleVertexAiGeminiChatModel.Config modelConfig = chatModel.modelConfig(); GoogleVertexAiGeminiChatModel.Config modelConfig = chatModel.modelConfig();
// construct service account credentials using service account key JSON // construct service account credentials using service account key JSON
ObjectNode serviceAccountKeyJson = providerConfig.serviceAccountKey();
ServiceAccountCredentials serviceAccountCredentials; ServiceAccountCredentials serviceAccountCredentials;
try { try {
serviceAccountCredentials = ServiceAccountCredentials serviceAccountCredentials = ServiceAccountCredentials
.fromStream(new ByteArrayInputStream(JacksonUtil.writeValueAsBytes(serviceAccountKeyJson))); .fromStream(new ByteArrayInputStream(JacksonUtil.writeValueAsBytes(providerConfig.serviceAccountKey())));
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException("Failed to parse service account key JSON", e); throw new RuntimeException("Failed to parse service account key JSON", e);
} }
@ -173,6 +172,18 @@ class Langchain4jChatModelConfigurerImpl implements Langchain4jChatModelConfigur
.build(); .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) { private static Duration toDuration(Integer timeoutSeconds) {
return timeoutSeconds != null ? Duration.ofSeconds(timeoutSeconds) : null; return timeoutSeconds != null ? Duration.ofSeconds(timeoutSeconds) : null;
} }

View File

@ -21,6 +21,7 @@ import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.jsontype.impl.TypeIdResolverBase; 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.AmazonBedrockChatModel;
import org.thingsboard.server.common.data.ai.model.chat.AnthropicChatModel; 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.GoogleAiGeminiChatModel;
import org.thingsboard.server.common.data.ai.model.chat.GoogleVertexAiGeminiChatModel; import org.thingsboard.server.common.data.ai.model.chat.GoogleVertexAiGeminiChatModel;
import org.thingsboard.server.common.data.ai.model.chat.MistralAiChatModel; import org.thingsboard.server.common.data.ai.model.chat.MistralAiChatModel;
@ -90,6 +91,9 @@ public final class AiModelTypeIdResolver extends TypeIdResolverBase {
// Amazon Bedrock models // Amazon Bedrock models
map.put("AMAZON_BEDROCK::amazon.nova-lite-v1:0", AmazonBedrockChatModel.class); 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); typeIdToModelClass = Collections.unmodifiableMap(map);
} }

View File

@ -23,7 +23,7 @@ public sealed interface AiChatModel<C extends AiChatModelConfig<C>> extends AiMo
permits permits
OpenAiChatModel, AzureOpenAiChatModel, GoogleAiGeminiChatModel, OpenAiChatModel, AzureOpenAiChatModel, GoogleAiGeminiChatModel,
GoogleVertexAiGeminiChatModel, MistralAiChatModel, AnthropicChatModel, GoogleVertexAiGeminiChatModel, MistralAiChatModel, AnthropicChatModel,
AmazonBedrockChatModel { AmazonBedrockChatModel, GitHubModelsChatModel {
ChatModel configure(Langchain4jChatModelConfigurer configurer); ChatModel configure(Langchain4jChatModelConfigurer configurer);

View File

@ -21,7 +21,7 @@ public sealed interface AiChatModelConfig<C extends AiChatModelConfig<C>> extend
permits permits
OpenAiChatModel.Config, AzureOpenAiChatModel.Config, GoogleAiGeminiChatModel.Config, OpenAiChatModel.Config, AzureOpenAiChatModel.Config, GoogleAiGeminiChatModel.Config,
GoogleVertexAiGeminiChatModel.Config, MistralAiChatModel.Config, AnthropicChatModel.Config, GoogleVertexAiGeminiChatModel.Config, MistralAiChatModel.Config, AnthropicChatModel.Config,
AmazonBedrockChatModel.Config { AmazonBedrockChatModel.Config, GitHubModelsChatModel.Config {
Double temperature(); Double temperature();

View File

@ -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<GitHubModelsChatModel.Config> {
public record Config(
String modelId,
Double temperature,
Integer timeoutSeconds,
Integer maxRetries
) implements AiChatModelConfig<GitHubModelsChatModel.Config> {
@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);
}
}

View File

@ -33,4 +33,6 @@ public interface Langchain4jChatModelConfigurer {
ChatModel configureChatModel(AmazonBedrockChatModel chatModel); ChatModel configureChatModel(AmazonBedrockChatModel chatModel);
ChatModel configureChatModel(GitHubModelsChatModel chatModel);
} }

View File

@ -23,6 +23,7 @@ public enum AiProvider {
GOOGLE_VERTEX_AI_GEMINI, GOOGLE_VERTEX_AI_GEMINI,
MISTRAL_AI, MISTRAL_AI,
ANTHROPIC, ANTHROPIC,
AMAZON_BEDROCK AMAZON_BEDROCK,
GITHUB_MODELS
} }

View File

@ -30,13 +30,14 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo;
@JsonSubTypes.Type(value = GoogleVertexAiGeminiProviderConfig.class, name = "GOOGLE_VERTEX_AI_GEMINI"), @JsonSubTypes.Type(value = GoogleVertexAiGeminiProviderConfig.class, name = "GOOGLE_VERTEX_AI_GEMINI"),
@JsonSubTypes.Type(value = MistralAiProviderConfig.class, name = "MISTRAL_AI"), @JsonSubTypes.Type(value = MistralAiProviderConfig.class, name = "MISTRAL_AI"),
@JsonSubTypes.Type(value = AnthropicProviderConfig.class, name = "ANTHROPIC"), @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 public sealed interface AiProviderConfig
permits permits
OpenAiProviderConfig, AzureOpenAiProviderConfig, GoogleAiGeminiProviderConfig, OpenAiProviderConfig, AzureOpenAiProviderConfig, GoogleAiGeminiProviderConfig,
GoogleVertexAiGeminiProviderConfig, MistralAiProviderConfig, AnthropicProviderConfig, GoogleVertexAiGeminiProviderConfig, MistralAiProviderConfig, AnthropicProviderConfig,
AmazonBedrockProviderConfig { AmazonBedrockProviderConfig, GithubModelsProviderConfig {
AiProvider provider(); AiProvider provider();

View File

@ -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;
}
}