feat: publish sanitized custom.97 source
This commit is contained in:
1022 files changed
+69903
-19207
No files matched your search
+121
-11
@@ -157,7 +157,8 @@ func GetResponseBody(method, url string, channel *model.Channel, headers http.He
|
||||
for k := range headers {
|
||||
req.Header.Add(k, headers.Get(k))
|
||||
}
|
||||
client, err := service.GetHttpClientWithProxy(channel.GetSetting().Proxy)
|
||||
setting := channel.GetSetting()
|
||||
client, err := service.GetHttpClientWithProxySettings(setting.Proxy, setting)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -420,6 +421,113 @@ func updateChannelMoonshotBalance(channel *model.Channel) (float64, error) {
|
||||
return availableBalanceUsd, nil
|
||||
}
|
||||
|
||||
func updateChannelSub2APIBalance(channel *model.Channel) (float64, error) {
|
||||
baseURL := channel.GetBaseURL()
|
||||
if baseURL == "" {
|
||||
return 0, errors.New("Sub2API channel is missing base_url")
|
||||
}
|
||||
url := fmt.Sprintf("%s/v1/usage", strings.TrimRight(baseURL, "/"))
|
||||
body, err := GetResponseBody("GET", url, channel, GetAuthHeader(channel.Key))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
type sub2apiUsageResponse struct {
|
||||
Balance *float64 `json:"balance"`
|
||||
Remaining *float64 `json:"remaining"`
|
||||
IsValid *bool `json:"isValid"`
|
||||
Subscription *struct {
|
||||
DailyLimitUSD *float64 `json:"daily_limit_usd"`
|
||||
WeeklyLimitUSD *float64 `json:"weekly_limit_usd"`
|
||||
MonthlyLimitUSD *float64 `json:"monthly_limit_usd"`
|
||||
} `json:"subscription"`
|
||||
}
|
||||
usage := sub2apiUsageResponse{}
|
||||
if err := common.Unmarshal(body, &usage); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if usage.IsValid != nil && !*usage.IsValid {
|
||||
return 0, errors.New("Sub2API key is invalid (isValid=false)")
|
||||
}
|
||||
|
||||
balance := usage.Remaining
|
||||
if balance == nil {
|
||||
balance = usage.Balance
|
||||
}
|
||||
if balance == nil {
|
||||
return 0, errors.New("Sub2API /v1/usage response is missing remaining and balance fields (the key may belong to a subscription group without an available balance)")
|
||||
}
|
||||
// Sub2API reports remaining=-1 for a subscription group with no daily, weekly,
|
||||
// or monthly limit. A wallet balance can legitimately be negative, so detect
|
||||
// the unlimited case from the subscription limits rather than the value.
|
||||
subscription := usage.Subscription
|
||||
if subscription != nil && subscription.DailyLimitUSD == nil && subscription.WeeklyLimitUSD == nil && subscription.MonthlyLimitUSD == nil {
|
||||
return 0, errors.New("Sub2API /v1/usage reports an unlimited subscription without a finite balance")
|
||||
}
|
||||
channel.UpdateBalance(*balance)
|
||||
return *balance, nil
|
||||
}
|
||||
|
||||
// updateChannelNewAPIBalance reads the upstream key's remaining quota. When the
|
||||
// upstream also returns its owner's quota, the smaller of the two is what the
|
||||
// key can still spend. Quota converts to USD with the upstream's quota_per_unit.
|
||||
func updateChannelNewAPIBalance(channel *model.Channel) (float64, error) {
|
||||
baseURL := strings.TrimRight(channel.GetBaseURL(), "/")
|
||||
if baseURL == "" {
|
||||
return 0, errors.New("New API channel is missing base_url")
|
||||
}
|
||||
body, err := GetResponseBody("GET", baseURL+"/api/usage/token/", channel, GetAuthHeader(channel.Key))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var usage struct {
|
||||
Code bool `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Data struct {
|
||||
TotalAvailable float64 `json:"total_available"`
|
||||
UnlimitedQuota bool `json:"unlimited_quota"`
|
||||
UserQuota *struct {
|
||||
TotalAvailable float64 `json:"total_available"`
|
||||
} `json:"user_quota"`
|
||||
} `json:"data"`
|
||||
}
|
||||
if err := common.Unmarshal(body, &usage); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if !usage.Code {
|
||||
return 0, fmt.Errorf("New API usage query failed: %s", usage.Message)
|
||||
}
|
||||
remaining := usage.Data.TotalAvailable
|
||||
if usage.Data.UnlimitedQuota {
|
||||
remaining = math.Inf(1)
|
||||
}
|
||||
if userQuota := usage.Data.UserQuota; userQuota != nil {
|
||||
remaining = min(remaining, userQuota.TotalAvailable)
|
||||
}
|
||||
if math.IsInf(remaining, 1) {
|
||||
return 0, common.NewMessage(`The upstream key has unlimited quota. Turn on "Allow querying user quota" for this key on the upstream to see the real balance.`)
|
||||
}
|
||||
|
||||
body, err = GetResponseBody("GET", baseURL+"/api/status", channel, nil)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var status struct {
|
||||
Data struct {
|
||||
QuotaPerUnit float64 `json:"quota_per_unit"`
|
||||
} `json:"data"`
|
||||
}
|
||||
if err := common.Unmarshal(body, &status); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if status.Data.QuotaPerUnit <= 0 {
|
||||
return 0, errors.New("New API /api/status response is missing quota_per_unit")
|
||||
}
|
||||
balance := remaining / status.Data.QuotaPerUnit
|
||||
channel.UpdateBalance(balance)
|
||||
return balance, nil
|
||||
}
|
||||
|
||||
func fetchAdvancedCustomBalance(channel *model.Channel) (channelBalanceResult, error) {
|
||||
key := strings.TrimSpace(channel.Key)
|
||||
info := &relaycommon.RelayInfo{
|
||||
@@ -453,7 +561,8 @@ func fetchAdvancedCustomBalance(channel *model.Channel) (channelBalanceResult, e
|
||||
request.Host = headers.Get(name)
|
||||
}
|
||||
}
|
||||
client, err := service.GetHttpClientWithProxy(channel.GetSetting().Proxy)
|
||||
setting := channel.GetSetting()
|
||||
client, err := service.GetHttpClientWithProxySettings(setting.Proxy, setting)
|
||||
if err != nil {
|
||||
return channelBalanceResult{}, sanitizeFetchModelsError(err, key)
|
||||
}
|
||||
@@ -524,7 +633,7 @@ func updateStandardChannelBalance(channel *model.Channel) (float64, error) {
|
||||
baseURL = channel.GetBaseURL()
|
||||
}
|
||||
case constant.ChannelTypeAzure:
|
||||
return 0, errors.New("尚未实现")
|
||||
return 0, common.NewMessage("Not implemented yet")
|
||||
case constant.ChannelTypeCustom:
|
||||
baseURL = channel.GetBaseURL()
|
||||
//case common.ChannelTypeOpenAISB:
|
||||
@@ -543,8 +652,12 @@ func updateStandardChannelBalance(channel *model.Channel) (float64, error) {
|
||||
return updateChannelOpenRouterBalance(channel)
|
||||
case constant.ChannelTypeMoonshot:
|
||||
return updateChannelMoonshotBalance(channel)
|
||||
case constant.ChannelTypeSub2API:
|
||||
return updateChannelSub2APIBalance(channel)
|
||||
case constant.ChannelTypeNewAPI:
|
||||
return updateChannelNewAPIBalance(channel)
|
||||
default:
|
||||
return 0, errors.New("尚未实现")
|
||||
return 0, common.NewMessage("Not implemented yet")
|
||||
}
|
||||
url := fmt.Sprintf("%s/v1/dashboard/billing/subscription", baseURL)
|
||||
|
||||
@@ -594,10 +707,7 @@ func UpdateChannelBalance(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
if channel.ChannelInfo.IsMultiKey {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": false,
|
||||
"message": "多密钥渠道不支持余额查询",
|
||||
})
|
||||
common.ApiErrorT(c, "Multi-key channels do not support balance queries")
|
||||
return
|
||||
}
|
||||
result, err := updateChannelBalance(channel)
|
||||
@@ -639,7 +749,7 @@ func updateAllChannelsBalance() error {
|
||||
} else if result.RawResponse == "" {
|
||||
// err is nil & balance <= 0 means quota is used up
|
||||
if result.Balance <= 0 {
|
||||
service.DisableChannel(*types.NewChannelError(channel.Id, channel.Type, channel.Name, channel.ChannelInfo.IsMultiKey, "", channel.GetAutoBan()), "余额不足")
|
||||
service.DisableChannel(*types.NewChannelError(channel.Id, channel.Type, channel.Name, channel.ChannelInfo.IsMultiKey, "", channel.GetAutoBan()), "Insufficient balance")
|
||||
}
|
||||
}
|
||||
time.Sleep(common.RequestInterval)
|
||||
@@ -664,8 +774,8 @@ func UpdateAllChannelsBalance(c *gin.Context) {
|
||||
func AutomaticallyUpdateChannels(frequency int) {
|
||||
for {
|
||||
time.Sleep(time.Duration(frequency) * time.Minute)
|
||||
common.SysLog("updating all channels")
|
||||
common.SysLog(common.LogText("updating all channels"))
|
||||
_ = updateAllChannelsBalance()
|
||||
common.SysLog("channels update done")
|
||||
common.SysLog(common.LogText("channels update done"))
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user