Browse Source

fixed some TB models for correct open-api json schema

pull/14929/head
dashevchenko 8 months ago
parent
commit
0a728ba4b7
  1. 2
      application/src/main/java/org/thingsboard/server/controller/AiModelController.java
  2. 3
      application/src/main/java/org/thingsboard/server/service/ai/AiChatModelServiceImpl.java
  3. 118
      application/src/main/java/org/thingsboard/server/service/ai/Langchain4jChatModelConfigurerImpl.java
  4. 2
      common/data/src/main/java/org/thingsboard/server/common/data/ai/dto/TbChatRequest.java
  5. 22
      common/data/src/main/java/org/thingsboard/server/common/data/ai/model/AiModelConfig.java
  6. 3
      common/data/src/main/java/org/thingsboard/server/common/data/ai/model/AiModelType.java
  7. 5
      common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/AiChatModelConfig.java
  8. 39
      common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/AmazonBedrockChatModelConfig.java
  9. 41
      common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/AnthropicChatModelConfig.java
  10. 44
      common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/AzureOpenAiChatModelConfig.java
  11. 42
      common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/GitHubModelsChatModelConfig.java
  12. 45
      common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/GoogleAiGeminiChatModelConfig.java
  13. 44
      common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/GoogleVertexAiGeminiChatModelConfig.java
  14. 42
      common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/MistralAiChatModelConfig.java
  15. 44
      common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/OllamaChatModelConfig.java
  16. 42
      common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/OpenAiChatModelConfig.java
  17. 3
      common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/AiProvider.java
  18. 2
      common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/AmazonBedrockProviderConfig.java
  19. 2
      common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/AnthropicProviderConfig.java
  20. 2
      common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/AzureOpenAiProviderConfig.java
  21. 2
      common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/GitHubModelsProviderConfig.java
  22. 2
      common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/GoogleAiGeminiProviderConfig.java
  23. 2
      common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/GoogleVertexAiGeminiProviderConfig.java
  24. 2
      common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/MistralAiProviderConfig.java
  25. 1
      common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/OllamaProviderConfig.java
  26. 2
      common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/OpenAiProviderConfig.java
  27. 3
      rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineAiChatModelService.java
  28. 7
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/ai/TbAiNode.java

2
application/src/main/java/org/thingsboard/server/controller/AiModelController.java

@ -174,7 +174,7 @@ class AiModelController extends BaseController {
@PostMapping("/chat")
public DeferredResult<TbChatResponse> sendChatRequest(@Valid @RequestBody TbChatRequest tbChatRequest) {
ChatRequest langChainChatRequest = tbChatRequest.toLangChainChatRequest();
AiChatModelConfig<?, ?> chatModelConfig = tbChatRequest.chatModelConfig();
AiChatModelConfig<?> chatModelConfig = tbChatRequest.chatModelConfig();
ListenableFuture<TbChatResponse> future = aiChatModelService.sendChatRequestAsync(chatModelConfig, langChainChatRequest)
.transform(chatResponse -> (TbChatResponse) new TbChatResponse.Success(chatResponse.aiMessage().text()), directExecutor())

3
application/src/main/java/org/thingsboard/server/service/ai/AiChatModelServiceImpl.java

@ -29,7 +29,6 @@ import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.ai.model.chat.AiChatModelConfig;
import org.thingsboard.server.common.data.ai.model.chat.Langchain4jChatModelConfigurer;
import org.thingsboard.server.common.data.ai.provider.AiProviderConfig;
import java.util.List;
import java.util.stream.Collectors;
@ -42,7 +41,7 @@ class AiChatModelServiceImpl implements AiChatModelService {
private final AiRequestsExecutor aiRequestsExecutor;
@Override
public <C extends AiChatModelConfig<C, P>, P extends AiProviderConfig> FluentFuture<ChatResponse> sendChatRequestAsync(AiChatModelConfig<C, P> chatModelConfig, ChatRequest chatRequest) {
public <C extends AiChatModelConfig<C>> FluentFuture<ChatResponse> sendChatRequestAsync(AiChatModelConfig<C> chatModelConfig, ChatRequest chatRequest) {
ChatModel langChainChatModel = chatModelConfig.configure(chatModelConfigurer);
if (langChainChatModel.provider() == ModelProvider.GITHUB_MODELS) {
chatRequest = prepareGithubChatRequest(chatRequest);

118
application/src/main/java/org/thingsboard/server/service/ai/Langchain4jChatModelConfigurerImpl.java

@ -72,12 +72,12 @@ class Langchain4jChatModelConfigurerImpl implements Langchain4jChatModelConfigur
return OpenAiChatModel.builder()
.baseUrl(chatModelConfig.providerConfig().baseUrl())
.apiKey(chatModelConfig.providerConfig().apiKey())
.modelName(chatModelConfig.getModelId())
.temperature(chatModelConfig.getTemperature())
.topP(chatModelConfig.getTopP())
.frequencyPenalty(chatModelConfig.getFrequencyPenalty())
.presencePenalty(chatModelConfig.getPresencePenalty())
.maxTokens(chatModelConfig.getMaxOutputTokens())
.modelName(chatModelConfig.modelId())
.temperature(chatModelConfig.temperature())
.topP(chatModelConfig.topP())
.frequencyPenalty(chatModelConfig.frequencyPenalty())
.presencePenalty(chatModelConfig.presencePenalty())
.maxTokens(chatModelConfig.maxOutputTokens())
.timeout(toDuration(chatModelConfig.timeoutSeconds()))
.maxRetries(chatModelConfig.maxRetries())
.build();
@ -90,12 +90,12 @@ class Langchain4jChatModelConfigurerImpl implements Langchain4jChatModelConfigur
.endpoint(providerConfig.endpoint())
.serviceVersion(providerConfig.serviceVersion())
.apiKey(providerConfig.apiKey())
.deploymentName(chatModelConfig.getModelId())
.temperature(chatModelConfig.getTemperature())
.topP(chatModelConfig.getTopP())
.frequencyPenalty(chatModelConfig.getFrequencyPenalty())
.presencePenalty(chatModelConfig.getPresencePenalty())
.maxTokens(chatModelConfig.getMaxOutputTokens())
.deploymentName(chatModelConfig.modelId())
.temperature(chatModelConfig.temperature())
.topP(chatModelConfig.topP())
.frequencyPenalty(chatModelConfig.frequencyPenalty())
.presencePenalty(chatModelConfig.presencePenalty())
.maxTokens(chatModelConfig.maxOutputTokens())
.timeout(toDuration(chatModelConfig.timeoutSeconds()))
.maxRetries(chatModelConfig.maxRetries())
.build();
@ -105,13 +105,13 @@ class Langchain4jChatModelConfigurerImpl implements Langchain4jChatModelConfigur
public ChatModel configureChatModel(GoogleAiGeminiChatModelConfig chatModelConfig) {
return GoogleAiGeminiChatModel.builder()
.apiKey(chatModelConfig.providerConfig().apiKey())
.modelName(chatModelConfig.getModelId())
.temperature(chatModelConfig.getTemperature())
.topP(chatModelConfig.getTopP())
.topK(chatModelConfig.getTopK())
.frequencyPenalty(chatModelConfig.getFrequencyPenalty())
.presencePenalty(chatModelConfig.getPresencePenalty())
.maxOutputTokens(chatModelConfig.getMaxOutputTokens())
.modelName(chatModelConfig.modelId())
.temperature(chatModelConfig.temperature())
.topP(chatModelConfig.topP())
.topK(chatModelConfig.topK())
.frequencyPenalty(chatModelConfig.frequencyPenalty())
.presencePenalty(chatModelConfig.presencePenalty())
.maxOutputTokens(chatModelConfig.maxOutputTokens())
.timeout(toDuration(chatModelConfig.timeoutSeconds()))
.maxRetries(chatModelConfig.maxRetries())
.build();
@ -165,28 +165,28 @@ class Langchain4jChatModelConfigurerImpl implements Langchain4jChatModelConfigur
// map model config to generation config
var generationConfigBuilder = GenerationConfig.newBuilder();
if (chatModelConfig.getTemperature() != null) {
generationConfigBuilder.setTemperature(chatModelConfig.getTemperature().floatValue());
if (chatModelConfig.temperature() != null) {
generationConfigBuilder.setTemperature(chatModelConfig.temperature().floatValue());
}
if (chatModelConfig.getTopP() != null) {
generationConfigBuilder.setTopP(chatModelConfig.getTopP().floatValue());
if (chatModelConfig.topP() != null) {
generationConfigBuilder.setTopP(chatModelConfig.topP().floatValue());
}
if (chatModelConfig.getTopK() != null) {
generationConfigBuilder.setTopK(chatModelConfig.getTopK());
if (chatModelConfig.topK() != null) {
generationConfigBuilder.setTopK(chatModelConfig.topK());
}
if (chatModelConfig.getFrequencyPenalty() != null) {
generationConfigBuilder.setFrequencyPenalty(chatModelConfig.getFrequencyPenalty().floatValue());
if (chatModelConfig.frequencyPenalty() != null) {
generationConfigBuilder.setFrequencyPenalty(chatModelConfig.frequencyPenalty().floatValue());
}
if (chatModelConfig.getFrequencyPenalty() != null) {
generationConfigBuilder.setPresencePenalty(chatModelConfig.getFrequencyPenalty().floatValue());
if (chatModelConfig.frequencyPenalty() != null) {
generationConfigBuilder.setPresencePenalty(chatModelConfig.frequencyPenalty().floatValue());
}
if (chatModelConfig.getMaxOutputTokens() != null) {
generationConfigBuilder.setMaxOutputTokens(chatModelConfig.getMaxOutputTokens());
if (chatModelConfig.maxOutputTokens() != null) {
generationConfigBuilder.setMaxOutputTokens(chatModelConfig.maxOutputTokens());
}
var generationConfig = generationConfigBuilder.build();
// construct generative model instance
var generativeModel = new GenerativeModel(chatModelConfig.getModelId(), vertexAI).withGenerationConfig(generationConfig);
var generativeModel = new GenerativeModel(chatModelConfig.modelId(), vertexAI).withGenerationConfig(generationConfig);
return new VertexAiGeminiChatModel(generativeModel, generationConfig, chatModelConfig.maxRetries());
}
@ -203,12 +203,12 @@ class Langchain4jChatModelConfigurerImpl implements Langchain4jChatModelConfigur
public ChatModel configureChatModel(MistralAiChatModelConfig chatModelConfig) {
return MistralAiChatModel.builder()
.apiKey(chatModelConfig.providerConfig().apiKey())
.modelName(chatModelConfig.getModelId())
.temperature(chatModelConfig.getTemperature())
.topP(chatModelConfig.getTopP())
.frequencyPenalty(chatModelConfig.getFrequencyPenalty())
.presencePenalty(chatModelConfig.getPresencePenalty())
.maxTokens(chatModelConfig.getMaxOutputTokens())
.modelName(chatModelConfig.modelId())
.temperature(chatModelConfig.temperature())
.topP(chatModelConfig.topP())
.frequencyPenalty(chatModelConfig.frequencyPenalty())
.presencePenalty(chatModelConfig.presencePenalty())
.maxTokens(chatModelConfig.maxOutputTokens())
.timeout(toDuration(chatModelConfig.timeoutSeconds()))
.maxRetries(chatModelConfig.maxRetries())
.build();
@ -218,11 +218,11 @@ class Langchain4jChatModelConfigurerImpl implements Langchain4jChatModelConfigur
public ChatModel configureChatModel(AnthropicChatModelConfig chatModelConfig) {
return AnthropicChatModel.builder()
.apiKey(chatModelConfig.providerConfig().apiKey())
.modelName(chatModelConfig.getModelId())
.temperature(chatModelConfig.getTemperature())
.topP(chatModelConfig.getTopP())
.topK(chatModelConfig.getTopK())
.maxTokens(chatModelConfig.getMaxOutputTokens())
.modelName(chatModelConfig.modelId())
.temperature(chatModelConfig.temperature())
.topP(chatModelConfig.topP())
.topK(chatModelConfig.topK())
.maxTokens(chatModelConfig.maxOutputTokens())
.timeout(toDuration(chatModelConfig.timeoutSeconds()))
.maxRetries(chatModelConfig.maxRetries())
.build();
@ -242,14 +242,14 @@ class Langchain4jChatModelConfigurerImpl implements Langchain4jChatModelConfigur
.build();
var defaultChatRequestParams = ChatRequestParameters.builder()
.temperature(chatModelConfig.getTemperature())
.topP(chatModelConfig.getTopP())
.maxOutputTokens(chatModelConfig.getMaxOutputTokens())
.temperature(chatModelConfig.temperature())
.topP(chatModelConfig.topP())
.maxOutputTokens(chatModelConfig.maxOutputTokens())
.build();
return BedrockChatModel.builder()
.client(bedrockClient)
.modelId(chatModelConfig.getModelId())
.modelId(chatModelConfig.modelId())
.defaultRequestParameters(defaultChatRequestParams)
.timeout(toDuration(chatModelConfig.timeoutSeconds()))
.maxRetries(chatModelConfig.maxRetries())
@ -260,12 +260,12 @@ class Langchain4jChatModelConfigurerImpl implements Langchain4jChatModelConfigur
public ChatModel configureChatModel(GitHubModelsChatModelConfig chatModelConfig) {
return GitHubModelsChatModel.builder()
.gitHubToken(chatModelConfig.providerConfig().personalAccessToken())
.modelName(chatModelConfig.getModelId())
.temperature(chatModelConfig.getTemperature())
.topP(chatModelConfig.getTopP())
.frequencyPenalty(chatModelConfig.getFrequencyPenalty())
.presencePenalty(chatModelConfig.getPresencePenalty())
.maxTokens(chatModelConfig.getMaxOutputTokens())
.modelName(chatModelConfig.modelId())
.temperature(chatModelConfig.temperature())
.topP(chatModelConfig.topP())
.frequencyPenalty(chatModelConfig.frequencyPenalty())
.presencePenalty(chatModelConfig.presencePenalty())
.maxTokens(chatModelConfig.maxOutputTokens())
.timeout(toDuration(chatModelConfig.timeoutSeconds()))
.maxRetries(chatModelConfig.maxRetries())
.build();
@ -275,12 +275,12 @@ class Langchain4jChatModelConfigurerImpl implements Langchain4jChatModelConfigur
public ChatModel configureChatModel(OllamaChatModelConfig chatModelConfig) {
var builder = OllamaChatModel.builder()
.baseUrl(chatModelConfig.providerConfig().baseUrl())
.modelName(chatModelConfig.getModelId())
.temperature(chatModelConfig.getTemperature())
.topP(chatModelConfig.getTopP())
.topK(chatModelConfig.getTopK())
.numCtx(chatModelConfig.getContextLength())
.numPredict(chatModelConfig.getContextLength())
.modelName(chatModelConfig.modelId())
.temperature(chatModelConfig.temperature())
.topP(chatModelConfig.topP())
.topK(chatModelConfig.topK())
.numCtx(chatModelConfig.contextLength())
.numPredict(chatModelConfig.maxOutputTokens())
.timeout(toDuration(chatModelConfig.timeoutSeconds()))
.maxRetries(chatModelConfig.maxRetries());

2
common/data/src/main/java/org/thingsboard/server/common/data/ai/dto/TbChatRequest.java

@ -73,7 +73,7 @@ public record TbChatRequest(
}
)
@NotNull @Valid
AiChatModelConfig<?, ?> chatModelConfig
AiChatModelConfig<?> chatModelConfig
) {
public ChatRequest toLangChainChatRequest() {

22
common/data/src/main/java/org/thingsboard/server/common/data/ai/model/AiModelConfig.java

@ -15,7 +15,6 @@
*/
package org.thingsboard.server.common.data.ai.model;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import io.swagger.v3.oas.annotations.media.DiscriminatorMapping;
@ -74,7 +73,7 @@ import org.thingsboard.server.common.data.ai.provider.OpenAiProviderConfig;
@DiscriminatorMapping(value = "OLLAMA", schema = OllamaChatModelConfig.class)
}
)
public interface AiModelConfig <T extends AiProviderConfig>{
public interface AiModelConfig {
@Schema(
description = "AI Provider",
@ -82,21 +81,7 @@ public interface AiModelConfig <T extends AiProviderConfig>{
)
AiProvider provider();
@Schema(
description = "Provider-specific configuration details",
discriminatorProperty = "provider",
discriminatorMapping = {
@DiscriminatorMapping(value = "OPENAI", schema = OpenAiProviderConfig.class),
@DiscriminatorMapping(value = "AZURE_OPENAI", schema = AzureOpenAiProviderConfig.class),
@DiscriminatorMapping(value = "GOOGLE_AI_GEMINI", schema = GoogleAiGeminiProviderConfig.class),
@DiscriminatorMapping(value = "GOOGLE_VERTEX_AI_GEMINI", schema = GoogleVertexAiGeminiProviderConfig.class),
@DiscriminatorMapping(value = "MISTRAL_AI", schema = MistralAiProviderConfig.class),
@DiscriminatorMapping(value = "ANTHROPIC", schema = AnthropicProviderConfig.class),
@DiscriminatorMapping(value = "AMAZON_BEDROCK", schema = AmazonBedrockProviderConfig.class),
@DiscriminatorMapping(value = "GITHUB_MODELS", schema = GitHubModelsProviderConfig.class),
@DiscriminatorMapping(value = "OLLAMA", schema = OllamaProviderConfig.class)
}
)
@Schema(hidden = true)
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.EXTERNAL_PROPERTY,
@ -113,8 +98,7 @@ public interface AiModelConfig <T extends AiProviderConfig>{
@JsonSubTypes.Type(value = GitHubModelsProviderConfig.class, name = "GITHUB_MODELS"),
@JsonSubTypes.Type(value = OllamaProviderConfig.class, name = "OLLAMA")
})
@JsonIgnore
T providerConfig();
AiProviderConfig providerConfig();
AiModelType modelType();

3
common/data/src/main/java/org/thingsboard/server/common/data/ai/model/AiModelType.java

@ -15,6 +15,9 @@
*/
package org.thingsboard.server.common.data.ai.model;
import io.swagger.v3.oas.annotations.media.Schema;
@Schema(enumAsRef = true)
public enum AiModelType {
CHAT

5
common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/AiChatModelConfig.java

@ -20,10 +20,9 @@ import dev.langchain4j.model.chat.ChatModel;
import io.swagger.v3.oas.annotations.media.Schema;
import org.thingsboard.server.common.data.ai.model.AiModelConfig;
import org.thingsboard.server.common.data.ai.model.AiModelType;
import org.thingsboard.server.common.data.ai.provider.AiProviderConfig;
@Schema(description = "AI provider-specific configuration")
public sealed interface AiChatModelConfig<C extends AiChatModelConfig<C, P>, P extends AiProviderConfig> extends AiModelConfig<P>
@Schema
public sealed interface AiChatModelConfig<C extends AiChatModelConfig<C>> extends AiModelConfig
permits
OpenAiChatModelConfig, AzureOpenAiChatModelConfig, GoogleAiGeminiChatModelConfig,
GoogleVertexAiGeminiChatModelConfig, MistralAiChatModelConfig, AnthropicChatModelConfig,

39
common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/AmazonBedrockChatModelConfig.java

@ -24,51 +24,36 @@ import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import jakarta.validation.constraints.PositiveOrZero;
import lombok.Builder;
import lombok.Data;
import lombok.With;
import org.thingsboard.server.common.data.ai.provider.AiProvider;
import org.thingsboard.server.common.data.ai.provider.AmazonBedrockProviderConfig;
@Schema(description = "Amazon Bedrock chat model configuration")
@Schema
@Builder
@Data
public final class AmazonBedrockChatModelConfig implements AiChatModelConfig<AmazonBedrockChatModelConfig, AmazonBedrockProviderConfig> {
@NotNull @Valid AmazonBedrockProviderConfig providerConfig;
@NotBlank String modelId;
@PositiveOrZero Double temperature;
@Positive @Max(1) Double topP;
Integer maxOutputTokens;
@With @Positive Integer timeoutSeconds;
@With @PositiveOrZero Integer maxRetries;
public record AmazonBedrockChatModelConfig(
@Schema(ref = "#/components/schemas/AmazonBedrockProviderConfig")
@NotNull @Valid AmazonBedrockProviderConfig providerConfig,
@NotBlank String modelId,
@PositiveOrZero Double temperature,
@Positive @Max(1) Double topP,
Integer maxOutputTokens,
@With @Positive Integer timeoutSeconds,
@With @PositiveOrZero Integer maxRetries
) implements AiChatModelConfig<AmazonBedrockChatModelConfig> {
@Override
public AiProvider provider() {
return AiProvider.AMAZON_BEDROCK;
}
@Override
public AmazonBedrockProviderConfig providerConfig() {
return providerConfig;
}
@Override
public ChatModel configure(Langchain4jChatModelConfigurer configurer) {
return configurer.configureChatModel(this);
}
@Override
public Integer timeoutSeconds() {
return timeoutSeconds;
}
@Override
public Integer maxRetries() {
return maxRetries;
}
@Override
public boolean supportsJsonMode() {
return false;
}
}

41
common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/AnthropicChatModelConfig.java

@ -24,51 +24,34 @@ import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import jakarta.validation.constraints.PositiveOrZero;
import lombok.Builder;
import lombok.Data;
import lombok.With;
import org.thingsboard.server.common.data.ai.provider.AiProvider;
import org.thingsboard.server.common.data.ai.provider.AnthropicProviderConfig;
@Schema()
@Schema
@Builder
@Data
public final class AnthropicChatModelConfig implements AiChatModelConfig<AnthropicChatModelConfig, AnthropicProviderConfig> {
@NotNull @Valid AnthropicProviderConfig providerConfig;
@NotBlank String modelId;
@PositiveOrZero Double temperature;
@Positive @Max(1) Double topP;
@PositiveOrZero Integer topK;
Integer maxOutputTokens;
@With
@Positive Integer timeoutSeconds;
@With
@PositiveOrZero Integer maxRetries;
public record AnthropicChatModelConfig(
@Schema(ref = "#/components/schemas/AnthropicProviderConfig")
@NotNull @Valid AnthropicProviderConfig providerConfig,
@NotBlank String modelId,
@PositiveOrZero Double temperature,
@Positive @Max(1) Double topP,
@PositiveOrZero Integer topK,
Integer maxOutputTokens,
@With @Positive Integer timeoutSeconds,
@With @PositiveOrZero Integer maxRetries
) implements AiChatModelConfig<AnthropicChatModelConfig> {
@Override
public AiProvider provider() {
return AiProvider.ANTHROPIC;
}
@Override
public AnthropicProviderConfig providerConfig() {
return providerConfig;
}
@Override
public ChatModel configure(Langchain4jChatModelConfigurer configurer) {
return configurer.configureChatModel(this);
}
@Override
public Integer timeoutSeconds() {
return timeoutSeconds;
}
@Override
public Integer maxRetries() {
return maxRetries;
}
@Override
public boolean supportsJsonMode() {
return false;

44
common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/AzureOpenAiChatModelConfig.java

@ -24,53 +24,35 @@ import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import jakarta.validation.constraints.PositiveOrZero;
import lombok.Builder;
import lombok.Data;
import lombok.With;
import org.thingsboard.server.common.data.ai.provider.AiProvider;
import org.thingsboard.server.common.data.ai.provider.AzureOpenAiProviderConfig;
@Schema()
@Schema
@Builder
@Data
public final class AzureOpenAiChatModelConfig implements AiChatModelConfig<AzureOpenAiChatModelConfig, AzureOpenAiProviderConfig> {
@NotNull @Valid AzureOpenAiProviderConfig providerConfig;
@NotBlank String modelId;
@PositiveOrZero Double temperature;
@Positive @Max(1) Double topP;
Double frequencyPenalty;
Double presencePenalty;
Integer maxOutputTokens;
@With
@Positive Integer timeoutSeconds;
@With
@PositiveOrZero Integer maxRetries;
public record AzureOpenAiChatModelConfig(
@Schema(ref = "#/components/schemas/AzureOpenAiProviderConfig")
@NotNull @Valid AzureOpenAiProviderConfig providerConfig,
@NotBlank String modelId,
@PositiveOrZero Double temperature,
@Positive @Max(1) Double topP,
Double frequencyPenalty,
Double presencePenalty,
Integer maxOutputTokens,
@With @Positive Integer timeoutSeconds,
@With @PositiveOrZero Integer maxRetries
) implements AiChatModelConfig<AzureOpenAiChatModelConfig> {
@Override
public AiProvider provider() {
return AiProvider.AZURE_OPENAI;
}
@Override
public AzureOpenAiProviderConfig providerConfig() {
return providerConfig;
}
@Override
public ChatModel configure(Langchain4jChatModelConfigurer configurer) {
return configurer.configureChatModel(this);
}
@Override
public Integer timeoutSeconds() {
return timeoutSeconds;
}
@Override
public Integer maxRetries() {
return maxRetries;
}
@Override
public boolean supportsJsonMode() {
return true;

42
common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/GitHubModelsChatModelConfig.java

@ -24,51 +24,35 @@ import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import jakarta.validation.constraints.PositiveOrZero;
import lombok.Builder;
import lombok.Data;
import lombok.With;
import org.thingsboard.server.common.data.ai.provider.AiProvider;
import org.thingsboard.server.common.data.ai.provider.GitHubModelsProviderConfig;
@Schema()
@Schema
@Builder
@Data
public final class GitHubModelsChatModelConfig implements AiChatModelConfig<GitHubModelsChatModelConfig, GitHubModelsProviderConfig> {
@NotNull @Valid GitHubModelsProviderConfig providerConfig;
@NotBlank String modelId;
@PositiveOrZero Double temperature;
@Positive @Max(1) Double topP;
Double frequencyPenalty;
Double presencePenalty;
Integer maxOutputTokens;
@With @Positive Integer timeoutSeconds;
@With @PositiveOrZero Integer maxRetries;
public record GitHubModelsChatModelConfig(
@Schema(ref = "#/components/schemas/GitHubModelsProviderConfig")
@NotNull @Valid GitHubModelsProviderConfig providerConfig,
@NotBlank String modelId,
@PositiveOrZero Double temperature,
@Positive @Max(1) Double topP,
Double frequencyPenalty,
Double presencePenalty,
Integer maxOutputTokens,
@With @Positive Integer timeoutSeconds,
@With @PositiveOrZero Integer maxRetries
) implements AiChatModelConfig<GitHubModelsChatModelConfig> {
@Override
public AiProvider provider() {
return AiProvider.GITHUB_MODELS;
}
@Override
public GitHubModelsProviderConfig providerConfig() {
return providerConfig;
}
@Override
public ChatModel configure(Langchain4jChatModelConfigurer configurer) {
return configurer.configureChatModel(this);
}
@Override
public Integer timeoutSeconds() {
return timeoutSeconds;
}
@Override
public Integer maxRetries() {
return maxRetries;
}
@Override
public boolean supportsJsonMode() {
return false;

45
common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/GoogleAiGeminiChatModelConfig.java

@ -15,7 +15,6 @@
*/
package org.thingsboard.server.common.data.ai.model.chat;
import com.fasterxml.jackson.annotation.JsonTypeName;
import dev.langchain4j.model.chat.ChatModel;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
@ -25,52 +24,36 @@ import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import jakarta.validation.constraints.PositiveOrZero;
import lombok.Builder;
import lombok.Data;
import lombok.With;
import org.thingsboard.server.common.data.ai.provider.AiProvider;
import org.thingsboard.server.common.data.ai.provider.GoogleAiGeminiProviderConfig;
@Schema()
@JsonTypeName("GOOGLE_AI_GEMINI")
@Schema
@Builder
@Data
public final class GoogleAiGeminiChatModelConfig implements AiChatModelConfig<GoogleAiGeminiChatModelConfig, GoogleAiGeminiProviderConfig> {
@NotNull @Valid GoogleAiGeminiProviderConfig providerConfig;
@NotBlank String modelId;
@PositiveOrZero Double temperature;
@Positive @Max(1) Double topP;
@PositiveOrZero Integer topK;
Double frequencyPenalty;
Double presencePenalty;
Integer maxOutputTokens;
@With @Positive Integer timeoutSeconds;
@With @PositiveOrZero Integer maxRetries;
public record GoogleAiGeminiChatModelConfig(
@Schema(ref = "#/components/schemas/GoogleAiGeminiProviderConfig")
@NotNull @Valid GoogleAiGeminiProviderConfig providerConfig,
@NotBlank String modelId,
@PositiveOrZero Double temperature,
@Positive @Max(1) Double topP,
@PositiveOrZero Integer topK,
Double frequencyPenalty,
Double presencePenalty,
Integer maxOutputTokens,
@With @Positive Integer timeoutSeconds,
@With @PositiveOrZero Integer maxRetries
) implements AiChatModelConfig<GoogleAiGeminiChatModelConfig> {
@Override
public AiProvider provider() {
return AiProvider.GOOGLE_AI_GEMINI;
}
@Override
public GoogleAiGeminiProviderConfig providerConfig() {
return providerConfig;
}
@Override
public ChatModel configure(Langchain4jChatModelConfigurer configurer) {
return configurer.configureChatModel(this);
}
@Override
public Integer timeoutSeconds() {
return timeoutSeconds;
}
@Override
public Integer maxRetries() {
return maxRetries;
}
@Override
public boolean supportsJsonMode() {
return true;

44
common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/GoogleVertexAiGeminiChatModelConfig.java

@ -24,52 +24,36 @@ import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import jakarta.validation.constraints.PositiveOrZero;
import lombok.Builder;
import lombok.Data;
import lombok.With;
import org.thingsboard.server.common.data.ai.provider.AiProvider;
import org.thingsboard.server.common.data.ai.provider.GoogleVertexAiGeminiProviderConfig;
@Schema()
@Schema
@Builder
@Data
public final class GoogleVertexAiGeminiChatModelConfig implements AiChatModelConfig<GoogleVertexAiGeminiChatModelConfig, GoogleVertexAiGeminiProviderConfig> {
@NotNull @Valid GoogleVertexAiGeminiProviderConfig providerConfig;
@NotBlank String modelId;
@PositiveOrZero Double temperature;
@Positive @Max(1) Double topP;
@PositiveOrZero Integer topK;
Double frequencyPenalty;
Double presencePenalty;
Integer maxOutputTokens;
@With @Positive Integer timeoutSeconds;
@With @PositiveOrZero Integer maxRetries;
public record GoogleVertexAiGeminiChatModelConfig(
@Schema(ref = "#/components/schemas/GoogleVertexAiGeminiProviderConfig")
@NotNull @Valid GoogleVertexAiGeminiProviderConfig providerConfig,
@NotBlank String modelId,
@PositiveOrZero Double temperature,
@Positive @Max(1) Double topP,
@PositiveOrZero Integer topK,
Double frequencyPenalty,
Double presencePenalty,
Integer maxOutputTokens,
@With @Positive Integer timeoutSeconds,
@With @PositiveOrZero Integer maxRetries
) implements AiChatModelConfig<GoogleVertexAiGeminiChatModelConfig> {
@Override
public AiProvider provider() {
return AiProvider.GOOGLE_VERTEX_AI_GEMINI;
}
@Override
public GoogleVertexAiGeminiProviderConfig providerConfig() {
return providerConfig;
}
@Override
public ChatModel configure(Langchain4jChatModelConfigurer configurer) {
return configurer.configureChatModel(this);
}
@Override
public Integer timeoutSeconds() {
return timeoutSeconds;
}
@Override
public Integer maxRetries() {
return maxRetries;
}
@Override
public boolean supportsJsonMode() {
return true;

42
common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/MistralAiChatModelConfig.java

@ -24,51 +24,35 @@ import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import jakarta.validation.constraints.PositiveOrZero;
import lombok.Builder;
import lombok.Data;
import lombok.With;
import org.thingsboard.server.common.data.ai.provider.AiProvider;
import org.thingsboard.server.common.data.ai.provider.MistralAiProviderConfig;
@Schema()
@Schema
@Builder
@Data
public final class MistralAiChatModelConfig implements AiChatModelConfig<MistralAiChatModelConfig, MistralAiProviderConfig> {
@NotNull @Valid MistralAiProviderConfig providerConfig;
@NotBlank String modelId;
@PositiveOrZero Double temperature;
@Positive @Max(1) Double topP;
Double frequencyPenalty;
Double presencePenalty;
Integer maxOutputTokens;
@With @Positive Integer timeoutSeconds;
@With @PositiveOrZero Integer maxRetries;
public record MistralAiChatModelConfig(
@Schema(ref = "#/components/schemas/MistralAiProviderConfig")
@NotNull @Valid MistralAiProviderConfig providerConfig,
@NotBlank String modelId,
@PositiveOrZero Double temperature,
@Positive @Max(1) Double topP,
Double frequencyPenalty,
Double presencePenalty,
Integer maxOutputTokens,
@With @Positive Integer timeoutSeconds,
@With @PositiveOrZero Integer maxRetries
) implements AiChatModelConfig<MistralAiChatModelConfig> {
@Override
public AiProvider provider() {
return AiProvider.MISTRAL_AI;
}
@Override
public MistralAiProviderConfig providerConfig() {
return providerConfig;
}
@Override
public ChatModel configure(Langchain4jChatModelConfigurer configurer) {
return configurer.configureChatModel(this);
}
@Override
public Integer timeoutSeconds() {
return timeoutSeconds;
}
@Override
public Integer maxRetries() {
return maxRetries;
}
@Override
public boolean supportsJsonMode() {
return true;

44
common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/OllamaChatModelConfig.java

@ -24,53 +24,35 @@ import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import jakarta.validation.constraints.PositiveOrZero;
import lombok.Builder;
import lombok.Data;
import lombok.With;
import org.thingsboard.server.common.data.ai.provider.AiProvider;
import org.thingsboard.server.common.data.ai.provider.OllamaProviderConfig;
@Schema()
@Schema
@Builder
@Data
public final class OllamaChatModelConfig implements AiChatModelConfig<OllamaChatModelConfig, OllamaProviderConfig> {
@NotNull @Valid OllamaProviderConfig providerConfig;
@NotBlank String modelId;
@PositiveOrZero Double temperature;
@Positive @Max(1) Double topP;
@PositiveOrZero Integer topK;
Integer contextLength;
Integer maxOutputTokens;
@With
@Positive Integer timeoutSeconds;
@With
@PositiveOrZero Integer maxRetries;
public record OllamaChatModelConfig(
@Schema(ref = "#/components/schemas/OllamaProviderConfig")
@NotNull @Valid OllamaProviderConfig providerConfig,
@NotBlank String modelId,
@PositiveOrZero Double temperature,
@Positive @Max(1) Double topP,
@PositiveOrZero Integer topK,
Integer contextLength,
Integer maxOutputTokens,
@With @Positive Integer timeoutSeconds,
@With @PositiveOrZero Integer maxRetries
) implements AiChatModelConfig<OllamaChatModelConfig> {
@Override
public AiProvider provider() {
return AiProvider.OLLAMA;
}
@Override
public OllamaProviderConfig providerConfig() {
return providerConfig;
}
@Override
public ChatModel configure(Langchain4jChatModelConfigurer configurer) {
return configurer.configureChatModel(this);
}
@Override
public Integer timeoutSeconds() {
return timeoutSeconds;
}
@Override
public Integer maxRetries() {
return maxRetries;
}
@Override
public boolean supportsJsonMode() {
return true;

42
common/data/src/main/java/org/thingsboard/server/common/data/ai/model/chat/OpenAiChatModelConfig.java

@ -24,51 +24,35 @@ import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import jakarta.validation.constraints.PositiveOrZero;
import lombok.Builder;
import lombok.Data;
import lombok.With;
import org.thingsboard.server.common.data.ai.provider.AiProvider;
import org.thingsboard.server.common.data.ai.provider.OpenAiProviderConfig;
@Schema()
@Schema
@Builder
@Data
public final class OpenAiChatModelConfig implements AiChatModelConfig<OpenAiChatModelConfig, OpenAiProviderConfig> {
@NotNull @Valid OpenAiProviderConfig providerConfig;
@NotBlank String modelId;
@PositiveOrZero Double temperature;
@Positive @Max(1) Double topP;
Double frequencyPenalty;
Double presencePenalty;
Integer maxOutputTokens;
@With @Positive Integer timeoutSeconds;
@With @PositiveOrZero Integer maxRetries;
public record OpenAiChatModelConfig(
@Schema(ref = "#/components/schemas/OpenAiProviderConfig")
@NotNull @Valid OpenAiProviderConfig providerConfig,
@NotBlank String modelId,
@PositiveOrZero Double temperature,
@Positive @Max(1) Double topP,
Double frequencyPenalty,
Double presencePenalty,
Integer maxOutputTokens,
@With @Positive Integer timeoutSeconds,
@With @PositiveOrZero Integer maxRetries
) implements AiChatModelConfig<OpenAiChatModelConfig> {
@Override
public AiProvider provider() {
return AiProvider.OPENAI;
}
@Override
public OpenAiProviderConfig providerConfig() {
return providerConfig;
}
@Override
public ChatModel configure(Langchain4jChatModelConfigurer configurer) {
return configurer.configureChatModel(this);
}
@Override
public Integer timeoutSeconds() {
return timeoutSeconds;
}
@Override
public Integer maxRetries() {
return maxRetries;
}
@Override
public boolean supportsJsonMode() {
return true;

3
common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/AiProvider.java

@ -15,6 +15,9 @@
*/
package org.thingsboard.server.common.data.ai.provider;
import io.swagger.v3.oas.annotations.media.Schema;
@Schema(enumAsRef = true)
public enum AiProvider {
OPENAI,

2
common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/AmazonBedrockProviderConfig.java

@ -18,7 +18,7 @@ package org.thingsboard.server.common.data.ai.provider;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
@Schema(description = "Amazon Bedrock AI provider configuration")
@Schema
public record AmazonBedrockProviderConfig(
@NotNull String region,
@NotNull String accessKeyId,

2
common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/AnthropicProviderConfig.java

@ -15,8 +15,10 @@
*/
package org.thingsboard.server.common.data.ai.provider;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
@Schema
public record AnthropicProviderConfig(
@NotNull String apiKey
) implements AiProviderConfig {}

2
common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/AzureOpenAiProviderConfig.java

@ -15,8 +15,10 @@
*/
package org.thingsboard.server.common.data.ai.provider;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
@Schema
public record AzureOpenAiProviderConfig(
@NotNull String endpoint,
String serviceVersion,

2
common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/GitHubModelsProviderConfig.java

@ -15,8 +15,10 @@
*/
package org.thingsboard.server.common.data.ai.provider;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
@Schema
public record GitHubModelsProviderConfig(
@NotNull String personalAccessToken
) implements AiProviderConfig {}

2
common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/GoogleAiGeminiProviderConfig.java

@ -15,8 +15,10 @@
*/
package org.thingsboard.server.common.data.ai.provider;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
@Schema
public record GoogleAiGeminiProviderConfig(
@NotNull String apiKey
) implements AiProviderConfig {}

2
common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/GoogleVertexAiGeminiProviderConfig.java

@ -15,9 +15,11 @@
*/
package org.thingsboard.server.common.data.ai.provider;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
@Schema
public record GoogleVertexAiGeminiProviderConfig(
@NotBlank String fileName, // not used on BE, but needed for UI
@NotNull String projectId,

2
common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/MistralAiProviderConfig.java

@ -15,8 +15,10 @@
*/
package org.thingsboard.server.common.data.ai.provider;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
@Schema
public record MistralAiProviderConfig(
@NotNull String apiKey
) implements AiProviderConfig {}

1
common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/OllamaProviderConfig.java

@ -22,6 +22,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
@Schema(allOf = AiProviderConfig.class)
public record OllamaProviderConfig(
@NotNull String baseUrl,
@NotNull @Valid OllamaAuth auth

2
common/data/src/main/java/org/thingsboard/server/common/data/ai/provider/OpenAiProviderConfig.java

@ -16,12 +16,14 @@
package org.thingsboard.server.common.data.ai.provider;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.AssertTrue;
import lombok.Builder;
import org.apache.commons.lang3.StringUtils;
import java.util.Objects;
@Schema
@Builder
public record OpenAiProviderConfig(
String baseUrl,

3
rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/RuleEngineAiChatModelService.java

@ -19,10 +19,9 @@ import com.google.common.util.concurrent.FluentFuture;
import dev.langchain4j.model.chat.request.ChatRequest;
import dev.langchain4j.model.chat.response.ChatResponse;
import org.thingsboard.server.common.data.ai.model.chat.AiChatModelConfig;
import org.thingsboard.server.common.data.ai.provider.AiProviderConfig;
public interface RuleEngineAiChatModelService {
<C extends AiChatModelConfig<C, P>, P extends AiProviderConfig> FluentFuture<ChatResponse> sendChatRequestAsync(AiChatModelConfig<C, P> chatModelConfig, ChatRequest chatRequest);
<C extends AiChatModelConfig<C>> FluentFuture<ChatResponse> sendChatRequestAsync(AiChatModelConfig<C> chatModelConfig, ChatRequest chatRequest);
}

7
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/ai/TbAiNode.java

@ -48,7 +48,6 @@ import org.thingsboard.server.common.data.TbResourceInfo;
import org.thingsboard.server.common.data.ai.AiModel;
import org.thingsboard.server.common.data.ai.model.AiModelType;
import org.thingsboard.server.common.data.ai.model.chat.AiChatModelConfig;
import org.thingsboard.server.common.data.ai.provider.AiProviderConfig;
import org.thingsboard.server.common.data.id.AiModelId;
import org.thingsboard.server.common.data.id.TbResourceId;
import org.thingsboard.server.common.data.id.TenantId;
@ -127,7 +126,7 @@ public final class TbAiNode extends TbAbstractExternalNode implements TbNode {
if (modelType != AiModelType.CHAT) {
throw new TbNodeException("[" + ctx.getTenantId() + "] AI model with ID: [" + modelId + "] must be of type CHAT, but was " + modelType, true);
}
AiChatModelConfig<?, ?> chatModelConfig = (AiChatModelConfig<?, ?>) model.getConfiguration();
AiChatModelConfig<?> chatModelConfig = (AiChatModelConfig<?>) model.getConfiguration();
if (isJsonModeConfigured(config)) {
if (!chatModelConfig.supportsJsonMode()) {
throw new TbNodeException("[" + ctx.getTenantId() + "] AI model with ID: [" + modelId + "] does not support '" + config.getResponseFormat().type() + "' response format", true);
@ -219,7 +218,7 @@ public final class TbAiNode extends TbAbstractExternalNode implements TbNode {
}, directExecutor());
}
private <C extends AiChatModelConfig<C, P>, P extends AiProviderConfig> FluentFuture<ChatResponse> sendChatRequestAsync(TbContext ctx, ChatRequest chatRequest) {
private <C extends AiChatModelConfig<C>> FluentFuture<ChatResponse> sendChatRequestAsync(TbContext ctx, ChatRequest chatRequest) {
return ctx.getAiModelService().findAiModelByTenantIdAndIdAsync(ctx.getTenantId(), modelId).transformAsync(modelOpt -> {
if (modelOpt.isEmpty()) {
throw new NoSuchElementException("[" + ctx.getTenantId() + "] AI model with ID: [" + modelId + "] was not found");
@ -231,7 +230,7 @@ public final class TbAiNode extends TbAbstractExternalNode implements TbNode {
}
@SuppressWarnings("unchecked")
AiChatModelConfig<C, P> chatModelConfig = (AiChatModelConfig<C, P>) model.getConfiguration();
AiChatModelConfig<C> chatModelConfig = (AiChatModelConfig<C>) model.getConfiguration();
chatModelConfig = chatModelConfig
.withTimeoutSeconds(timeoutSeconds)

Loading…
Cancel
Save