Skip to content

Conversation

@sihyeonn
Copy link

@sihyeonn sihyeonn commented Jan 2, 2026

Description

Add override.path_mode option to ai-proxy and ai-proxy-multi plugins to control how the upstream request path is determined.

Background (Problem)
Currently, when override.endpoint is set, the URL path is fixed to either the endpoint's path or the driver's default path. This causes issues when:

  • Using a single route for multiple API endpoints (e.g., /v1/chat/completions and /v1/responses)
  • Wanting to preserve the original request URI path while only overriding the host

Solution
Add path_mode option with three modes:

  • fixed (default): Existing behavior - uses endpoint path or driver default
  • preserve: Uses the original request URI path (with query string)
  • append: Appends original request URI path to the endpoint path

This allows using a single route with the same model for multiple API endpoints, reducing route explosion in multi-model deployments.

Which issue(s) this PR fixes:

Fixes #

Checklist

  • I have explained the need for this PR and the problem it solves
  • I have explained the changes or the new features added to this PR
  • I have added tests corresponding to this change
  • I have updated the documentation to reflect this change
  • I have verified that this change is backward compatible (If not, please discuss on the APISIX mailing list first)

Add `override.path_mode` option to control upstream request path:
- fixed (default): use endpoint path or driver default
- preserve: use original request URI path with query string
- append: append original request URI path to endpoint path

This allows using a single route for multiple API endpoints
(e.g., /v1/chat/completions and /v1/responses) with the same model,
reducing route explosion in multi-model deployments.

Signed-off-by: Sihyeon Jang <[email protected]>
Add test cases for ai-proxy and ai-proxy-multi:
- path_mode=preserve: preserves original request URI path
- path_mode=append: appends request path to endpoint path
- path_mode=fixed: uses endpoint path (existing behavior)
- query string preservation with preserve/append modes

Signed-off-by: Sihyeon Jang <[email protected]>
@dosubot dosubot bot added size:L This PR changes 100-499 lines, ignoring generated files. plugin labels Jan 2, 2026
@sihyeonn sihyeonn changed the title Feat/sh path extend feat(ai-proxy): add path_mode option to override.endpoint Jan 2, 2026
@Baoyuantop
Copy link
Contributor

Hi @sihyeonn, thank you for your contribution. Could you please describe your original requirements in detail? What problems did you encounter while using the current version, and why was this change necessary?

@sihyeonn
Copy link
Author

sihyeonn commented Jan 5, 2026

Hi @Baoyuantop ,

I'm running an LLM gateway. Many models expose multiple endpoints from the same base URL:

  • /v1/chat/completions
  • /v1/responses
  • /v1/embeddings
  • /v1/moderations

Currently, I must create separate routes for each endpoint because override.endpoint replaces the entire URI:

# Example Current: 4 routes per model
routes:
  - uri: /v1/chat/completions
    plugins:
      ai-proxy:
        override:
          endpoint: https://api.example.com/models/gpt-4/v1/chat/completions

  - uri: /v1/responses
    plugins:
      ai-proxy:
        override:
          endpoint: https://api.example.com/models/gpt-4/v1/responses
  # ... 2 more routes

This creates unnecessary duplication:

  • 4+ routes per model
  • Same base URL repeated across routes
  • Duplicate plugin configs (healthcheck, auth, timeout)

Comment on lines 263 to 267
if ctx.var.is_args and ctx.var.args and #ctx.var.args > 0 then
local req_args_tab = core.string.decode_args(ctx.var.args)
if type(req_args_tab) == "table" then
core.table.merge(query_params, req_args_tab)
end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can be extracted into a common function.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is recommended to use ctx.var.is_args == "?" to check.

end
end
else
path = (endpoint_path and endpoint_path ~= "" and endpoint_path) or self.path
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It needs to be confirmed whether the behavior remains consistent when endpoint_path is an empty string.

type = "string",
description = "To be specified to override the endpoint of the AI Instance",
},
path_mode = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is recommended to extract it as a common schema variable to avoid duplication.

@Baoyuantop Baoyuantop added the wait for update wait for the author's response in this issue/PR label Jan 7, 2026
- Extract merge_request_query_params() to avoid code duplication
- Use ctx.var.is_args == "?" for proper string comparison
- Extract path_mode_schema as shared variable to avoid duplication
- Clarify empty string handling in fixed mode path selection

Signed-off-by: Sihyeon Jang <[email protected]>
@sihyeonn
Copy link
Author

sihyeonn commented Jan 7, 2026

@Baoyuantop Thanks for your review. I handled it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

plugin size:L This PR changes 100-499 lines, ignoring generated files. user responded wait for update wait for the author's response in this issue/PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants