mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-02-03 13:00:52 +08:00
feat: add client availability tracking and error handling improvements
- Introduced `IsAvailable` and `SetUnavailable` methods to clients for availability tracking. - Integrated availability checks in client selection logic to skip unavailable clients. - Enhanced error handling by marking clients unavailable on specific error codes (e.g., 401, 402). - Removed redundant quota verification logs in client reordering logic.
This commit is contained in:
@@ -205,9 +205,13 @@ outLoop:
|
|||||||
err := cliClient.RefreshTokens(cliCtx)
|
err := cliClient.RefreshTokens(cliCtx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugf("refresh token failed, switch client, %s", util.HideAPIKey(cliClient.GetEmail()))
|
log.Debugf("refresh token failed, switch client, %s", util.HideAPIKey(cliClient.GetEmail()))
|
||||||
|
cliClient.SetUnavailable()
|
||||||
}
|
}
|
||||||
retryCount++
|
retryCount++
|
||||||
continue outLoop
|
continue outLoop
|
||||||
|
case 402:
|
||||||
|
cliClient.SetUnavailable()
|
||||||
|
continue outLoop
|
||||||
default:
|
default:
|
||||||
// Forward other errors directly to the client
|
// Forward other errors directly to the client
|
||||||
c.Status(errInfo.StatusCode)
|
c.Status(errInfo.StatusCode)
|
||||||
|
|||||||
@@ -221,6 +221,18 @@ outLoop:
|
|||||||
log.Debugf("http status code %d, switch client", err.StatusCode)
|
log.Debugf("http status code %d, switch client", err.StatusCode)
|
||||||
retryCount++
|
retryCount++
|
||||||
continue outLoop
|
continue outLoop
|
||||||
|
case 401:
|
||||||
|
log.Debugf("unauthorized request, try to refresh token, %s", util.HideAPIKey(cliClient.GetEmail()))
|
||||||
|
errRefreshTokens := cliClient.RefreshTokens(cliCtx)
|
||||||
|
if errRefreshTokens != nil {
|
||||||
|
log.Debugf("refresh token failed, switch client, %s", util.HideAPIKey(cliClient.GetEmail()))
|
||||||
|
cliClient.SetUnavailable()
|
||||||
|
}
|
||||||
|
retryCount++
|
||||||
|
continue outLoop
|
||||||
|
case 402:
|
||||||
|
cliClient.SetUnavailable()
|
||||||
|
continue outLoop
|
||||||
default:
|
default:
|
||||||
// Forward other errors directly to the client
|
// Forward other errors directly to the client
|
||||||
c.Status(err.StatusCode)
|
c.Status(err.StatusCode)
|
||||||
@@ -293,9 +305,13 @@ func (h *GeminiCLIAPIHandler) handleInternalGenerateContent(c *gin.Context, rawJ
|
|||||||
errRefreshTokens := cliClient.RefreshTokens(cliCtx)
|
errRefreshTokens := cliClient.RefreshTokens(cliCtx)
|
||||||
if errRefreshTokens != nil {
|
if errRefreshTokens != nil {
|
||||||
log.Debugf("refresh token failed, switch client, %s", util.HideAPIKey(cliClient.GetEmail()))
|
log.Debugf("refresh token failed, switch client, %s", util.HideAPIKey(cliClient.GetEmail()))
|
||||||
|
cliClient.SetUnavailable()
|
||||||
}
|
}
|
||||||
retryCount++
|
retryCount++
|
||||||
continue
|
continue
|
||||||
|
case 402:
|
||||||
|
cliClient.SetUnavailable()
|
||||||
|
continue
|
||||||
default:
|
default:
|
||||||
// Forward other errors directly to the client
|
// Forward other errors directly to the client
|
||||||
c.Status(err.StatusCode)
|
c.Status(err.StatusCode)
|
||||||
|
|||||||
@@ -276,6 +276,18 @@ outLoop:
|
|||||||
log.Debugf("http status code %d, switch client", err.StatusCode)
|
log.Debugf("http status code %d, switch client", err.StatusCode)
|
||||||
retryCount++
|
retryCount++
|
||||||
continue outLoop
|
continue outLoop
|
||||||
|
case 401:
|
||||||
|
log.Debugf("unauthorized request, try to refresh token, %s", util.HideAPIKey(cliClient.GetEmail()))
|
||||||
|
errRefreshTokens := cliClient.RefreshTokens(cliCtx)
|
||||||
|
if errRefreshTokens != nil {
|
||||||
|
log.Debugf("refresh token failed, switch client, %s", util.HideAPIKey(cliClient.GetEmail()))
|
||||||
|
cliClient.SetUnavailable()
|
||||||
|
}
|
||||||
|
retryCount++
|
||||||
|
continue outLoop
|
||||||
|
case 402:
|
||||||
|
cliClient.SetUnavailable()
|
||||||
|
continue outLoop
|
||||||
default:
|
default:
|
||||||
// Forward other errors directly to the client
|
// Forward other errors directly to the client
|
||||||
c.Status(err.StatusCode)
|
c.Status(err.StatusCode)
|
||||||
@@ -406,9 +418,13 @@ func (h *GeminiAPIHandler) handleGenerateContent(c *gin.Context, modelName strin
|
|||||||
errRefreshTokens := cliClient.RefreshTokens(cliCtx)
|
errRefreshTokens := cliClient.RefreshTokens(cliCtx)
|
||||||
if errRefreshTokens != nil {
|
if errRefreshTokens != nil {
|
||||||
log.Debugf("refresh token failed, switch client, %s", util.HideAPIKey(cliClient.GetEmail()))
|
log.Debugf("refresh token failed, switch client, %s", util.HideAPIKey(cliClient.GetEmail()))
|
||||||
|
cliClient.SetUnavailable()
|
||||||
}
|
}
|
||||||
retryCount++
|
retryCount++
|
||||||
continue
|
continue
|
||||||
|
case 402:
|
||||||
|
cliClient.SetUnavailable()
|
||||||
|
continue
|
||||||
default:
|
default:
|
||||||
// Forward other errors directly to the client
|
// Forward other errors directly to the client
|
||||||
c.Status(err.StatusCode)
|
c.Status(err.StatusCode)
|
||||||
|
|||||||
@@ -8,11 +8,9 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/luispater/CLIProxyAPI/v5/internal/client"
|
|
||||||
"github.com/luispater/CLIProxyAPI/v5/internal/config"
|
"github.com/luispater/CLIProxyAPI/v5/internal/config"
|
||||||
"github.com/luispater/CLIProxyAPI/v5/internal/interfaces"
|
"github.com/luispater/CLIProxyAPI/v5/internal/interfaces"
|
||||||
"github.com/luispater/CLIProxyAPI/v5/internal/util"
|
"github.com/luispater/CLIProxyAPI/v5/internal/util"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -97,7 +95,7 @@ func (h *BaseAPIHandler) UpdateClients(clients []interfaces.Client, cfg *config.
|
|||||||
func (h *BaseAPIHandler) GetClient(modelName string, isGenerateContent ...bool) (interfaces.Client, *interfaces.ErrorMessage) {
|
func (h *BaseAPIHandler) GetClient(modelName string, isGenerateContent ...bool) (interfaces.Client, *interfaces.ErrorMessage) {
|
||||||
clients := make([]interfaces.Client, 0)
|
clients := make([]interfaces.Client, 0)
|
||||||
for i := 0; i < len(h.CliClients); i++ {
|
for i := 0; i < len(h.CliClients); i++ {
|
||||||
if h.CliClients[i].CanProvideModel(modelName) {
|
if h.CliClients[i].CanProvideModel(modelName) && h.CliClients[i].IsAvailable() && !h.CliClients[i].IsModelQuotaExceeded(modelName) {
|
||||||
clients = append(clients, h.CliClients[i])
|
clients = append(clients, h.CliClients[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -126,24 +124,6 @@ func (h *BaseAPIHandler) GetClient(modelName string, isGenerateContent ...bool)
|
|||||||
reorderedClients := make([]interfaces.Client, 0)
|
reorderedClients := make([]interfaces.Client, 0)
|
||||||
for i := 0; i < len(clients); i++ {
|
for i := 0; i < len(clients); i++ {
|
||||||
cliClient = clients[(startIndex+1+i)%len(clients)]
|
cliClient = clients[(startIndex+1+i)%len(clients)]
|
||||||
if cliClient.IsModelQuotaExceeded(modelName) {
|
|
||||||
if cliClient.Provider() == "gemini-cli" {
|
|
||||||
log.Debugf("Gemini Model %s is quota exceeded for account %s, project id: %s", modelName, cliClient.GetEmail(), cliClient.(*client.GeminiCLIClient).GetProjectID())
|
|
||||||
} else if cliClient.Provider() == "gemini" {
|
|
||||||
log.Debugf("Gemini Model %s is quota exceeded for account %s", modelName, cliClient.GetEmail())
|
|
||||||
} else if cliClient.Provider() == "codex" {
|
|
||||||
log.Debugf("Codex Model %s is quota exceeded for account %s", modelName, cliClient.GetEmail())
|
|
||||||
} else if cliClient.Provider() == "claude" {
|
|
||||||
log.Debugf("Claude Model %s is quota exceeded for account %s", modelName, cliClient.GetEmail())
|
|
||||||
} else if cliClient.Provider() == "qwen" {
|
|
||||||
log.Debugf("Qwen Model %s is quota exceeded for account %s", modelName, cliClient.GetEmail())
|
|
||||||
} else if cliClient.Type() == "openai-compatibility" {
|
|
||||||
log.Debugf("OpenAI Compatibility Model %s is quota exceeded for provider %s", modelName, cliClient.Provider())
|
|
||||||
}
|
|
||||||
cliClient = nil
|
|
||||||
continue
|
|
||||||
|
|
||||||
}
|
|
||||||
reorderedClients = append(reorderedClients, cliClient)
|
reorderedClients = append(reorderedClients, cliClient)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -442,9 +442,13 @@ func (h *OpenAIAPIHandler) handleNonStreamingResponse(c *gin.Context, rawJSON []
|
|||||||
errRefreshTokens := cliClient.RefreshTokens(cliCtx)
|
errRefreshTokens := cliClient.RefreshTokens(cliCtx)
|
||||||
if errRefreshTokens != nil {
|
if errRefreshTokens != nil {
|
||||||
log.Debugf("refresh token failed, switch client, %s", util.HideAPIKey(cliClient.GetEmail()))
|
log.Debugf("refresh token failed, switch client, %s", util.HideAPIKey(cliClient.GetEmail()))
|
||||||
|
cliClient.SetUnavailable()
|
||||||
}
|
}
|
||||||
retryCount++
|
retryCount++
|
||||||
continue
|
continue
|
||||||
|
case 402:
|
||||||
|
cliClient.SetUnavailable()
|
||||||
|
continue
|
||||||
default:
|
default:
|
||||||
// Forward other errors directly to the client
|
// Forward other errors directly to the client
|
||||||
c.Status(err.StatusCode)
|
c.Status(err.StatusCode)
|
||||||
@@ -557,6 +561,18 @@ outLoop:
|
|||||||
log.Debugf("http status code %d, switch client", err.StatusCode)
|
log.Debugf("http status code %d, switch client", err.StatusCode)
|
||||||
retryCount++
|
retryCount++
|
||||||
continue outLoop
|
continue outLoop
|
||||||
|
case 401:
|
||||||
|
log.Debugf("unauthorized request, try to refresh token, %s", util.HideAPIKey(cliClient.GetEmail()))
|
||||||
|
errRefreshTokens := cliClient.RefreshTokens(cliCtx)
|
||||||
|
if errRefreshTokens != nil {
|
||||||
|
log.Debugf("refresh token failed, switch client, %s", util.HideAPIKey(cliClient.GetEmail()))
|
||||||
|
cliClient.SetUnavailable()
|
||||||
|
}
|
||||||
|
retryCount++
|
||||||
|
continue outLoop
|
||||||
|
case 402:
|
||||||
|
cliClient.SetUnavailable()
|
||||||
|
continue outLoop
|
||||||
default:
|
default:
|
||||||
// Forward other errors directly to the client
|
// Forward other errors directly to the client
|
||||||
c.Status(err.StatusCode)
|
c.Status(err.StatusCode)
|
||||||
@@ -632,6 +648,18 @@ func (h *OpenAIAPIHandler) handleCompletionsNonStreamingResponse(c *gin.Context,
|
|||||||
log.Debugf("http status code %d, switch client", err.StatusCode)
|
log.Debugf("http status code %d, switch client", err.StatusCode)
|
||||||
retryCount++
|
retryCount++
|
||||||
continue
|
continue
|
||||||
|
case 401:
|
||||||
|
log.Debugf("unauthorized request, try to refresh token, %s", util.HideAPIKey(cliClient.GetEmail()))
|
||||||
|
errRefreshTokens := cliClient.RefreshTokens(cliCtx)
|
||||||
|
if errRefreshTokens != nil {
|
||||||
|
log.Debugf("refresh token failed, switch client, %s", util.HideAPIKey(cliClient.GetEmail()))
|
||||||
|
cliClient.SetUnavailable()
|
||||||
|
}
|
||||||
|
retryCount++
|
||||||
|
continue
|
||||||
|
case 402:
|
||||||
|
cliClient.SetUnavailable()
|
||||||
|
continue
|
||||||
default:
|
default:
|
||||||
// Forward other errors directly to the client
|
// Forward other errors directly to the client
|
||||||
c.Status(err.StatusCode)
|
c.Status(err.StatusCode)
|
||||||
@@ -755,6 +783,18 @@ outLoop:
|
|||||||
log.Debugf("http status code %d, switch client", err.StatusCode)
|
log.Debugf("http status code %d, switch client", err.StatusCode)
|
||||||
retryCount++
|
retryCount++
|
||||||
continue outLoop
|
continue outLoop
|
||||||
|
case 401:
|
||||||
|
log.Debugf("unauthorized request, try to refresh token, %s", util.HideAPIKey(cliClient.GetEmail()))
|
||||||
|
errRefreshTokens := cliClient.RefreshTokens(cliCtx)
|
||||||
|
if errRefreshTokens != nil {
|
||||||
|
log.Debugf("refresh token failed, switch client, %s", util.HideAPIKey(cliClient.GetEmail()))
|
||||||
|
cliClient.SetUnavailable()
|
||||||
|
}
|
||||||
|
retryCount++
|
||||||
|
continue outLoop
|
||||||
|
case 402:
|
||||||
|
cliClient.SetUnavailable()
|
||||||
|
continue outLoop
|
||||||
default:
|
default:
|
||||||
// Forward other errors directly to the client
|
// Forward other errors directly to the client
|
||||||
c.Status(err.StatusCode)
|
c.Status(err.StatusCode)
|
||||||
|
|||||||
@@ -146,9 +146,13 @@ func (h *OpenAIResponsesAPIHandler) handleNonStreamingResponse(c *gin.Context, r
|
|||||||
errRefreshTokens := cliClient.RefreshTokens(cliCtx)
|
errRefreshTokens := cliClient.RefreshTokens(cliCtx)
|
||||||
if errRefreshTokens != nil {
|
if errRefreshTokens != nil {
|
||||||
log.Debugf("refresh token failed, switch client, %s", util.HideAPIKey(cliClient.GetEmail()))
|
log.Debugf("refresh token failed, switch client, %s", util.HideAPIKey(cliClient.GetEmail()))
|
||||||
|
cliClient.SetUnavailable()
|
||||||
}
|
}
|
||||||
retryCount++
|
retryCount++
|
||||||
continue
|
continue
|
||||||
|
case 402:
|
||||||
|
cliClient.SetUnavailable()
|
||||||
|
continue
|
||||||
default:
|
default:
|
||||||
// Forward other errors directly to the client
|
// Forward other errors directly to the client
|
||||||
c.Status(err.StatusCode)
|
c.Status(err.StatusCode)
|
||||||
@@ -260,6 +264,18 @@ outLoop:
|
|||||||
log.Debugf("http status code %d, switch client", err.StatusCode)
|
log.Debugf("http status code %d, switch client", err.StatusCode)
|
||||||
retryCount++
|
retryCount++
|
||||||
continue outLoop
|
continue outLoop
|
||||||
|
case 401:
|
||||||
|
log.Debugf("unauthorized request, try to refresh token, %s", util.HideAPIKey(cliClient.GetEmail()))
|
||||||
|
errRefreshTokens := cliClient.RefreshTokens(cliCtx)
|
||||||
|
if errRefreshTokens != nil {
|
||||||
|
log.Debugf("refresh token failed, switch client, %s", util.HideAPIKey(cliClient.GetEmail()))
|
||||||
|
cliClient.SetUnavailable()
|
||||||
|
}
|
||||||
|
retryCount++
|
||||||
|
continue outLoop
|
||||||
|
case 402:
|
||||||
|
cliClient.SetUnavailable()
|
||||||
|
continue outLoop
|
||||||
default:
|
default:
|
||||||
// Forward other errors directly to the client
|
// Forward other errors directly to the client
|
||||||
c.Status(err.StatusCode)
|
c.Status(err.StatusCode)
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ func NewClaudeClient(cfg *config.Config, ts *claude.ClaudeTokenStorage) *ClaudeC
|
|||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
modelQuotaExceeded: make(map[string]*time.Time),
|
modelQuotaExceeded: make(map[string]*time.Time),
|
||||||
tokenStorage: ts,
|
tokenStorage: ts,
|
||||||
|
isAvailable: true,
|
||||||
},
|
},
|
||||||
claudeAuth: claude.NewClaudeAuth(cfg),
|
claudeAuth: claude.NewClaudeAuth(cfg),
|
||||||
apiKeyIndex: -1,
|
apiKeyIndex: -1,
|
||||||
@@ -102,6 +103,7 @@ func NewClaudeClientWithKey(cfg *config.Config, apiKeyIndex int) *ClaudeClient {
|
|||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
modelQuotaExceeded: make(map[string]*time.Time),
|
modelQuotaExceeded: make(map[string]*time.Time),
|
||||||
tokenStorage: &empty.EmptyStorage{},
|
tokenStorage: &empty.EmptyStorage{},
|
||||||
|
isAvailable: true,
|
||||||
},
|
},
|
||||||
claudeAuth: claude.NewClaudeAuth(cfg),
|
claudeAuth: claude.NewClaudeAuth(cfg),
|
||||||
apiKeyIndex: apiKeyIndex,
|
apiKeyIndex: apiKeyIndex,
|
||||||
@@ -581,3 +583,13 @@ func (c *ClaudeClient) IsModelQuotaExceeded(model string) bool {
|
|||||||
func (c *ClaudeClient) GetRequestMutex() *sync.Mutex {
|
func (c *ClaudeClient) GetRequestMutex() *sync.Mutex {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsAvailable returns true if the client is available for use.
|
||||||
|
func (c *ClaudeClient) IsAvailable() bool {
|
||||||
|
return c.isAvailable
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUnavailable sets the client to unavailable.
|
||||||
|
func (c *ClaudeClient) SetUnavailable() {
|
||||||
|
c.isAvailable = false
|
||||||
|
}
|
||||||
|
|||||||
@@ -41,6 +41,9 @@ type ClientBase struct {
|
|||||||
|
|
||||||
// modelRegistry is the global model registry for tracking model availability.
|
// modelRegistry is the global model registry for tracking model availability.
|
||||||
modelRegistry *registry.ModelRegistry
|
modelRegistry *registry.ModelRegistry
|
||||||
|
|
||||||
|
// unavailable tracks whether the client is unavailable
|
||||||
|
isAvailable bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRequestMutex returns the mutex used to synchronize requests for this client.
|
// GetRequestMutex returns the mutex used to synchronize requests for this client.
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ func NewCodexClient(cfg *config.Config, ts *codex.CodexTokenStorage) (*CodexClie
|
|||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
modelQuotaExceeded: make(map[string]*time.Time),
|
modelQuotaExceeded: make(map[string]*time.Time),
|
||||||
tokenStorage: ts,
|
tokenStorage: ts,
|
||||||
|
isAvailable: true,
|
||||||
},
|
},
|
||||||
codexAuth: codex.NewCodexAuth(cfg),
|
codexAuth: codex.NewCodexAuth(cfg),
|
||||||
apiKeyIndex: -1,
|
apiKeyIndex: -1,
|
||||||
@@ -100,6 +101,7 @@ func NewCodexClientWithKey(cfg *config.Config, apiKeyIndex int) *CodexClient {
|
|||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
modelQuotaExceeded: make(map[string]*time.Time),
|
modelQuotaExceeded: make(map[string]*time.Time),
|
||||||
tokenStorage: &empty.EmptyStorage{},
|
tokenStorage: &empty.EmptyStorage{},
|
||||||
|
isAvailable: true,
|
||||||
},
|
},
|
||||||
codexAuth: codex.NewCodexAuth(cfg),
|
codexAuth: codex.NewCodexAuth(cfg),
|
||||||
apiKeyIndex: apiKeyIndex,
|
apiKeyIndex: apiKeyIndex,
|
||||||
@@ -557,3 +559,13 @@ func (c *CodexClient) IsModelQuotaExceeded(model string) bool {
|
|||||||
func (c *CodexClient) GetRequestMutex() *sync.Mutex {
|
func (c *CodexClient) GetRequestMutex() *sync.Mutex {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsAvailable returns true if the client is available for use.
|
||||||
|
func (c *CodexClient) IsAvailable() bool {
|
||||||
|
return c.isAvailable
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUnavailable sets the client to unavailable.
|
||||||
|
func (c *CodexClient) SetUnavailable() {
|
||||||
|
c.isAvailable = false
|
||||||
|
}
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ func NewGeminiCLIClient(httpClient *http.Client, ts *geminiAuth.GeminiTokenStora
|
|||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
tokenStorage: ts,
|
tokenStorage: ts,
|
||||||
modelQuotaExceeded: make(map[string]*time.Time),
|
modelQuotaExceeded: make(map[string]*time.Time),
|
||||||
|
isAvailable: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -871,7 +872,18 @@ func (c *GeminiCLIClient) GetRequestMutex() *sync.Mutex {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RefreshTokens is not applicable for Gemini CLI clients as they use API keys.
|
||||||
func (c *GeminiCLIClient) RefreshTokens(ctx context.Context) error {
|
func (c *GeminiCLIClient) RefreshTokens(ctx context.Context) error {
|
||||||
// API keys don't need refreshing
|
// API keys don't need refreshing
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsAvailable returns true if the client is available for use.
|
||||||
|
func (c *GeminiCLIClient) IsAvailable() bool {
|
||||||
|
return c.isAvailable
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUnavailable sets the client to unavailable.
|
||||||
|
func (c *GeminiCLIClient) SetUnavailable() {
|
||||||
|
c.isAvailable = false
|
||||||
|
}
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ func NewGeminiWebClient(cfg *config.Config, ts *gemini.GeminiWebTokenStorage, to
|
|||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
tokenStorage: ts,
|
tokenStorage: ts,
|
||||||
modelQuotaExceeded: make(map[string]*time.Time),
|
modelQuotaExceeded: make(map[string]*time.Time),
|
||||||
|
isAvailable: true,
|
||||||
},
|
},
|
||||||
tokenFilePath: tokenFilePath,
|
tokenFilePath: tokenFilePath,
|
||||||
convStore: make(map[string][]string),
|
convStore: make(map[string][]string),
|
||||||
@@ -1072,3 +1073,13 @@ func (c *GeminiWebClient) storeConversationJSON(model string, history []geminiWe
|
|||||||
c.convMutex.Unlock()
|
c.convMutex.Unlock()
|
||||||
_ = geminiWeb.SaveConvData(geminiWeb.ConvDataPath(c.tokenFilePath), items, index)
|
_ = geminiWeb.SaveConvData(geminiWeb.ConvDataPath(c.tokenFilePath), items, index)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsAvailable returns true if the client is available for use.
|
||||||
|
func (c *GeminiWebClient) IsAvailable() bool {
|
||||||
|
return c.isAvailable
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUnavailable sets the client to unavailable.
|
||||||
|
func (c *GeminiWebClient) SetUnavailable() {
|
||||||
|
c.isAvailable = false
|
||||||
|
}
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ func NewGeminiClient(httpClient *http.Client, cfg *config.Config, glAPIKey strin
|
|||||||
httpClient: httpClient,
|
httpClient: httpClient,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
modelQuotaExceeded: make(map[string]*time.Time),
|
modelQuotaExceeded: make(map[string]*time.Time),
|
||||||
|
isAvailable: true,
|
||||||
},
|
},
|
||||||
glAPIKey: glAPIKey,
|
glAPIKey: glAPIKey,
|
||||||
}
|
}
|
||||||
@@ -445,3 +446,13 @@ func (c *GeminiClient) RefreshTokens(ctx context.Context) error {
|
|||||||
// API keys don't need refreshing
|
// API keys don't need refreshing
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsAvailable returns true if the client is available for use.
|
||||||
|
func (c *GeminiClient) IsAvailable() bool {
|
||||||
|
return c.isAvailable
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUnavailable sets the client to unavailable.
|
||||||
|
func (c *GeminiClient) SetUnavailable() {
|
||||||
|
c.isAvailable = false
|
||||||
|
}
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ func NewOpenAICompatibilityClient(cfg *config.Config, compatConfig *config.OpenA
|
|||||||
httpClient: httpClient,
|
httpClient: httpClient,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
modelQuotaExceeded: make(map[string]*time.Time),
|
modelQuotaExceeded: make(map[string]*time.Time),
|
||||||
|
isAvailable: true,
|
||||||
},
|
},
|
||||||
compatConfig: compatConfig,
|
compatConfig: compatConfig,
|
||||||
currentAPIKeyIndex: apiKeyIndex,
|
currentAPIKeyIndex: apiKeyIndex,
|
||||||
@@ -425,3 +426,13 @@ func (c *OpenAICompatibilityClient) RefreshTokens(ctx context.Context) error {
|
|||||||
func (c *OpenAICompatibilityClient) GetRequestMutex() *sync.Mutex {
|
func (c *OpenAICompatibilityClient) GetRequestMutex() *sync.Mutex {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsAvailable returns true if the client is available for use.
|
||||||
|
func (c *OpenAICompatibilityClient) IsAvailable() bool {
|
||||||
|
return c.isAvailable
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUnavailable sets the client to unavailable.
|
||||||
|
func (c *OpenAICompatibilityClient) SetUnavailable() {
|
||||||
|
c.isAvailable = false
|
||||||
|
}
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ func NewQwenClient(cfg *config.Config, ts *qwen.QwenTokenStorage) *QwenClient {
|
|||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
modelQuotaExceeded: make(map[string]*time.Time),
|
modelQuotaExceeded: make(map[string]*time.Time),
|
||||||
tokenStorage: ts,
|
tokenStorage: ts,
|
||||||
|
isAvailable: true,
|
||||||
},
|
},
|
||||||
qwenAuth: qwen.NewQwenAuth(cfg),
|
qwenAuth: qwen.NewQwenAuth(cfg),
|
||||||
}
|
}
|
||||||
@@ -447,3 +448,13 @@ func (c *QwenClient) IsModelQuotaExceeded(model string) bool {
|
|||||||
func (c *QwenClient) GetRequestMutex() *sync.Mutex {
|
func (c *QwenClient) GetRequestMutex() *sync.Mutex {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsAvailable returns true if the client is available for use.
|
||||||
|
func (c *QwenClient) IsAvailable() bool {
|
||||||
|
return c.isAvailable
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetUnavailable sets the client to unavailable.
|
||||||
|
func (c *QwenClient) SetUnavailable() {
|
||||||
|
c.isAvailable = false
|
||||||
|
}
|
||||||
|
|||||||
@@ -52,5 +52,12 @@ type Client interface {
|
|||||||
// Provider returns the name of the AI service provider (e.g., "gemini", "claude").
|
// Provider returns the name of the AI service provider (e.g., "gemini", "claude").
|
||||||
Provider() string
|
Provider() string
|
||||||
|
|
||||||
|
// RefreshTokens refreshes the access tokens if needed
|
||||||
RefreshTokens(ctx context.Context) error
|
RefreshTokens(ctx context.Context) error
|
||||||
|
|
||||||
|
// IsAvailable returns true if the client is available for use.
|
||||||
|
IsAvailable() bool
|
||||||
|
|
||||||
|
// SetUnavailable sets the client to unavailable.
|
||||||
|
SetUnavailable()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user