Merge pull request #32 from SlimusMinus/fix-image_moderation
fixed image moderation and dell redis config
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
package com.krylov.refound.ai.client;
|
||||
|
||||
import com.krylov.refound.ai.dto.ImageModerationResponse;
|
||||
import com.krylov.refound.ai.dto.ModerationResponse;
|
||||
import com.krylov.refound.ai.exception.AiServerException;
|
||||
import com.krylov.refound.ai.exception.AiUnavailableException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -29,12 +28,16 @@ public class ImageModerationClient {
|
||||
}
|
||||
|
||||
@Recover
|
||||
public ModerationResponse recover(AiUnavailableException e, String text) {
|
||||
public ImageModerationResponse recover(AiUnavailableException e, MultipartFile file) {
|
||||
log.error("AI ImageModeration unavailable after retries", e);
|
||||
|
||||
return ModerationResponse.builder()
|
||||
.approved(false)
|
||||
.reason("AI_UNAVAILABLE")
|
||||
.build();
|
||||
return null;
|
||||
}
|
||||
|
||||
@Recover
|
||||
public ImageModerationResponse recover(AiServerException e, MultipartFile file) {
|
||||
log.error("AI ImageModeration server error after retries", e);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,9 +32,13 @@ public class TextModerationClient {
|
||||
public ModerationResponse recover(AiUnavailableException e, String text) {
|
||||
log.error("AI TextModeration unavailable after retries", e);
|
||||
|
||||
return ModerationResponse.builder()
|
||||
.approved(false)
|
||||
.reason("AI_UNAVAILABLE")
|
||||
.build();
|
||||
return null;
|
||||
}
|
||||
|
||||
@Recover
|
||||
public ModerationResponse recover(AiServerException e, String text) {
|
||||
log.error("AI TextModeration server error after retries", e);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ public class ContentModerationFacade {
|
||||
|
||||
private final ContentModerationPipeline pipeline;
|
||||
|
||||
public void moderate(String title, String description, List<MultipartFile> images) {
|
||||
public Boolean moderate(String title, String description, List<MultipartFile> images) {
|
||||
log.info("moderate ContentModerationFacade get");
|
||||
ModerationContext context =
|
||||
ModerationContext.builder()
|
||||
@@ -24,7 +24,7 @@ public class ContentModerationFacade {
|
||||
.images(images)
|
||||
.build();
|
||||
|
||||
pipeline.execute(context);
|
||||
return pipeline.execute(context);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,11 +12,15 @@ public class ContentModerationPipeline {
|
||||
|
||||
private final List<ModerationProcessor> processors;
|
||||
|
||||
public void execute(ModerationContext context) {
|
||||
public Boolean execute(ModerationContext context) {
|
||||
log.info("execute ContentModerationPipeline");
|
||||
for (ModerationProcessor processor : processors) {
|
||||
processor.process(context);
|
||||
Boolean approved = processor.process(context);
|
||||
if (approved == null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,11 +21,11 @@ public class ImageModerationProcessor implements ModerationProcessor {
|
||||
private final ImageModerationClient client;
|
||||
|
||||
@Override
|
||||
public void process(ModerationContext context) {
|
||||
public Boolean process(ModerationContext context) {
|
||||
List<MultipartFile> images = context.getImages();
|
||||
|
||||
if (images == null || images.isEmpty()) {
|
||||
return;
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
|
||||
for (MultipartFile image : images) {
|
||||
@@ -34,6 +34,10 @@ public class ImageModerationProcessor implements ModerationProcessor {
|
||||
}
|
||||
|
||||
ImageModerationResponse response = client.moderate(image);
|
||||
if (response == null) {
|
||||
log.warn("AI ImageModeration вернул null, пропускаем модерацию изображения");
|
||||
return null;
|
||||
}
|
||||
|
||||
log.info("Image moderation result approved={}, score={}, reason={}, labels={}",
|
||||
response.getApproved(),
|
||||
@@ -42,10 +46,12 @@ public class ImageModerationProcessor implements ModerationProcessor {
|
||||
response.getDetectedLabels()
|
||||
);
|
||||
|
||||
if (!response.getApproved()) {
|
||||
if (!Boolean.TRUE.equals(response.getApproved())) {
|
||||
throw new ContentBlockedException(response.getReason(), response.getDetectedLabels());
|
||||
}
|
||||
}
|
||||
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,6 @@ package com.krylov.refound.ai.pipeline;
|
||||
|
||||
public interface ModerationProcessor {
|
||||
|
||||
void process(ModerationContext context);
|
||||
Boolean process(ModerationContext context);
|
||||
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ public class TextModerationProcessor implements ModerationProcessor {
|
||||
private final TextModerationClient client;
|
||||
|
||||
@Override
|
||||
public void process(ModerationContext context) {
|
||||
public Boolean process(ModerationContext context) {
|
||||
log.info("process TextModerationProcessor for context {}", context);
|
||||
String text = buildText(
|
||||
context.getTitle(),
|
||||
@@ -25,12 +25,18 @@ public class TextModerationProcessor implements ModerationProcessor {
|
||||
);
|
||||
|
||||
ModerationResponse response = client.moderate(text);
|
||||
if (response == null) {
|
||||
log.warn("AI TextModeration вернул null, пропускаем модерацию текста");
|
||||
return null;
|
||||
}
|
||||
|
||||
log.info("AI moderation result approved={}, score={}, reason={}", response.getApproved(), response.getScore(), response.getReason());
|
||||
|
||||
if (!response.getApproved()) {
|
||||
if (!Boolean.TRUE.equals(response.getApproved())) {
|
||||
throw new ContentBlockedException(response.getReason(), response.getDetectedLabels());
|
||||
}
|
||||
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
|
||||
private String buildText(String title, String description) {
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
package com.krylov.refound.config;
|
||||
|
||||
import org.springframework.cache.annotation.EnableCaching;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@EnableCaching
|
||||
@Configuration
|
||||
public class CacheConfig {
|
||||
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package com.krylov.refound.config;
|
||||
|
||||
import java.time.Duration;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.cache.RedisCacheConfiguration;
|
||||
import org.springframework.data.redis.cache.RedisCacheManager;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
|
||||
@Configuration
|
||||
public class RedisConfig {
|
||||
@Value("${cache.ttl}")
|
||||
private long ttl;
|
||||
|
||||
@Bean
|
||||
public RedisCacheManager cacheManager(RedisConnectionFactory factory) {
|
||||
|
||||
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
|
||||
.entryTtl(Duration.ofSeconds(ttl))
|
||||
.disableCachingNullValues();
|
||||
|
||||
return RedisCacheManager.builder(factory)
|
||||
.cacheDefaults(config)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,12 @@ public class PostModerationExecutorService {
|
||||
.map(this::toMultipartFile)
|
||||
.toList();
|
||||
|
||||
contentModerationFacade.moderate(post.getTitle(), post.getDescription(), images);
|
||||
Boolean approved = contentModerationFacade.moderate(post.getTitle(), post.getDescription(), images);
|
||||
|
||||
if (approved == null) {
|
||||
log.warn("Пост {} не отмодерирован из-за недоступности AI, статус не меняем", post.getId());
|
||||
return;
|
||||
}
|
||||
|
||||
postRepository.updateStatus(postId, PostStatus.ACTIVE);
|
||||
log.info("Пост {} прошёл модерацию.", post.getId());
|
||||
|
||||
@@ -23,9 +23,6 @@ spring:
|
||||
host: localhost
|
||||
port: 6379
|
||||
|
||||
cache:
|
||||
type: redis
|
||||
|
||||
liquibase:
|
||||
change-log: classpath:db/changelog/db.changelog-master.yaml
|
||||
|
||||
@@ -44,9 +41,6 @@ ai:
|
||||
delay: 500
|
||||
multiplier: 2
|
||||
|
||||
cache:
|
||||
ttl: 300
|
||||
|
||||
jwt:
|
||||
secret: ${JWT_SECRET:mySuperSecretKeyForJwtTokenGenerationThatIsLongEnough2024!ReFound}
|
||||
access-expiration: ${JWT_ACCESS_EXPIRATION:900000} # 15 минут
|
||||
|
||||
Reference in New Issue
Block a user