Repository navigation
feat: grok Usage Guidelines Violation Fee #2753
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
6714fa5
bdfd2c9
d7c48e3
34f32fd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,163 @@ | ||
| package service | ||
|
|
||
| import ( | ||
| "fmt" | ||
| "strings" | ||
| "time" | ||
|
|
||
| "github.com/QuantumNous/new-api/common" | ||
| "github.com/QuantumNous/new-api/logger" | ||
| "github.com/QuantumNous/new-api/model" | ||
| relaycommon "github.com/QuantumNous/new-api/relay/common" | ||
| "github.com/QuantumNous/new-api/setting/model_setting" | ||
| "github.com/QuantumNous/new-api/types" | ||
|
|
||
| "github.com/shopspring/decimal" | ||
|
|
||
| "github.com/gin-gonic/gin" | ||
| ) | ||
|
|
||
| const ( | ||
| ViolationFeeCodePrefix = "violation_fee." | ||
| CSAMViolationMarker = "Failed check: SAFETY_CHECK_TYPE_CSAM" | ||
| ) | ||
|
|
||
| func IsViolationFeeCode(code types.ErrorCode) bool { | ||
| return strings.HasPrefix(string(code), ViolationFeeCodePrefix) | ||
| } | ||
|
|
||
| func HasCSAMViolationMarker(err *types.NewAPIError) bool { | ||
| if err == nil { | ||
| return false | ||
| } | ||
| if strings.Contains(err.Error(), CSAMViolationMarker) { | ||
| return true | ||
| } | ||
| msg := err.ToOpenAIError().Message | ||
| return strings.Contains(msg, CSAMViolationMarker) | ||
| } | ||
|
|
||
| func WrapAsViolationFeeGrokCSAM(err *types.NewAPIError) *types.NewAPIError { | ||
| if err == nil { | ||
| return nil | ||
| } | ||
| oai := err.ToOpenAIError() | ||
| oai.Type = string(types.ErrorCodeViolationFeeGrokCSAM) | ||
| oai.Code = string(types.ErrorCodeViolationFeeGrokCSAM) | ||
| return types.WithOpenAIError(oai, err.StatusCode, types.ErrOptionWithSkipRetry()) | ||
| } | ||
|
|
||
| // NormalizeViolationFeeError ensures: | ||
| // - if the CSAM marker is present, error.code is set to a stable violation-fee code and skip-retry is enabled. | ||
| // - if error.code already has the violation-fee prefix, skip-retry is enabled. | ||
| // | ||
| // It must be called before retry decision logic. | ||
| func NormalizeViolationFeeError(err *types.NewAPIError) *types.NewAPIError { | ||
| if err == nil { | ||
| return nil | ||
| } | ||
|
|
||
| if HasCSAMViolationMarker(err) { | ||
| return WrapAsViolationFeeGrokCSAM(err) | ||
| } | ||
|
|
||
| if IsViolationFeeCode(err.GetErrorCode()) { | ||
| oai := err.ToOpenAIError() | ||
| return types.WithOpenAIError(oai, err.StatusCode, types.ErrOptionWithSkipRetry()) | ||
| } | ||
|
|
||
| return err | ||
| } | ||
|
|
||
| func shouldChargeViolationFee(err *types.NewAPIError) bool { | ||
| if err == nil { | ||
| return false | ||
| } | ||
| if err.GetErrorCode() == types.ErrorCodeViolationFeeGrokCSAM { | ||
| return true | ||
| } | ||
| // In case some callers didn't normalize, keep a safety net. | ||
| return HasCSAMViolationMarker(err) | ||
| } | ||
|
|
||
| func calcViolationFeeQuota(amount, groupRatio float64) int { | ||
| if amount <= 0 { | ||
| return 0 | ||
| } | ||
| if groupRatio <= 0 { | ||
| return 0 | ||
| } | ||
| quota := decimal.NewFromFloat(amount). | ||
| Mul(decimal.NewFromFloat(common.QuotaPerUnit)). | ||
| Mul(decimal.NewFromFloat(groupRatio)). | ||
| Round(0). | ||
| IntPart() | ||
| if quota <= 0 { | ||
| return 0 | ||
| } | ||
| return int(quota) | ||
| } | ||
|
|
||
| // ChargeViolationFeeIfNeeded charges an additional fee after the normal flow finishes (including refund). | ||
| // It uses Grok fee settings as the fee policy. | ||
| func ChargeViolationFeeIfNeeded(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, apiErr *types.NewAPIError) bool { | ||
| if ctx == nil || relayInfo == nil || apiErr == nil { | ||
| return false | ||
| } | ||
| //if relayInfo.IsPlayground { | ||
| // return false | ||
| //} | ||
| if !shouldChargeViolationFee(apiErr) { | ||
| return false | ||
| } | ||
|
|
||
| settings := model_setting.GetGrokSettings() | ||
| if settings == nil || !settings.ViolationDeductionEnabled { | ||
| return false | ||
| } | ||
|
|
||
| groupRatio := relayInfo.PriceData.GroupRatioInfo.GroupRatio | ||
| feeQuota := calcViolationFeeQuota(settings.ViolationDeductionAmount, groupRatio) | ||
| if feeQuota <= 0 { | ||
| return false | ||
| } | ||
|
|
||
| if err := PostConsumeQuota(relayInfo, feeQuota, 0, true); err != nil { | ||
| logger.LogError(ctx, fmt.Sprintf("failed to charge violation fee: %s", err.Error())) | ||
| return false | ||
| } | ||
|
|
||
| model.UpdateUserUsedQuotaAndRequestCount(relayInfo.UserId, feeQuota) | ||
| model.UpdateChannelUsedQuota(relayInfo.ChannelId, feeQuota) | ||
|
Comment on lines
+125
to
+131
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Quota updates lack atomicity.
Consider wrapping these operations in a transaction or implementing compensation logic if a later operation fails. 🤖 Prompt for AI Agents |
||
|
|
||
| useTimeSeconds := time.Now().Unix() - relayInfo.StartTime.Unix() | ||
| tokenName := ctx.GetString("token_name") | ||
| oai := apiErr.ToOpenAIError() | ||
|
|
||
| other := map[string]any{ | ||
| "violation_fee": true, | ||
| "violation_fee_code": string(types.ErrorCodeViolationFeeGrokCSAM), | ||
| "fee_quota": feeQuota, | ||
| "base_amount": settings.ViolationDeductionAmount, | ||
| "group_ratio": groupRatio, | ||
| "status_code": apiErr.StatusCode, | ||
| "upstream_error_type": oai.Type, | ||
| "upstream_error_code": fmt.Sprintf("%v", oai.Code), | ||
| "violation_fee_marker": CSAMViolationMarker, | ||
| } | ||
|
|
||
| model.RecordConsumeLog(ctx, relayInfo.UserId, model.RecordConsumeLogParams{ | ||
| ChannelId: relayInfo.ChannelId, | ||
| ModelName: relayInfo.OriginModelName, | ||
| TokenName: tokenName, | ||
| Quota: feeQuota, | ||
| Content: "Violation fee charged", | ||
| TokenId: relayInfo.TokenId, | ||
| UseTimeSeconds: int(useTimeSeconds), | ||
| IsStream: relayInfo.IsStream, | ||
| Group: relayInfo.UsingGroup, | ||
| Other: other, | ||
| }) | ||
|
|
||
| return true | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| package model_setting | ||
|
|
||
| import "github.com/QuantumNous/new-api/setting/config" | ||
|
|
||
| // GrokSettings defines Grok model configuration. | ||
| type GrokSettings struct { | ||
| ViolationDeductionEnabled bool `json:"violation_deduction_enabled"` | ||
| ViolationDeductionAmount float64 `json:"violation_deduction_amount"` | ||
| } | ||
|
|
||
| var defaultGrokSettings = GrokSettings{ | ||
| ViolationDeductionEnabled: true, | ||
| ViolationDeductionAmount: 0.05, | ||
| } | ||
|
|
||
| var grokSettings = defaultGrokSettings | ||
|
|
||
| func init() { | ||
| config.GlobalConfig.Register("grok", &grokSettings) | ||
| } | ||
|
|
||
| func GetGrokSettings() *GrokSettings { | ||
| return &grokSettings | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: QuantumNous/new-api
Length of output: 82
🏁 Script executed:
Repository: QuantumNous/new-api
Length of output: 3017
🏁 Script executed:
Repository: QuantumNous/new-api
Length of output: 1916
🏁 Script executed:
Repository: QuantumNous/new-api
Length of output: 4255
🏁 Script executed:
Repository: QuantumNous/new-api
Length of output: 658
🏁 Script executed:
Repository: QuantumNous/new-api
Length of output: 628
🏁 Script executed:
Repository: QuantumNous/new-api
Length of output: 995
🏁 Script executed:
Repository: QuantumNous/new-api
Length of output: 582
🏁 Script executed:
Repository: QuantumNous/new-api
Length of output: 1489
🏁 Script executed:
Repository: QuantumNous/new-api
Length of output: 1365
🏁 Script executed:
Repository: QuantumNous/new-api
Length of output: 114
Line 222 unnecessarily normalizes errors that will be normalized again in the defer block.
In the relay handler path (line 216 success/failure that passes shouldRetry check),
NormalizeViolationFeeErroris called at line 222, then again at line 171 in the defer block. While the function is idempotent by design—IsViolationFeeCodeuses a prefix check that handles already-wrapped errors, andHasCSAMViolationMarkerpersists in the message—the redundant call is unnecessary. Either normalize once in the defer (removing line 222) for all error paths, or skip the defer normalization if already normalized in the loop.🤖 Prompt for AI Agents