diff --git a/app/(admin)/settings/admin-settings-client.tsx b/app/(admin)/settings/admin-settings-client.tsx
index 95039602a..2cae5d26a 100644
--- a/app/(admin)/settings/admin-settings-client.tsx
+++ b/app/(admin)/settings/admin-settings-client.tsx
@@ -171,6 +171,9 @@ import {
buildVideoDurationCostMap,
getConfiguredVideoDurationOptions,
normalizeVideoDurationOptions,
+ recommendedVideoDurationOptions,
+ recommendedVideoDurationReferenceCosts,
+ recommendedVideoResolutionBaseCosts,
} from "@/lib/ai/video-model-config";
import type { AgentWorkflowUiConfig } from "@/types/agent";
@@ -421,6 +424,20 @@ function syncVideoDurationPricing(config: ImageGenConfig | undefined, durationOp
};
}
+function applyRecommendedVideoPricing(config: ImageGenConfig | undefined) {
+ return {
+ ...(config || createDefaultImageGenConfig()),
+ creditCostPerVideo: recommendedVideoResolutionBaseCosts["480p"],
+ videoDurationOptions: [...recommendedVideoDurationOptions],
+ creditCostPerVideoByDuration: {
+ ...recommendedVideoDurationReferenceCosts,
+ },
+ creditCostPerVideoByResolution: {
+ ...recommendedVideoResolutionBaseCosts,
+ },
+ };
+}
+
type AiModel = {
id: string;
providerId: string;
@@ -5597,7 +5614,7 @@ export function AdminSettingsClient() {
type="number"
min="1"
max={enhanceProConfig.maxUpscaleFactor}
- step="0.5"
+ step="0.25"
value={preset.imageScalingFactor}
onChange={(e) =>
handleEnhanceProPresetChange(
@@ -8301,7 +8318,7 @@ export function AdminSettingsClient() {
图像增强 Pro 积分规则
- Krea 官方文档没有公开精确单价公式,建议按输出分辨率档位、候选数量和模型倍率计算本地积分。
+ 默认按基础 20 积分计算;启用放大后按放大倍数折算,例如 1.25x 为 25 积分、2x 为 40 积分。
- 可按放大倍率、分辨率档位或固定单价计算本地积分。
+ 可按放大倍率、分辨率档位或固定单价计算本地积分;推荐使用“放大倍率”。
@@ -8372,7 +8389,7 @@ export function AdminSettingsClient() {
固定模式单次基础积分
- 仅在“固定单价”模式下生效,后续仍会叠加候选数量和模型倍率。
+ 固定单价模式直接使用该值;放大倍率模式会用它作为基础价计算未配置的小数倍数。
@@ -8405,7 +8422,7 @@ export function AdminSettingsClient() {
- 开启后用户关闭“启用放大”时不扣除图像增强 Pro 积分。
+ 关闭时即使用户不启用放大也按 1x 基础价扣积分。
- “放大倍率”计费模式下使用;小数倍率会匹配大于等于该倍率的最近档位。
+ “放大倍率”计费模式下优先使用精确档位;未配置的小数倍率按基础价 × 倍数自动计算。
@@ -12007,6 +12024,34 @@ export function AdminSettingsClient() {
{isVideoProviderForModelConfig && (
+
+
+
+ 推荐视频积分规则
+
+
+ 按 5/10/15 秒 = 1/1.5/2 倍,480p/720p/1080p 基础价 =
+ 100/200/250 积分计算。
+
+
+
+
+
) : (
@@ -12155,7 +12201,7 @@ export function AdminSettingsClient() {
)}
- 配置后会优先按用户选择的视频分辨率扣积分;未命中再回退到秒数或统一价格。
+ 配置后按用户选择的视频分辨率取基础价,再叠加上方秒数倍率;未命中再回退到秒数或统一价格。
diff --git a/app/(chat)/api/chat/route.ts b/app/(chat)/api/chat/route.ts
index 168b98faf..aabde3bed 100644
--- a/app/(chat)/api/chat/route.ts
+++ b/app/(chat)/api/chat/route.ts
@@ -9064,6 +9064,7 @@ export async function POST(request: Request) {
);
const modelCreditCostPerVideo = getModelCreditCost(currentModelConfig, "video", {
duration: Number(effectiveVideoParams?.duration) || undefined,
+ resolution: String(effectiveVideoParams?.resolution || "").trim() || undefined,
});
if (isImageGenerationModel) {
diff --git a/components/canvas/EnhanceProWorkspace.tsx b/components/canvas/EnhanceProWorkspace.tsx
index d2fdda464..1b602b39f 100644
--- a/components/canvas/EnhanceProWorkspace.tsx
+++ b/components/canvas/EnhanceProWorkspace.tsx
@@ -1163,7 +1163,7 @@ export function EnhanceProWorkspace({
)
)
}
- step="0.5"
+ step="0.25"
type="number"
value={formState.image_scaling_factor}
/>
diff --git a/components/enhance-pro-dialog.tsx b/components/enhance-pro-dialog.tsx
index 7e979f392..f640e7974 100644
--- a/components/enhance-pro-dialog.tsx
+++ b/components/enhance-pro-dialog.tsx
@@ -560,7 +560,7 @@ export function EnhanceProDialog({
type="number"
min="1"
max={config.maxUpscaleFactor}
- step="0.5"
+ step="0.25"
value={formState.image_scaling_factor}
onChange={(event) =>
handleNumericField(
diff --git a/lib/ai/krea-models.ts b/lib/ai/krea-models.ts
index f272048d7..4b4cc10d3 100644
--- a/lib/ai/krea-models.ts
+++ b/lib/ai/krea-models.ts
@@ -1,3 +1,9 @@
+import {
+ recommendedVideoDurationOptions,
+ recommendedVideoDurationReferenceCosts,
+ recommendedVideoResolutionBaseCosts,
+} from "@/lib/ai/video-model-config";
+
export type KreaModelKind = "image" | "image-enhance" | "video";
export interface KreaModelPreset {
@@ -212,10 +218,13 @@ function videoPreset(
capabilities: createVideoCapabilities(modelId, fields),
defaultParams: createDefaultParams("video", modelId, fields, requiredFields),
imageGenConfig: {
- creditCostPerVideo: 0,
- videoDurationOptions: durationOptions,
- creditCostPerVideoByDuration: {},
- creditCostPerVideoByResolution: {},
+ creditCostPerVideo: recommendedVideoResolutionBaseCosts["480p"],
+ videoDurationOptions:
+ durationOptions.length > 0
+ ? durationOptions
+ : [...recommendedVideoDurationOptions],
+ creditCostPerVideoByDuration: recommendedVideoDurationReferenceCosts,
+ creditCostPerVideoByResolution: recommendedVideoResolutionBaseCosts,
enableLoadingPlaceholder: true,
loadingColor: "blue",
},
diff --git a/lib/ai/video-model-config.ts b/lib/ai/video-model-config.ts
index 5ece8d1aa..0127db840 100644
--- a/lib/ai/video-model-config.ts
+++ b/lib/ai/video-model-config.ts
@@ -1,6 +1,18 @@
export type VideoDurationCostMap = Record;
export type VideoResolutionCostMap = Record;
+export const recommendedVideoDurationOptions = [5, 10, 15] as const;
+export const recommendedVideoDurationReferenceCosts: VideoDurationCostMap = {
+ "5": 100,
+ "10": 150,
+ "15": 200,
+};
+export const recommendedVideoResolutionBaseCosts: VideoResolutionCostMap = {
+ "480p": 100,
+ "720p": 200,
+ "1080p": 250,
+};
+
export type VideoImageGenConfigLike = {
creditCostPerVideo?: number | null;
videoDurationOptions?: number[] | null;
@@ -131,25 +143,55 @@ export function getVideoCreditCostFromConfig(
const resolutionCostMap = normalizeVideoResolutionCostMap(
config?.creditCostPerVideoByResolution
);
- if (normalizedResolution && resolutionCostMap) {
- if (Object.prototype.hasOwnProperty.call(resolutionCostMap, normalizedResolution)) {
- return resolutionCostMap[normalizedResolution] || 0;
- }
- }
-
const durationOptions = getConfiguredVideoDurationOptions(config);
const durationCostMap = normalizeVideoDurationCostMap(
config?.creditCostPerVideoByDuration,
durationOptions
);
+ const durationCostEntries = durationCostMap
+ ? Object.entries(durationCostMap)
+ .map(([durationKey, cost]) => ({
+ duration: normalizePositiveInteger(durationKey),
+ cost: Math.max(0, Math.round(Number(cost) || 0)),
+ }))
+ .filter((item): item is { duration: number; cost: number } =>
+ item.duration !== null
+ )
+ .sort((left, right) => left.duration - right.duration)
+ : [];
+ const getDurationCost = () => {
+ if (normalizedDuration === null || durationCostEntries.length === 0) return null;
+ const matched =
+ durationCostEntries.find((item) => item.duration === normalizedDuration) ||
+ durationCostEntries.find((item) => normalizedDuration <= item.duration) ||
+ durationCostEntries[durationCostEntries.length - 1] ||
+ null;
+ return matched?.cost ?? null;
+ };
- if (normalizedDuration !== null && durationCostMap) {
- const durationKey = String(normalizedDuration);
- if (Object.prototype.hasOwnProperty.call(durationCostMap, durationKey)) {
- return durationCostMap[durationKey] || 0;
+ if (normalizedResolution && resolutionCostMap) {
+ if (Object.prototype.hasOwnProperty.call(resolutionCostMap, normalizedResolution)) {
+ const resolutionBaseCost = resolutionCostMap[normalizedResolution] || 0;
+ const durationCost = getDurationCost();
+ const baseDurationCost =
+ durationCostEntries.find((item) => item.cost > 0)?.cost ?? null;
+
+ if (durationCost !== null && baseDurationCost && baseDurationCost > 0) {
+ return Math.max(
+ 0,
+ Math.round((resolutionBaseCost * durationCost) / baseDurationCost)
+ );
+ }
+
+ return resolutionBaseCost;
}
}
+ const durationCost = getDurationCost();
+ if (durationCost !== null) {
+ return durationCost;
+ }
+
const fallbackCost = Number(config?.creditCostPerVideo ?? 0);
return Number.isFinite(fallbackCost) ? Math.max(0, Math.round(fallbackCost)) : 0;
}
diff --git a/lib/points/config.ts b/lib/points/config.ts
index 2fc5ddad0..ddd33f942 100644
--- a/lib/points/config.ts
+++ b/lib/points/config.ts
@@ -55,15 +55,16 @@ export const defaultPointsSystemConfig: PointsSystemConfig = {
enhanceProPricing: {
enabled: false,
chargeMode: "scale_factor",
- fixedCost: 45,
- chargeOnlyWhenUpscaling: true,
+ fixedCost: 20,
+ chargeOnlyWhenUpscaling: false,
batchSizeMultiplierEnabled: true,
scaleFactorCosts: {
- "1": 0,
- "1.5": 15,
- "2": 20,
- "3": 35,
- "4": 45,
+ "1": 20,
+ "1.25": 25,
+ "1.5": 30,
+ "2": 40,
+ "3": 60,
+ "4": 80,
},
resolutionBuckets: [
{
@@ -100,8 +101,8 @@ export const defaultPointsSystemConfig: PointsSystemConfig = {
modelMultipliers: {
Redefine: 1,
Recovery: 1,
- "Recovery V2": 1.1,
- Reimagine: 1.25,
+ "Recovery V2": 1,
+ Reimagine: 1,
},
},
};
@@ -316,6 +317,7 @@ export function calculateEnhanceProPointsEstimate(
const batchSize = Math.max(1, Math.round(Number(params.batchSize) || 1));
const upscalingActivated = params.upscalingActivated === true;
const scaleFactor = normalizeScaleFactor(params.scaleFactor ?? 1, 1);
+ const billingScaleFactor = upscalingActivated ? scaleFactor : 1;
const batchMultiplier = pricing.batchSizeMultiplierEnabled ? batchSize : 1;
const modelMultiplier =
pricing.modelMultipliers[model] || defaultPointsSystemConfig.enhanceProPricing.modelMultipliers[model];
@@ -338,18 +340,16 @@ export function calculateEnhanceProPointsEstimate(
.sort((left, right) => left.factor - right.factor);
const scaleFactorCost =
scaleFactorCostEntries.find(
- (item) => Math.abs(item.factor - scaleFactor) < 0.001
- ) ||
- scaleFactorCostEntries.find((item) => scaleFactor <= item.factor) ||
- scaleFactorCostEntries[scaleFactorCostEntries.length - 1] ||
- null;
+ (item) => Math.abs(item.factor - billingScaleFactor) < 0.001
+ ) || null;
+ const linearScaleFactorCost = Math.ceil(pricing.fixedCost * billingScaleFactor);
const baseCost =
pricing.chargeOnlyWhenUpscaling && !upscalingActivated
? 0
: pricing.chargeMode === "fixed"
? pricing.fixedCost
: pricing.chargeMode === "scale_factor"
- ? Math.max(0, Number(scaleFactorCost?.cost || 0))
+ ? Math.max(0, Number(scaleFactorCost?.cost ?? linearScaleFactorCost))
: Math.max(0, Number(bucket?.cost || 0));
const rawCost = baseCost * batchMultiplier * modelMultiplier;
const cost = baseCost > 0 ? Math.max(1, Math.ceil(rawCost)) : 0;
@@ -362,6 +362,7 @@ export function calculateEnhanceProPointsEstimate(
chargeOnlyWhenUpscaling: pricing.chargeOnlyWhenUpscaling,
upscalingActivated,
scaleFactor,
+ billingScaleFactor,
megapixels,
batchSize,
batchMultiplier,