chore: upgrade CLIProxyAPI dependency to v7 across the project
- Updated all references from v6 to v7 for `github.com/router-for-me/CLIProxyAPI`. - Ensured consistency in imports within core libraries, tests, and integration tests. - Added missing tests for new features in Redis Protocol integration.
This commit is contained in:
@@ -16,10 +16,10 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
. "github.com/router-for-me/CLIProxyAPI/v6/internal/constant"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/interfaces"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/registry"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/sdk/api/handlers"
|
||||
. "github.com/router-for-me/CLIProxyAPI/v7/internal/constant"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/interfaces"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/registry"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/sdk/api/handlers"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
@@ -15,10 +15,10 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
. "github.com/router-for-me/CLIProxyAPI/v6/internal/constant"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/interfaces"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/util"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/sdk/api/handlers"
|
||||
. "github.com/router-for-me/CLIProxyAPI/v7/internal/constant"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/interfaces"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/util"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/sdk/api/handlers"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
@@ -13,10 +13,10 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
. "github.com/router-for-me/CLIProxyAPI/v6/internal/constant"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/interfaces"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/registry"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/sdk/api/handlers"
|
||||
. "github.com/router-for-me/CLIProxyAPI/v7/internal/constant"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/interfaces"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/registry"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/sdk/api/handlers"
|
||||
)
|
||||
|
||||
// GeminiAPIHandler contains the handlers for Gemini API endpoints.
|
||||
|
||||
@@ -14,14 +14,14 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/interfaces"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/logging"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/thinking"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/util"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
|
||||
coreexecutor "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/executor"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/sdk/config"
|
||||
sdktranslator "github.com/router-for-me/CLIProxyAPI/v6/sdk/translator"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/interfaces"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/logging"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/thinking"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/util"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/auth"
|
||||
coreexecutor "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/executor"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/sdk/config"
|
||||
sdktranslator "github.com/router-for-me/CLIProxyAPI/v7/sdk/translator"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
@@ -850,14 +850,22 @@ func (h *BaseAPIHandler) getRequestDetails(modelName string) (providers []string
|
||||
resolvedModelName := modelName
|
||||
initialSuffix := thinking.ParseSuffix(modelName)
|
||||
if initialSuffix.ModelName == "auto" {
|
||||
resolvedBase := util.ResolveAutoModel(initialSuffix.ModelName)
|
||||
if initialSuffix.HasSuffix {
|
||||
resolvedModelName = fmt.Sprintf("%s(%s)", resolvedBase, initialSuffix.RawSuffix)
|
||||
if h != nil && h.AuthManager != nil && h.AuthManager.HomeEnabled() {
|
||||
resolvedModelName = modelName
|
||||
} else {
|
||||
resolvedModelName = resolvedBase
|
||||
resolvedBase := util.ResolveAutoModel(initialSuffix.ModelName)
|
||||
if initialSuffix.HasSuffix {
|
||||
resolvedModelName = fmt.Sprintf("%s(%s)", resolvedBase, initialSuffix.RawSuffix)
|
||||
} else {
|
||||
resolvedModelName = resolvedBase
|
||||
}
|
||||
}
|
||||
} else {
|
||||
resolvedModelName = util.ResolveAutoModel(modelName)
|
||||
if h != nil && h.AuthManager != nil && h.AuthManager.HomeEnabled() {
|
||||
resolvedModelName = modelName
|
||||
} else {
|
||||
resolvedModelName = util.ResolveAutoModel(modelName)
|
||||
}
|
||||
}
|
||||
|
||||
parsed := thinking.ParseSuffix(resolvedModelName)
|
||||
@@ -870,6 +878,10 @@ func (h *BaseAPIHandler) getRequestDetails(modelName string) (providers []string
|
||||
}
|
||||
}
|
||||
|
||||
if h != nil && h.AuthManager != nil && h.AuthManager.HomeEnabled() {
|
||||
return []string{"home"}, resolvedModelName, nil
|
||||
}
|
||||
|
||||
providers = util.GetProviderName(baseModel)
|
||||
// Fallback: if baseModel has no provider but differs from resolvedModelName,
|
||||
// try using the full model name. This handles edge cases where custom models
|
||||
|
||||
@@ -9,9 +9,9 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/interfaces"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
|
||||
sdkconfig "github.com/router-for-me/CLIProxyAPI/v6/sdk/config"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/interfaces"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/auth"
|
||||
sdkconfig "github.com/router-for-me/CLIProxyAPI/v7/sdk/config"
|
||||
)
|
||||
|
||||
func TestWriteErrorResponse_AddonHeadersDisabledByDefault(t *testing.T) {
|
||||
|
||||
@@ -3,7 +3,7 @@ package handlers
|
||||
import (
|
||||
"testing"
|
||||
|
||||
coreexecutor "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/executor"
|
||||
coreexecutor "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/executor"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
|
||||
@@ -7,9 +7,9 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/registry"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
|
||||
sdkconfig "github.com/router-for-me/CLIProxyAPI/v6/sdk/config"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/registry"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/auth"
|
||||
sdkconfig "github.com/router-for-me/CLIProxyAPI/v7/sdk/config"
|
||||
)
|
||||
|
||||
func TestGetRequestDetails_PreservesSuffix(t *testing.T) {
|
||||
|
||||
@@ -8,11 +8,11 @@ import (
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/interfaces"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/registry"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
|
||||
coreexecutor "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/executor"
|
||||
sdkconfig "github.com/router-for-me/CLIProxyAPI/v6/sdk/config"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/interfaces"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/registry"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/auth"
|
||||
coreexecutor "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/executor"
|
||||
sdkconfig "github.com/router-for-me/CLIProxyAPI/v7/sdk/config"
|
||||
)
|
||||
|
||||
type failOnceStreamExecutor struct {
|
||||
|
||||
@@ -14,11 +14,11 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
. "github.com/router-for-me/CLIProxyAPI/v6/internal/constant"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/interfaces"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/registry"
|
||||
responsesconverter "github.com/router-for-me/CLIProxyAPI/v6/internal/translator/openai/openai/responses"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/sdk/api/handlers"
|
||||
. "github.com/router-for-me/CLIProxyAPI/v7/internal/constant"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/interfaces"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/registry"
|
||||
responsesconverter "github.com/router-for-me/CLIProxyAPI/v7/internal/translator/openai/openai/responses"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/sdk/api/handlers"
|
||||
"github.com/tidwall/gjson"
|
||||
"github.com/tidwall/sjson"
|
||||
)
|
||||
|
||||
@@ -14,9 +14,9 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
internalconfig "github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/interfaces"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/sdk/api/handlers"
|
||||
internalconfig "github.com/router-for-me/CLIProxyAPI/v7/internal/config"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/interfaces"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/sdk/api/handlers"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/tidwall/gjson"
|
||||
"github.com/tidwall/sjson"
|
||||
|
||||
@@ -10,9 +10,9 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
internalconfig "github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/sdk/api/handlers"
|
||||
sdkconfig "github.com/router-for-me/CLIProxyAPI/v6/sdk/config"
|
||||
internalconfig "github.com/router-for-me/CLIProxyAPI/v7/internal/config"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/sdk/api/handlers"
|
||||
sdkconfig "github.com/router-for-me/CLIProxyAPI/v7/sdk/config"
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
|
||||
@@ -9,11 +9,11 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/registry"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/sdk/api/handlers"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
|
||||
coreexecutor "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/executor"
|
||||
sdkconfig "github.com/router-for-me/CLIProxyAPI/v6/sdk/config"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/registry"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/sdk/api/handlers"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/auth"
|
||||
coreexecutor "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/executor"
|
||||
sdkconfig "github.com/router-for-me/CLIProxyAPI/v7/sdk/config"
|
||||
)
|
||||
|
||||
type compactCaptureExecutor struct {
|
||||
|
||||
@@ -16,10 +16,10 @@ import (
|
||||
"sort"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
. "github.com/router-for-me/CLIProxyAPI/v6/internal/constant"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/interfaces"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/registry"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/sdk/api/handlers"
|
||||
. "github.com/router-for-me/CLIProxyAPI/v7/internal/constant"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/interfaces"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/registry"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/sdk/api/handlers"
|
||||
"github.com/tidwall/gjson"
|
||||
"github.com/tidwall/sjson"
|
||||
)
|
||||
|
||||
@@ -8,9 +8,9 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/interfaces"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/sdk/api/handlers"
|
||||
sdkconfig "github.com/router-for-me/CLIProxyAPI/v6/sdk/config"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/interfaces"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/sdk/api/handlers"
|
||||
sdkconfig "github.com/router-for-me/CLIProxyAPI/v7/sdk/config"
|
||||
)
|
||||
|
||||
func TestForwardResponsesStreamTerminalErrorUsesResponsesErrorChunk(t *testing.T) {
|
||||
|
||||
@@ -7,9 +7,9 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/interfaces"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/sdk/api/handlers"
|
||||
sdkconfig "github.com/router-for-me/CLIProxyAPI/v6/sdk/config"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/interfaces"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/sdk/api/handlers"
|
||||
sdkconfig "github.com/router-for-me/CLIProxyAPI/v7/sdk/config"
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
|
||||
@@ -13,13 +13,13 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/interfaces"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/registry"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/thinking"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/util"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/sdk/api/handlers"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
|
||||
cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/executor"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/interfaces"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/registry"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/thinking"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/util"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/sdk/api/handlers"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/auth"
|
||||
cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/executor"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/tidwall/gjson"
|
||||
"github.com/tidwall/sjson"
|
||||
|
||||
@@ -14,12 +14,12 @@ import (
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/interfaces"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/registry"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/sdk/api/handlers"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
|
||||
coreexecutor "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/executor"
|
||||
sdkconfig "github.com/router-for-me/CLIProxyAPI/v6/sdk/config"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/interfaces"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/registry"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/sdk/api/handlers"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/auth"
|
||||
coreexecutor "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/executor"
|
||||
sdkconfig "github.com/router-for-me/CLIProxyAPI/v7/sdk/config"
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/interfaces"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/interfaces"
|
||||
)
|
||||
|
||||
type StreamForwardOptions struct {
|
||||
|
||||
@@ -6,9 +6,9 @@ package api
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
internalmanagement "github.com/router-for-me/CLIProxyAPI/v6/internal/api/handlers/management"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/sdk/config"
|
||||
internalmanagement "github.com/router-for-me/CLIProxyAPI/v7/internal/api/handlers/management"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/auth"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/sdk/config"
|
||||
)
|
||||
|
||||
// ManagementTokenRequester exposes a limited subset of management endpoints for requesting tokens.
|
||||
|
||||
+4
-4
@@ -8,10 +8,10 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
internalapi "github.com/router-for-me/CLIProxyAPI/v6/internal/api"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/sdk/api/handlers"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/sdk/config"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/sdk/logging"
|
||||
internalapi "github.com/router-for-me/CLIProxyAPI/v7/internal/api"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/sdk/api/handlers"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/sdk/config"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/sdk/logging"
|
||||
)
|
||||
|
||||
// ServerOption customises HTTP server construction.
|
||||
|
||||
@@ -8,12 +8,12 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/auth/antigravity"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/browser"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/misc"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/util"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/auth/antigravity"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/browser"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/config"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/misc"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/util"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/auth"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
|
||||
+6
-6
@@ -7,13 +7,13 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/auth/claude"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/browser"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/auth/claude"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/browser"
|
||||
// legacy client removed
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/misc"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/util"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/config"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/misc"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/util"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/auth"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
|
||||
+6
-6
@@ -7,13 +7,13 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/auth/codex"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/browser"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/auth/codex"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/browser"
|
||||
// legacy client removed
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/misc"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/util"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/config"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/misc"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/util"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/auth"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
|
||||
@@ -13,11 +13,11 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/auth/codex"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/browser"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/util"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/auth/codex"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/browser"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/config"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/util"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/auth"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ package auth
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/interfaces"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/interfaces"
|
||||
)
|
||||
|
||||
// ProjectSelectionError indicates that the user must choose a specific project ID.
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
cliproxyauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
|
||||
cliproxyauth "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/auth"
|
||||
)
|
||||
|
||||
// FileTokenStore persists token records and auth metadata using the filesystem as backing storage.
|
||||
|
||||
+3
-3
@@ -5,10 +5,10 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/auth/gemini"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/auth/gemini"
|
||||
// legacy client removed
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/config"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/auth"
|
||||
)
|
||||
|
||||
// GeminiAuthenticator implements the login flow for Google Gemini CLI accounts.
|
||||
|
||||
@@ -5,8 +5,8 @@ import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/config"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/auth"
|
||||
)
|
||||
|
||||
var ErrRefreshNotSupported = errors.New("cliproxy auth: refresh not supported")
|
||||
|
||||
+4
-4
@@ -6,10 +6,10 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/auth/kimi"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/browser"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/auth/kimi"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/browser"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/config"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/auth"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
|
||||
+2
-2
@@ -4,8 +4,8 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/config"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/auth"
|
||||
)
|
||||
|
||||
// Manager aggregates authenticators and coordinates persistence via a token store.
|
||||
|
||||
@@ -3,7 +3,7 @@ package auth
|
||||
import (
|
||||
"time"
|
||||
|
||||
cliproxyauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
|
||||
cliproxyauth "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/auth"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -3,7 +3,7 @@ package auth
|
||||
import (
|
||||
"sync"
|
||||
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/auth"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -7,9 +7,9 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
internalconfig "github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/registry"
|
||||
cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/executor"
|
||||
internalconfig "github.com/router-for-me/CLIProxyAPI/v7/internal/config"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/registry"
|
||||
cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/executor"
|
||||
)
|
||||
|
||||
type antigravityCreditsFallbackExecutor struct {
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
internalconfig "github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
||||
internalconfig "github.com/router-for-me/CLIProxyAPI/v7/internal/config"
|
||||
)
|
||||
|
||||
func TestLookupAPIKeyUpstreamModel(t *testing.T) {
|
||||
|
||||
@@ -16,13 +16,14 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
internalconfig "github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/logging"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/registry"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/thinking"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/util"
|
||||
cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/executor"
|
||||
coreusage "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/usage"
|
||||
internalconfig "github.com/router-for-me/CLIProxyAPI/v7/internal/config"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/home"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/logging"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/registry"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/thinking"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/util"
|
||||
cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/executor"
|
||||
coreusage "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/usage"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@@ -377,6 +378,15 @@ func (m *Manager) SetConfig(cfg *internalconfig.Config) {
|
||||
m.rebuildAPIKeyModelAliasFromRuntimeConfig()
|
||||
}
|
||||
|
||||
// HomeEnabled reports whether the home control plane integration is enabled in the runtime config.
|
||||
func (m *Manager) HomeEnabled() bool {
|
||||
if m == nil {
|
||||
return false
|
||||
}
|
||||
cfg, _ := m.runtimeConfig.Load().(*internalconfig.Config)
|
||||
return cfg != nil && cfg.Home.Enabled
|
||||
}
|
||||
|
||||
func (m *Manager) lookupAPIKeyUpstreamModel(authID, requestedModel string) string {
|
||||
if m == nil {
|
||||
return ""
|
||||
@@ -522,6 +532,11 @@ func preserveRequestedModelSuffix(requestedModel, resolved string) string {
|
||||
}
|
||||
|
||||
func (m *Manager) executionModelCandidates(auth *Auth, routeModel string) []string {
|
||||
if auth != nil && auth.Attributes != nil {
|
||||
if homeModel := strings.TrimSpace(auth.Attributes[homeUpstreamModelAttributeKey]); homeModel != "" {
|
||||
return []string{homeModel}
|
||||
}
|
||||
}
|
||||
requestedModel := rewriteModelForAuth(routeModel, auth)
|
||||
requestedModel = m.applyOAuthModelAlias(auth, requestedModel)
|
||||
if pool := m.resolveOpenAICompatUpstreamModelPool(auth, requestedModel); len(pool) > 0 {
|
||||
@@ -555,6 +570,14 @@ func (m *Manager) selectionModelKeyForAuth(auth *Auth, routeModel string) string
|
||||
}
|
||||
|
||||
func (m *Manager) stateModelForExecution(auth *Auth, routeModel, upstreamModel string, pooled bool) string {
|
||||
if auth != nil && auth.Attributes != nil {
|
||||
if homeModel := strings.TrimSpace(auth.Attributes[homeUpstreamModelAttributeKey]); homeModel != "" {
|
||||
if resolved := strings.TrimSpace(upstreamModel); resolved != "" {
|
||||
return resolved
|
||||
}
|
||||
return homeModel
|
||||
}
|
||||
}
|
||||
stateModel := executionResultModel(routeModel, upstreamModel, pooled)
|
||||
selectionModel := m.selectionModelForAuth(auth, routeModel)
|
||||
if canonicalModelKey(selectionModel) == canonicalModelKey(upstreamModel) && strings.TrimSpace(selectionModel) != "" {
|
||||
@@ -2710,6 +2733,11 @@ func (m *Manager) routeAwareSelectionRequired(auth *Auth, routeModel string) boo
|
||||
}
|
||||
|
||||
func (m *Manager) pickNextLegacy(ctx context.Context, provider, model string, opts cliproxyexecutor.Options, tried map[string]struct{}) (*Auth, ProviderExecutor, error) {
|
||||
if m.HomeEnabled() {
|
||||
auth, exec, _, err := m.pickNextViaHome(ctx, model, opts)
|
||||
return auth, exec, err
|
||||
}
|
||||
|
||||
pinnedAuthID := pinnedAuthIDFromMetadata(opts.Metadata)
|
||||
disallowFreeAuth := disallowFreeAuthFromMetadata(opts.Metadata)
|
||||
|
||||
@@ -2779,6 +2807,11 @@ func (m *Manager) pickNextLegacy(ctx context.Context, provider, model string, op
|
||||
}
|
||||
|
||||
func (m *Manager) pickNext(ctx context.Context, provider, model string, opts cliproxyexecutor.Options, tried map[string]struct{}) (*Auth, ProviderExecutor, error) {
|
||||
if m.HomeEnabled() {
|
||||
auth, exec, _, err := m.pickNextViaHome(ctx, model, opts)
|
||||
return auth, exec, err
|
||||
}
|
||||
|
||||
if !m.useSchedulerFastPath() {
|
||||
return m.pickNextLegacy(ctx, provider, model, opts, tried)
|
||||
}
|
||||
@@ -2836,6 +2869,10 @@ func (m *Manager) pickNext(ctx context.Context, provider, model string, opts cli
|
||||
}
|
||||
|
||||
func (m *Manager) pickNextMixedLegacy(ctx context.Context, providers []string, model string, opts cliproxyexecutor.Options, tried map[string]struct{}) (*Auth, ProviderExecutor, string, error) {
|
||||
if m.HomeEnabled() {
|
||||
return m.pickNextViaHome(ctx, model, opts)
|
||||
}
|
||||
|
||||
pinnedAuthID := pinnedAuthIDFromMetadata(opts.Metadata)
|
||||
disallowFreeAuth := disallowFreeAuthFromMetadata(opts.Metadata)
|
||||
|
||||
@@ -2928,6 +2965,10 @@ func (m *Manager) pickNextMixedLegacy(ctx context.Context, providers []string, m
|
||||
}
|
||||
|
||||
func (m *Manager) pickNextMixed(ctx context.Context, providers []string, model string, opts cliproxyexecutor.Options, tried map[string]struct{}) (*Auth, ProviderExecutor, string, error) {
|
||||
if m.HomeEnabled() {
|
||||
return m.pickNextViaHome(ctx, model, opts)
|
||||
}
|
||||
|
||||
if !m.useSchedulerFastPath() {
|
||||
return m.pickNextMixedLegacy(ctx, providers, model, opts, tried)
|
||||
}
|
||||
@@ -3012,6 +3053,148 @@ func (m *Manager) pickNextMixed(ctx context.Context, providers []string, model s
|
||||
}
|
||||
}
|
||||
|
||||
type homeErrorEnvelope struct {
|
||||
Error *homeErrorDetail `json:"error"`
|
||||
}
|
||||
|
||||
type homeErrorDetail struct {
|
||||
Type string `json:"type"`
|
||||
Message string `json:"message"`
|
||||
Code string `json:"code,omitempty"`
|
||||
}
|
||||
|
||||
const homeUpstreamModelAttributeKey = "home_upstream_model"
|
||||
|
||||
type homeAuthDispatchResponse struct {
|
||||
Model string `json:"model"`
|
||||
Provider string `json:"provider"`
|
||||
AuthIndex string `json:"auth_index"`
|
||||
UserAPIKey string `json:"user_api_key"`
|
||||
Auth Auth `json:"auth"`
|
||||
}
|
||||
|
||||
func setHomeUserAPIKeyOnGinContext(ctx context.Context, apiKey string) {
|
||||
apiKey = strings.TrimSpace(apiKey)
|
||||
if apiKey == "" || ctx == nil {
|
||||
return
|
||||
}
|
||||
ginCtx, ok := ctx.Value("gin").(interface{ Set(string, any) })
|
||||
if !ok || ginCtx == nil {
|
||||
return
|
||||
}
|
||||
ginCtx.Set("userApiKey", apiKey)
|
||||
}
|
||||
|
||||
func (m *Manager) pickNextViaHome(ctx context.Context, model string, opts cliproxyexecutor.Options) (*Auth, ProviderExecutor, string, error) {
|
||||
if m == nil {
|
||||
return nil, nil, "", &Error{Code: "auth_not_found", Message: "no auth available"}
|
||||
}
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
client := home.Current()
|
||||
if client == nil || !client.HeartbeatOK() {
|
||||
return nil, nil, "", &Error{Code: "home_unavailable", Message: "home control center unavailable", HTTPStatus: http.StatusServiceUnavailable}
|
||||
}
|
||||
|
||||
requestedModel := requestedModelFromMetadata(opts.Metadata, model)
|
||||
sessionID := ExtractSessionID(opts.Headers, opts.OriginalRequest, opts.Metadata)
|
||||
|
||||
raw, err := client.RPopAuth(ctx, requestedModel, sessionID, opts.Headers)
|
||||
if err != nil {
|
||||
return nil, nil, "", &Error{Code: "auth_not_found", Message: err.Error(), HTTPStatus: http.StatusServiceUnavailable}
|
||||
}
|
||||
|
||||
var env homeErrorEnvelope
|
||||
if errUnmarshal := json.Unmarshal(raw, &env); errUnmarshal == nil && env.Error != nil {
|
||||
code := strings.TrimSpace(env.Error.Type)
|
||||
if code == "" {
|
||||
code = strings.TrimSpace(env.Error.Code)
|
||||
}
|
||||
msg := strings.TrimSpace(env.Error.Message)
|
||||
if msg == "" {
|
||||
msg = "home returned error"
|
||||
}
|
||||
status := http.StatusBadGateway
|
||||
switch strings.ToLower(code) {
|
||||
case "model_not_found":
|
||||
status = http.StatusNotFound
|
||||
case "authentication_error", "unauthorized":
|
||||
status = http.StatusUnauthorized
|
||||
}
|
||||
return nil, nil, "", &Error{Code: code, Message: msg, HTTPStatus: status}
|
||||
}
|
||||
|
||||
var dispatch homeAuthDispatchResponse
|
||||
if errUnmarshal := json.Unmarshal(raw, &dispatch); errUnmarshal != nil {
|
||||
return nil, nil, "", &Error{Code: "invalid_auth", Message: "home returned invalid auth payload", HTTPStatus: http.StatusBadGateway}
|
||||
}
|
||||
setHomeUserAPIKeyOnGinContext(ctx, dispatch.UserAPIKey)
|
||||
auth := dispatch.Auth
|
||||
if strings.TrimSpace(auth.ID) == "" {
|
||||
// Backward compatibility: older home instances returned the auth directly.
|
||||
if errUnmarshal := json.Unmarshal(raw, &auth); errUnmarshal != nil {
|
||||
return nil, nil, "", &Error{Code: "invalid_auth", Message: "home returned invalid auth payload", HTTPStatus: http.StatusBadGateway}
|
||||
}
|
||||
}
|
||||
if upstreamModel := strings.TrimSpace(dispatch.Model); upstreamModel != "" {
|
||||
if auth.Attributes == nil {
|
||||
auth.Attributes = make(map[string]string, 1)
|
||||
}
|
||||
auth.Attributes[homeUpstreamModelAttributeKey] = upstreamModel
|
||||
}
|
||||
if strings.TrimSpace(auth.ID) == "" {
|
||||
return nil, nil, "", &Error{Code: "invalid_auth", Message: "home returned auth without id", HTTPStatus: http.StatusBadGateway}
|
||||
}
|
||||
providerKey := strings.ToLower(strings.TrimSpace(auth.Provider))
|
||||
if providerKey == "" {
|
||||
return nil, nil, "", &Error{Code: "invalid_auth", Message: "home returned auth without provider", HTTPStatus: http.StatusBadGateway}
|
||||
}
|
||||
|
||||
homeAuthIndex := strings.TrimSpace(dispatch.AuthIndex)
|
||||
if homeAuthIndex != "" {
|
||||
auth.Index = homeAuthIndex
|
||||
auth.indexAssigned = true
|
||||
} else {
|
||||
auth.EnsureIndex()
|
||||
}
|
||||
|
||||
executor, ok := m.Executor(providerKey)
|
||||
if !ok && auth.Attributes != nil && strings.TrimSpace(auth.Attributes["base_url"]) != "" {
|
||||
executor, ok = m.Executor("openai-compatibility")
|
||||
if ok {
|
||||
providerKey = "openai-compatibility"
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
return nil, nil, "", &Error{Code: "executor_not_found", Message: "executor not registered", HTTPStatus: http.StatusBadGateway}
|
||||
}
|
||||
|
||||
return auth.Clone(), executor, providerKey, nil
|
||||
}
|
||||
|
||||
func requestedModelFromMetadata(metadata map[string]any, fallback string) string {
|
||||
if metadata != nil {
|
||||
if v, ok := metadata[cliproxyexecutor.RequestedModelMetadataKey]; ok {
|
||||
switch typed := v.(type) {
|
||||
case string:
|
||||
if trimmed := strings.TrimSpace(typed); trimmed != "" {
|
||||
return trimmed
|
||||
}
|
||||
case []byte:
|
||||
if trimmed := strings.TrimSpace(string(typed)); trimmed != "" {
|
||||
return trimmed
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fallback = strings.TrimSpace(fallback)
|
||||
if fallback == "" {
|
||||
return "unknown"
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
|
||||
func (m *Manager) findAllAntigravityCreditsCandidateAuths(routeModel string, opts cliproxyexecutor.Options) []creditsCandidateEntry {
|
||||
if m == nil {
|
||||
return nil
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/executor"
|
||||
cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/executor"
|
||||
)
|
||||
|
||||
func TestFindAllAntigravityCreditsCandidateAuths_PrefersKnownCreditsThenUnknown(t *testing.T) {
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/executor"
|
||||
cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/executor"
|
||||
)
|
||||
|
||||
type replaceAwareExecutor struct {
|
||||
|
||||
@@ -7,10 +7,10 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
internalconfig "github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/registry"
|
||||
cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/executor"
|
||||
coreusage "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/usage"
|
||||
internalconfig "github.com/router-for-me/CLIProxyAPI/v7/internal/config"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/registry"
|
||||
cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/executor"
|
||||
coreusage "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/usage"
|
||||
)
|
||||
|
||||
type aliasRoutingExecutor struct {
|
||||
|
||||
@@ -8,9 +8,9 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
internalconfig "github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/registry"
|
||||
cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/executor"
|
||||
internalconfig "github.com/router-for-me/CLIProxyAPI/v7/internal/config"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/registry"
|
||||
cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/executor"
|
||||
)
|
||||
|
||||
const requestScopedNotFoundMessage = "Item with id 'rs_0b5f3eb6f51f175c0169ca74e4a85881998539920821603a74' not found. Items are not persisted when `store` is set to false. Try again with `store` set to true, or remove this item from your input."
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/registry"
|
||||
cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/executor"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/registry"
|
||||
cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/executor"
|
||||
)
|
||||
|
||||
type schedulerProviderTestExecutor struct {
|
||||
|
||||
@@ -3,8 +3,8 @@ package auth
|
||||
import (
|
||||
"strings"
|
||||
|
||||
internalconfig "github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/thinking"
|
||||
internalconfig "github.com/router-for-me/CLIProxyAPI/v7/internal/config"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/thinking"
|
||||
)
|
||||
|
||||
type modelAliasEntry interface {
|
||||
|
||||
@@ -3,7 +3,7 @@ package auth
|
||||
import (
|
||||
"testing"
|
||||
|
||||
internalconfig "github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
||||
internalconfig "github.com/router-for-me/CLIProxyAPI/v7/internal/config"
|
||||
)
|
||||
|
||||
func TestResolveOAuthUpstreamModel_SuffixPreservation(t *testing.T) {
|
||||
|
||||
@@ -7,9 +7,9 @@ import (
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
internalconfig "github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/registry"
|
||||
cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/executor"
|
||||
internalconfig "github.com/router-for-me/CLIProxyAPI/v7/internal/config"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/registry"
|
||||
cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/executor"
|
||||
)
|
||||
|
||||
type openAICompatPoolExecutor struct {
|
||||
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/registry"
|
||||
cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/executor"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/registry"
|
||||
cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/executor"
|
||||
)
|
||||
|
||||
// schedulerStrategy identifies which built-in routing semantics the scheduler should apply.
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/registry"
|
||||
cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/executor"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/registry"
|
||||
cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/executor"
|
||||
)
|
||||
|
||||
type schedulerBenchmarkExecutor struct {
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/registry"
|
||||
cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/executor"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/registry"
|
||||
cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/executor"
|
||||
)
|
||||
|
||||
type schedulerTestExecutor struct{}
|
||||
|
||||
@@ -18,9 +18,9 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/tidwall/gjson"
|
||||
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/logging"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/thinking"
|
||||
cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/executor"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/logging"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/thinking"
|
||||
cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/executor"
|
||||
)
|
||||
|
||||
// RoundRobinSelector provides a simple provider scoped round-robin selection strategy.
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/executor"
|
||||
cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/executor"
|
||||
)
|
||||
|
||||
func TestFillFirstSelectorPick_Deterministic(t *testing.T) {
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
baseauth "github.com/router-for-me/CLIProxyAPI/v6/internal/auth"
|
||||
baseauth "github.com/router-for-me/CLIProxyAPI/v7/internal/auth"
|
||||
)
|
||||
|
||||
// PostAuthHook defines a function that is called after an Auth record is created
|
||||
|
||||
@@ -8,12 +8,12 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
configaccess "github.com/router-for-me/CLIProxyAPI/v6/internal/access/config_access"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/api"
|
||||
sdkaccess "github.com/router-for-me/CLIProxyAPI/v6/sdk/access"
|
||||
sdkAuth "github.com/router-for-me/CLIProxyAPI/v6/sdk/auth"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/sdk/config"
|
||||
configaccess "github.com/router-for-me/CLIProxyAPI/v7/internal/access/config_access"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/api"
|
||||
sdkaccess "github.com/router-for-me/CLIProxyAPI/v7/sdk/access"
|
||||
sdkAuth "github.com/router-for-me/CLIProxyAPI/v7/sdk/auth"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/auth"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/sdk/config"
|
||||
)
|
||||
|
||||
// Builder constructs a Service instance with customizable providers.
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
sdktranslator "github.com/router-for-me/CLIProxyAPI/v6/sdk/translator"
|
||||
sdktranslator "github.com/router-for-me/CLIProxyAPI/v7/sdk/translator"
|
||||
)
|
||||
|
||||
// RequestedModelMetadataKey stores the client-requested model name in Options.Metadata.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package cliproxy
|
||||
|
||||
import "github.com/router-for-me/CLIProxyAPI/v6/internal/registry"
|
||||
import "github.com/router-for-me/CLIProxyAPI/v7/internal/registry"
|
||||
|
||||
// ModelInfo re-exports the registry model info structure.
|
||||
type ModelInfo = registry.ModelInfo
|
||||
|
||||
@@ -4,9 +4,9 @@ import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
cliproxyauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
|
||||
cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/executor"
|
||||
sdktranslator "github.com/router-for-me/CLIProxyAPI/v6/sdk/translator"
|
||||
cliproxyauth "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/auth"
|
||||
cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/executor"
|
||||
sdktranslator "github.com/router-for-me/CLIProxyAPI/v7/sdk/translator"
|
||||
)
|
||||
|
||||
// Context encapsulates execution state shared across middleware, translators, and executors.
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/config"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@ package cliproxy
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/watcher"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/sdk/config"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/watcher"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/sdk/config"
|
||||
)
|
||||
|
||||
// NewFileTokenClientProvider returns the default token-backed client loader.
|
||||
|
||||
@@ -5,8 +5,8 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/sdk/proxyutil"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/auth"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/sdk/proxyutil"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/auth"
|
||||
)
|
||||
|
||||
func TestRoundTripperForDirectBypassesProxy(t *testing.T) {
|
||||
|
||||
+325
-124
@@ -12,17 +12,18 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/api"
|
||||
_ "github.com/router-for-me/CLIProxyAPI/v6/internal/redisqueue"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/registry"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/runtime/executor"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/watcher"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/wsrelay"
|
||||
sdkaccess "github.com/router-for-me/CLIProxyAPI/v6/sdk/access"
|
||||
sdkAuth "github.com/router-for-me/CLIProxyAPI/v6/sdk/auth"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/usage"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/sdk/config"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/api"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/home"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/redisqueue"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/registry"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/runtime/executor"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/watcher"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/wsrelay"
|
||||
sdkaccess "github.com/router-for-me/CLIProxyAPI/v7/sdk/access"
|
||||
sdkAuth "github.com/router-for-me/CLIProxyAPI/v7/sdk/auth"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/auth"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/usage"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/sdk/config"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@@ -36,6 +37,9 @@ type Service struct {
|
||||
// cfgMu protects concurrent access to the configuration.
|
||||
cfgMu sync.RWMutex
|
||||
|
||||
// configUpdateMu serializes config updates across watcher + home.
|
||||
configUpdateMu sync.Mutex
|
||||
|
||||
// configPath is the path to the configuration file.
|
||||
configPath string
|
||||
|
||||
@@ -89,6 +93,9 @@ type Service struct {
|
||||
|
||||
// wsGateway manages websocket Gemini providers.
|
||||
wsGateway *wsrelay.Manager
|
||||
|
||||
homeClient *home.Client
|
||||
homeCancel context.CancelFunc
|
||||
}
|
||||
|
||||
// RegisterUsagePlugin registers a usage plugin on the global usage manager.
|
||||
@@ -462,6 +469,248 @@ func (s *Service) rebindExecutors() {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) applyConfigUpdate(newCfg *config.Config) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
|
||||
s.configUpdateMu.Lock()
|
||||
defer s.configUpdateMu.Unlock()
|
||||
|
||||
previousStrategy := ""
|
||||
var previousSessionAffinity bool
|
||||
var previousSessionAffinityTTL string
|
||||
s.cfgMu.RLock()
|
||||
if s.cfg != nil {
|
||||
previousStrategy = strings.ToLower(strings.TrimSpace(s.cfg.Routing.Strategy))
|
||||
previousSessionAffinity = s.cfg.Routing.ClaudeCodeSessionAffinity || s.cfg.Routing.SessionAffinity
|
||||
previousSessionAffinityTTL = s.cfg.Routing.SessionAffinityTTL
|
||||
}
|
||||
s.cfgMu.RUnlock()
|
||||
|
||||
if newCfg == nil {
|
||||
s.cfgMu.RLock()
|
||||
newCfg = s.cfg
|
||||
s.cfgMu.RUnlock()
|
||||
}
|
||||
if newCfg == nil {
|
||||
return
|
||||
}
|
||||
|
||||
nextStrategy := strings.ToLower(strings.TrimSpace(newCfg.Routing.Strategy))
|
||||
normalizeStrategy := func(strategy string) string {
|
||||
switch strategy {
|
||||
case "fill-first", "fillfirst", "ff":
|
||||
return "fill-first"
|
||||
default:
|
||||
return "round-robin"
|
||||
}
|
||||
}
|
||||
previousStrategy = normalizeStrategy(previousStrategy)
|
||||
nextStrategy = normalizeStrategy(nextStrategy)
|
||||
|
||||
nextSessionAffinity := newCfg.Routing.ClaudeCodeSessionAffinity || newCfg.Routing.SessionAffinity
|
||||
nextSessionAffinityTTL := newCfg.Routing.SessionAffinityTTL
|
||||
|
||||
selectorChanged := previousStrategy != nextStrategy ||
|
||||
previousSessionAffinity != nextSessionAffinity ||
|
||||
previousSessionAffinityTTL != nextSessionAffinityTTL
|
||||
|
||||
if s.coreManager != nil && selectorChanged {
|
||||
var selector coreauth.Selector
|
||||
switch nextStrategy {
|
||||
case "fill-first":
|
||||
selector = &coreauth.FillFirstSelector{}
|
||||
default:
|
||||
selector = &coreauth.RoundRobinSelector{}
|
||||
}
|
||||
|
||||
if nextSessionAffinity {
|
||||
ttl := time.Hour
|
||||
if ttlStr := strings.TrimSpace(nextSessionAffinityTTL); ttlStr != "" {
|
||||
if parsed, err := time.ParseDuration(ttlStr); err == nil && parsed > 0 {
|
||||
ttl = parsed
|
||||
}
|
||||
}
|
||||
selector = coreauth.NewSessionAffinitySelectorWithConfig(coreauth.SessionAffinityConfig{
|
||||
Fallback: selector,
|
||||
TTL: ttl,
|
||||
})
|
||||
}
|
||||
|
||||
s.coreManager.SetSelector(selector)
|
||||
}
|
||||
|
||||
s.applyRetryConfig(newCfg)
|
||||
s.applyPprofConfig(newCfg)
|
||||
if s.server != nil {
|
||||
s.server.UpdateClients(newCfg)
|
||||
}
|
||||
s.cfgMu.Lock()
|
||||
s.cfg = newCfg
|
||||
s.cfgMu.Unlock()
|
||||
if s.coreManager != nil {
|
||||
s.coreManager.SetConfig(newCfg)
|
||||
s.coreManager.SetOAuthModelAlias(newCfg.OAuthModelAlias)
|
||||
}
|
||||
s.rebindExecutors()
|
||||
}
|
||||
|
||||
func forceHomeRuntimeConfig(cfg *config.Config) {
|
||||
if cfg == nil {
|
||||
return
|
||||
}
|
||||
cfg.APIKeys = nil
|
||||
cfg.DisableCooling = true
|
||||
cfg.WebsocketAuth = false
|
||||
cfg.EnableGeminiCLIEndpoint = false
|
||||
cfg.RemoteManagement.AllowRemote = false
|
||||
cfg.RemoteManagement.DisableControlPanel = true
|
||||
}
|
||||
|
||||
func (s *Service) registerHomeExecutors() {
|
||||
if s == nil || s.coreManager == nil || s.cfg == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Register baseline executors so home-dispatched auth entries can execute without
|
||||
// requiring any local auth-dir credentials.
|
||||
s.coreManager.RegisterExecutor(executor.NewCodexAutoExecutor(s.cfg))
|
||||
s.coreManager.RegisterExecutor(executor.NewClaudeExecutor(s.cfg))
|
||||
s.coreManager.RegisterExecutor(executor.NewGeminiExecutor(s.cfg))
|
||||
s.coreManager.RegisterExecutor(executor.NewGeminiVertexExecutor(s.cfg))
|
||||
s.coreManager.RegisterExecutor(executor.NewGeminiCLIExecutor(s.cfg))
|
||||
s.coreManager.RegisterExecutor(executor.NewAIStudioExecutor(s.cfg, "", s.wsGateway))
|
||||
s.coreManager.RegisterExecutor(executor.NewAntigravityExecutor(s.cfg))
|
||||
s.coreManager.RegisterExecutor(executor.NewKimiExecutor(s.cfg))
|
||||
s.coreManager.RegisterExecutor(executor.NewOpenAICompatExecutor("openai-compatibility", s.cfg))
|
||||
}
|
||||
|
||||
func (s *Service) applyHomeOverlay(remoteCfg *config.Config) {
|
||||
if s == nil || remoteCfg == nil {
|
||||
return
|
||||
}
|
||||
|
||||
s.cfgMu.RLock()
|
||||
baseCfg := s.cfg
|
||||
s.cfgMu.RUnlock()
|
||||
if baseCfg == nil {
|
||||
return
|
||||
}
|
||||
|
||||
merged := *remoteCfg
|
||||
merged.Host = baseCfg.Host
|
||||
merged.Port = baseCfg.Port
|
||||
merged.TLS = baseCfg.TLS
|
||||
merged.Home = baseCfg.Home
|
||||
forceHomeRuntimeConfig(&merged)
|
||||
|
||||
s.applyConfigUpdate(&merged)
|
||||
}
|
||||
|
||||
func (s *Service) startHomeUsageForwarder(ctx context.Context, client *home.Client) {
|
||||
if s == nil || client == nil {
|
||||
return
|
||||
}
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
|
||||
sleep := func(d time.Duration) bool {
|
||||
if d <= 0 {
|
||||
return true
|
||||
}
|
||||
timer := time.NewTimer(d)
|
||||
defer timer.Stop()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return false
|
||||
case <-timer.C:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
if !client.HeartbeatOK() {
|
||||
if !sleep(time.Second) {
|
||||
return
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
items := redisqueue.PopOldest(64)
|
||||
if len(items) == 0 {
|
||||
if !sleep(500 * time.Millisecond) {
|
||||
return
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
for i := range items {
|
||||
if errPush := client.LPushUsage(ctx, items[i]); errPush != nil {
|
||||
for j := i; j < len(items); j++ {
|
||||
redisqueue.Enqueue(items[j])
|
||||
}
|
||||
if !sleep(time.Second) {
|
||||
return
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (s *Service) startHomeSubscriber(ctx context.Context) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
s.cfgMu.RLock()
|
||||
cfg := s.cfg
|
||||
s.cfgMu.RUnlock()
|
||||
if cfg == nil || !cfg.Home.Enabled {
|
||||
return
|
||||
}
|
||||
|
||||
if s.homeCancel != nil {
|
||||
s.homeCancel()
|
||||
s.homeCancel = nil
|
||||
}
|
||||
if s.homeClient != nil {
|
||||
s.homeClient.Close()
|
||||
s.homeClient = nil
|
||||
}
|
||||
|
||||
homeCtx := ctx
|
||||
if homeCtx == nil {
|
||||
homeCtx = context.Background()
|
||||
}
|
||||
homeCtx, cancel := context.WithCancel(homeCtx)
|
||||
s.homeCancel = cancel
|
||||
|
||||
client := home.New(cfg.Home)
|
||||
s.homeClient = client
|
||||
home.SetCurrent(client)
|
||||
|
||||
go client.StartConfigSubscriber(homeCtx, func(raw []byte) error {
|
||||
parsed, err := config.ParseConfigBytes(raw)
|
||||
if err != nil {
|
||||
log.Warnf("failed to parse home config payload: %v", err)
|
||||
return err
|
||||
}
|
||||
s.applyHomeOverlay(parsed)
|
||||
return nil
|
||||
})
|
||||
s.startHomeUsageForwarder(homeCtx, client)
|
||||
}
|
||||
|
||||
// Run starts the service and blocks until the context is cancelled or the server stops.
|
||||
// It initializes all components including authentication, file watching, HTTP server,
|
||||
// and starts processing requests. The method blocks until the context is cancelled.
|
||||
@@ -480,6 +729,10 @@ func (s *Service) Run(ctx context.Context) error {
|
||||
}
|
||||
|
||||
usage.StartDefault(ctx)
|
||||
homeEnabled := s.cfg != nil && s.cfg.Home.Enabled
|
||||
if homeEnabled {
|
||||
forceHomeRuntimeConfig(s.cfg)
|
||||
}
|
||||
|
||||
shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer shutdownCancel()
|
||||
@@ -489,32 +742,36 @@ func (s *Service) Run(ctx context.Context) error {
|
||||
}
|
||||
}()
|
||||
|
||||
if err := s.ensureAuthDir(); err != nil {
|
||||
return err
|
||||
if !homeEnabled {
|
||||
if errEnsureAuthDir := s.ensureAuthDir(); errEnsureAuthDir != nil {
|
||||
return errEnsureAuthDir
|
||||
}
|
||||
}
|
||||
|
||||
s.applyRetryConfig(s.cfg)
|
||||
|
||||
if s.coreManager != nil {
|
||||
if s.coreManager != nil && !homeEnabled {
|
||||
if errLoad := s.coreManager.Load(ctx); errLoad != nil {
|
||||
log.Warnf("failed to load auth store: %v", errLoad)
|
||||
}
|
||||
}
|
||||
|
||||
tokenResult, err := s.tokenProvider.Load(ctx, s.cfg)
|
||||
if err != nil && !errors.Is(err, context.Canceled) {
|
||||
return err
|
||||
}
|
||||
if tokenResult == nil {
|
||||
tokenResult = &TokenClientResult{}
|
||||
}
|
||||
if !homeEnabled {
|
||||
tokenResult, err := s.tokenProvider.Load(ctx, s.cfg)
|
||||
if err != nil && !errors.Is(err, context.Canceled) {
|
||||
return err
|
||||
}
|
||||
if tokenResult == nil {
|
||||
tokenResult = &TokenClientResult{}
|
||||
}
|
||||
|
||||
apiKeyResult, err := s.apiKeyProvider.Load(ctx, s.cfg)
|
||||
if err != nil && !errors.Is(err, context.Canceled) {
|
||||
return err
|
||||
}
|
||||
if apiKeyResult == nil {
|
||||
apiKeyResult = &APIKeyClientResult{}
|
||||
apiKeyResult, err := s.apiKeyProvider.Load(ctx, s.cfg)
|
||||
if err != nil && !errors.Is(err, context.Canceled) {
|
||||
return err
|
||||
}
|
||||
if apiKeyResult == nil {
|
||||
apiKeyResult = &APIKeyClientResult{}
|
||||
}
|
||||
}
|
||||
|
||||
// legacy clients removed; no caches to refresh
|
||||
@@ -526,6 +783,10 @@ func (s *Service) Run(ctx context.Context) error {
|
||||
s.authManager = newDefaultAuthManager()
|
||||
}
|
||||
|
||||
if homeEnabled {
|
||||
s.startHomeSubscriber(ctx)
|
||||
}
|
||||
|
||||
s.ensureWebsocketGateway()
|
||||
if s.server != nil && s.wsGateway != nil {
|
||||
s.server.AttachWebsocketRoute(s.wsGateway.Path(), s.wsGateway.Handler())
|
||||
@@ -547,6 +808,12 @@ func (s *Service) Run(ctx context.Context) error {
|
||||
})
|
||||
}
|
||||
|
||||
if homeEnabled {
|
||||
s.registerHomeExecutors()
|
||||
// Home mode does not expose in-process Redis RESP usage output; usage is forwarded to home instead.
|
||||
redisqueue.SetEnabled(true)
|
||||
}
|
||||
|
||||
if s.hooks.OnBeforeStart != nil {
|
||||
s.hooks.OnBeforeStart(s.cfg)
|
||||
}
|
||||
@@ -607,107 +874,31 @@ func (s *Service) Run(ctx context.Context) error {
|
||||
s.hooks.OnAfterStart(s)
|
||||
}
|
||||
|
||||
var watcherWrapper *WatcherWrapper
|
||||
reloadCallback := func(newCfg *config.Config) {
|
||||
previousStrategy := ""
|
||||
var previousSessionAffinity bool
|
||||
var previousSessionAffinityTTL string
|
||||
s.cfgMu.RLock()
|
||||
if s.cfg != nil {
|
||||
previousStrategy = strings.ToLower(strings.TrimSpace(s.cfg.Routing.Strategy))
|
||||
previousSessionAffinity = s.cfg.Routing.ClaudeCodeSessionAffinity || s.cfg.Routing.SessionAffinity
|
||||
previousSessionAffinityTTL = s.cfg.Routing.SessionAffinityTTL
|
||||
if !homeEnabled {
|
||||
var watcherWrapper *WatcherWrapper
|
||||
reloadCallback := func(newCfg *config.Config) { s.applyConfigUpdate(newCfg) }
|
||||
|
||||
watcherWrapper, errCreate := s.watcherFactory(s.configPath, s.cfg.AuthDir, reloadCallback)
|
||||
if errCreate != nil {
|
||||
return fmt.Errorf("cliproxy: failed to create watcher: %w", errCreate)
|
||||
}
|
||||
s.cfgMu.RUnlock()
|
||||
|
||||
if newCfg == nil {
|
||||
s.cfgMu.RLock()
|
||||
newCfg = s.cfg
|
||||
s.cfgMu.RUnlock()
|
||||
s.watcher = watcherWrapper
|
||||
s.ensureAuthUpdateQueue(ctx)
|
||||
if s.authUpdates != nil {
|
||||
watcherWrapper.SetAuthUpdateQueue(s.authUpdates)
|
||||
}
|
||||
if newCfg == nil {
|
||||
return
|
||||
watcherWrapper.SetConfig(s.cfg)
|
||||
|
||||
watcherCtx, watcherCancel := context.WithCancel(context.Background())
|
||||
s.watcherCancel = watcherCancel
|
||||
if errStart := watcherWrapper.Start(watcherCtx); errStart != nil {
|
||||
return fmt.Errorf("cliproxy: failed to start watcher: %w", errStart)
|
||||
}
|
||||
|
||||
nextStrategy := strings.ToLower(strings.TrimSpace(newCfg.Routing.Strategy))
|
||||
normalizeStrategy := func(strategy string) string {
|
||||
switch strategy {
|
||||
case "fill-first", "fillfirst", "ff":
|
||||
return "fill-first"
|
||||
default:
|
||||
return "round-robin"
|
||||
}
|
||||
}
|
||||
previousStrategy = normalizeStrategy(previousStrategy)
|
||||
nextStrategy = normalizeStrategy(nextStrategy)
|
||||
|
||||
nextSessionAffinity := newCfg.Routing.ClaudeCodeSessionAffinity || newCfg.Routing.SessionAffinity
|
||||
nextSessionAffinityTTL := newCfg.Routing.SessionAffinityTTL
|
||||
|
||||
selectorChanged := previousStrategy != nextStrategy ||
|
||||
previousSessionAffinity != nextSessionAffinity ||
|
||||
previousSessionAffinityTTL != nextSessionAffinityTTL
|
||||
|
||||
if s.coreManager != nil && selectorChanged {
|
||||
var selector coreauth.Selector
|
||||
switch nextStrategy {
|
||||
case "fill-first":
|
||||
selector = &coreauth.FillFirstSelector{}
|
||||
default:
|
||||
selector = &coreauth.RoundRobinSelector{}
|
||||
}
|
||||
|
||||
if nextSessionAffinity {
|
||||
ttl := time.Hour
|
||||
if ttlStr := strings.TrimSpace(nextSessionAffinityTTL); ttlStr != "" {
|
||||
if parsed, err := time.ParseDuration(ttlStr); err == nil && parsed > 0 {
|
||||
ttl = parsed
|
||||
}
|
||||
}
|
||||
selector = coreauth.NewSessionAffinitySelectorWithConfig(coreauth.SessionAffinityConfig{
|
||||
Fallback: selector,
|
||||
TTL: ttl,
|
||||
})
|
||||
}
|
||||
|
||||
s.coreManager.SetSelector(selector)
|
||||
}
|
||||
|
||||
s.applyRetryConfig(newCfg)
|
||||
s.applyPprofConfig(newCfg)
|
||||
if s.server != nil {
|
||||
s.server.UpdateClients(newCfg)
|
||||
}
|
||||
s.cfgMu.Lock()
|
||||
s.cfg = newCfg
|
||||
s.cfgMu.Unlock()
|
||||
if s.coreManager != nil {
|
||||
s.coreManager.SetConfig(newCfg)
|
||||
s.coreManager.SetOAuthModelAlias(newCfg.OAuthModelAlias)
|
||||
}
|
||||
s.rebindExecutors()
|
||||
log.Info("file watcher started for config and auth directory changes")
|
||||
}
|
||||
|
||||
watcherWrapper, err = s.watcherFactory(s.configPath, s.cfg.AuthDir, reloadCallback)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cliproxy: failed to create watcher: %w", err)
|
||||
}
|
||||
s.watcher = watcherWrapper
|
||||
s.ensureAuthUpdateQueue(ctx)
|
||||
if s.authUpdates != nil {
|
||||
watcherWrapper.SetAuthUpdateQueue(s.authUpdates)
|
||||
}
|
||||
watcherWrapper.SetConfig(s.cfg)
|
||||
|
||||
watcherCtx, watcherCancel := context.WithCancel(context.Background())
|
||||
s.watcherCancel = watcherCancel
|
||||
if err = watcherWrapper.Start(watcherCtx); err != nil {
|
||||
return fmt.Errorf("cliproxy: failed to start watcher: %w", err)
|
||||
}
|
||||
log.Info("file watcher started for config and auth directory changes")
|
||||
|
||||
// Prefer core auth manager auto refresh if available.
|
||||
if s.coreManager != nil {
|
||||
if s.coreManager != nil && !homeEnabled {
|
||||
interval := 15 * time.Minute
|
||||
s.coreManager.StartAutoRefresh(context.Background(), interval)
|
||||
log.Infof("core auth auto-refresh started (interval=%s)", interval)
|
||||
@@ -717,8 +908,8 @@ func (s *Service) Run(ctx context.Context) error {
|
||||
case <-ctx.Done():
|
||||
log.Debug("service context cancelled, shutting down...")
|
||||
return ctx.Err()
|
||||
case err = <-s.serverErr:
|
||||
return err
|
||||
case errServer := <-s.serverErr:
|
||||
return errServer
|
||||
}
|
||||
}
|
||||
|
||||
@@ -741,6 +932,16 @@ func (s *Service) Shutdown(ctx context.Context) error {
|
||||
ctx = context.Background()
|
||||
}
|
||||
|
||||
if s.homeCancel != nil {
|
||||
s.homeCancel()
|
||||
s.homeCancel = nil
|
||||
}
|
||||
if s.homeClient != nil {
|
||||
s.homeClient.Close()
|
||||
s.homeClient = nil
|
||||
}
|
||||
home.ClearCurrent()
|
||||
|
||||
// legacy refresh loop removed; only stopping core auth manager below
|
||||
|
||||
if s.watcherCancel != nil {
|
||||
|
||||
@@ -3,8 +3,8 @@ package cliproxy
|
||||
import (
|
||||
"testing"
|
||||
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/sdk/config"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/auth"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/sdk/config"
|
||||
)
|
||||
|
||||
func TestEnsureExecutorsForAuth_CodexDoesNotReplaceInNormalMode(t *testing.T) {
|
||||
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/sdk/config"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/auth"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/sdk/config"
|
||||
)
|
||||
|
||||
func TestRegisterModelsForAuth_UsesPreMergedExcludedModelsAttribute(t *testing.T) {
|
||||
|
||||
@@ -3,7 +3,7 @@ package cliproxy
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/sdk/config"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/sdk/config"
|
||||
)
|
||||
|
||||
func TestApplyOAuthModelAlias_Rename(t *testing.T) {
|
||||
|
||||
@@ -5,9 +5,9 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/registry"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/sdk/config"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/registry"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/auth"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/sdk/config"
|
||||
)
|
||||
|
||||
func TestServiceApplyCoreAuthAddOrUpdate_DeleteReAddDoesNotInheritStaleRuntimeState(t *testing.T) {
|
||||
|
||||
@@ -6,9 +6,9 @@ package cliproxy
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/watcher"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/sdk/config"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/watcher"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/auth"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/sdk/config"
|
||||
)
|
||||
|
||||
// TokenClientProvider loads clients backed by stored authentication tokens.
|
||||
|
||||
@@ -3,9 +3,9 @@ package cliproxy
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/watcher"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
|
||||
"github.com/router-for-me/CLIProxyAPI/v6/sdk/config"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/internal/watcher"
|
||||
coreauth "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/auth"
|
||||
"github.com/router-for-me/CLIProxyAPI/v7/sdk/config"
|
||||
)
|
||||
|
||||
func defaultWatcherFactory(configPath, authDir string, reload func(*config.Config)) (*WatcherWrapper, error) {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
// embed CLIProxyAPI without importing internal packages.
|
||||
package config
|
||||
|
||||
import internalconfig "github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
||||
import internalconfig "github.com/router-for-me/CLIProxyAPI/v7/internal/config"
|
||||
|
||||
type SDKConfig = internalconfig.SDKConfig
|
||||
|
||||
@@ -41,6 +41,8 @@ func LoadConfigOptional(configFile string, optional bool) (*Config, error) {
|
||||
return internalconfig.LoadConfigOptional(configFile, optional)
|
||||
}
|
||||
|
||||
func ParseConfigBytes(data []byte) (*Config, error) { return internalconfig.ParseConfigBytes(data) }
|
||||
|
||||
func SaveConfigPreserveComments(configFile string, cfg *Config) error {
|
||||
return internalconfig.SaveConfigPreserveComments(configFile, cfg)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Package logging re-exports request logging primitives for SDK consumers.
|
||||
package logging
|
||||
|
||||
import internallogging "github.com/router-for-me/CLIProxyAPI/v6/internal/logging"
|
||||
import internallogging "github.com/router-for-me/CLIProxyAPI/v7/internal/logging"
|
||||
|
||||
const defaultErrorLogsMaxFiles = 10
|
||||
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
package builtin
|
||||
|
||||
import (
|
||||
sdktranslator "github.com/router-for-me/CLIProxyAPI/v6/sdk/translator"
|
||||
sdktranslator "github.com/router-for-me/CLIProxyAPI/v7/sdk/translator"
|
||||
|
||||
_ "github.com/router-for-me/CLIProxyAPI/v6/internal/translator"
|
||||
_ "github.com/router-for-me/CLIProxyAPI/v7/internal/translator"
|
||||
)
|
||||
|
||||
// Registry exposes the default registry populated with all built-in translators.
|
||||
|
||||
Reference in New Issue
Block a user