114 lines
4.1 KiB
Go
114 lines
4.1 KiB
Go
package app
|
|
|
|
import "infinite-canvas/backend/internal/model"
|
|
|
|
// LogicalModelQuote 是创作端当前参数命中的实际供应线路报价。
|
|
// Token 视频在上游返回真实 usage 前只能预估,创建任务仍以账务预留逻辑为准。
|
|
type LogicalModelQuote struct {
|
|
LogicalModelID string `json:"logicalModelId"`
|
|
BillingMode string `json:"billingMode"`
|
|
Quantity int64 `json:"quantity"`
|
|
AmountMicrocredits int64 `json:"amountMicrocredits"`
|
|
Estimated bool `json:"estimated"`
|
|
}
|
|
|
|
func (s *Service) QuoteLogicalModel(logicalModelID string, intent ModelRequestIntent) (*LogicalModelQuote, error) {
|
|
routed, err := s.ResolveLogicalModel(logicalModelID, intent)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
capability := normalizeCapability(intent.Capability)
|
|
if capability == "" {
|
|
return nil, BadAuthRequest("报价请求缺少模型能力类型")
|
|
}
|
|
resolvedIntent := intent
|
|
resolvedIntent.Options = mergeIntentDefaults(intent.Options, routed.Defaults)
|
|
input := quoteInput(resolvedIntent, routed.ChannelModel.ModelKey)
|
|
quantity := billingQuantity(capability, inputConfigValue(input, "videoSeconds"))
|
|
if capability != "video" {
|
|
quantity = 1
|
|
}
|
|
tokenEstimate := estimateTaskBillingTokens(input, capability)
|
|
|
|
if routed.LogicalModel.PricePolicy == "channel" {
|
|
priceTierID := ""
|
|
if routed.PriceTier != nil {
|
|
priceTierID = routed.PriceTier.ID
|
|
}
|
|
order, billingErr := s.newBillingOrderWithPriceTier("", "", "quote", routed.ChannelModel.ChannelID, routed.ChannelModel.ModelKey, capability, "model_quote", quantity, tokenEstimate, priceTierID)
|
|
if billingErr != nil {
|
|
return nil, billingErr
|
|
}
|
|
return &LogicalModelQuote{
|
|
LogicalModelID: routed.LogicalModel.ID,
|
|
BillingMode: order.BillingMode,
|
|
Quantity: order.Quantity,
|
|
AmountMicrocredits: order.AmountMicrocredits,
|
|
Estimated: order.BillingMode == "token",
|
|
}, nil
|
|
}
|
|
|
|
if routed.LogicalModel.PricePolicy != "unified" {
|
|
return nil, BadAuthRequest("当前模型价格策略无效")
|
|
}
|
|
amount := int64(0)
|
|
switch routed.LogicalModel.BillingMode {
|
|
case "fixed_request":
|
|
quantity = 1
|
|
amount = routed.LogicalModel.UnitPriceMicrocredits
|
|
case "per_second":
|
|
if capability != "video" || quantity <= 0 {
|
|
return nil, BadAuthRequest("当前模型按时长计费,但请求未提供有效时长")
|
|
}
|
|
amount, err = creditAmount(routed.LogicalModel.UnitPriceMicrocredits, quantity, 10_000)
|
|
case "token":
|
|
if routed.ChannelModel.Capability != capability || !supportsTokenBilling(capability, routed.ChannelModel.Protocol) {
|
|
return nil, BadAuthRequest("当前供应线路不支持前台模型的 Token 计费方式")
|
|
}
|
|
pricing := &model.ChannelModel{
|
|
InputTokenPriceMicrocredits: routed.LogicalModel.InputPriceMicrocredits,
|
|
OutputTokenPriceMicrocredits: routed.LogicalModel.OutputPriceMicrocredits,
|
|
CachedTokenPriceMicrocredits: routed.LogicalModel.CachedPriceMicrocredits,
|
|
}
|
|
amount, err = tokenEstimateAmount(pricing, tokenEstimate, 10_000)
|
|
quantity = tokenEstimate.InputTokens + tokenEstimate.OutputTokens
|
|
default:
|
|
return nil, BadAuthRequest("当前模型计费方式暂不支持")
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if amount <= 0 {
|
|
return nil, BadAuthRequest("当前模型尚未配置有效的用户价格")
|
|
}
|
|
return &LogicalModelQuote{
|
|
LogicalModelID: routed.LogicalModel.ID,
|
|
BillingMode: routed.LogicalModel.BillingMode,
|
|
Quantity: quantity,
|
|
AmountMicrocredits: amount,
|
|
Estimated: routed.LogicalModel.BillingMode == "token",
|
|
}, nil
|
|
}
|
|
|
|
func quoteInput(intent ModelRequestIntent, modelKey string) map[string]any {
|
|
config := make(map[string]any, len(intent.Options)+1)
|
|
for key, value := range intent.Options {
|
|
config[key] = value
|
|
}
|
|
config["model"] = modelKey
|
|
input := map[string]any{"mode": intent.Capability, "config": config}
|
|
if count := intent.Inputs["video"]; count > 0 {
|
|
references := make([]any, count)
|
|
input["referenceVideos"] = references
|
|
}
|
|
return input
|
|
}
|
|
|
|
func inputConfigValue(input map[string]any, key string) any {
|
|
config, _ := input["config"].(map[string]any)
|
|
if config == nil {
|
|
return nil
|
|
}
|
|
return config[key]
|
|
}
|