dhivakar020 opened a new issue, #14129: URL: https://github.com/apache/apisix/issues/14129
### Current Behavior When a route uses `proxy-rewrite` to change the path and `ai-proxy` (or `ai-proxy-multi`) handles the request in passthrough mode, the LLM upstream receives the **original client path**. The path rewrite is silently ignored, while header rewrites from the same `proxy-rewrite` do apply. Passthrough is used when the body is not OpenAI chat/embeddings/responses, Anthropic or Bedrock, and `override.endpoint` has no path of its own. A common setup that hits this: exposing an LLM service under a prefix such as `/llm/*` and stripping it with `proxy-rewrite`, as the `ai-proxy` docs suggest combining the two plugins. Cause: the passthrough branch in `apisix/plugins/ai-proxy/base.lua` sets `target_path = ctx.var.uri`. Underlying this is a known gap worth flagging: after `proxy-rewrite` calls `ngx.req.set_uri`, `ngx.var.uri` holds the rewritten path but `ctx.var.uri` still returns the original one. `ctx.var` caches variables on first read, the router reads `uri` in `apisix/init.lua` before any plugin runs, and `uri` is not in `no_cacheable_var_names` (`apisix/core/ctx.lua`). `proxy-mirror` already works around it (`-- read the var directly as ctx.var.uri may be cached before the rewrite`). Other readers of `ctx.var.uri` after a rewrite (for example `oas-validator`, `_meta.filter` expressions) see the original path too. This report is only about the `ai-proxy` case, where it changes where the request is sent; I'm mentioning the general behaviour in case maintainers prefer to handle it at the core level instead. ### Expected Behavior Passthrough sends the same path a plain route would send upstream, i.e. the path produced by `proxy-rewrite` (`$upstream_uri`). In the example below: `/anything/rewritten/x`. ### Error Logs No error is logged. httpbin echoes the URL it received: ``` "url": "https://httpbin.org/anything/original/x" ``` Expected `https://httpbin.org/anything/rewritten/x`. ### Steps to Reproduce 1. Run APISIX (master) with etcd. 2. Create a route with `proxy-rewrite` and `ai-proxy` (endpoint without a path): ```bash curl -X PUT http://127.0.0.1:9180/apisix/admin/routes/1 -H "X-API-KEY: $KEY" -d '{ "uri": "/anything/original/*", "plugins": { "proxy-rewrite": { "regex_uri": ["^/anything/original/(.*)", "/anything/rewritten/$1"] }, "ai-proxy": { "provider": "openai-compatible", "auth": { "header": { "Authorization": "Bearer dummy" } }, "override": { "endpoint": "https://httpbin.org" } } } }' ``` 3. Send a request whose body is not a chat/embeddings payload, so passthrough is used: ```bash curl -X POST http://127.0.0.1:9080/anything/original/x \ -H "Content-Type: application/json" -d '{"foo":"bar"}' ``` 4. The response shows `"url": "https://httpbin.org/anything/original/x"`. I have a fix with tests ready: in passthrough, use the path part of `$upstream_uri` when it is set (the path a plain `proxy_pass` uses) and fall back to `ctx.var.uri` otherwise; about 20 lines in `ai-proxy/base.lua` plus a test in `t/plugin/ai-proxy-passthrough.t`. Happy to open the PR if this direction looks right. ### Environment - APISIX version (run `apisix version`): master (`53009939`), 3.19.0 - Operating system (run `uname -a`): Linux 6.18.54.1-microsoft-standard-WSL2 x86_64 (Ubuntu 24.04) - OpenResty / Nginx version (run `openresty -V` or `nginx -V`): openresty/1.31.1.1 (apisix-runtime 1.4.1) - etcd version, if relevant (run `curl http://127.0.0.1:9090/v1/server_info`): 3.5.4 - APISIX Dashboard version, if relevant: N/A - Plugin runner version, for issues related to plugin runners: N/A - LuaRocks version, for installation issues (run `luarocks --version`): N/A -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
