|
|
|
@ -48,15 +48,6 @@ function stringFromUnknown(value: unknown): string | null { |
|
|
|
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null; |
|
|
|
} |
|
|
|
|
|
|
|
function isSeedanceVideoEstimateModel(model: SelectedModel): boolean { |
|
|
|
return /seedance/i.test([ |
|
|
|
model.modelId, |
|
|
|
model.displayName, |
|
|
|
stringFromUnknown(selectedModelMetadataValue(model, "aiModelCode")), |
|
|
|
stringFromUnknown(selectedModelMetadataValue(model, "aiModelCodeAlias")), |
|
|
|
].filter(Boolean).join(" ")); |
|
|
|
} |
|
|
|
|
|
|
|
function getPopiserverBillingMethod(model: SelectedModel): number | null { |
|
|
|
const billingMethod = finiteNumberFromUnknown(selectedModelMetadataValue(model, "billingMethod")); |
|
|
|
return billingMethod === null ? null : Math.trunc(billingMethod); |
|
|
|
@ -64,7 +55,7 @@ function getPopiserverBillingMethod(model: SelectedModel): number | null { |
|
|
|
|
|
|
|
function shouldUsePopiserverPointsEstimate(model: SelectedModel): boolean { |
|
|
|
if (model.provider !== "popiserver") return false; |
|
|
|
return getPopiserverBillingMethod(model) === 2 || isSeedanceVideoEstimateModel(model); |
|
|
|
return getPopiserverBillingMethod(model) === 2; |
|
|
|
} |
|
|
|
|
|
|
|
function shouldUsePopiserverTaskPrice(model: SelectedModel): boolean { |
|
|
|
@ -72,10 +63,6 @@ function shouldUsePopiserverTaskPrice(model: SelectedModel): boolean { |
|
|
|
return getPopiserverBillingMethod(model) === 1; |
|
|
|
} |
|
|
|
|
|
|
|
function shouldUsePopiserverSeedanceEstimate(model: SelectedModel): boolean { |
|
|
|
return model.provider === "popiserver" && isSeedanceVideoEstimateModel(model); |
|
|
|
} |
|
|
|
|
|
|
|
function getPopiserverModelName(model: SelectedModel): string { |
|
|
|
return ( |
|
|
|
stringFromUnknown(selectedModelMetadataValue(model, "aiModelCodeAlias")) || |
|
|
|
@ -108,20 +95,6 @@ function buildPopiserverPointsEstimatePayload( |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
if (shouldUsePopiserverSeedanceEstimate(selectedModel)) { |
|
|
|
return { |
|
|
|
model_name: getPopiserverModelName(selectedModel), |
|
|
|
estimation_type: "auto", |
|
|
|
parameters: { |
|
|
|
...(config.parameters ?? {}), |
|
|
|
input_video_duration_seconds: inputVideoDurationSeconds, |
|
|
|
}, |
|
|
|
// Seedance pricing scales with the requested video count; forward batchSize
|
|
|
|
// top-level like the generic estimate branch so switching 视频数量 re-quotes.
|
|
|
|
...(config.batchSize ? { batchSize: config.batchSize } : {}), |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
return { |
|
|
|
model_name: getPopiserverModelName(selectedModel), |
|
|
|
estimation_type: "auto", |
|
|
|
@ -134,7 +107,8 @@ function buildPopiserverPointsEstimatePayload( |
|
|
|
...(inputVideoDurationSeconds > 0 ? { input_video_duration_seconds: inputVideoDurationSeconds } : {}), |
|
|
|
}, |
|
|
|
...(Object.keys(config.extraTaskParams ?? {}).length > 0 ? { extraTaskParams: config.extraTaskParams } : {}), |
|
|
|
...(config.batchSize ? { batchSize: config.batchSize } : {}), |
|
|
|
// The estimate endpoint does not support batchSize; it returns points for a
|
|
|
|
// single generation. batchSize is applied client-side in resolveQuotedPointAmount().
|
|
|
|
...(config.audioVoiceId ? { audioVoiceId: config.audioVoiceId } : {}), |
|
|
|
...(inputVideoDurationSeconds > 0 ? { input_video_duration_seconds: inputVideoDurationSeconds } : {}), |
|
|
|
}; |
|
|
|
@ -144,6 +118,27 @@ export function getQuotedPointAmount(response: PopiserverPriceQuoteResponse): nu |
|
|
|
return finiteNumberFromUnknown(response.data?.points); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Resolves the total point amount to display for a quote. |
|
|
|
* |
|
|
|
* The estimate endpoint quotes a single generation and ignores batchSize, so we |
|
|
|
* multiply by the requested count on the client. task-price already accounts for |
|
|
|
* batchSize upstream, so it is returned as-is. |
|
|
|
*/ |
|
|
|
export function resolveQuotedPointAmount( |
|
|
|
response: PopiserverPriceQuoteResponse, |
|
|
|
source: PopiserverPriceQuoteSource, |
|
|
|
batchSize: number | undefined |
|
|
|
): number | null { |
|
|
|
const points = getQuotedPointAmount(response); |
|
|
|
if (points === null) return null; |
|
|
|
if (source !== "estimate") return points; |
|
|
|
const count = typeof batchSize === "number" && Number.isFinite(batchSize) && batchSize > 0 |
|
|
|
? Math.trunc(batchSize) |
|
|
|
: 1; |
|
|
|
return points * count; |
|
|
|
} |
|
|
|
|
|
|
|
function buildComposerPopiserverTaskPricePayload( |
|
|
|
context: ComposerPricingContext |
|
|
|
) { |
|
|
|
|