|
|
|
@ -59,6 +59,10 @@ export type PopiAIModel = { |
|
|
|
categories?: PopiCategory[]; |
|
|
|
metadata?: unknown; |
|
|
|
otherShowData?: unknown; |
|
|
|
supportParameterDimensions?: unknown; |
|
|
|
uploadImageLimit?: unknown; |
|
|
|
uploadVideoLimit?: unknown; |
|
|
|
uploadAudioLimit?: unknown; |
|
|
|
}; |
|
|
|
|
|
|
|
type PopiDisplayDimensionOption = { |
|
|
|
@ -89,6 +93,26 @@ type ProviderResult = { |
|
|
|
|
|
|
|
export type PopiModelExtensionFields = Record<string, unknown> | unknown[]; |
|
|
|
|
|
|
|
export type NumericRange = { |
|
|
|
min?: number | null; |
|
|
|
max?: number | null; |
|
|
|
}; |
|
|
|
|
|
|
|
export type MediaSupportParameterRule = { |
|
|
|
formats?: string[]; |
|
|
|
count?: NumericRange; |
|
|
|
aspectRatio?: NumericRange; |
|
|
|
width?: NumericRange; |
|
|
|
height?: NumericRange; |
|
|
|
totalPixels?: NumericRange; |
|
|
|
}; |
|
|
|
|
|
|
|
export type ModelSupportParameterDimensions = { |
|
|
|
image?: MediaSupportParameterRule; |
|
|
|
video?: MediaSupportParameterRule; |
|
|
|
audio?: MediaSupportParameterRule; |
|
|
|
}; |
|
|
|
|
|
|
|
export type ModelsSuccessResponse = { |
|
|
|
success: true; |
|
|
|
models: ProviderModel[]; |
|
|
|
@ -211,6 +235,98 @@ export function getPopiModelExtensionFields(model: PopiAIModel): PopiModelExtens |
|
|
|
return parsePopiExtensionFields(model.otherShowData); |
|
|
|
} |
|
|
|
|
|
|
|
function parseSupportParameterDimensionsPayload(value: unknown): Record<string, unknown> | undefined { |
|
|
|
if (!value) return undefined; |
|
|
|
if (typeof value === "object" && !Array.isArray(value)) return value as Record<string, unknown>; |
|
|
|
if (typeof value !== "string" || value.trim().length === 0) return undefined; |
|
|
|
try { |
|
|
|
const parsed = JSON.parse(value) as unknown; |
|
|
|
return parsed && typeof parsed === "object" && !Array.isArray(parsed) |
|
|
|
? parsed as Record<string, unknown> |
|
|
|
: undefined; |
|
|
|
} catch { |
|
|
|
return undefined; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
function finiteNumberOrNull(value: unknown): number | null | undefined { |
|
|
|
if (typeof value === "number" && Number.isFinite(value)) return value; |
|
|
|
if (typeof value === "string" && value.trim().length > 0) { |
|
|
|
const parsed = Number(value); |
|
|
|
if (Number.isFinite(parsed)) return parsed; |
|
|
|
} |
|
|
|
return undefined; |
|
|
|
} |
|
|
|
|
|
|
|
function normalizeNumericRange(value: unknown): NumericRange | undefined { |
|
|
|
if (!value || typeof value !== "object" || Array.isArray(value)) return undefined; |
|
|
|
const record = value as Record<string, unknown>; |
|
|
|
const min = finiteNumberOrNull(record.min); |
|
|
|
const max = finiteNumberOrNull(record.max); |
|
|
|
const range: NumericRange = {}; |
|
|
|
if (min !== undefined) range.min = min; |
|
|
|
if (max !== undefined) range.max = max; |
|
|
|
return Object.keys(range).length > 0 ? range : undefined; |
|
|
|
} |
|
|
|
|
|
|
|
function normalizeFormats(value: unknown): string[] | undefined { |
|
|
|
if (!Array.isArray(value)) return undefined; |
|
|
|
const formats = value |
|
|
|
.map((item) => typeof item === "string" ? item.trim().toLowerCase() : "") |
|
|
|
.filter((item) => item.length > 0); |
|
|
|
return formats.length > 0 ? formats : undefined; |
|
|
|
} |
|
|
|
|
|
|
|
function normalizeUploadLimit(value: unknown): NumericRange | undefined { |
|
|
|
if (!Array.isArray(value)) return undefined; |
|
|
|
const min = finiteNumberOrNull(value[0]); |
|
|
|
const max = finiteNumberOrNull(value[1]); |
|
|
|
const range: NumericRange = {}; |
|
|
|
if (min !== undefined) range.min = min; |
|
|
|
if (max !== undefined) range.max = max; |
|
|
|
return Object.keys(range).length > 0 ? range : undefined; |
|
|
|
} |
|
|
|
|
|
|
|
function normalizeMediaRule(value: unknown, options: { dimensions: boolean }): MediaSupportParameterRule | undefined { |
|
|
|
if (!value || typeof value !== "object" || Array.isArray(value)) return undefined; |
|
|
|
const record = value as Record<string, unknown>; |
|
|
|
const rule: MediaSupportParameterRule = {}; |
|
|
|
const formats = normalizeFormats(record.formats); |
|
|
|
if (formats) rule.formats = formats; |
|
|
|
if (options.dimensions) { |
|
|
|
const aspectRatio = normalizeNumericRange(record.aspectRatio); |
|
|
|
const width = normalizeNumericRange(record.width); |
|
|
|
const height = normalizeNumericRange(record.height); |
|
|
|
const totalPixels = normalizeNumericRange(record.totalPixels); |
|
|
|
if (aspectRatio) rule.aspectRatio = aspectRatio; |
|
|
|
if (width) rule.width = width; |
|
|
|
if (height) rule.height = height; |
|
|
|
if (totalPixels) rule.totalPixels = totalPixels; |
|
|
|
} |
|
|
|
return Object.keys(rule).length > 0 ? rule : undefined; |
|
|
|
} |
|
|
|
|
|
|
|
export function getPopiModelSupportParameterDimensions( |
|
|
|
model: PopiAIModel |
|
|
|
): ModelSupportParameterDimensions | undefined { |
|
|
|
const payload = parseSupportParameterDimensionsPayload(model.supportParameterDimensions); |
|
|
|
if (!payload) return undefined; |
|
|
|
const result: ModelSupportParameterDimensions = {}; |
|
|
|
const image = normalizeMediaRule(payload.image, { dimensions: true }); |
|
|
|
const video = normalizeMediaRule(payload.video, { dimensions: true }); |
|
|
|
const audio = normalizeMediaRule(payload.audio, { dimensions: false }); |
|
|
|
if (image) result.image = image; |
|
|
|
if (video) result.video = video; |
|
|
|
if (audio) result.audio = audio; |
|
|
|
const imageCount = normalizeUploadLimit(model.uploadImageLimit); |
|
|
|
const videoCount = normalizeUploadLimit(model.uploadVideoLimit); |
|
|
|
const audioCount = normalizeUploadLimit(model.uploadAudioLimit); |
|
|
|
if (imageCount) result.image = { ...(result.image ?? {}), count: imageCount }; |
|
|
|
if (videoCount) result.video = { ...(result.video ?? {}), count: videoCount }; |
|
|
|
if (audioCount) result.audio = { ...(result.audio ?? {}), count: audioCount }; |
|
|
|
return Object.keys(result).length > 0 ? result : undefined; |
|
|
|
} |
|
|
|
|
|
|
|
export function popiModelToProviderModel(model: PopiAIModel): ProviderModel { |
|
|
|
const subTypes = [...getTaskSubTypes(model)]; |
|
|
|
const primarySubType = subTypes[0]; |
|
|
|
|