This is an automated email from the ASF dual-hosted git repository.
Aias00 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shenyu.git
The following commit(s) were added to refs/heads/master by this push:
new b4a67ccb3b fix(ai): preserve headers on transform fallback (#7122)
b4a67ccb3b is described below
commit b4a67ccb3bd0bae42b3a578456f99558a8950b87
Author: Liming Deng <[email protected]>
AuthorDate: Tue Sep 22 08:12:10 2026 +0800
fix(ai): preserve headers on transform fallback (#7122)
Co-authored-by: aias00 <[email protected]>
---
.../response/AiResponseTransformerPlugin.java | 5 ++--
.../response/AiResponseTransformerPluginTest.java | 29 ++++++++++++++++++++++
2 files changed, 31 insertions(+), 3 deletions(-)
diff --git
a/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-response-transformer/src/main/java/org/apache/shenyu/plugin/ai/transformer/response/AiResponseTransformerPlugin.java
b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-response-transformer/src/main/java/org/apache/shenyu/plugin/ai/transformer/response/AiResponseTransformerPlugin.java
index 6dfea7c12d..8718bc1b3b 100644
---
a/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-response-transformer/src/main/java/org/apache/shenyu/plugin/ai/transformer/response/AiResponseTransformerPlugin.java
+++
b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-response-transformer/src/main/java/org/apache/shenyu/plugin/ai/transformer/response/AiResponseTransformerPlugin.java
@@ -358,10 +358,9 @@ public class AiResponseTransformerPlugin extends
AbstractShenyuPlugin {
HttpHeaders newHeaders =
extractHeadersFromAiResponse(aiResponse);
String newBody =
extractBodyFromAiResponse(aiResponse);
- this.getHeaders().clear();
- this.getHeaders().putAll(newHeaders);
-
if (Objects.nonNull(newBody) &&
!newBody.isEmpty()) {
+ this.getHeaders().clear();
+
this.getHeaders().putAll(newHeaders);
LOG.debug("Returning transformed
response body: {}", newBody);
return
WebFluxResultUtils.result(this.exchange,
newBody.getBytes(StandardCharsets.UTF_8));
} else {
diff --git
a/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-response-transformer/src/test/java/org/apache/shenyu/plugin/ai/transformer/response/AiResponseTransformerPluginTest.java
b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-response-transformer/src/test/java/org/apache/shenyu/plugin/ai/transformer/response/AiResponseTransformerPluginTest.java
index 2a7f835c6a..875ecc3ba9 100644
---
a/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-response-transformer/src/test/java/org/apache/shenyu/plugin/ai/transformer/response/AiResponseTransformerPluginTest.java
+++
b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-response-transformer/src/test/java/org/apache/shenyu/plugin/ai/transformer/response/AiResponseTransformerPluginTest.java
@@ -28,12 +28,16 @@ import
org.apache.shenyu.plugin.ai.common.spring.ai.AiModelFactory;
import
org.apache.shenyu.plugin.ai.common.spring.ai.registry.AiModelFactoryRegistry;
import
org.apache.shenyu.plugin.ai.transformer.response.template.AiResponseTransformerTemplate;
import org.apache.shenyu.plugin.api.ShenyuPluginChain;
+import org.apache.shenyu.plugin.api.result.DefaultShenyuResult;
+import org.apache.shenyu.plugin.api.result.ShenyuResult;
+import org.apache.shenyu.plugin.api.utils.SpringBeanUtils;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.MockedConstruction;
import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.core.io.buffer.DataBuffer;
@@ -44,6 +48,7 @@ import org.springframework.http.MediaType;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
import org.springframework.mock.http.server.reactive.MockServerHttpResponse;
import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
@@ -56,6 +61,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockConstruction;
import static org.mockito.Mockito.verify;
@@ -171,6 +178,28 @@ class AiResponseTransformerPluginTest {
}
}
+ @Test
+ void testOriginalHeadersRemainWhenTransformedBodyIsInvalid() {
+ MockServerHttpResponse response = (MockServerHttpResponse)
exchange.getResponse();
+ response.getHeaders().set("X-Original", "original");
+ AiResponseTransformerTemplate template =
mock(AiResponseTransformerTemplate.class);
+
when(template.assembleMessage(exchange)).thenReturn(Mono.just("{\"response\":{\"body\":\"\"}}"));
+ ChatClient chatClient = mock(ChatClient.class, RETURNS_DEEP_STUBS);
+ when(chatClient.prompt().user(anyString()).stream().content())
+ .thenReturn(Flux.just("HTTP/1.1 200 OK\nX-New: transformed"));
+ ConfigurableApplicationContext context =
mock(ConfigurableApplicationContext.class);
+ when(context.getBean(ShenyuResult.class)).thenReturn(new
DefaultShenyuResult());
+ SpringBeanUtils.getInstance().setApplicationContext(context);
+ AiResponseTransformerPlugin.AiResponseTransformerDecorator decorator =
+ new
AiResponseTransformerPlugin.AiResponseTransformerDecorator(exchange, template,
chatClient);
+ DataBuffer responseBody =
response.bufferFactory().wrap("original".getBytes(StandardCharsets.UTF_8));
+
+
StepVerifier.create(decorator.writeWith(Mono.just(responseBody))).verifyComplete();
+
+ assertEquals("original", response.getHeaders().getFirst("X-Original"));
+ assertNull(response.getHeaders().getFirst("X-New"));
+ }
+
@Test
void testExtractBodyFromAiResponse() {
String aiResponse = "HTTP/1.1 200 OK\nContent-Type:
application/json\n\n{\"status\":\"success\"}";