tokenFactory/setting/ratio_setting/video_pricing_rule.go

275 lines
9.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package ratio_setting
import (
"strconv"
"strings"
"github.com/QuantumNous/new-api/common"
"github.com/QuantumNous/new-api/types"
)
type VideoResolutionPriceRule struct {
Resolution string `json:"resolution"`
TokenPrice float64 `json:"token_price"`
PixelCompression float64 `json:"pixel_compression"`
}
// VideoResolutionPerVideoRule is fixed USD per completed video for a resolution
// tier (same monetary unit as VideoPrice / ModelPrice: dollars per job).
type VideoResolutionPerVideoRule struct {
Resolution string `json:"resolution"`
VideoPrice float64 `json:"video_price"`
}
// VideoResolutionAudioPriceRule represents price by resolution + audio flag.
// Price semantics are USD-based internal unit (same as ModelPrice/VideoPrice).
type VideoResolutionAudioPriceRule struct {
Resolution string `json:"resolution"`
HasAudio bool `json:"has_audio"`
Price float64 `json:"price"`
}
type VideoImagePriceRule struct {
TokenPrice float64 `json:"token_price"`
PixelCompression float64 `json:"pixel_compression"`
}
type VideoPricingRules struct {
TextToVideo []VideoResolutionPriceRule `json:"text_to_video,omitempty"`
ImageToVideo *VideoImagePriceRule `json:"image_to_video,omitempty"`
ImageToVideoRules []VideoResolutionPriceRule `json:"image_to_video_rules,omitempty"`
VideoToVideo []VideoResolutionPriceRule `json:"video_to_video,omitempty"`
VideoToVideoInput []VideoResolutionPriceRule `json:"video_to_video_input,omitempty"`
VideoToVideoOutput []VideoResolutionPriceRule `json:"video_to_video_output,omitempty"`
SimilarityThreshold float64 `json:"similarity_threshold,omitempty"`
// Per-video (flat $ per output) by resolution; same dollar semantics as VideoPrice.
TextToVideoPerVideo []VideoResolutionPerVideoRule `json:"text_to_video_per_video,omitempty"`
ImageToVideoPerVideo []VideoResolutionPerVideoRule `json:"image_to_video_per_video,omitempty"`
VideoToVideoInputPerVideo []VideoResolutionPerVideoRule `json:"video_to_video_input_per_video,omitempty"`
VideoToVideoOutputPerVideo []VideoResolutionPerVideoRule `json:"video_to_video_output_per_video,omitempty"`
// New billing tables:
// - *_per_second: by ceil(seconds) × unit price
// - *_per_item: by generated video count
TextToVideoPerSecond []VideoResolutionAudioPriceRule `json:"text_to_video_per_second,omitempty"`
ImageToVideoPerSecond []VideoResolutionAudioPriceRule `json:"image_to_video_per_second,omitempty"`
VideoToVideoPerSecond []VideoResolutionAudioPriceRule `json:"video_to_video_per_second,omitempty"`
TextToVideoPerItem []VideoResolutionAudioPriceRule `json:"text_to_video_per_item,omitempty"`
ImageToVideoPerItem []VideoResolutionAudioPriceRule `json:"image_to_video_per_item,omitempty"`
VideoToVideoPerItem []VideoResolutionAudioPriceRule `json:"video_to_video_per_item,omitempty"`
}
var videoPricingRulesMap = types.NewRWMap[string, VideoPricingRules]()
var channelVideoPricingRulesMap = types.NewRWMap[string, map[string]VideoPricingRules]()
func normalizeVideoRules(v VideoPricingRules) VideoPricingRules {
if v.SimilarityThreshold <= 0 {
v.SimilarityThreshold = 0.35
}
for i := range v.TextToVideo {
if v.TextToVideo[i].PixelCompression <= 0 {
v.TextToVideo[i].PixelCompression = 1024
}
}
for i := range v.VideoToVideo {
if v.VideoToVideo[i].PixelCompression <= 0 {
v.VideoToVideo[i].PixelCompression = 1024
}
}
for i := range v.ImageToVideoRules {
if v.ImageToVideoRules[i].PixelCompression <= 0 {
v.ImageToVideoRules[i].PixelCompression = 1024
}
}
for i := range v.VideoToVideoInput {
if v.VideoToVideoInput[i].PixelCompression <= 0 {
v.VideoToVideoInput[i].PixelCompression = 1024
}
}
for i := range v.VideoToVideoOutput {
if v.VideoToVideoOutput[i].PixelCompression <= 0 {
v.VideoToVideoOutput[i].PixelCompression = 1024
}
}
if v.ImageToVideo != nil && v.ImageToVideo.PixelCompression <= 0 {
v.ImageToVideo.PixelCompression = 1024
}
for i := range v.TextToVideoPerVideo {
v.TextToVideoPerVideo[i].Resolution = strings.TrimSpace(v.TextToVideoPerVideo[i].Resolution)
}
for i := range v.ImageToVideoPerVideo {
v.ImageToVideoPerVideo[i].Resolution = strings.TrimSpace(v.ImageToVideoPerVideo[i].Resolution)
}
for i := range v.VideoToVideoInputPerVideo {
v.VideoToVideoInputPerVideo[i].Resolution = strings.TrimSpace(v.VideoToVideoInputPerVideo[i].Resolution)
}
for i := range v.VideoToVideoOutputPerVideo {
v.VideoToVideoOutputPerVideo[i].Resolution = strings.TrimSpace(v.VideoToVideoOutputPerVideo[i].Resolution)
}
for i := range v.TextToVideoPerSecond {
v.TextToVideoPerSecond[i].Resolution = strings.TrimSpace(v.TextToVideoPerSecond[i].Resolution)
}
for i := range v.ImageToVideoPerSecond {
v.ImageToVideoPerSecond[i].Resolution = strings.TrimSpace(v.ImageToVideoPerSecond[i].Resolution)
}
for i := range v.VideoToVideoPerSecond {
v.VideoToVideoPerSecond[i].Resolution = strings.TrimSpace(v.VideoToVideoPerSecond[i].Resolution)
}
for i := range v.TextToVideoPerItem {
v.TextToVideoPerItem[i].Resolution = strings.TrimSpace(v.TextToVideoPerItem[i].Resolution)
}
for i := range v.ImageToVideoPerItem {
v.ImageToVideoPerItem[i].Resolution = strings.TrimSpace(v.ImageToVideoPerItem[i].Resolution)
}
for i := range v.VideoToVideoPerItem {
v.VideoToVideoPerItem[i].Resolution = strings.TrimSpace(v.VideoToVideoPerItem[i].Resolution)
}
return v
}
// HasUsableVideoPerVideoRules reports whether any per-resolution flat video price tier exists
// with a positive video_price (USD per completed video, same unit as VideoPrice).
func HasUsableVideoPerVideoRules(v VideoPricingRules) bool {
for _, r := range v.TextToVideoPerItem {
if r.Price > 0 {
return true
}
}
for _, r := range v.ImageToVideoPerItem {
if r.Price > 0 {
return true
}
}
for _, r := range v.VideoToVideoPerItem {
if r.Price > 0 {
return true
}
}
for _, r := range v.TextToVideoPerVideo {
if r.VideoPrice > 0 {
return true
}
}
for _, r := range v.ImageToVideoPerVideo {
if r.VideoPrice > 0 {
return true
}
}
for _, r := range v.VideoToVideoInputPerVideo {
if r.VideoPrice > 0 {
return true
}
}
for _, r := range v.VideoToVideoOutputPerVideo {
if r.VideoPrice > 0 {
return true
}
}
return false
}
func HasUsableVideoPerSecondRules(v VideoPricingRules) bool {
for _, r := range v.TextToVideoPerSecond {
if r.Price > 0 {
return true
}
}
for _, r := range v.ImageToVideoPerSecond {
if r.Price > 0 {
return true
}
}
for _, r := range v.VideoToVideoPerSecond {
if r.Price > 0 {
return true
}
}
return false
}
func normalizeVideoRulesMap(src map[string]VideoPricingRules) map[string]VideoPricingRules {
dst := make(map[string]VideoPricingRules, len(src))
for model, rules := range src {
name := FormatMatchingModelName(strings.TrimSpace(model))
if name == "" {
continue
}
dst[name] = normalizeVideoRules(rules)
}
return dst
}
func UpdateVideoPricingRulesByJSONString(jsonStr string) error {
trimmed := strings.TrimSpace(jsonStr)
if trimmed == "" {
videoPricingRulesMap.Clear()
return nil
}
var parsed map[string]VideoPricingRules
if err := common.UnmarshalJsonStr(trimmed, &parsed); err != nil {
return err
}
videoPricingRulesMap.Clear()
videoPricingRulesMap.AddAll(normalizeVideoRulesMap(parsed))
InvalidateExposedDataCache()
return nil
}
func VideoPricingRules2JSONString() string {
jsonBytes, err := common.Marshal(videoPricingRulesMap.ReadAll())
if err != nil {
common.SysError("error marshalling video pricing rules: " + err.Error())
return "{}"
}
return string(jsonBytes)
}
func GetVideoPricingRules(modelName string) (VideoPricingRules, bool) {
name := FormatMatchingModelName(modelName)
rules, ok := videoPricingRulesMap.Get(name)
return rules, ok
}
func UpdateChannelVideoPricingRulesByJSONString(jsonStr string) error {
trimmed := strings.TrimSpace(jsonStr)
if trimmed == "" {
channelVideoPricingRulesMap.Clear()
return nil
}
var parsed map[string]map[string]VideoPricingRules
if err := common.UnmarshalJsonStr(trimmed, &parsed); err != nil {
return err
}
normalized := make(map[string]map[string]VideoPricingRules, len(parsed))
for channelID, modelRules := range parsed {
if _, err := strconv.Atoi(channelID); err != nil {
continue
}
normalized[channelID] = normalizeVideoRulesMap(modelRules)
}
channelVideoPricingRulesMap.Clear()
channelVideoPricingRulesMap.AddAll(normalized)
return nil
}
func ChannelVideoPricingRules2JSONString() string {
jsonBytes, err := common.Marshal(channelVideoPricingRulesMap.ReadAll())
if err != nil {
common.SysError("error marshalling channel video pricing rules: " + err.Error())
return "{}"
}
return string(jsonBytes)
}
func GetChannelVideoPricingRules(channelID int, modelName string) (VideoPricingRules, bool) {
key := normalizeChannelID(channelID)
if key == "" {
return VideoPricingRules{}, false
}
channelMap, ok := channelVideoPricingRulesMap.Get(key)
if !ok {
return VideoPricingRules{}, false
}
rules, ok := channelMap[FormatMatchingModelName(modelName)]
return rules, ok
}