Browse Source

feat: add Gemini video model schemas for Veo 3.1

Add getGeminiVideoSchema() function returning parameter schemas (aspect
ratio, duration, resolution, negative prompt, seed) and input definitions
for all 4 Veo model variants. Wire into GET handler with gemini provider
support.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
handoff-20260429-1057
shrimbly 5 months ago
parent
commit
7370d92efe
  1. 56
      src/app/api/models/[modelId]/route.ts

56
src/app/api/models/[modelId]/route.ts

@ -891,6 +891,47 @@ function getKieSchema(modelId: string): ExtractedSchema {
return schemas[modelId] || { parameters: [], inputs: [] };
}
/**
* Get schema for Gemini video models (native Veo via Gemini API)
* Returns null if the model is not a Gemini video model.
*/
function getGeminiVideoSchema(modelId: string): ExtractedSchema | null {
const commonParams: ModelParameter[] = [
{ name: "aspectRatio", type: "string", description: "Output aspect ratio", enum: ["16:9", "9:16"], default: "16:9" },
{ name: "durationSeconds", type: "string", description: "Video duration in seconds", enum: ["4", "6", "8"], default: "8" },
{ name: "resolution", type: "string", description: "Output resolution", enum: ["720p", "1080p", "4k"], default: "720p" },
{ name: "negativePrompt", type: "string", description: "What to avoid in the generated video" },
{ name: "seed", type: "integer", description: "Random seed for reproducibility", minimum: 0 },
];
const schemas: Record<string, ExtractedSchema> = {
"veo-3.1/text-to-video": {
parameters: commonParams,
inputs: [{ name: "prompt", type: "text", required: true, label: "Prompt" }],
},
"veo-3.1/image-to-video": {
parameters: commonParams,
inputs: [
{ name: "prompt", type: "text", required: true, label: "Prompt" },
{ name: "image", type: "image", required: true, label: "Image" },
],
},
"veo-3.1-fast/text-to-video": {
parameters: commonParams,
inputs: [{ name: "prompt", type: "text", required: true, label: "Prompt" }],
},
"veo-3.1-fast/image-to-video": {
parameters: commonParams,
inputs: [
{ name: "prompt", type: "text", required: true, label: "Prompt" },
{ name: "image", type: "image", required: true, label: "Image" },
],
},
};
return schemas[modelId] ?? null;
}
/**
* Get static schema for WaveSpeed models (fallback when dynamic schema not available)
*/
@ -1125,11 +1166,11 @@ export async function GET(
const decodedModelId = decodeURIComponent(modelId);
const provider = request.nextUrl.searchParams.get("provider") as ProviderType | null;
if (!provider || (provider !== "replicate" && provider !== "fal" && provider !== "kie" && provider !== "wavespeed")) {
if (!provider || (provider !== "replicate" && provider !== "fal" && provider !== "kie" && provider !== "wavespeed" && provider !== "gemini")) {
return NextResponse.json<SchemaErrorResponse>(
{
success: false,
error: "Invalid or missing provider. Use ?provider=replicate, ?provider=fal, ?provider=kie, or ?provider=wavespeed",
error: "Invalid or missing provider. Use ?provider=replicate, ?provider=fal, ?provider=kie, ?provider=wavespeed, or ?provider=gemini",
},
{ status: 400 }
);
@ -1150,7 +1191,16 @@ export async function GET(
try {
let result: ExtractedSchema;
if (provider === "replicate") {
if (provider === "gemini") {
// Gemini video models use hardcoded schemas
const geminiVideoSchema = getGeminiVideoSchema(decodedModelId);
if (geminiVideoSchema) {
result = geminiVideoSchema;
} else {
// Gemini image models don't use schema endpoint (params are built-in)
result = { parameters: [], inputs: [] };
}
} else if (provider === "replicate") {
// User-provided key takes precedence over env variable
const apiKey = request.headers.get("X-Replicate-Key") || process.env.REPLICATE_API_KEY;
if (!apiKey) {

Loading…
Cancel
Save