feat(grok): 视频改走 Grok Console 并新增图片模型
- grok 视频/图片统一走 console.x.ai:sso 换 DPoP 短期 token,绕开 grok.com 的 statsig 反爬 - 新增 grok-image:不带参考图走 /v1/images/generations,带图自动走 quality 上游 + /v1/images/edits - grok 额度改本地写死 图5/视频2,成功各扣一次,两份归零直接判死,无恢复时间 - 视频渲染预算统一 20 分钟(adobe/runway/grok/custom) - 删除参考图人脸打码(facemask + onnxruntime 依赖 + 前台开关)
This commit is contained in:
@@ -124,6 +124,58 @@ func (r *TokenRepository) ReserveQuota(ctx context.Context, pool, id string, amo
|
||||
return allowed, deducted, err
|
||||
}
|
||||
|
||||
// Grok accounts carry a forced local quota instead of an upstream balance:
|
||||
// Console 没有额度接口,所以导入时写死 图 5 / 视频 2,用一次扣一次,两个都归零直接判死。
|
||||
const (
|
||||
GrokImageQuotaKey = "grok_image_remaining"
|
||||
GrokVideoQuotaKey = "grok_video_remaining"
|
||||
GrokImageQuota = 5
|
||||
GrokVideoQuota = 2
|
||||
)
|
||||
|
||||
// ConsumeGrokQuota deducts one unit from a grok account's local per-kind quota
|
||||
// under a row lock. Zeroed kinds are flagged (image_limited / video_limited) so
|
||||
// scheduling skips them; once both are zero the account is dead (no reset time —
|
||||
// 用完就废).
|
||||
func (r *TokenRepository) ConsumeGrokQuota(ctx context.Context, id, kind string) error {
|
||||
return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
var item model.TokenAccount
|
||||
if e := tx.Clauses(clause.Locking{Strength: "UPDATE"}).
|
||||
First(&item, "pool = ? AND id = ?", "grok", id).Error; e != nil {
|
||||
return e
|
||||
}
|
||||
images, known := metaInt(item.Meta, GrokImageQuotaKey)
|
||||
if !known {
|
||||
images = GrokImageQuota
|
||||
}
|
||||
videos, known := metaInt(item.Meta, GrokVideoQuotaKey)
|
||||
if !known {
|
||||
videos = GrokVideoQuota
|
||||
}
|
||||
if kind == "video" {
|
||||
videos = max(0, videos-1)
|
||||
} else {
|
||||
images = max(0, images-1)
|
||||
}
|
||||
meta := cloneMeta(item.Meta)
|
||||
meta[GrokImageQuotaKey] = images
|
||||
meta[GrokVideoQuotaKey] = videos
|
||||
patch := map[string]any{
|
||||
"meta": meta,
|
||||
"image_limited": images <= 0,
|
||||
"video_limited": videos <= 0,
|
||||
"updated_at": time.Now(),
|
||||
}
|
||||
if images <= 0 && videos <= 0 {
|
||||
patch["status"] = "disabled"
|
||||
patch["dead"] = true
|
||||
}
|
||||
return tx.Model(&model.TokenAccount{}).
|
||||
Where("pool = ? AND id = ?", "grok", id).
|
||||
Updates(patch).Error
|
||||
})
|
||||
}
|
||||
|
||||
// RefundQuota atomically adds `amount` back to cached_quota_remaining (releasing a
|
||||
// hold from a reservation whose render then failed). No-op if the balance is
|
||||
// unknown. Row-locked like ReserveQuota.
|
||||
|
||||
Reference in New Issue
Block a user