Sub2API v1.0 - AI API 网关(二开初始版本,基于上游 Wei-Shaw/sub2api)
Release / update-version (push) Has been cancelled
Release / build-frontend (push) Has been cancelled
Release / release (push) Has been cancelled
Release / sync-version-file (push) Has been cancelled
CI / shell (push) Canceled after 0s
CI / test (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
Security Scan / backend-security (push) Canceled after 0s
Security Scan / frontend-security (push) Canceled after 0s
Release / update-version (push) Has been cancelled
Release / build-frontend (push) Has been cancelled
Release / release (push) Has been cancelled
Release / sync-version-file (push) Has been cancelled
CI / shell (push) Canceled after 0s
CI / test (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
Security Scan / backend-security (push) Canceled after 0s
Security Scan / frontend-security (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
const (
|
||||
grokMissingUsageErrorCode = "grok_missing_usage"
|
||||
grokMissingUsageMessage = "xAI upstream returned a successful chat completion without billable usage"
|
||||
)
|
||||
|
||||
// hasBillableGrokChatUsage stays aligned with the aggregate token buckets used
|
||||
// to account for chat completions. Detail fields alone do not prove that the
|
||||
// successful response can be settled safely.
|
||||
func hasBillableGrokChatUsage(usage OpenAIUsage) bool {
|
||||
return usage.InputTokens > 0 ||
|
||||
usage.OutputTokens > 0 ||
|
||||
usage.CacheCreationInputTokens > 0 ||
|
||||
usage.CacheReadInputTokens > 0
|
||||
}
|
||||
|
||||
// requiresBillableGrokChatUsage identifies Grok traffic by both account
|
||||
// platform and model identity. Grok models may be served through generic
|
||||
// OpenAI-compatible accounts, so account.Platform alone is not a safe billing
|
||||
// boundary.
|
||||
func requiresBillableGrokChatUsage(account *Account, models ...string) bool {
|
||||
if account != nil && account.Platform == PlatformGrok {
|
||||
return true
|
||||
}
|
||||
for _, model := range models {
|
||||
normalized := strings.ToLower(strings.TrimSpace(model))
|
||||
if separator := strings.LastIndex(normalized, "/"); separator >= 0 {
|
||||
normalized = strings.TrimSpace(normalized[separator+1:])
|
||||
}
|
||||
if normalized == "grok" || strings.HasPrefix(normalized, "grok-") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func newGrokMissingUsageFailoverError(c *gin.Context, account *Account, upstreamRequestID string) *UpstreamFailoverError {
|
||||
accountID := int64(0)
|
||||
accountName := ""
|
||||
if account != nil {
|
||||
accountID = account.ID
|
||||
accountName = account.Name
|
||||
}
|
||||
|
||||
setOpsUpstreamError(c, http.StatusBadGateway, grokMissingUsageMessage, "")
|
||||
appendOpsUpstreamError(c, OpsUpstreamErrorEvent{
|
||||
Platform: PlatformGrok,
|
||||
AccountID: accountID,
|
||||
AccountName: accountName,
|
||||
UpstreamStatusCode: http.StatusBadGateway,
|
||||
UpstreamRequestID: strings.TrimSpace(upstreamRequestID),
|
||||
Kind: "failover",
|
||||
Message: grokMissingUsageMessage,
|
||||
})
|
||||
|
||||
body, _ := json.Marshal(gin.H{
|
||||
"error": gin.H{
|
||||
"type": "upstream_error",
|
||||
"code": grokMissingUsageErrorCode,
|
||||
"message": grokMissingUsageMessage,
|
||||
},
|
||||
})
|
||||
headers := http.Header{}
|
||||
if requestID := strings.TrimSpace(upstreamRequestID); requestID != "" {
|
||||
headers.Set("x-request-id", requestID)
|
||||
}
|
||||
return &UpstreamFailoverError{
|
||||
StatusCode: http.StatusBadGateway,
|
||||
ResponseBody: body,
|
||||
ResponseHeaders: headers,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user