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 e47d1ad9a3 fix(ai): synchronize routing context after request path 
rewrite (#7535)
e47d1ad9a3 is described below

commit e47d1ad9a39d6e34810e18afb79ae4fd0017b848
Author: lymerin <[email protected]>
AuthorDate: Sun Oct 11 22:45:21 2026 +0800

    fix(ai): synchronize routing context after request path rewrite (#7535)
---
 .../request/AiRequestTransformerPlugin.java        | 12 +++++
 .../request/AiRequestTransformerPluginTest.java    | 56 ++++++++++++++++++++++
 2 files changed, 68 insertions(+)

diff --git 
a/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-request-transformer/src/main/java/org/apache/shenyu/plugin/ai/transformer/request/AiRequestTransformerPlugin.java
 
b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-request-transformer/src/main/java/org/apache/shenyu/plugin/ai/transformer/request/AiRequestTransformerPlugin.java
index dbaa551d73..1c3dfaa6d8 100644
--- 
a/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-request-transformer/src/main/java/org/apache/shenyu/plugin/ai/transformer/request/AiRequestTransformerPlugin.java
+++ 
b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-request-transformer/src/main/java/org/apache/shenyu/plugin/ai/transformer/request/AiRequestTransformerPlugin.java
@@ -18,6 +18,7 @@
 package org.apache.shenyu.plugin.ai.transformer.request;
 
 import com.google.gson.JsonElement;
+import org.apache.shenyu.common.constant.Constants;
 import org.apache.shenyu.common.dto.RuleData;
 import org.apache.shenyu.common.dto.SelectorData;
 import org.apache.shenyu.common.dto.convert.plugin.AiRequestTransformerConfig;
@@ -31,6 +32,7 @@ import 
org.apache.shenyu.plugin.ai.transformer.request.cache.ChatClientCache;
 import 
org.apache.shenyu.plugin.ai.transformer.request.handler.AiRequestTransformerPluginHandler;
 import 
org.apache.shenyu.plugin.ai.transformer.request.template.AiRequestTransformerTemplate;
 import org.apache.shenyu.plugin.api.ShenyuPluginChain;
+import org.apache.shenyu.plugin.api.context.ShenyuContext;
 import org.apache.shenyu.plugin.base.AbstractShenyuPlugin;
 import org.apache.shenyu.plugin.base.utils.CacheKeyUtils;
 import org.apache.shenyu.plugin.base.utils.ServerWebExchangeUtils;
@@ -292,6 +294,16 @@ public class AiRequestTransformerPlugin extends 
AbstractShenyuPlugin {
                 .request(newRequest)
                 .build();
 
+        ShenyuContext context = newExchange.getAttribute(Constants.CONTEXT);
+        if (Objects.nonNull(context)) {
+            String rewrittenPath = newUri.getRawPath();
+            // Preserve an already transformed forwarding path.
+            if (Objects.equals(context.getRealUrl(), context.getPath())) {
+                context.setRealUrl(rewrittenPath);
+            }
+            context.setPath(rewrittenPath);
+        }
+
         return Mono.just(newExchange);
     }
 
diff --git 
a/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-request-transformer/src/test/java/org/apache/shenyu/plugin/ai/transformer/request/AiRequestTransformerPluginTest.java
 
b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-request-transformer/src/test/java/org/apache/shenyu/plugin/ai/transformer/request/AiRequestTransformerPluginTest.java
index 3258960e9f..f7b367d9fb 100644
--- 
a/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-request-transformer/src/test/java/org/apache/shenyu/plugin/ai/transformer/request/AiRequestTransformerPluginTest.java
+++ 
b/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-request-transformer/src/test/java/org/apache/shenyu/plugin/ai/transformer/request/AiRequestTransformerPluginTest.java
@@ -18,6 +18,7 @@
 package org.apache.shenyu.plugin.ai.transformer.request;
 
 import com.google.gson.JsonSyntaxException;
+import org.apache.shenyu.common.constant.Constants;
 import org.apache.shenyu.common.dto.RuleData;
 import org.apache.shenyu.common.dto.SelectorData;
 import org.apache.shenyu.common.dto.convert.rule.AiRequestTransformerHandle;
@@ -25,10 +26,13 @@ import 
org.apache.shenyu.plugin.ai.common.spring.ai.registry.AiModelFactoryRegis
 import org.apache.shenyu.plugin.ai.transformer.request.cache.ChatClientCache;
 import 
org.apache.shenyu.plugin.ai.transformer.request.handler.AiRequestTransformerPluginHandler;
 import org.apache.shenyu.plugin.api.ShenyuPluginChain;
+import org.apache.shenyu.plugin.api.context.ShenyuContext;
+import org.apache.shenyu.plugin.api.utils.RequestUrlUtils;
 import org.apache.shenyu.plugin.base.utils.CacheKeyUtils;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
 import org.junit.jupiter.params.provider.ValueSource;
 import org.mockito.MockedStatic;
 import org.springframework.ai.chat.client.ChatClient;
@@ -46,6 +50,7 @@ import reactor.test.StepVerifier;
 
 import java.net.URI;
 import java.util.List;
+import java.util.Objects;
 import java.util.concurrent.atomic.AtomicReference;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -143,6 +148,57 @@ class AiRequestTransformerPluginTest {
         assertThrows(JsonSyntaxException.class, () -> 
AiRequestTransformerPlugin.convertBodyJson(aiResponse));
     }
 
+    @ParameterizedTest
+    @CsvSource({
+        ", http://upstream.example/rewritten";,
+        "/forced, http://upstream.example/forced";
+    })
+    void testRewriteRequestRouting(final String rewriteUri, final String 
expectedOutboundUri) {
+        AiRequestTransformerHandle handle = new AiRequestTransformerHandle();
+        handle.setProvider("TEST_PROVIDER");
+        handle.setBaseUrl("http://test.com";);
+        handle.setApiKey("test-api-key");
+        handle.setModel("test-model");
+
+        ChatClient mockClient = mock(ChatClient.class, RETURNS_DEEP_STUBS);
+        String aiResponse = "POST /rewritten HTTP/1.1\nContent-Type: 
application/json\n\n{}";
+        
when(mockClient.prompt().user(anyString()).stream().content()).thenReturn(Flux.just(aiResponse));
+        MockServerWebExchange exchange = 
MockServerWebExchange.from(MockServerHttpRequest.post("http://localhost/original";)
+                .header(HttpHeaders.CONTENT_TYPE, 
MediaType.APPLICATION_JSON_VALUE)
+                .body("{}"));
+        ShenyuContext context = new ShenyuContext();
+        context.setPath("/original");
+        context.setRealUrl("/original");
+        exchange.getAttributes().put(Constants.CONTEXT, context);
+        if (Objects.nonNull(rewriteUri)) {
+            exchange.getAttributes().put(Constants.REWRITE_URI, rewriteUri);
+        }
+        RuleData rule = new RuleData();
+        rule.setId("test-routing-rule-id");
+        rule.setSelectorId("test-selector-id");
+        when(chatClientCache.getClient(rule.getId())).thenReturn(mockClient);
+        AtomicReference<ServerWebExchange> downstreamExchange = new 
AtomicReference<>();
+        
when(chain.execute(any(ServerWebExchange.class))).thenAnswer(invocation -> {
+            downstreamExchange.set(invocation.getArgument(0));
+            return Mono.empty();
+        });
+
+        String cacheKey = CacheKeyUtils.INST.getKey(rule);
+        
AiRequestTransformerPluginHandler.CACHED_HANDLE.get().cachedHandle(cacheKey, 
handle);
+        try (MockedStatic<ChatClientCache> mockedCache = 
mockStatic(ChatClientCache.class)) {
+            
mockedCache.when(ChatClientCache::getInstance).thenReturn(chatClientCache);
+            StepVerifier.create(plugin.doExecute(exchange, chain, new 
SelectorData(), rule)).verifyComplete();
+
+            ServerWebExchange downstream = downstreamExchange.get();
+            assertEquals(URI.create("http://localhost/rewritten";), 
downstream.getRequest().getURI());
+            assertEquals(URI.create(expectedOutboundUri), 
RequestUrlUtils.buildRequestUri(downstream, "http://upstream.example";));
+            assertEquals("/rewritten", context.getPath());
+            assertEquals("/rewritten", context.getRealUrl());
+        } finally {
+            
AiRequestTransformerPluginHandler.CACHED_HANDLE.get().removeHandle(cacheKey);
+        }
+    }
+
     @Test
     void testExtractHeadersFromAiResponse() {
 

Reply via email to