若干更新迭代

This commit is contained in:
andy committed 2026-10-02 19:56:32 +08:00
1 parent deb9cc5bcf
commit 4ac786aafb
64 files changed
+3251 -291

No files matched your search

+7
View File
@@ -30,6 +30,9 @@ func DefaultBillingPriceRules() []PriceRule {
{ID: "base-seedream-5-0-pro-layers", Provider: "seedream", Capability: "image.generate", ReqKey: Seedream50ProModel, Unit: UnitImage, StandardUnitPriceFen: 15, Conditions: Conditions{"layerDecomposition": true}, Priority: 10, Note: "图层拆分:≤261万像素 ¥0.15/张,>261万像素 ¥0.30/张;最多预留底图加16个图层,完成后按每张实际像素结算。", Source: catalogSource("https://docs.volcengine.com/docs/82379/1544106?lang=zh"), Dimensions: seedreamDimensions()},
{ID: "base-bailian-wan27-i2v-720p", Provider: "bailian", Capability: "video.generate", ReqKey: "wan2.7-i2v-2026-04-25", VariantKey: "resolution=720p", Unit: UnitVideoSecond, StandardUnitPriceFen: 60, Source: catalogSource("https://help.aliyun.com/zh/model-studio/wan2-7-i2v")},
{ID: "base-bailian-wan27-i2v-1080p", Provider: "bailian", Capability: "video.generate", ReqKey: "wan2.7-i2v-2026-04-25", VariantKey: "resolution=1080p", Unit: UnitVideoSecond, StandardUnitPriceFen: 100, Source: catalogSource("https://help.aliyun.com/zh/model-studio/wan2-7-i2v")},
{ID: "base-bailian-wan30-video-480p", Provider: "bailian", Capability: "video.generate", ReqKey: BailianWan30VideoModel, VariantKey: "resolution=480p", Unit: UnitVideoSecond, StandardUnitPriceFen: 30, Note: "北京地域官方原价 ¥0.30/秒;官网限时折扣未计入标准成本,图片输入不另收费。", Source: wan30CatalogSource()},
{ID: "base-bailian-wan30-video-720p", Provider: "bailian", Capability: "video.generate", ReqKey: BailianWan30VideoModel, VariantKey: "resolution=720p", Unit: UnitVideoSecond, StandardUnitPriceFen: 60, Note: "北京地域官方原价 ¥0.60/秒;官网限时折扣未计入标准成本,图片输入不另收费。", Source: wan30CatalogSource()},
{ID: "base-bailian-wan30-video-1080p", Provider: "bailian", Capability: "video.generate", ReqKey: BailianWan30VideoModel, VariantKey: "resolution=1080p", Unit: UnitVideoSecond, StandardUnitPriceFen: 120, Note: "北京地域官方原价 ¥1.20/秒;官网限时折扣未计入标准成本,图片输入不另收费。", Source: wan30CatalogSource()},
{ID: "base-seedance-2-0-480p", Provider: "seedance", Capability: "video.generate", ReqKey: "doubao-seedance-2-0-260128", VariantKey: "resolution=480p", Unit: UnitVideoSecond, StandardUnitPriceFen: 46, Source: catalogSource("https://www.volcengine.com/docs/82379/1544106?lang=zh")},
{ID: "base-seedance-2-0-720p", Provider: "seedance", Capability: "video.generate", ReqKey: "doubao-seedance-2-0-260128", VariantKey: "resolution=720p", Unit: UnitVideoSecond, StandardUnitPriceFen: 99, Source: catalogSource("https://www.volcengine.com/docs/82379/1544106?lang=zh")},
{ID: "base-seedance-2-0-1080p", Provider: "seedance", Capability: "video.generate", ReqKey: "doubao-seedance-2-0-260128", VariantKey: "resolution=1080p", Unit: UnitVideoSecond, StandardUnitPriceFen: 248, Source: catalogSource("https://www.volcengine.com/docs/82379/1544106?lang=zh")},
@@ -51,6 +54,10 @@ func minimaxCatalogSource() map[string]any {
return map[string]any{"url": "https://platform.minimaxi.com/docs/guides/pricing-paygo#视频", "observedAt": "2026-09-04"}
}
func wan30CatalogSource() map[string]any {
return map[string]any{"url": "https://help.aliyun.com/zh/model-studio/model-pricing", "observedAt": "2026-10-01", "basis": "北京地域官方原价;限时7折未计入标准成本;仅输出视频按实际秒数计费(本接入仅支持图片输入)"}
}
func seedreamDimensions() []ParameterDimension {
return []ParameterDimension{{
Key: "size", Label: "分辨率", BaselineValue: "1.5K", DefaultValue: "1.5K",
@@ -14,6 +14,9 @@ func TestDefaultBillingPriceRulesFreezeOfficialCatalogMatchKeys(t *testing.T) {
"base-seedream-5-0-pro-layers": false,
"base-bailian-wan27-i2v-720p": false,
"base-bailian-wan27-i2v-1080p": false,
"base-bailian-wan30-video-480p": false,
"base-bailian-wan30-video-720p": false,
"base-bailian-wan30-video-1080p": false,
"base-seedance-2-0-480p": false,
"base-seedance-2-0-720p": false,
"base-seedance-2-0-1080p": false,
+1
View File
@@ -112,6 +112,7 @@ type AccountConfigStore interface {
// Billing HTTP module. Implementations can execute overview reads concurrently.
type ReadService interface {
Overview(context.Context, string, string) (Overview, error)
Wallet(context.Context, string) (Wallet, error)
AdminOverview(context.Context) (AdminOverview, error)
ListPrices(context.Context) ([]PriceRule, error)
GetPrice(context.Context, string) (*PriceRule, error)
+1 -2
View File
@@ -24,8 +24,7 @@ func MinimaxH3VideoPriceFenPerSecond(resolution string) int64 {
}
// CalculateMinimaxH3ActualAmountFen follows the official pay-as-you-go H3
// formula. The first five input images are free; this first product release
// accepts at most one, while keeping the calculator correct for future modes.
// formula. The first five input images are free.
func CalculateMinimaxH3ActualAmountFen(input MinimaxH3ActualAmountInput) (int64, error) {
if input.OutputSeconds <= 0 {
return 0, errors.New("MiniMax H3 output seconds must be positive")
+19
View File
@@ -69,6 +69,9 @@ func (s *Service) Overview(ctx context.Context, organizationID, accountID string
}
return Overview{Wallet: wallet, Ledger: ledger, Summary: Summarize(ledger), Personal: Summarize(personal)}, nil
}
func (s *Service) Wallet(ctx context.Context, organizationID string) (Wallet, error) {
return s.store.BillingWallet(ctx, organizationID)
}
func (s *Service) Quote(ctx context.Context, command QuoteCommand) (*Quote, error) {
if !s.enabled {
return nil, nil
@@ -114,6 +117,22 @@ func (s *Service) Quote(ctx context.Context, command QuoteCommand) (*Quote, erro
return quote, nil
}
if command.Provider == "minimax" && IsMinimaxH3BillingModel(command.ReqKey) {
inputImageCount := 0
if count, ok := number(parameters["referenceImageCount"]); ok {
inputImageCount = int(math.Ceil(count))
}
extraInputPriceFen := int64(max(0, inputImageCount-5) * 20)
quote.AmountFen = int64(math.Ceil((float64(quote.StandardUnitPriceFen)*quote.Quantity + float64(extraInputPriceFen)) * quote.MarkupMultiplier))
if quote.Parameters == nil {
quote.Parameters = Parameters{}
}
quote.Parameters["inputImageCount"] = float64(inputImageCount)
quote.Parameters["inputImagePriceFen"] = float64(extraInputPriceFen)
quote.ReservedAmountFen = quote.AmountFen
quote.SettlementStatus = "pending"
return quote, nil
}
if command.Provider == "bailian" && command.ReqKey == BailianWan30VideoModel {
quote.ReservedAmountFen = quote.AmountFen
quote.SettlementStatus = "pending"
return quote, nil
+35
View File
@@ -157,6 +157,41 @@ func TestServiceQuoteReservesMinimaxH3OfficialRateWithMarkup(t *testing.T) {
}
}
func TestServiceQuoteReservesMinimaxH3ExtraReferenceImages(t *testing.T) {
store := &quoteStoreStub{rules: []PriceRule{{
ID: "minimax-h3-2k", Provider: "minimax", Capability: "video.generate", ReqKey: MinimaxH3Model,
VariantKey: "resolution=2k", Unit: UnitVideoSecond, StandardUnitPriceFen: 80, MarkupMultiplier: 1.2, Enabled: true,
}}}
materials := make([]any, 9)
for i := range materials {
materials[i] = map[string]any{"type": "image", "url": "https://cdn.test/ref.png"}
}
quote, err := NewService(store, nil).Quote(context.Background(), QuoteCommand{
Provider: "minimax", Capability: "video.generate", ReqKey: MinimaxH3Model,
Payload: map[string]any{"settings": map[string]any{"duration": 5, "resolution": "2K"}, "materials": materials},
})
if err != nil {
t.Fatal(err)
}
if quote.AmountFen != 576 || quote.ReservedAmountFen != 576 || quote.Parameters["inputImageCount"] != float64(9) || quote.Parameters["inputImagePriceFen"] != float64(80) {
t.Fatalf("quote=%#v", quote)
}
}
func TestServiceQuoteReservesIndependentWan30Price(t *testing.T) {
store := &quoteStoreStub{rules: DefaultBillingPriceRules()}
quote, err := NewService(store, nil).Quote(context.Background(), QuoteCommand{
Provider: "bailian", Capability: "video.generate", ReqKey: BailianWan30VideoModel,
Payload: map[string]any{"settings": map[string]any{"duration": 5, "resolution": "1080P"}},
})
if err != nil {
t.Fatal(err)
}
if quote.PriceRuleID != "base-bailian-wan30-video-1080p" || quote.AmountFen != 720 || quote.ReservedAmountFen != 720 || quote.SettlementStatus != "pending" {
t.Fatalf("quote=%#v", quote)
}
}
func TestServiceQuoteAppliesSeedreamSizeAndReferenceInputPricing(t *testing.T) {
store := &quoteStoreStub{rules: []PriceRule{{
ID: "seedream-5", Provider: "seedream", Capability: "image.generate", ReqKey: Seedream50ProModel,
+20
View File
@@ -0,0 +1,20 @@
package billing
import (
"errors"
"math"
)
const BailianWan30VideoModel = "wan3.0-video"
// CalculateBailianWan30ActualAmountFen bills successful output duration. This
// integration accepts image inputs only, so there are no input video seconds.
func CalculateBailianWan30ActualAmountFen(outputSeconds float64, standardUnitPriceFen int64, markupMultiplier float64) (int64, error) {
if math.IsNaN(outputSeconds) || math.IsInf(outputSeconds, 0) || outputSeconds <= 0 {
return 0, errors.New("Wan 3.0 output seconds must be positive")
}
if standardUnitPriceFen <= 0 || math.IsNaN(markupMultiplier) || math.IsInf(markupMultiplier, 0) || markupMultiplier < 1 {
return 0, errors.New("Wan 3.0 price or markup is invalid")
}
return int64(math.Ceil(outputSeconds * float64(standardUnitPriceFen) * markupMultiplier)), nil
}