41
41
import org .springframework .web .client .ResponseErrorHandler ;
42
42
43
43
/**
44
- * {@link AutoConfiguration Auto-configuration} for AI Retry.
44
+ * {@link AutoConfiguration Auto-configuration} for AI Retry. Provides beans for retry
45
+ * template and response error handling. Handles transient and non-transient exceptions
46
+ * based on HTTP status codes.
45
47
*
46
- * @author Christian Tzolov
48
+ * Author: Christian Tzolov
47
49
*/
48
50
@ AutoConfiguration
49
51
@ ConditionalOnClass (RetryUtils .class )
@@ -63,9 +65,10 @@ public RetryTemplate retryTemplate(SpringAiRetryProperties properties) {
63
65
.withListener (new RetryListener () {
64
66
65
67
@ Override
66
- public <T extends Object , E extends Throwable > void onError (RetryContext context ,
67
- RetryCallback <T , E > callback , Throwable throwable ) {
68
- logger .warn ("Retry error. Retry count:" + context .getRetryCount (), throwable );
68
+ public <T , E extends Throwable > void onError (RetryContext context , RetryCallback <T , E > callback ,
69
+ Throwable throwable ) {
70
+ logger .warn ("Retry error. Retry count: {}, Exception: {}" , context .getRetryCount (),
71
+ throwable .getMessage (), throwable );
69
72
}
70
73
})
71
74
.build ();
@@ -84,29 +87,35 @@ public boolean hasError(@NonNull ClientHttpResponse response) throws IOException
84
87
85
88
@ Override
86
89
public void handleError (@ NonNull ClientHttpResponse response ) throws IOException {
87
- if (response .getStatusCode ().isError ()) {
88
- String error = StreamUtils .copyToString (response .getBody (), StandardCharsets .UTF_8 );
89
- String message = String .format ("%s - %s" , response .getStatusCode ().value (), error );
90
-
91
- // Explicitly configured transient codes
92
- if (properties .getOnHttpCodes ().contains (response .getStatusCode ().value ())) {
93
- throw new TransientAiException (message );
94
- }
95
-
96
- // onClientErrors - If true, do not throw a NonTransientAiException,
97
- // and do not attempt retry for 4xx client error codes, false by
98
- // default.
99
- if (!properties .isOnClientErrors () && response .getStatusCode ().is4xxClientError ()) {
100
- throw new NonTransientAiException (message );
101
- }
102
-
103
- // Explicitly configured non-transient codes
104
- if (!CollectionUtils .isEmpty (properties .getExcludeOnHttpCodes ())
105
- && properties .getExcludeOnHttpCodes ().contains (response .getStatusCode ().value ())) {
106
- throw new NonTransientAiException (message );
107
- }
90
+ if (!response .getStatusCode ().isError ()) {
91
+ return ;
92
+ }
93
+
94
+ String error = StreamUtils .copyToString (response .getBody (), StandardCharsets .UTF_8 );
95
+ if (error == null || error .isEmpty ()) {
96
+ error = "No response body available" ;
97
+ }
98
+
99
+ String message = String .format ("HTTP %s - %s" , response .getStatusCode ().value (), error );
100
+
101
+ // Explicitly configured transient codes
102
+ if (properties .getOnHttpCodes ().contains (response .getStatusCode ().value ())) {
108
103
throw new TransientAiException (message );
109
104
}
105
+
106
+ // Handle client errors (4xx)
107
+ if (!properties .isOnClientErrors () && response .getStatusCode ().is4xxClientError ()) {
108
+ throw new NonTransientAiException (message );
109
+ }
110
+
111
+ // Explicitly configured non-transient codes
112
+ if (!CollectionUtils .isEmpty (properties .getExcludeOnHttpCodes ())
113
+ && properties .getExcludeOnHttpCodes ().contains (response .getStatusCode ().value ())) {
114
+ throw new NonTransientAiException (message );
115
+ }
116
+
117
+ // Default to transient exception
118
+ throw new TransientAiException (message );
110
119
}
111
120
};
112
121
}
0 commit comments