Re: [PR] feat(ai-proxy-multi): support client-driven model selection via models request body field [apisix]

2026-06-02 Thread via GitHub


Baoyuantop commented on PR #13084:
URL: https://github.com/apache/apisix/pull/13084#issuecomment-4600343884

   Hi @kjprice, please resolve the code conflicts.


-- 
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]



Re: [PR] feat(ai-proxy-multi): support client-driven model selection via models request body field [apisix]

2026-05-10 Thread via GitHub


github-actions[bot] commented on PR #13084:
URL: https://github.com/apache/apisix/pull/13084#issuecomment-4415059860

   This pull request has been marked as stale due to 60 days of inactivity. It 
will be closed in 4 weeks if no further activity occurs. If you think that's 
incorrect or this pull request should instead be reviewed, please simply write 
any comment. Even if closed, you can still revive the PR at any time or discuss 
it on the [email protected] list. Thank you for your contributions.


-- 
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]



Re: [PR] feat(ai-proxy-multi): support client-driven model selection via models request body field [apisix]

2026-03-11 Thread via GitHub


Baoyuantop commented on PR #13084:
URL: https://github.com/apache/apisix/pull/13084#issuecomment-4037393403

   Hi @kjprice, please fix failed CI


-- 
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]



Re: [PR] feat(ai-proxy-multi): support client-driven model selection via models request body field [apisix]

2026-03-10 Thread via GitHub


Copilot commented on code in PR #13084:
URL: https://github.com/apache/apisix/pull/13084#discussion_r2915641166


##
apisix/plugins/ai-proxy-multi.lua:
##
@@ -409,6 +480,33 @@ end
 
 
 function _M.access(conf, ctx)
+if conf.allow_client_model_preference then
+local body, err = core.request.get_body()
+if body then
+local request_body, decode_err = core.json.decode(body)
+if request_body and not decode_err and request_body.models then
+ctx.client_model_preference = match_client_models(
+conf.instances, request_body.models)
+core.log.info("client model preference: ",
+  
core.json.delay_encode(ctx.client_model_preference))
+request_body.models = nil
+ngx.req.set_body_data(core.json.encode(request_body))
+end
+end
+end

Review Comment:
   The `models` field is only stripped when `allow_client_model_preference` is 
enabled. This contradicts the PR description/docs (and the new tests) which 
state that `models` is always stripped before forwarding upstream. As-is, 
requests sent with `models` while the feature is disabled will forward an extra 
`models` field to upstream providers.
   
   Suggestion: always remove `models` from the JSON request body when present, 
but only apply client-driven reordering when `allow_client_model_preference` is 
true (i.e., strip unconditionally; reorder conditionally).



##
apisix/plugins/ai-proxy-multi.lua:
##
@@ -299,6 +300,76 @@ local function get_instance_conf(instances, name)
 end
 
 
+local function match_client_models(instances, models)
+local ordered_names = {}
+local matched = {}
+
+for _, model_pref in ipairs(models) do
+local target_model, target_provider
+if type(model_pref) == "string" then

Review Comment:
   `match_client_models()` assumes `models` is an array and iterates it with 
`ipairs(models)`. If a client sends a non-array JSON value (e.g. 
`"models":"gpt-4"`), `ipairs` will raise an error and the request will 500.
   
   Suggestion: validate `request_body.models` is a table/array before calling 
`match_client_models` (and/or harden `match_client_models` to return the 
default ordering when `type(models) ~= "table"`). Also consider skipping 
elements that are not a string or object.



##
apisix/plugins/ai-proxy-multi.lua:
##
@@ -430,6 +528,25 @@ end
 
 
 local function retry_on_error(ctx, conf, code)
+if ctx.client_model_preference then
+ctx.client_model_tried = ctx.client_model_tried or {}
+ctx.client_model_tried[ctx.picked_ai_instance_name] = true
+if (code == 429 and fallback_strategy_has(conf.fallback_strategy, 
"http_429")) or
+   (code >= 500 and code < 600 and
+   fallback_strategy_has(conf.fallback_strategy, "http_5xx")) then
+local name, ai_instance, err = pick_preferred_instance(ctx, conf)
+if err then
+core.log.error("all preferred instances failed: ", err)
+return 502
+end
+ctx.balancer_ip = name
+ctx.picked_ai_instance_name = name
+ctx.picked_ai_instance = ai_instance
+return
+end
+return code
+end

Review Comment:
   The new client-preference retry path in `retry_on_error()` (falling back 
through `ctx.client_model_preference` on 429/5xx) isn’t covered by the added 
tests. Current test cases validate selection/reordering and stripping, but not 
that a 429/5xx from the preferred instance causes the plugin to retry the next 
preferred instance (and that it stops retrying on non-matching status codes).
   
   Suggestion: add a test that makes the first preferred instance return 
429/5xx and asserts the response comes from the next preferred instance 
(similar to existing fallback tests in `ai-proxy-multi.balancer.t`).



##
apisix/plugins/ai-proxy-multi.lua:
##
@@ -299,6 +300,76 @@ local function get_instance_conf(instances, name)
 end
 
 
+local function match_client_models(instances, models)
+local ordered_names = {}
+local matched = {}
+
+for _, model_pref in ipairs(models) do
+local target_model, target_provider
+if type(model_pref) == "string" then
+target_model = model_pref
+elseif type(model_pref) == "table" then
+target_model = model_pref.model
+target_provider = model_pref.provider
+end
+
+for _, instance in ipairs(instances) do
+if not matched[instance.name] then
+local inst_model = instance.options and instance.options.model
+local matches = false
+if target_provider then
+matches = (instance.provider == target_provider
+   and inst_model == target_model)
+else
+