feat(advanced-custom): remove fallback option and enhance path matching for advanced custom routes

This commit is contained in:
CaIon
2026-06-18 20:29:24 +08:00
parent 3f2c0aeda7
commit 55b00fcf09
22 changed files with 378 additions and 329 deletions
+4 -70
View File
@@ -32,7 +32,6 @@ type Adaptor struct {
geminiAdaptor gemini.Adaptor
resolved bool
fallback bool
converted bool
route dto.AdvancedCustomRoute
converter string
@@ -49,7 +48,7 @@ func (a *Adaptor) ConvertOpenAIRequest(c *gin.Context, info *relaycommon.RelayIn
if err != nil {
return nil, err
}
if a.fallback || converter == dto.AdvancedCustomConverterNone {
if converter == dto.AdvancedCustomConverterNone {
return a.convertOpenAICompatibleRequest(c, info, request)
}
@@ -73,9 +72,6 @@ func (a *Adaptor) ConvertClaudeRequest(c *gin.Context, info *relaycommon.RelayIn
if err != nil {
return nil, err
}
if a.fallback {
return a.convertClaudeToOpenAICompatibleRequest(c, info, request)
}
switch converter {
case dto.AdvancedCustomConverterNone:
@@ -92,9 +88,6 @@ func (a *Adaptor) ConvertGeminiRequest(c *gin.Context, info *relaycommon.RelayIn
if err != nil {
return nil, err
}
if a.fallback {
return a.convertGeminiToOpenAICompatibleRequest(c, info, request)
}
switch converter {
case dto.AdvancedCustomConverterNone:
@@ -159,11 +152,6 @@ func (a *Adaptor) GetRequestURL(info *relaycommon.RelayInfo) (string, error) {
if err := a.resolve(nil, info); err != nil {
return "", err
}
if a.fallback {
return a.withTemporaryChannelType(info, constant.ChannelTypeOpenAI, func() (string, error) {
return a.openaiAdaptor.GetRequestURL(info)
})
}
return a.routeURL(info)
}
@@ -171,13 +159,6 @@ func (a *Adaptor) SetupRequestHeader(c *gin.Context, header *http.Header, info *
if err := a.resolve(c, info); err != nil {
return err
}
if a.fallback {
old := info.ChannelType
info.ChannelType = constant.ChannelTypeOpenAI
err := a.openaiAdaptor.SetupRequestHeader(c, header, info)
info.ChannelType = old
return err
}
channel.SetupApiRequestHeader(info, c, header)
auth := a.route.Auth
@@ -205,7 +186,7 @@ func (a *Adaptor) DoRequest(c *gin.Context, info *relaycommon.RelayInfo, request
if err := a.resolve(c, info); err != nil {
return nil, err
}
if !a.converted && (a.fallback || a.converter != dto.AdvancedCustomConverterNone) {
if !a.converted && a.converter != dto.AdvancedCustomConverterNone {
return nil, errors.New("advanced custom converter routes cannot be used with pass-through request body")
}
@@ -224,9 +205,6 @@ func (a *Adaptor) DoResponse(c *gin.Context, resp *http.Response, info *relaycom
if err := a.resolve(c, info); err != nil {
return nil, types.NewOpenAIError(err, types.ErrorCodeInvalidRequest, http.StatusBadRequest, types.ErrOptionWithSkipRetry())
}
if a.fallback {
return a.openaiAdaptor.DoResponse(c, resp, info)
}
switch a.converter {
case dto.AdvancedCustomConverterNone:
@@ -295,9 +273,7 @@ func (a *Adaptor) resolve(c *gin.Context, info *relaycommon.RelayInfo) error {
}
incomingPath := incomingRequestPath(c, info)
route, ok := lo.Find(config.Routes, func(route dto.AdvancedCustomRoute) bool {
return matchIncomingPath(strings.TrimSpace(route.IncomingPath), incomingPath)
})
route, ok := config.MatchPath(incomingPath)
if ok {
route.Converter = strings.TrimSpace(route.Converter)
if route.Converter == "" {
@@ -308,13 +284,7 @@ func (a *Adaptor) resolve(c *gin.Context, info *relaycommon.RelayInfo) error {
a.resolved = true
return nil
}
if config.Fallback.Enabled {
a.fallback = true
a.converter = dto.AdvancedCustomConverterNone
a.resolved = true
return nil
}
return fmt.Errorf("advanced custom route not found for path: %s", incomingPath)
return fmt.Errorf("advanced custom channel does not support request path: %s", incomingPath)
}
func incomingRequestPath(c *gin.Context, info *relaycommon.RelayInfo) string {
@@ -327,34 +297,6 @@ func incomingRequestPath(c *gin.Context, info *relaycommon.RelayInfo) string {
return strings.Split(info.RequestURLPath, "?")[0]
}
func matchIncomingPath(configuredPath string, requestPath string) bool {
if matchIncomingPathTemplate(configuredPath, requestPath) {
return true
}
if strings.Contains(configuredPath, ":generateContent") {
streamPath := strings.Replace(configuredPath, ":generateContent", ":streamGenerateContent", 1)
return matchIncomingPathTemplate(streamPath, requestPath)
}
return false
}
func matchIncomingPathTemplate(configuredPath string, requestPath string) bool {
if !strings.Contains(configuredPath, advancedCustomModelPlaceholder) {
return configuredPath == requestPath
}
parts := strings.Split(configuredPath, advancedCustomModelPlaceholder)
if len(parts) != 2 {
return false
}
if !strings.HasPrefix(requestPath, parts[0]) || !strings.HasSuffix(requestPath, parts[1]) {
return false
}
model := strings.TrimSuffix(strings.TrimPrefix(requestPath, parts[0]), parts[1])
return model != "" && !strings.Contains(model, "/")
}
func (a *Adaptor) routeURL(info *relaycommon.RelayInfo) (string, error) {
parsedURL, err := resolveUpstreamTargetURL(applyUpstreamPathTemplate(strings.TrimSpace(a.route.UpstreamPath), info), info)
if err != nil {
@@ -535,11 +477,3 @@ func (a *Adaptor) convertOpenAICompatibleImageRequest(c *gin.Context, info *rela
info.ChannelType = old
return converted, err
}
func (a *Adaptor) withTemporaryChannelType(info *relaycommon.RelayInfo, channelType int, fn func() (string, error)) (string, error) {
old := info.ChannelType
info.ChannelType = channelType
value, err := fn()
info.ChannelType = old
return value, err
}
+2 -15
View File
@@ -161,7 +161,7 @@ func TestAdaptorSetupRequestHeaderAddsClaudeDefaultHeaders(t *testing.T) {
assert.Equal(t, "2023-06-01", header.Get("anthropic-version"))
}
func TestAdaptorReturnsErrorWhenNoRouteAndFallbackDisabled(t *testing.T) {
func TestAdaptorReturnsErrorWhenNoRouteMatchesPath(t *testing.T) {
adaptor := &Adaptor{}
info := advancedCustomRelayInfo(&dto.AdvancedCustomConfig{
Routes: []dto.AdvancedCustomRoute{
@@ -176,20 +176,7 @@ func TestAdaptorReturnsErrorWhenNoRouteAndFallbackDisabled(t *testing.T) {
_, err := adaptor.GetRequestURL(info)
require.Error(t, err)
assert.Contains(t, err.Error(), "route not found")
}
func TestAdaptorFallbackUsesOpenAICompatibleBaseURL(t *testing.T) {
adaptor := &Adaptor{}
info := advancedCustomRelayInfo(&dto.AdvancedCustomConfig{
Fallback: dto.AdvancedCustomFallback{Enabled: true},
})
info.RequestURLPath = "/v1/messages"
info.RelayFormat = types.RelayFormatClaude
requestURL, err := adaptor.GetRequestURL(info)
require.NoError(t, err)
assert.Equal(t, "https://fallback.example/v1/chat/completions", requestURL)
assert.Contains(t, err.Error(), "does not support request path")
}
func TestAdaptorReplacesModelPlaceholderInRouteURL(t *testing.T) {