refactor(*): 平台架构 - 拆分后端领域模块并同步插件协议与模型能力
This commit is contained in:
@@ -61,17 +61,29 @@ export function imageResolutionUsesQuality(profile: ImageCapabilityConfig) {
|
||||
}
|
||||
|
||||
export function imageTierAvailable(profile: ImageCapabilityConfig, tier: ImageResolutionTier) {
|
||||
// 比例协议必须能把档位传给上游;允许自定义比例不等于支持任意分辨率。
|
||||
// 质量参数能映射档位时按上游质量裁剪;管理员尺寸预设存在时再取交集,避免默认 low/medium/high 把未启用的 1K 显示出来。
|
||||
if (profile.size.parameter !== "aspect_ratio") return profile.size.parameter !== "none";
|
||||
return imageResolutionUsesQuality(profile) ? Boolean(imageQualityForTier(profile, tier)) : tier === "1k";
|
||||
const configuredTiers = new Set(profile.size.presets?.map((preset) => preset.tier));
|
||||
if (imageResolutionUsesQuality(profile)) {
|
||||
if (!imageQualityForTier(profile, tier)) return false;
|
||||
return configuredTiers.size ? configuredTiers.has(tier) : true;
|
||||
}
|
||||
if (configuredTiers.size) return configuredTiers.has(tier);
|
||||
// 无质量映射且无预设时,后端 filterImageSizePresets 会返回全部预设或空;
|
||||
// 前端不应硬编码 1k 兜底,避免产生后端不认可的幻影选项。
|
||||
return false;
|
||||
}
|
||||
|
||||
export function imageQualityForSelection(profile: ImageCapabilityConfig, tier: ImageResolutionTier) {
|
||||
return imageQualityForTier(profile, tier) || (!imageResolutionUsesQuality(profile) && imageTierAvailable(profile, tier) ? tier : undefined);
|
||||
}
|
||||
|
||||
export function imageSizePresets(profile: ImageCapabilityConfig): ImageResolutionOption[] {
|
||||
if (profile.size.parameter === "none") return [];
|
||||
if (profile.size.presets) return profile.size.presets;
|
||||
const pixels = buildImageResolutionOptions(profile.size.values);
|
||||
const tiers = profile.size.parameter === "aspect_ratio" ? IMAGE_RESOLUTIONS.filter((tier) => imageQualityForTier(profile, tier)) : ["1k" as const];
|
||||
const ratios = (tiers.length ? tiers : ["1k" as const]).flatMap((tier) =>
|
||||
const tiers = profile.size.parameter === "aspect_ratio" ? IMAGE_RESOLUTIONS.filter((tier) => imageQualityForTier(profile, tier)) : [];
|
||||
const ratios = tiers.flatMap((tier) =>
|
||||
profile.size.values.flatMap((ratio) => {
|
||||
try {
|
||||
const preset = imagePresetForRatio(tier, ratio);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { ModelProtocol, ModelProtocolWorkflow } from "@/lib/model-protocols";
|
||||
import type { ImageResolutionOption } from "@/lib/image-resolution-tiers";
|
||||
import type { ImageResolutionOption, ImageResolutionTier } from "@/lib/image-resolution-tiers";
|
||||
|
||||
export type ModelCapabilityConfig = {
|
||||
version: number;
|
||||
@@ -897,18 +897,29 @@ function matchWorkflowValue(value: string, options: string[]) {
|
||||
export function normalizeImageValue(profile: ImageCapabilityConfig, value: { size?: string; quality?: string; count?: string; transparentBackground?: string }) {
|
||||
const size = normalizeImageSizeSetting(profile, value.size);
|
||||
const requestedQuality = String(value.quality || "").trim().toLowerCase();
|
||||
// 比例协议的固定分辨率预设没有独立 quality 字段时,UI 仍需把当前比例对应的
|
||||
// 预设档位带入请求。仅在 quality 未声明支持时启用,避免与 auto/low/medium/high
|
||||
// 这组真实图片质量语义混用。
|
||||
const presetTier = !profile.quality.supported ? imagePresetTierForSelection(profile, size) : undefined;
|
||||
const quality = profile.quality.supported
|
||||
? requestedQuality === "auto" || requestedQuality === "any"
|
||||
? "auto"
|
||||
: value.quality && profile.quality.values.includes(value.quality)
|
||||
? value.quality
|
||||
: profile.quality.default || "auto"
|
||||
: profile.quality.default || "auto";
|
||||
: requestedQuality === "1k" || requestedQuality === "2k" || requestedQuality === "4k"
|
||||
? requestedQuality
|
||||
: presetTier || profile.quality.default || "auto";
|
||||
const count = String(Math.max(1, Math.min(profile.maxOutputs, Math.floor(Math.abs(Number(value.count)) || 1))));
|
||||
const transparentBackground = profile.transparentBackground.supported && value.transparentBackground === "true" ? "true" : "false";
|
||||
return { size, quality, count, transparentBackground };
|
||||
}
|
||||
|
||||
function imagePresetTierForSelection(profile: ImageCapabilityConfig, size: string): ImageResolutionTier | undefined {
|
||||
if (profile.size.parameter !== "aspect_ratio" || !size || size === "auto") return undefined;
|
||||
return profile.size.presets?.find((preset) => preset.ratio === size)?.tier;
|
||||
}
|
||||
|
||||
export function normalizeImageSizeSetting(profile: ImageCapabilityConfig, value?: string) {
|
||||
if (profile.size.parameter === "none") return "auto";
|
||||
const candidate = value?.trim() || profile.size.default;
|
||||
|
||||
@@ -9,6 +9,7 @@ import { ASSET_STORE_KEY, flushAssetStorePersistence, useAssetStore } from "@/st
|
||||
import { CONFIG_STORE_KEY, PUBLIC_MODEL_CATALOG_ID, defaultConfig, normalizeConfigSnapshot, useConfigStore, type ModelCapability, type ModelChannel } from "@/stores/use-config-store";
|
||||
import { CREATION_PREFERENCES_STORE_KEY, useCreationPreferencesStore } from "@/stores/use-creation-preferences-store";
|
||||
import { defaultModelCapabilityConfig, STANDARD_IMAGE_SIZE_VALUES, type ModelCapabilityConfig } from "@/lib/model-capabilities";
|
||||
import { imageSizeConfigWithPresets } from "@/lib/image-size-presets";
|
||||
import { useUserStore } from "@/stores/use-user-store";
|
||||
import { PLUGIN_STORE_KEY, usePluginStore } from "@/stores/use-plugin-store";
|
||||
import { initializeRemoteUserDataSession, installRemoteUserDataAutoSync, resetRemoteUserDataSync, withRemoteUserDataSyncExclusive } from "@/services/user-data-sync";
|
||||
@@ -198,7 +199,7 @@ export function systemChannelModelChannels(channels: PublicChannelCatalog[]): Mo
|
||||
});
|
||||
}
|
||||
|
||||
function projectLogicalCapability(spec: CapabilitySpec, defaults: Record<string, unknown>): ModelCapabilityConfig {
|
||||
export function projectLogicalCapability(spec: CapabilitySpec, defaults: Record<string, unknown>): ModelCapabilityConfig {
|
||||
const projected = defaultModelCapabilityConfig();
|
||||
if (spec.capability === "image" && projected.image) {
|
||||
projected.image.references.maxImages = spec.inputs?.image?.max ?? 0;
|
||||
@@ -208,11 +209,13 @@ function projectLogicalCapability(spec: CapabilitySpec, defaults: Record<string,
|
||||
projected.image.transparentBackground = { supported: false, default: false };
|
||||
const sizeOption = spec.options?.size || spec.options?.aspectRatio;
|
||||
const sizeValues = stringValues(sizeOption);
|
||||
const sizeAllowsCustom = sizeValues.includes("*");
|
||||
const sizeAllowsCustom = sizeValues.includes("*") || Boolean(spec.imageSize?.allowCustom);
|
||||
const concreteSizeValues = sizeValues.filter((value) => value !== "*");
|
||||
const sizePresets = concreteSizeValues.length ? concreteSizeValues : sizeAllowsCustom ? [...STANDARD_IMAGE_SIZE_VALUES] : [];
|
||||
if (sizePresets.length || sizeAllowsCustom) {
|
||||
projected.image!.size = { parameter: "size", values: sizePresets, default: concreteDefault(defaults.size, sizePresets, "1:1"), allowCustom: sizeAllowsCustom };
|
||||
if (sizePresets.length || sizeAllowsCustom || spec.imageSize?.presets?.length) {
|
||||
const parameter = spec.imageSize?.parameter === "aspect_ratio" || spec.imageSize?.parameter === "size" ? spec.imageSize.parameter : "size";
|
||||
projected.image.size = { parameter, values: sizePresets, default: concreteDefault(defaults.size, sizePresets, "1:1"), allowCustom: sizeAllowsCustom };
|
||||
if (spec.imageSize?.presets?.length) projected.image.size = imageSizeConfigWithPresets(projected.image, spec.imageSize.presets);
|
||||
}
|
||||
applyStringOption(spec.options?.quality, defaults.quality, (values, initial) => {
|
||||
projected.image!.quality = { supported: true, values, default: initial };
|
||||
|
||||
Reference in New Issue
Block a user