fix: align model provider configuration

This commit is contained in:
2026-05-19 09:07:59 +08:00
parent 12e3b97c06
commit ffa6b2efdf
7 changed files with 64 additions and 26 deletions

View File

@@ -1,10 +1,10 @@
import type { VideoGenerationRequest, VideoGenerationResponse } from './types';
export const SEEDANCE_MODEL = process.env.SEEDANCE_MODEL || 'seedance-1-0-pro';
export const SEEDANCE_MODEL = process.env.SEEDANCE_MODEL || 'doubao-seedance-2-0-260128';
const SEEDANCE_API_BASE = process.env.SEEDANCE_API_BASE || 'https://ark.cn-beijing.volces.com/api/v3';
function durationOrDefault(duration?: number): number {
return Math.min(Math.max(duration ?? 6, 3), 10);
return Math.min(Math.max(duration ?? 6, 3), 15);
}
function normalizeStatus(status?: string): VideoGenerationResponse['status'] {
@@ -14,15 +14,49 @@ function normalizeStatus(status?: string): VideoGenerationResponse['status'] {
return 'submitted';
}
function publicUrlOrUndefined(url?: string): string | undefined {
if (!url) return undefined;
if (!/^https?:\/\//i.test(url)) return undefined;
if (/^https?:\/\/(localhost|127\.0\.0\.1|\[::1\])/i.test(url)) return undefined;
return url;
}
function buildContent(opts: VideoGenerationRequest): Array<Record<string, unknown>> {
const content: Array<Record<string, unknown>> = [{ type: 'text', text: opts.prompt.trim() }];
const refs = [...(opts.references ?? [])];
if (opts.imageUrl) refs.unshift({ type: 'image_url', url: opts.imageUrl, role: 'reference_image' });
for (const ref of refs) {
const url = publicUrlOrUndefined(ref.url);
if (!url) continue;
if (ref.type === 'image_url') {
content.push({ type: 'image_url', image_url: { url }, role: ref.role ?? 'reference_image' });
}
if (ref.type === 'video_url') {
content.push({ type: 'video_url', video_url: { url }, role: ref.role ?? 'reference_video' });
}
if (ref.type === 'audio_url') {
content.push({ type: 'audio_url', audio_url: { url }, role: ref.role ?? 'reference_audio' });
}
}
return content;
}
export async function generateSeedanceVideo(opts: VideoGenerationRequest): Promise<VideoGenerationResponse> {
const key = process.env.SEEDANCE_API_KEY;
if (!key) throw new Error('SEEDANCE_API_KEY missing');
if (!opts.prompt?.trim()) throw new Error('prompt required');
const content: Array<Record<string, unknown>> = [{ type: 'text', text: opts.prompt.trim() }];
if (opts.imageUrl) {
content.push({ type: 'image_url', image_url: { url: opts.imageUrl } });
}
const content = buildContent(opts);
const body: Record<string, unknown> = {
model: SEEDANCE_MODEL,
content,
generate_audio: opts.generateAudio ?? true,
ratio: opts.ratio || '16:9',
duration: durationOrDefault(opts.duration),
watermark: opts.watermark ?? false,
};
if (opts.resolution) body.resolution = opts.resolution;
const res = await fetch(`${SEEDANCE_API_BASE}/contents/generations/tasks`, {
method: 'POST',
@@ -30,13 +64,7 @@ export async function generateSeedanceVideo(opts: VideoGenerationRequest): Promi
'Content-Type': 'application/json',
Authorization: `Bearer ${key}`,
},
body: JSON.stringify({
model: SEEDANCE_MODEL,
content,
duration: durationOrDefault(opts.duration),
ratio: opts.ratio || '16:9',
resolution: opts.resolution || '1080p',
}),
body: JSON.stringify(body),
});
if (!res.ok) throw new Error(`Seedance ${res.status}: ${await res.text()}`);