AI rule node: add polymorphic JSON config to AI settings
This commit is contained in:
parent
ad0161e3df
commit
f2075c6c39
@ -15,13 +15,13 @@
|
|||||||
--
|
--
|
||||||
|
|
||||||
CREATE TABLE ai_settings (
|
CREATE TABLE ai_settings (
|
||||||
id UUID NOT NULL PRIMARY KEY,
|
id UUID NOT NULL PRIMARY KEY,
|
||||||
created_time BIGINT NOT NULL,
|
created_time BIGINT NOT NULL,
|
||||||
tenant_id UUID NOT NULL,
|
tenant_id UUID NOT NULL,
|
||||||
version BIGINT NOT NULL DEFAULT 1,
|
version BIGINT NOT NULL DEFAULT 1,
|
||||||
name VARCHAR(255) NOT NULL,
|
name VARCHAR(255) NOT NULL,
|
||||||
provider VARCHAR(255) NOT NULL,
|
provider VARCHAR(255) NOT NULL,
|
||||||
model VARCHAR(255) NOT NULL,
|
model VARCHAR(255) NOT NULL,
|
||||||
api_key VARCHAR(1000) NOT NULL,
|
configuration JSONB NOT NULL,
|
||||||
CONSTRAINT ai_settings_name_unq_key UNIQUE (tenant_id, name)
|
CONSTRAINT ai_settings_name_unq_key UNIQUE (tenant_id, name)
|
||||||
);
|
);
|
||||||
|
|||||||
@ -46,15 +46,15 @@ class AiServiceImpl implements RuleEngineAiService {
|
|||||||
|
|
||||||
return switch (aiSettings.getProvider()) {
|
return switch (aiSettings.getProvider()) {
|
||||||
case OPENAI -> OpenAiChatModel.builder()
|
case OPENAI -> OpenAiChatModel.builder()
|
||||||
.apiKey(aiSettings.getApiKey())
|
.apiKey(aiSettings.getConfiguration().getApiKey())
|
||||||
.modelName(aiSettings.getModel())
|
.modelName(aiSettings.getModel())
|
||||||
.build();
|
.build();
|
||||||
case MISTRAL_AI -> MistralAiChatModel.builder()
|
case MISTRAL_AI -> MistralAiChatModel.builder()
|
||||||
.apiKey(aiSettings.getApiKey())
|
.apiKey(aiSettings.getConfiguration().getApiKey())
|
||||||
.modelName(aiSettings.getModel())
|
.modelName(aiSettings.getModel())
|
||||||
.build();
|
.build();
|
||||||
case GOOGLE_AI_GEMINI -> GoogleAiGeminiChatModel.builder()
|
case GOOGLE_AI_GEMINI -> GoogleAiGeminiChatModel.builder()
|
||||||
.apiKey(aiSettings.getApiKey())
|
.apiKey(aiSettings.getConfiguration().getApiKey())
|
||||||
.modelName(aiSettings.getModel())
|
.modelName(aiSettings.getModel())
|
||||||
.build();
|
.build();
|
||||||
};
|
};
|
||||||
|
|||||||
@ -0,0 +1,47 @@
|
|||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonSubTypes;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@JsonTypeInfo(
|
||||||
|
use = JsonTypeInfo.Id.NAME,
|
||||||
|
include = JsonTypeInfo.As.EXISTING_PROPERTY,
|
||||||
|
property = "provider",
|
||||||
|
visible = true
|
||||||
|
)
|
||||||
|
@JsonSubTypes({
|
||||||
|
@JsonSubTypes.Type(value = OpenAiConfig.class, name = "OPENAI"),
|
||||||
|
@JsonSubTypes.Type(value = GoogleAiGeminiConfig.class, name = "GOOGLE_AI_GEMINI"),
|
||||||
|
@JsonSubTypes.Type(value = MistralAiConfig.class, name = "MISTRAL_AI")
|
||||||
|
})
|
||||||
|
public abstract class AiConfig {
|
||||||
|
|
||||||
|
@Schema(
|
||||||
|
requiredMode = Schema.RequiredMode.REQUIRED,
|
||||||
|
accessMode = Schema.AccessMode.READ_WRITE,
|
||||||
|
description = "API key for authenticating with the AI provider",
|
||||||
|
example = "sk-********************************"
|
||||||
|
)
|
||||||
|
private String apiKey;
|
||||||
|
|
||||||
|
}
|
||||||
@ -83,11 +83,10 @@ public final class AiSettings extends BaseData<AiSettingsId> implements HasTenan
|
|||||||
|
|
||||||
@Schema(
|
@Schema(
|
||||||
requiredMode = Schema.RequiredMode.REQUIRED,
|
requiredMode = Schema.RequiredMode.REQUIRED,
|
||||||
accessMode = Schema.AccessMode.WRITE_ONLY,
|
accessMode = Schema.AccessMode.READ_WRITE,
|
||||||
description = "API key for authenticating with the selected AI provider",
|
description = "Provider-specific settings for the chosen AI model"
|
||||||
example = "sk-********************************"
|
|
||||||
)
|
)
|
||||||
String apiKey;
|
AiConfig configuration;
|
||||||
|
|
||||||
public AiSettings() {}
|
public AiSettings() {}
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,40 @@
|
|||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@Schema(
|
||||||
|
name = "GoogleAiGemini",
|
||||||
|
description = "Configuration properties for the Google AI Gemini"
|
||||||
|
)
|
||||||
|
public class GoogleAiGeminiConfig extends AiConfig {
|
||||||
|
|
||||||
|
@Schema(
|
||||||
|
requiredMode = Schema.RequiredMode.REQUIRED,
|
||||||
|
accessMode = Schema.AccessMode.READ_WRITE,
|
||||||
|
description = "Name of the AI provider",
|
||||||
|
example = "GOOGLE_AI_GEMINI",
|
||||||
|
allowableValues = "GOOGLE_AI_GEMINI",
|
||||||
|
type = "string"
|
||||||
|
)
|
||||||
|
private AiProvider provider = AiProvider.GOOGLE_AI_GEMINI;
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@Schema(
|
||||||
|
name = "MistralAi",
|
||||||
|
description = "Configuration properties for the Mistral AI"
|
||||||
|
)
|
||||||
|
public class MistralAiConfig extends AiConfig {
|
||||||
|
|
||||||
|
@Schema(
|
||||||
|
requiredMode = Schema.RequiredMode.REQUIRED,
|
||||||
|
accessMode = Schema.AccessMode.READ_WRITE,
|
||||||
|
description = "Name of the AI provider",
|
||||||
|
example = "MISTRAL_AI",
|
||||||
|
allowableValues = "MISTRAL_AI",
|
||||||
|
type = "string"
|
||||||
|
)
|
||||||
|
private AiProvider provider = AiProvider.MISTRAL_AI;
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@Schema(
|
||||||
|
name = "OpenAiConfig",
|
||||||
|
description = "Configuration properties for the OpenAI"
|
||||||
|
)
|
||||||
|
public class OpenAiConfig extends AiConfig {
|
||||||
|
|
||||||
|
@Schema(
|
||||||
|
requiredMode = Schema.RequiredMode.REQUIRED,
|
||||||
|
accessMode = Schema.AccessMode.READ_WRITE,
|
||||||
|
description = "Name of the AI provider",
|
||||||
|
example = "OPENAI",
|
||||||
|
allowableValues = "OPENAI",
|
||||||
|
type = "string"
|
||||||
|
)
|
||||||
|
private AiProvider provider = AiProvider.OPENAI;
|
||||||
|
|
||||||
|
}
|
||||||
@ -36,7 +36,7 @@ public final class AiSettingsId extends UUIDBased implements EntityId {
|
|||||||
@Override
|
@Override
|
||||||
@Schema(
|
@Schema(
|
||||||
requiredMode = Schema.RequiredMode.REQUIRED,
|
requiredMode = Schema.RequiredMode.REQUIRED,
|
||||||
description = "Entity type of the AI settings, always 'AI_SETTINGS'",
|
description = "Entity type of the AI settings",
|
||||||
example = "AI_SETTINGS",
|
example = "AI_SETTINGS",
|
||||||
allowableValues = "AI_SETTINGS"
|
allowableValues = "AI_SETTINGS"
|
||||||
)
|
)
|
||||||
|
|||||||
@ -747,7 +747,7 @@ public class ModelConstants {
|
|||||||
public static final String AI_SETTINGS_NAME_COLUMN_NAME = NAME_PROPERTY;
|
public static final String AI_SETTINGS_NAME_COLUMN_NAME = NAME_PROPERTY;
|
||||||
public static final String AI_SETTINGS_PROVIDER_COLUMN_NAME = "provider";
|
public static final String AI_SETTINGS_PROVIDER_COLUMN_NAME = "provider";
|
||||||
public static final String AI_SETTINGS_MODEL_COLUMN_NAME = "model";
|
public static final String AI_SETTINGS_MODEL_COLUMN_NAME = "model";
|
||||||
public static final String AI_SETTINGS_API_KEY_COLUMN_NAME = "api_key";
|
public static final String AI_SETTINGS_CONFIGURATION_COLUMN_NAME = "configuration";
|
||||||
|
|
||||||
protected static final String[] NONE_AGGREGATION_COLUMNS = new String[]{LONG_VALUE_COLUMN, DOUBLE_VALUE_COLUMN, BOOLEAN_VALUE_COLUMN, STRING_VALUE_COLUMN, JSON_VALUE_COLUMN, KEY_COLUMN, TS_COLUMN};
|
protected static final String[] NONE_AGGREGATION_COLUMNS = new String[]{LONG_VALUE_COLUMN, DOUBLE_VALUE_COLUMN, BOOLEAN_VALUE_COLUMN, STRING_VALUE_COLUMN, JSON_VALUE_COLUMN, KEY_COLUMN, TS_COLUMN};
|
||||||
|
|
||||||
|
|||||||
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.thingsboard.server.dao.model.sql;
|
package org.thingsboard.server.dao.model.sql;
|
||||||
|
|
||||||
|
import io.hypersistence.utils.hibernate.type.json.JsonBinaryType;
|
||||||
import jakarta.persistence.Column;
|
import jakarta.persistence.Column;
|
||||||
import jakarta.persistence.Entity;
|
import jakarta.persistence.Entity;
|
||||||
import jakarta.persistence.EnumType;
|
import jakarta.persistence.EnumType;
|
||||||
@ -23,7 +24,9 @@ import jakarta.persistence.Table;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
|
import org.hibernate.annotations.Type;
|
||||||
import org.hibernate.proxy.HibernateProxy;
|
import org.hibernate.proxy.HibernateProxy;
|
||||||
|
import org.thingsboard.server.common.data.ai.AiConfig;
|
||||||
import org.thingsboard.server.common.data.ai.AiProvider;
|
import org.thingsboard.server.common.data.ai.AiProvider;
|
||||||
import org.thingsboard.server.common.data.ai.AiSettings;
|
import org.thingsboard.server.common.data.ai.AiSettings;
|
||||||
import org.thingsboard.server.common.data.id.AiSettingsId;
|
import org.thingsboard.server.common.data.id.AiSettingsId;
|
||||||
@ -41,7 +44,7 @@ import java.util.UUID;
|
|||||||
@Table(name = ModelConstants.AI_SETTINGS_TABLE_NAME)
|
@Table(name = ModelConstants.AI_SETTINGS_TABLE_NAME)
|
||||||
public class AiSettingsEntity extends BaseVersionedEntity<AiSettings> {
|
public class AiSettingsEntity extends BaseVersionedEntity<AiSettings> {
|
||||||
|
|
||||||
@Column(name = ModelConstants.AI_SETTINGS_TENANT_ID_COLUMN_NAME, nullable = false, columnDefinition = "uuid")
|
@Column(name = ModelConstants.AI_SETTINGS_TENANT_ID_COLUMN_NAME, nullable = false, columnDefinition = "UUID")
|
||||||
private UUID tenantId;
|
private UUID tenantId;
|
||||||
|
|
||||||
@Column(name = ModelConstants.AI_SETTINGS_NAME_COLUMN_NAME, nullable = false)
|
@Column(name = ModelConstants.AI_SETTINGS_NAME_COLUMN_NAME, nullable = false)
|
||||||
@ -54,8 +57,9 @@ public class AiSettingsEntity extends BaseVersionedEntity<AiSettings> {
|
|||||||
@Column(name = ModelConstants.AI_SETTINGS_MODEL_COLUMN_NAME, nullable = false)
|
@Column(name = ModelConstants.AI_SETTINGS_MODEL_COLUMN_NAME, nullable = false)
|
||||||
private String model;
|
private String model;
|
||||||
|
|
||||||
@Column(name = ModelConstants.AI_SETTINGS_API_KEY_COLUMN_NAME, nullable = false)
|
@Type(JsonBinaryType.class)
|
||||||
private String apiKey;
|
@Column(name = ModelConstants.AI_SETTINGS_CONFIGURATION_COLUMN_NAME, nullable = false, columnDefinition = "JSONB")
|
||||||
|
private AiConfig configuration;
|
||||||
|
|
||||||
public AiSettingsEntity() {}
|
public AiSettingsEntity() {}
|
||||||
|
|
||||||
@ -65,7 +69,7 @@ public class AiSettingsEntity extends BaseVersionedEntity<AiSettings> {
|
|||||||
name = aiSettings.getName();
|
name = aiSettings.getName();
|
||||||
provider = aiSettings.getProvider();
|
provider = aiSettings.getProvider();
|
||||||
model = aiSettings.getModel();
|
model = aiSettings.getModel();
|
||||||
apiKey = aiSettings.getApiKey();
|
configuration = aiSettings.getConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -77,7 +81,7 @@ public class AiSettingsEntity extends BaseVersionedEntity<AiSettings> {
|
|||||||
settings.setName(name);
|
settings.setName(name);
|
||||||
settings.setProvider(provider);
|
settings.setProvider(provider);
|
||||||
settings.setModel(model);
|
settings.setModel(model);
|
||||||
settings.setApiKey(apiKey);
|
settings.setConfiguration(configuration);
|
||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -950,13 +950,13 @@ CREATE TABLE IF NOT EXISTS cf_debug_event (
|
|||||||
) PARTITION BY RANGE (ts);
|
) PARTITION BY RANGE (ts);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS ai_settings (
|
CREATE TABLE IF NOT EXISTS ai_settings (
|
||||||
id UUID NOT NULL PRIMARY KEY,
|
id UUID NOT NULL PRIMARY KEY,
|
||||||
created_time BIGINT NOT NULL,
|
created_time BIGINT NOT NULL,
|
||||||
tenant_id UUID NOT NULL,
|
tenant_id UUID NOT NULL,
|
||||||
version BIGINT NOT NULL DEFAULT 1,
|
version BIGINT NOT NULL DEFAULT 1,
|
||||||
name VARCHAR(255) NOT NULL,
|
name VARCHAR(255) NOT NULL,
|
||||||
provider VARCHAR(255) NOT NULL,
|
provider VARCHAR(255) NOT NULL,
|
||||||
model VARCHAR(255) NOT NULL,
|
model VARCHAR(255) NOT NULL,
|
||||||
api_key VARCHAR(1000) NOT NULL,
|
configuration JSONB NOT NULL,
|
||||||
CONSTRAINT ai_settings_name_unq_key UNIQUE (tenant_id, name)
|
CONSTRAINT ai_settings_name_unq_key UNIQUE (tenant_id, name)
|
||||||
);
|
);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user