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:
Luis Pater
2026-05-08 11:46:46 +08:00
parent 785b00c312
commit e50cabac4b
317 changed files with 2415 additions and 1035 deletions
+4 -4
View File
@@ -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"
)
+4 -4
View File
@@ -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.
+25 -13
View File
@@ -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) {
+1 -1
View File
@@ -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 {
+5 -5
View File
@@ -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"
)
+1 -1
View File
@@ -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 {
+3 -3
View File
@@ -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
View File
@@ -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.
+6 -6
View File
@@ -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
View File
@@ -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
View File
@@ -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"
)
+5 -5
View File
@@ -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
View File
@@ -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.
+1 -1
View File
@@ -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
View File
@@ -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.
+2 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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.
+1 -1
View File
@@ -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() {
+1 -1
View File
@@ -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) {
+190 -7
View File
@@ -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 {
+2 -2
View File
@@ -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 {
+1 -1
View File
@@ -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) {
+3 -3
View File
@@ -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 {
+2 -2
View File
@@ -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 {
+2 -2
View File
@@ -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{}
+3 -3
View File
@@ -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.
+1 -1
View File
@@ -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) {
+1 -1
View File
@@ -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
+6 -6
View File
@@ -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.
+1 -1
View File
@@ -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 -1
View File
@@ -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
+3 -3
View File
@@ -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.
+1 -1
View File
@@ -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"
)
+2 -2
View File
@@ -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.
+2 -2
View File
@@ -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"
)
+1 -1
View File
@@ -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
View File
@@ -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) {
+2 -2
View File
@@ -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) {
+3 -3
View File
@@ -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) {
+3 -3
View File
@@ -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 -3
View File
@@ -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) {
+3 -1
View File
@@ -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 -1
View File
@@ -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 -2
View File
@@ -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.