Compare commits
4 Commits
d3ef42ee32
...
7fbe80e0a2
| Author | SHA1 | Date |
|---|---|---|
|
|
7fbe80e0a2 | 2 months ago |
|
|
62f2fec366 | 2 months ago |
|
|
ecda6debb8 | 2 months ago |
|
|
18c85e70e4 | 2 months ago |
18 changed files with 645 additions and 70 deletions
@ -0,0 +1,77 @@ |
|||
import type { AspectRatio, ImageGenerationCount } from "@/types"; |
|||
import type { ModelParameter } from "@/lib/providers/types"; |
|||
import { normalizeVideoDurationSeconds, VIDEO_SOUND_PARAMETER_NAMES } from "@/utils/videoGenerationSettings"; |
|||
|
|||
const IMAGE_COUNT_PARAMETER_NAMES = ["imageCount", "numImages", "num_images", "numOutputs", "num_outputs", "count"]; |
|||
|
|||
function getParameterDefault(param: ModelParameter): unknown { |
|||
return param.default ?? param.options?.[0]?.value ?? param.enum?.[0]; |
|||
} |
|||
|
|||
function findParameter(schema: ModelParameter[], names: string[]): ModelParameter | undefined { |
|||
const nameSet = new Set(names); |
|||
return schema.find((param) => nameSet.has(param.name)); |
|||
} |
|||
|
|||
function hasValue(value: unknown): boolean { |
|||
return value !== undefined && value !== null && value !== ""; |
|||
} |
|||
|
|||
export function buildDefaultParametersFromSchema(schema: ModelParameter[]): Record<string, unknown> { |
|||
const parameters: Record<string, unknown> = {}; |
|||
for (const param of schema) { |
|||
const value = getParameterDefault(param); |
|||
if (hasValue(value)) parameters[param.name] = value; |
|||
} |
|||
return parameters; |
|||
} |
|||
|
|||
export function buildImageDefaultsFromSchema(schema: ModelParameter[]): { |
|||
aspectRatio?: AspectRatio; |
|||
resolution?: string; |
|||
imageCount?: ImageGenerationCount; |
|||
parameters: Record<string, unknown>; |
|||
} { |
|||
const parameters = buildDefaultParametersFromSchema(schema); |
|||
const ratio = findParameter(schema, ["ratio", "aspectRatio", "aspect_ratio"]); |
|||
const resolution = findParameter(schema, ["resolution"]); |
|||
const imageCount = findParameter(schema, IMAGE_COUNT_PARAMETER_NAMES); |
|||
const ratioValue = ratio ? getParameterDefault(ratio) : undefined; |
|||
const resolutionValue = resolution ? getParameterDefault(resolution) : undefined; |
|||
const imageCountValue = imageCount ? Number(getParameterDefault(imageCount)) : undefined; |
|||
|
|||
return { |
|||
...(hasValue(ratioValue) ? { aspectRatio: String(ratioValue) as AspectRatio } : {}), |
|||
...(hasValue(resolutionValue) ? { resolution: String(resolutionValue) } : {}), |
|||
...(Number.isInteger(imageCountValue) && imageCountValue > 0 |
|||
? { imageCount: imageCountValue as ImageGenerationCount } |
|||
: {}), |
|||
parameters, |
|||
}; |
|||
} |
|||
|
|||
export function buildVideoDefaultsFromSchema(schema: ModelParameter[]): { |
|||
aspectRatio?: AspectRatio; |
|||
resolution?: string; |
|||
durationSeconds?: number; |
|||
soundEnabled?: boolean; |
|||
parameters: Record<string, unknown>; |
|||
} { |
|||
const parameters = buildDefaultParametersFromSchema(schema); |
|||
const ratio = findParameter(schema, ["ratio", "aspectRatio", "aspect_ratio"]); |
|||
const resolution = findParameter(schema, ["resolution"]); |
|||
const duration = findParameter(schema, ["duration", "duration_seconds", "durationSeconds"]); |
|||
const sound = findParameter(schema, VIDEO_SOUND_PARAMETER_NAMES); |
|||
const ratioValue = ratio ? getParameterDefault(ratio) : undefined; |
|||
const resolutionValue = resolution ? getParameterDefault(resolution) : undefined; |
|||
const durationValue = duration ? getParameterDefault(duration) : undefined; |
|||
const soundValue = sound ? getParameterDefault(sound) : undefined; |
|||
|
|||
return { |
|||
...(hasValue(ratioValue) ? { aspectRatio: String(ratioValue) as AspectRatio } : {}), |
|||
...(hasValue(resolutionValue) ? { resolution: String(resolutionValue) } : {}), |
|||
...(hasValue(durationValue) ? { durationSeconds: normalizeVideoDurationSeconds(durationValue) } : {}), |
|||
...(typeof soundValue === "boolean" ? { soundEnabled: soundValue } : {}), |
|||
parameters, |
|||
}; |
|||
} |
|||
@ -0,0 +1,95 @@ |
|||
import type { ModelParameter } from "@/lib/providers/types"; |
|||
|
|||
const SPEECH_VOICE_OPTIONS = [ |
|||
{ value: "male-qn-qingse", label: "青涩青年音色 (male-qn-qingse)" }, |
|||
{ value: "male-qn-jingying", label: "精英青年音色 (male-qn-jingying)" }, |
|||
{ value: "male-qn-badao", label: "霸道青年音色 (male-qn-badao)" }, |
|||
{ value: "male-qn-daxuesheng", label: "青年大学生音色 (male-qn-daxuesheng)" }, |
|||
{ value: "female-shaonv", label: "少女音色 (female-shaonv)" }, |
|||
{ value: "female-yujie", label: "御姐音色 (female-yujie)" }, |
|||
{ value: "female-chengshu", label: "成熟女性音色 (female-chengshu)" }, |
|||
{ value: "female-tianmei", label: "甜美女性音色 (female-tianmei)" }, |
|||
{ value: "male-qn-qingse-jingpin", label: "青涩青年音色-beta (male-qn-qingse-jingpin)" }, |
|||
{ value: "male-qn-jingying-jingpin", label: "精英青年音色-beta (male-qn-jingying-jingpin)" }, |
|||
{ value: "male-qn-badao-jingpin", label: "霸道青年音色-beta (male-qn-badao-jingpin)" }, |
|||
{ value: "male-qn-daxuesheng-jingpin", label: "青年大学生音色-beta (male-qn-daxuesheng-jingpin)" }, |
|||
{ value: "female-shaonv-jingpin", label: "少女音色-beta (female-shaonv-jingpin)" }, |
|||
{ value: "female-yujie-jingpin", label: "御姐音色-beta (female-yujie-jingpin)" }, |
|||
{ value: "female-chengshu-jingpin", label: "成熟女性音色-beta (female-chengshu-jingpin)" }, |
|||
{ value: "female-tianmei-jingpin", label: "甜美女性音色-beta (female-tianmei-jingpin)" }, |
|||
{ value: "clever_boy", label: "聪明男童 (clever_boy)" }, |
|||
{ value: "cute_boy", label: "可爱男童 (cute_boy)" }, |
|||
{ value: "lovely_girl", label: "萌萌女童 (lovely_girl)" }, |
|||
{ value: "cartoon_pig", label: "卡通猪小琪 (cartoon_pig)" }, |
|||
{ value: "bingjiao_didi", label: "病娇弟弟 (bingjiao_didi)" }, |
|||
{ value: "junlang_nanyou", label: "俊朗男友 (junlang_nanyou)" }, |
|||
{ value: "chunzhen_xuedi", label: "纯真学弟 (chunzhen_xuedi)" }, |
|||
{ value: "lengdan_xiongzhang", label: "冷淡学长 (lengdan_xiongzhang)" }, |
|||
{ value: "badao_shaoye", label: "霸道少爷 (badao_shaoye)" }, |
|||
{ value: "tianxin_xiaoling", label: "甜心小玲 (tianxin_xiaoling)" }, |
|||
{ value: "qiaopi_mengmei", label: "俏皮萌妹 (qiaopi_mengmei)" }, |
|||
{ value: "wumei_yujie", label: "妩媚御姐 (wumei_yujie)" }, |
|||
{ value: "diadia_xuemei", label: "嗲嗲学妹 (diadia_xuemei)" }, |
|||
{ value: "danya_xuejie", label: "淡雅学姐 (danya_xuejie)" }, |
|||
{ value: "Chinese (Mandarin)_Reliable_Executive", label: "沉稳高管 (Chinese (Mandarin)_Reliable_Executive)" }, |
|||
{ value: "Chinese (Mandarin)_News_Anchor", label: "新闻女声 (Chinese (Mandarin)_News_Anchor)" }, |
|||
{ value: "Chinese (Mandarin)_Mature_Woman", label: "傲娇御姐 (Chinese (Mandarin)_Mature_Woman)" }, |
|||
{ value: "Chinese (Mandarin)_Unrestrained_Young_Man", label: "不羁青年 (Chinese (Mandarin)_Unrestrained_Young_Man)" }, |
|||
{ value: "Arrogant_Miss", label: "嚣张小姐 (Arrogant_Miss)" }, |
|||
{ value: "Robot_Armor", label: "机械战甲 (Robot_Armor)" }, |
|||
{ value: "Chinese (Mandarin)_Kind-hearted_Antie", label: "热心大婶 (Chinese (Mandarin)_Kind-hearted_Antie)" }, |
|||
{ value: "Chinese (Mandarin)_HK_Flight_Attendant", label: "港普空姐 (Chinese (Mandarin)_HK_Flight_Attendant)" }, |
|||
{ value: "Chinese (Mandarin)_Humorous_Elder", label: "搞笑大爷 (Chinese (Mandarin)_Humorous_Elder)" }, |
|||
{ value: "Chinese (Mandarin)_Gentleman", label: "温润男声 (Chinese (Mandarin)_Gentleman)" }, |
|||
{ value: "Chinese (Mandarin)_Warm_Bestie", label: "温暖闺蜜 (Chinese (Mandarin)_Warm_Bestie)" }, |
|||
{ value: "Chinese (Mandarin)_Male_Announcer", label: "播报男声 (Chinese (Mandarin)_Male_Announcer)" }, |
|||
{ value: "Chinese (Mandarin)_Sweet_Lady", label: "甜美女声 (Chinese (Mandarin)_Sweet_Lady)" }, |
|||
{ value: "Chinese (Mandarin)_Southern_Young_Man", label: "南方小哥 (Chinese (Mandarin)_Southern_Young_Man)" }, |
|||
{ value: "Chinese (Mandarin)_Wise_Women", label: "阅历姐姐 (Chinese (Mandarin)_Wise_Women)" }, |
|||
{ value: "Chinese (Mandarin)_Gentle_Youth", label: "温润青年 (Chinese (Mandarin)_Gentle_Youth)" }, |
|||
{ value: "Chinese (Mandarin)_Warm_Girl", label: "温暖少女 (Chinese (Mandarin)_Warm_Girl)" }, |
|||
{ value: "Chinese (Mandarin)_Kind-hearted_Elder", label: "花甲奶奶 (Chinese (Mandarin)_Kind-hearted_Elder)" }, |
|||
{ value: "Chinese (Mandarin)_Cute_Spirit", label: "憨憨萌兽 (Chinese (Mandarin)_Cute_Spirit)" }, |
|||
{ value: "Chinese (Mandarin)_Radio_Host", label: "电台男主播 (Chinese (Mandarin)_Radio_Host)" }, |
|||
{ value: "Chinese (Mandarin)_Lyrical_Voice", label: "抒情男声 (Chinese (Mandarin)_Lyrical_Voice)" }, |
|||
{ value: "Chinese (Mandarin)_Straightforward_Boy", label: "率真弟弟 (Chinese (Mandarin)_Straightforward_Boy)" }, |
|||
{ value: "Chinese (Mandarin)_Sincere_Adult", label: "真诚青年 (Chinese (Mandarin)_Sincere_Adult)" }, |
|||
{ value: "Chinese (Mandarin)_Gentle_Senior", label: "温柔学姐 (Chinese (Mandarin)_Gentle_Senior)" }, |
|||
{ value: "Chinese (Mandarin)_Stubborn_Friend", label: "嘴硬竹马 (Chinese (Mandarin)_Stubborn_Friend)" }, |
|||
{ value: "Chinese (Mandarin)_Crisp_Girl", label: "清脆少女 (Chinese (Mandarin)_Crisp_Girl)" }, |
|||
{ value: "Chinese (Mandarin)_Pure-hearted_Boy", label: "清澈邻家弟弟 (Chinese (Mandarin)_Pure-hearted_Boy)" }, |
|||
{ value: "Chinese (Mandarin)_Soft_Girl", label: "柔和少女 (Chinese (Mandarin)_Soft_Girl)" }, |
|||
{ value: "Cantonese_ProfessionalHost(F)", label: "专业女主持 (Cantonese_ProfessionalHost(F))" }, |
|||
{ value: "Cantonese_GentleLady", label: "温柔女声 (Cantonese_GentleLady)" }, |
|||
{ value: "Cantonese_ProfessionalHost(M)", label: "专业男主持 (Cantonese_ProfessionalHost(M))" }, |
|||
{ value: "Cantonese_PlayfulMan", label: "活泼男声 (Cantonese_PlayfulMan)" }, |
|||
{ value: "Cantonese_CuteGirl", label: "可爱女孩 (Cantonese_CuteGirl)" }, |
|||
{ value: "Cantonese_KindWoman", label: "善良女声 (Cantonese_KindWoman)" }, |
|||
] satisfies Array<{ value: string; label: string }>; |
|||
|
|||
const SPEECH_VOICE_PARAMETER: ModelParameter = { |
|||
name: "voiceId", |
|||
type: "string", |
|||
label: "音色", |
|||
description: "Speech 模型合成音色", |
|||
default: "Chinese (Mandarin)_Reliable_Executive", |
|||
options: SPEECH_VOICE_OPTIONS, |
|||
}; |
|||
|
|||
const SPEECH_VOICE_PARAMETER_NAMES = new Set(["voiceId", "voice_id"]); |
|||
|
|||
export function isSpeechAudioModel(...identifiers: Array<string | null | undefined>): boolean { |
|||
return identifiers.some((identifier) => { |
|||
const normalized = (identifier ?? "").trim().toLowerCase(); |
|||
return normalized === "speech" || normalized.startsWith("speech-"); |
|||
}); |
|||
} |
|||
|
|||
export function withSpeechVoiceParameter( |
|||
schema: ModelParameter[] | undefined, |
|||
...modelIdentifiers: Array<string | null | undefined> |
|||
): ModelParameter[] { |
|||
const nextSchema = (schema ?? []).filter((param) => !SPEECH_VOICE_PARAMETER_NAMES.has(param.name)); |
|||
if (!isSpeechAudioModel(...modelIdentifiers)) return nextSchema; |
|||
return [SPEECH_VOICE_PARAMETER, ...nextSchema]; |
|||
} |
|||
Loading…
Reference in new issue