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]