From f6b8807327a8b186bdd96c745b2ec8987267b115 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 10 May 2026 19:06:31 +0000
Subject: [PATCH 1/3] Initial plan
From 13365cbf4da39e461212a4346749c44ba9a0aa88 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 10 May 2026 19:09:28 +0000
Subject: [PATCH 2/3] fix: retry only transient AI exceptions in ProdAIService
Agent-Logs-Url: https://github.com/JUCanEat/java-backend/sessions/fe2aec73-220a-4625-9642-39c3925e8d46
Co-authored-by: kon-mtal <50518677+kon-mtal@users.noreply.github.com>
---
pom.xml | 8 +++++++
.../com/backend/services/ProdAIService.java | 5 +++--
.../ProdAIServiceRetryConfigTest.java | 22 +++++++++++++++++++
3 files changed, 33 insertions(+), 2 deletions(-)
create mode 100644 src/test/java/com/backend/servicesTests/ProdAIServiceRetryConfigTest.java
diff --git a/pom.xml b/pom.xml
index 5df8603..44653a3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -90,6 +90,14 @@
org.springframework.boot
spring-boot-starter-actuator
+
+ org.springframework.boot
+ spring-boot-starter-aop
+
+
+ org.springframework.retry
+ spring-retry
+
io.micrometer
diff --git a/src/main/java/com/backend/services/ProdAIService.java b/src/main/java/com/backend/services/ProdAIService.java
index 44582b9..0de3186 100644
--- a/src/main/java/com/backend/services/ProdAIService.java
+++ b/src/main/java/com/backend/services/ProdAIService.java
@@ -9,6 +9,7 @@
import org.springframework.ai.content.Media;
import org.springframework.ai.chat.client.ChatClient;
+import org.springframework.ai.retry.TransientAiException;
import org.springframework.context.annotation.Profile;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.io.ByteArrayResource;
@@ -33,7 +34,7 @@ public class ProdAIService implements MenuAIService {
private final DishRepository dishRepository;
@Retryable(
- retryFor = Exception.class,
+ retryFor = TransientAiException.class,
maxAttempts = 5,
backoff = @Backoff(delay = 1000, multiplier = 2) // 1s, 2s, 4s, 8s
)
@@ -130,4 +131,4 @@ private Dish convertToEntity(AIDishDTO dto) {
dish.setAllergens(dto.getAllergens());
return dish;
}
-}
\ No newline at end of file
+}
diff --git a/src/test/java/com/backend/servicesTests/ProdAIServiceRetryConfigTest.java b/src/test/java/com/backend/servicesTests/ProdAIServiceRetryConfigTest.java
new file mode 100644
index 0000000..6485648
--- /dev/null
+++ b/src/test/java/com/backend/servicesTests/ProdAIServiceRetryConfigTest.java
@@ -0,0 +1,22 @@
+package com.backend.servicesTests;
+
+import com.backend.services.ProdAIService;
+import org.junit.jupiter.api.Test;
+import org.springframework.ai.retry.TransientAiException;
+import org.springframework.retry.annotation.Retryable;
+
+import java.lang.reflect.Method;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class ProdAIServiceRetryConfigTest {
+
+ @Test
+ void parseMenuFromImageShouldRetryOnlyTransientAiExceptions() throws NoSuchMethodException {
+ Method method = ProdAIService.class.getMethod("parseMenuFromImage", byte[].class);
+ Retryable retryable = method.getAnnotation(Retryable.class);
+
+ assertThat(retryable).isNotNull();
+ assertThat(retryable.retryFor()).containsExactly(TransientAiException.class);
+ }
+}
From e9c3b7b7f1e4267bacf71d7ad9d4c50ec93b870d Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 10 May 2026 19:10:58 +0000
Subject: [PATCH 3/3] test: assert full retry annotation settings for
ProdAIService
Agent-Logs-Url: https://github.com/JUCanEat/java-backend/sessions/fe2aec73-220a-4625-9642-39c3925e8d46
Co-authored-by: kon-mtal <50518677+kon-mtal@users.noreply.github.com>
---
.../backend/servicesTests/ProdAIServiceRetryConfigTest.java | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/test/java/com/backend/servicesTests/ProdAIServiceRetryConfigTest.java b/src/test/java/com/backend/servicesTests/ProdAIServiceRetryConfigTest.java
index 6485648..f9e96b1 100644
--- a/src/test/java/com/backend/servicesTests/ProdAIServiceRetryConfigTest.java
+++ b/src/test/java/com/backend/servicesTests/ProdAIServiceRetryConfigTest.java
@@ -18,5 +18,8 @@ void parseMenuFromImageShouldRetryOnlyTransientAiExceptions() throws NoSuchMetho
assertThat(retryable).isNotNull();
assertThat(retryable.retryFor()).containsExactly(TransientAiException.class);
+ assertThat(retryable.maxAttempts()).isEqualTo(5);
+ assertThat(retryable.backoff().delay()).isEqualTo(1000L);
+ assertThat(retryable.backoff().multiplier()).isEqualTo(2.0);
}
}