| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283 |
- 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{
- ProxyID: opsUpstreamProxyID(account),
- ProxyName: opsUpstreamProxyName(account),
- 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,
- }
- }
|