Skip to content

feat: add new LocalizationValue type for displayName, description, intro fields for models, applications, toolsets, inteseptors - #1117

Open
Katerina-Charakhovich wants to merge 13 commits into
developmentfrom
feat/Localization-value-for-displayName-version-intro
Open

Katerina-Charakhovich wants to merge 13 commits into
developmentfrom
feat/Localization-value-for-displayName-version-intro

Conversation

@Katerina-Charakhovich

@Katerina-Charakhovich Katerina-Charakhovich commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Applicable issues

Description of changes

Checklist

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@ai-dial-actions

This comment has been minimized.

@KirylKurnosenka KirylKurnosenka left a comment

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.

Automated review findings (see inline comments). Items #1-#5 are correctness bugs I'd fix before merge; finding #1 in particular blocks updates to any Application/Model using the new multi-language displayName feature.

Comment thread src/main/java/com/epam/aidial/cfg/domain/service/ApplicationService.java Outdated

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

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.

Bug: the serializer collapses any single-entry locale map to a bare string regardless of which locale it is, contradicting LocalizedValue.normalize()'s documented contract of only collapsing the default-locale single-entry case.

Example: displayName = {"fr": "Bonjour"} (default locale is "en") — on any response/export round-trip this is written as plain "Bonjour", silently discarding the "fr" tag. If later re-saved unmodified, the value has silently degraded from an explicit French label into a locale-less plain string.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This behavior is already implemented in DIAL Core, so we must keep the Admin/client implementation consistent with Core. Please use the Core implementation as the source of truth and align the serializer with it rather than introducing a different serialization contract.

@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.

Are we sure that such behavior is expected? Won't users be confused that after saving single locale value, this locale is lost (at least on UI)?

Comment thread src/main/java/com/epam/aidial/cfg/domain/model/LocalizedValue.java
Comment thread src/main/java/com/epam/aidial/cfg/client/dto/ApplicationResourceDto.java Outdated
Comment thread src/main/java/com/epam/aidial/cfg/domain/mapper/AddonCoreMapper.java Outdated
Comment thread src/main/java/com/epam/aidial/cfg/domain/mapper/AssistantCoreMapper.java Outdated
Comment thread src/main/java/com/epam/aidial/cfg/domain/service/ApplicationService.java Outdated
Comment thread src/main/java/com/epam/aidial/cfg/domain/value/LocalizedValue.java Outdated
Comment thread src/main/java/com/epam/aidial/cfg/dto/ApplicationDto.java Outdated
Comment thread src/main/java/com/epam/aidial/cfg/model/ToolSetExim.java
Comment thread src/main/java/com/epam/aidial/cfg/service/Assertions.java Outdated
@ai-dial-actions

Copy link
Copy Markdown
Contributor

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

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?

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?

@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?

.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?

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?

@Component
public class LocalizedValueCoreMapper {

public CoreLocalizedValue toCoreLocalizedValue(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.

Is this custom mapping really needed? Can't we rely on mapstruct auto-mapping?

private void assertDisplayNameAndDisplayVersionUnique(String currentApplicationName, Application application) {
Map<String, LocalizedValue> candidates = applicationJpaRepository.findByDisplayVersion(application.getDisplayVersion()).stream()
.map(mapper::toDomain)
.filter(a -> a.getDisplayName() != null)

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 we do it in DB?

return false;
}

private boolean isBlank(LocalizedValue value) {

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.

Looks like it repeats validation in DisplayFieldsValidator. This logic can be put directly into LocalizedValue class

return value.isPlain()
? StringUtils.isNotBlank(value.getPlainValue())
: value.getLocaleMap() != null
&& value.getLocaleMap().values().stream().anyMatch(StringUtils::isNotBlank);

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.

Shouldn't we also check that map key should not be blank?

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.

I also didn't catch why we allow to have blank value for locales, e.g. why we allow displayName = {"en": "", "fr": "Bonjour"}?

@Component
public class LocalizedValueMapper {

public LocalizedValueDto toDto(LocalizedValue domain) {

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.

The same as for other mappers: can't we rely on mapstruct auto-mapping?

import java.util.Map;

@Getter
@JsonDeserialize(using = LocalizedValueDeserializer.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 this supposed to be serialized/deserialized?


private String endpoint;
private String iconUrl;
private String description;

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.

I see that description didn't have validation, but for now I think we should validate that if description != null, then it's valid (otherwise I'm not sure how we should handle e.g.

"description": {"":""}

@Component
public class LocalizedValueDtoMapper {

public LocalizedValueDto toDto(LocalizedValue domain) {

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.

The same as for other mappers

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants