feat(*): 创作工作台 - 完善画布生成、模型定价与创建流程
This commit is contained in:
38
web/src/lib/image-pricing.ts
Normal file
38
web/src/lib/image-pricing.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { buildImageResolutionOptions } from "./image-resolution-tiers";
|
||||
import { isImageResolutionTier } from "./image-quality";
|
||||
import type { ImageCapabilityConfig } from "./model-capabilities";
|
||||
|
||||
/** 旧图片价格档把 1K/2K/4K 存在 quality;只在读取价格条件时转换。 */
|
||||
export function normalizeImagePriceSelector(input: Record<string, string>) {
|
||||
const selector = { ...input };
|
||||
for (const key of ["quality", "resolution", "size"] as const) {
|
||||
if (selector[key]) selector[key] = selector[key].trim().toLowerCase();
|
||||
}
|
||||
if (isImageResolutionTier(selector.quality) && (!selector.resolution || selector.resolution === "*")) {
|
||||
selector.resolution = selector.quality;
|
||||
delete selector.quality;
|
||||
}
|
||||
return selector;
|
||||
}
|
||||
|
||||
export function imagePriceSelector(quality: string, size: string, profile?: ImageCapabilityConfig): Record<string, string> {
|
||||
quality = quality.trim().toLowerCase();
|
||||
size = size.trim().toLowerCase();
|
||||
const selector: Record<string, string> = {};
|
||||
if (quality && quality !== "auto" && quality !== "any") selector.quality = quality;
|
||||
if (size && size !== "auto" && size !== "any") selector.size = size;
|
||||
const pixel = buildImageResolutionOptions([size])[0];
|
||||
if (pixel) {
|
||||
selector.resolution = profile?.size.presets?.find((preset) => preset.size.toLowerCase() === size)?.tier || pixel.tier;
|
||||
} else if (isImageResolutionTier(quality)) {
|
||||
selector.resolution = quality;
|
||||
} else if (profile?.size.parameter === "aspect_ratio") {
|
||||
const tier = ({ low: "1k", medium: "2k", high: "4k" } as Record<string, string>)[quality];
|
||||
if (tier && profile.quality.supported) selector.resolution = tier;
|
||||
else if (!profile.quality.supported) {
|
||||
const tiers = new Set(profile.size.presets?.filter((preset) => preset.ratio === size).map((preset) => preset.tier));
|
||||
if (tiers.size === 1) selector.resolution = [...tiers][0];
|
||||
}
|
||||
}
|
||||
return selector;
|
||||
}
|
||||
45
web/src/lib/image-quality.ts
Normal file
45
web/src/lib/image-quality.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
const IMAGE_QUALITY_LABELS: Record<string, string> = {
|
||||
auto: "自动",
|
||||
low: "低",
|
||||
medium: "中",
|
||||
high: "高",
|
||||
xhigh: "超高",
|
||||
max: "最高",
|
||||
"1k": "1K",
|
||||
"2k": "2K",
|
||||
"4k": "4K",
|
||||
};
|
||||
|
||||
export function imageQualityLabel(value: string) {
|
||||
const normalized = String(value || "").trim().toLowerCase();
|
||||
return IMAGE_QUALITY_LABELS[normalized] || value;
|
||||
}
|
||||
|
||||
export function imageQualityDescription(value: string) {
|
||||
switch (String(value || "").trim().toLowerCase()) {
|
||||
case "auto":
|
||||
return "由模型决定";
|
||||
case "low":
|
||||
return "更快生成";
|
||||
case "medium":
|
||||
return "均衡模式";
|
||||
case "high":
|
||||
return "优先速度";
|
||||
case "xhigh":
|
||||
return "优先质量";
|
||||
case "max":
|
||||
return "优先细节";
|
||||
case "1k":
|
||||
return "标准清晰度";
|
||||
case "2k":
|
||||
return "更高清晰度";
|
||||
case "4k":
|
||||
return "最高清晰度";
|
||||
default:
|
||||
return "模型支持的质量/分辨率";
|
||||
}
|
||||
}
|
||||
|
||||
export function isImageResolutionTier(value: string) {
|
||||
return ["1k", "2k", "4k"].includes(String(value || "").trim().toLowerCase());
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { modelRequestOptions, resolveVideoOperation, type ModelRequirements } from "@/lib/model-selection";
|
||||
import { videoResolutionComparisonKey } from "@/lib/video-generation-options";
|
||||
import { buildImageResolutionOptions, imageResolutionOption } from "@/lib/image-resolution-tiers";
|
||||
import { imagePriceSelector, normalizeImagePriceSelector } from "@/lib/image-pricing";
|
||||
import { modelCapabilityConfigFor } from "@/lib/model-capabilities";
|
||||
import type { LogicalModelQuote, ModelQuoteRequest, ModelRequestIntent } from "@/services/api/logical-models";
|
||||
import { modelOptionName, resolveModelChannel, type AiConfig, type ModelCapability } from "@/stores/use-config-store";
|
||||
|
||||
@@ -141,14 +142,7 @@ function priceSelectorForRequest(capability: ModelCapability | undefined, config
|
||||
const options = { ...modelRequestOptions(config, "image"), ...requirements?.options, ...(requirements?.imageSize ? { size: requirements.imageSize } : {}) };
|
||||
const imageQuality = String(options.quality ?? "").trim().toLowerCase();
|
||||
const imageSize = String(options.size ?? "").trim();
|
||||
if ((imageQuality === "" || imageQuality === "auto" || imageQuality === "any") && imageSize) {
|
||||
const resolution = imageResolutionOption(buildImageResolutionOptions([imageSize]), imageSize)?.tier;
|
||||
if (resolution) requested.quality = resolution;
|
||||
}
|
||||
for (const key of ["quality", "size"] as const) {
|
||||
const value = String(options[key] ?? "").trim().toLowerCase();
|
||||
if (value && value !== "auto" && value !== "any" && !requested[key]) requested[key] = value;
|
||||
}
|
||||
Object.assign(requested, imagePriceSelector(imageQuality, imageSize, modelCapabilityConfigFor(config, config.model || config.imageModel)?.image));
|
||||
}
|
||||
return requested;
|
||||
}
|
||||
@@ -159,7 +153,7 @@ function imagePriceOperation(requirements?: ModelRequirements) {
|
||||
}
|
||||
|
||||
function priceSelectorForTier(tier: ModelPriceTier) {
|
||||
const selector = { ...(tier.selector || {}) };
|
||||
const selector = normalizeImagePriceSelector({ ...(tier.selector || {}) });
|
||||
if (!Object.keys(selector).length) {
|
||||
const resolution = normalizeTierResolution(tier.resolution);
|
||||
if (resolution !== "*") selector.vquality = resolution;
|
||||
|
||||
Reference in New Issue
Block a user