This is an automated email from the ASF dual-hosted git repository.

hsluoyz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/casbin-gateway.git


The following commit(s) were added to refs/heads/master by this push:
     new 9bda4a2  fix(proxy): send each provider a model it serves
9bda4a2 is described below

commit 9bda4a2960a5290fed0699911330e79ecdbc1d4f
Author: Yang Luo <[email protected]>
AuthorDate: Tue Aug 25 16:50:07 2026 +0800

    fix(proxy): send each provider a model it serves
---
 controllers/proxy.go | 34 ++++++++++++++++++++++++++++++----
 object/provider.go   | 19 +++++++++++++++++++
 2 files changed, 49 insertions(+), 4 deletions(-)

diff --git a/controllers/proxy.go b/controllers/proxy.go
index 596b339..c765507 100644
--- a/controllers/proxy.go
+++ b/controllers/proxy.go
@@ -123,19 +123,45 @@ func (route *proxyRoute) passthrough(upstream 
protocol.Upstream) bool {
        return route.target.protocol == upstream.Name()
 }
 
-// upstreamBody is the request body an upstream speaking the given format 
takes.
-func (route *proxyRoute) upstreamBody(upstream protocol.Upstream) ([]byte, 
error) {
+// upstreamBody is the request body an upstream speaking the given format 
takes,
+// asking for the model that upstream's provider serves.
+func (route *proxyRoute) upstreamBody(upstream protocol.Upstream, model 
string) ([]byte, error) {
        if route.passthrough(upstream) {
-               return route.body, nil
+               if model == route.model {
+                       return route.body, nil
+               }
+               return setBodyModel(route.body, model)
        }
 
        request, err := route.canonical()
        if err != nil {
                return nil, err
        }
+       if model != request.Model {
+               // The canonical form is decoded once and forwarded to every 
provider of
+               // the chain, each of which names its models differently.
+               swapped := *request
+               swapped.Model = model
+               request = &swapped
+       }
        return upstream.EncodeRequest(request)
 }
 
+// setBodyModel rewrites the model of a body relayed as-is, leaving every other
+// field exactly as the client wrote it.
+func setBodyModel(body []byte, model string) ([]byte, error) {
+       fields := map[string]json.RawMessage{}
+       if err := json.Unmarshal(body, &fields); err != nil {
+               return nil, err
+       }
+       name, err := json.Marshal(model)
+       if err != nil {
+               return nil, err
+       }
+       fields["model"] = name
+       return json.Marshal(fields)
+}
+
 // upstreamEndpoint is the path on the provider that answers this request.
 func (route *proxyRoute) upstreamEndpoint(upstream protocol.Upstream) string {
        if route.target.countTokens {
@@ -390,7 +416,7 @@ func (c *ApiController) forwardToProvider(provider 
*object.Provider, route *prox
                return c.answerCountTokens(route)
        }
 
-       requestBody, err := route.upstreamBody(upstream)
+       requestBody, err := route.upstreamBody(upstream, 
object.ProviderModel(provider, route.model))
        if err != nil {
                return http.StatusBadRequest, err.Error(), false
        }
diff --git a/object/provider.go b/object/provider.go
index faf569a..9b1d4fb 100644
--- a/object/provider.go
+++ b/object/provider.go
@@ -822,3 +822,22 @@ func GetProvidersByModel(model string) ([]*Provider, 
error) {
        }
        return matchedProviders, nil
 }
+
+// ProviderModel is the model name to send a provider for the model the client
+// asked for. An agent picks its model on its own - Codex Desktop remembers the
+// one chosen in its own state, whatever the config file says - so a provider
+// that never heard of it answers with an error naming the models it does 
serve,
+// rather than with a completion. That agent is sent the first model of the
+// provider it is bound to instead. A provider that serves no named model, 
which
+// is the client-auth case, takes whatever arrives.
+func ProviderModel(provider *Provider, model string) string {
+       if len(provider.Models) == 0 {
+               return model
+       }
+       for _, candidate := range provider.Models {
+               if candidate == model {
+                       return model
+               }
+       }
+       return provider.Models[0]
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to