Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,12 @@ The system creates an empty configuration. To utilize existing Dial Core configu

1. Import configuration file in the AIDIAL admin panel using special import endpoint

**Compatibility Note**
>
> Since **v0.47.0**, the Admin Panel supports multilingual metadata through the `LocalizedValue` type.
> Exported configurations that contain multiple localized values are compatible only with **AIDIAL Core v0.47.0+**.
> Configurations containing only plain string values remain backward compatible with earlier Core versions.

## Getting Started

### Run Application with Gradle
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ public class ApplicationResourceDto {
private String endpoint;
private String responsesEndpoint;
private Map<String, DeploymentInterfaceResourceDto> interfaces;
private String displayName;
private LocalizedValueDto displayName;
private String displayVersion;
private String overrideName;
private String iconUrl;
private String description;
private String intro;
private LocalizedValueDto description;
private LocalizedValueDto intro;
private String reference;
private Boolean forwardAuthToken;
private List<String> inputAttachmentTypes;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,11 @@ public class DeploymentDataDto {
private String model;
private String application;
private String toolset;
private String displayName;
private LocalizedValueDto displayName;
private String displayVersion;
private String iconUrl;
private String description;
private LocalizedValueDto description;
private LocalizedValueDto intro;
private String reference;
private String owner;
private String object;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.epam.aidial.cfg.client.dto;

import com.epam.aidial.cfg.client.dto.databind.LocalizedValueDeserializer;
import com.epam.aidial.cfg.client.dto.databind.LocalizedValueSerializer;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import lombok.EqualsAndHashCode;
import lombok.Getter;

import java.util.LinkedHashMap;
import java.util.Map;

@Getter
@JsonDeserialize(using = LocalizedValueDeserializer.class)
@JsonSerialize(using = LocalizedValueSerializer.class)
@EqualsAndHashCode
public final class LocalizedValueDto {

private final String plainValue;
private final Map<String, String> localeMap;

private LocalizedValueDto(String plainValue, Map<String, String> localeMap) {
this.plainValue = plainValue;
this.localeMap = localeMap;
}

public static LocalizedValueDto of(String value) {
return value == null ? null : new LocalizedValueDto(value, null);
}

public static LocalizedValueDto of(Map<String, String> localeMap) {
return localeMap == null ? null : new LocalizedValueDto(null, new LinkedHashMap<>(localeMap));
}

public boolean isMap() {
return localeMap != null;
}

public boolean isPlain() {
return plainValue != null;
}

@Override
public String toString() {
return plainValue != null ? plainValue : String.valueOf(localeMap);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ public class ToolSetResourceDto {

private String name;
private String endpoint;
private String displayName;
private LocalizedValueDto displayName;
private String displayVersion;
private String iconUrl;
private String description;
private String intro;
private LocalizedValueDto description;
private LocalizedValueDto intro;
private String vendorWebsite;
private List<String> descriptionKeywords;
private Integer maxRetryAttempts;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.epam.aidial.cfg.client.dto.databind;

import com.epam.aidial.cfg.client.dto.LocalizedValueDto;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;

import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;

public class LocalizedValueDeserializer extends JsonDeserializer<LocalizedValueDto> {

@Override
public LocalizedValueDto deserialize(JsonParser p, DeserializationContext ctx) throws IOException {
if (p.getCurrentToken() == JsonToken.VALUE_STRING) {
return LocalizedValueDto.of(p.getValueAsString());
}

if (p.getCurrentToken() == JsonToken.START_OBJECT) {
Map<String, String> localeMap = new LinkedHashMap<>();
while (p.nextToken() != JsonToken.END_OBJECT) {
String locale = p.getCurrentName();
p.nextToken();
localeMap.put(locale, p.getValueAsString());
}
return LocalizedValueDto.of(localeMap);
}

if (p.getCurrentToken() == JsonToken.VALUE_NULL) {
return null;
}

return ctx.reportInputMismatch(LocalizedValueDto.class,
"Expected a string or a locale-to-value object, got %s", p.getCurrentToken());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.epam.aidial.cfg.client.dto.databind;

import com.epam.aidial.cfg.client.dto.LocalizedValueDto;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;

import java.io.IOException;

public class LocalizedValueSerializer extends JsonSerializer<LocalizedValueDto> {

@Override
public void serialize(LocalizedValueDto value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
if (!value.isMap()) {
gen.writeString(value.getPlainValue());
return;
}

if (value.getLocaleMap().size() == 1) {
gen.writeString(value.getLocaleMap().values().iterator().next());
return;
}

gen.writeStartObject();
for (var entry : value.getLocaleMap().entrySet()) {
gen.writeStringField(entry.getKey(), entry.getValue());
}
gen.writeEndObject();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.epam.aidial.cfg.client.dto.ApplicationResourceDto;
import com.epam.aidial.cfg.client.dto.NodeTypeDto;
import com.epam.aidial.cfg.dto.ApplicationEximDto;
import com.epam.aidial.cfg.mapper.LocalizedValueMapper;
import com.epam.aidial.cfg.model.ApplicationExim;
import com.epam.aidial.cfg.model.ApplicationResource;
import com.epam.aidial.cfg.model.ApplicationResourceNodeInfo;
Expand All @@ -20,7 +21,7 @@
import static com.epam.aidial.cfg.client.mapper.CoreMetadataUtils.extractPath;
import static com.epam.aidial.cfg.client.mapper.CoreMetadataUtils.parseEncodedVersionedPath;

@Mapper(componentModel = "spring", uses = {RouteMapper.class})
@Mapper(componentModel = "spring", uses = {RouteMapper.class, LocalizedValueClientMapper.class, LocalizedValueMapper.class})

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is LocalizedValueMapper.class used?

@Slf4j
public abstract class ApplicationClientMapper {
public static final String APPLICATIONS_PREFIX = "applications/";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

import java.util.List;

@Mapper(componentModel = "spring", uses = RouteMapper.class)
@Mapper(componentModel = "spring", uses = {RouteMapper.class, LocalizedValueClientMapper.class})
public interface DeploymentClientMapper {

List<DeploymentData> toDeploymentDataList(List<DeploymentDataDto> dtos);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.epam.aidial.cfg.client.mapper;

import com.epam.aidial.cfg.client.dto.LocalizedValueDto;
import com.epam.aidial.cfg.model.LocalizedValue;
import org.springframework.stereotype.Component;

@Component
public class LocalizedValueClientMapper {

public LocalizedValueDto toDto(LocalizedValue domain) {

@KirylKurnosenka KirylKurnosenka Aug 17, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need this custom mapping? Won't regular mapstruct auto-mapping work?

if (domain == null) {
return null;
}
return domain.isPlain() ? LocalizedValueDto.of(domain.getPlainValue()) : LocalizedValueDto.of(domain.getLocaleMap());
}

public LocalizedValue toDomain(LocalizedValueDto dto) {
if (dto == null) {
return null;
}
return dto.isPlain() ? LocalizedValue.of(dto.getPlainValue()) : LocalizedValue.of(dto.getLocaleMap());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.epam.aidial.cfg.client.dto.ToolSetMetadataDto;
import com.epam.aidial.cfg.client.dto.ToolSetResourceDto;
import com.epam.aidial.cfg.dto.ToolSetEximDto;
import com.epam.aidial.cfg.mapper.LocalizedValueMapper;
import com.epam.aidial.cfg.model.CreateToolSetResource;
import com.epam.aidial.cfg.model.NodeType;
import com.epam.aidial.cfg.model.ToolSetExim;
Expand All @@ -19,7 +20,7 @@
import static com.epam.aidial.cfg.client.mapper.CoreMetadataUtils.extractPath;
import static com.epam.aidial.cfg.client.mapper.CoreMetadataUtils.parseEncodedVersionedPath;

@Mapper(componentModel = "spring")
@Mapper(componentModel = "spring", uses = {LocalizedValueClientMapper.class, LocalizedValueMapper.class})

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is LocalizedValueMapper.class used?

@Slf4j
public abstract class ToolSetClientMapper {
public static final String TOOLSETS_PREFIX = "toolsets/";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.epam.aidial.cfg.configuration;

import com.epam.aidial.cfg.configuration.databind.LocalizedValueDeserializer;
import com.epam.aidial.cfg.configuration.databind.LocalizedValueSerializer;
import com.epam.aidial.cfg.domain.model.LocalizedValue;
import com.epam.aidial.core.config.CoreCostLimit;
import com.epam.aidial.core.config.CoreCostLimitMixinForCoreObjectMapper;
import com.epam.aidial.core.config.CoreLimit;
Expand All @@ -19,6 +22,7 @@
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.cfg.EnumFeature;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
Expand Down Expand Up @@ -73,6 +77,9 @@ private static JsonMapper.Builder createDefaultJsonMapperBuilder() {
.addModule(new QueryLanguageModule())
.addModule(new ValidationModule())
.addModule(new JavaTimeModule())
.addModule(new SimpleModule()
.addSerializer(LocalizedValue.class, new LocalizedValueSerializer())

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we put serializer/deserializer right into LocalizedValue class?

.addDeserializer(LocalizedValue.class, new LocalizedValueDeserializer()))
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.epam.aidial.cfg.configuration.databind;

import com.epam.aidial.cfg.domain.model.LocalizedValue;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;

import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;

public class LocalizedValueDeserializer extends JsonDeserializer<LocalizedValue> {

@Override
public LocalizedValue deserialize(JsonParser p, DeserializationContext ctx) throws IOException {
if (p.getCurrentToken() == JsonToken.VALUE_STRING) {
return LocalizedValue.of(p.getValueAsString());
}

if (p.getCurrentToken() == JsonToken.START_OBJECT) {
Map<String, String> localeMap = new LinkedHashMap<>();
while (p.nextToken() != JsonToken.END_OBJECT) {
String locale = p.getCurrentName();
p.nextToken();
localeMap.put(locale, p.getValueAsString());
}
return LocalizedValue.of(localeMap);
}

if (p.getCurrentToken() == JsonToken.VALUE_NULL) {
return null;
}

return ctx.reportInputMismatch(LocalizedValue.class,
"Expected a string or a locale-to-value object, got %s", p.getCurrentToken());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.epam.aidial.cfg.configuration.databind;

import com.epam.aidial.cfg.domain.model.LocalizedValue;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;

import java.io.IOException;

public class LocalizedValueSerializer extends JsonSerializer<LocalizedValue> {

@Override
public void serialize(LocalizedValue value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
if (!value.isMap()) {
gen.writeString(value.getPlainValue());
return;
}

if (value.getLocaleMap().size() == 1) {
gen.writeString(value.getLocaleMap().values().iterator().next());
return;
}

gen.writeStartObject();
for (var entry : value.getLocaleMap().entrySet()) {
gen.writeStringField(entry.getKey(), entry.getValue());
}
gen.writeEndObject();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,15 @@
import java.util.List;

public interface ApplicationJpaRepository extends JpaRepository<ApplicationEntity, String> {

boolean existsByDisplayNameAndDisplayVersion(String displayName, String displayVersion);

List<ApplicationEntity> findByIdNotIn(Collection<String> ids);

List<ApplicationEntity> findAllByValidityStateIsValidTrue();

List<ApplicationEntity> findAllByOrderByDisplayNameAscDisplayVersionAscIdAsc();

List<ApplicationEntity> findByValidityStateIsValidTrueOrderByDisplayNameAscDisplayVersionAscIdAsc();

List<ApplicationEntity> findByIdInOrderByDisplayNameAscDisplayVersionAscIdAsc(Collection<String> ids);

List<ApplicationEntity> findByApplicationContainerIsNotNull();

List<ApplicationEntity> findByDisplayVersion(String displayVersion);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@

public interface ModelJpaRepository extends JpaRepository<ModelEntity, String> {

boolean existsByDisplayNameAndDisplayVersion(String displayName, String displayVersion);
@Query("SELECT m FROM ModelEntity m WHERE m.modelContainer IS NOT NULL")
List<ModelEntity> findByContainerIdIsNotNull();

List<ModelEntity> findByIdNotIn(Collection<String> ids);

@Query("SELECT m FROM ModelEntity m WHERE m.modelContainer IS NOT NULL")
List<ModelEntity> findByContainerIdIsNotNull();
List<ModelEntity> findByDisplayVersion(String displayVersion);

List<ModelEntity> findAllByOrderByDisplayNameAscDisplayVersionAscIdAsc();

List<ModelEntity> findByIdInOrderByDisplayNameAscDisplayVersionAscIdAsc(Collection<String> ids);
List<ModelEntity> findByIdInOrderByDisplayNameAscDisplayVersionAscIdAsc(Collection<String> names);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this change needed?

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

import java.util.List;

@Mapper(componentModel = "spring", uses = {DeploymentEntityMapper.class})
@Mapper(componentModel = "spring", uses = {DeploymentEntityMapper.class, LocalizedValueEntityMapper.class})
public abstract class AddonEntityMapper {

@Autowired
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
@Mapper(componentModel = "spring", uses = {
DeploymentEntityMapper.class, MapPropertiesMapper.class, DependentRouteEntityMapper.class,
FeaturesEntityMapper.class, ValidityStateEntityMapper.class, DeploymentInterfacesEntityMapper.class,
ExternalServicesEntityMapper.class, UriMapper.class
ExternalServicesEntityMapper.class, UriMapper.class, LocalizedValueEntityMapper.class
})
public abstract class ApplicationEntityMapper {

Expand Down
Loading
Loading