feat: integrate templates with pipeline creation
- POST /api/pipelines now accepts templateId + customization
- Added createPipelineWithTemplate() function
- Template config stored in pipeline.metadata
- Stage inputs pre-populated from template config
- Schema: added templateId and metadata fields to Pipeline
Usage:
POST /api/pipelines
{
"topic": "Why cats rule",
"templateId": "reddit-minecraft",
"customization": {
"voice": { "narrator": { "speed": 1.2 } }
}
}
This commit is contained in:
@@ -13,6 +13,8 @@ model Pipeline {
|
||||
description String?
|
||||
status PipelineStatus @default(DRAFT)
|
||||
currentStage StageName @default(RESEARCH)
|
||||
templateId String? // Reference to template used
|
||||
metadata Json? // Template config and customizations
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
stages Stage[]
|
||||
@@ -20,6 +22,7 @@ model Pipeline {
|
||||
|
||||
@@index([status])
|
||||
@@index([createdAt])
|
||||
@@index([templateId])
|
||||
}
|
||||
|
||||
model Stage {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { createPipeline, listPipelines } from '@/lib/pipeline/manager'
|
||||
import { createPipeline, listPipelines, createPipelineWithTemplate } from '@/lib/pipeline/manager'
|
||||
import { getVideoTemplate, TemplatePipelineRequestSchema } from '@/lib/templates'
|
||||
|
||||
// GET /api/pipelines - List all pipelines
|
||||
export async function GET(request: NextRequest) {
|
||||
@@ -27,7 +28,7 @@ export async function GET(request: NextRequest) {
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const body = await request.json()
|
||||
const { topic, description } = body
|
||||
const { topic, description, templateId, customization, platforms, targetDuration } = body
|
||||
|
||||
if (!topic) {
|
||||
return NextResponse.json(
|
||||
@@ -36,6 +37,31 @@ export async function POST(request: NextRequest) {
|
||||
)
|
||||
}
|
||||
|
||||
// If templateId provided, create with template
|
||||
if (templateId) {
|
||||
const template = getVideoTemplate(templateId)
|
||||
if (!template) {
|
||||
return NextResponse.json(
|
||||
{ error: `Template not found: ${templateId}` },
|
||||
{ status: 404 }
|
||||
)
|
||||
}
|
||||
|
||||
const pipeline = await createPipelineWithTemplate(
|
||||
topic,
|
||||
template,
|
||||
{
|
||||
description,
|
||||
customization,
|
||||
platforms: platforms || template.platforms,
|
||||
targetDuration,
|
||||
}
|
||||
)
|
||||
|
||||
return NextResponse.json(pipeline, { status: 201 })
|
||||
}
|
||||
|
||||
// Otherwise create basic pipeline
|
||||
const pipeline = await createPipeline(topic, description)
|
||||
|
||||
return NextResponse.json(pipeline, { status: 201 })
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import prisma from '@/lib/db/client'
|
||||
import { PipelineStatus, StageStatus, StageName, Pipeline, Stage } from '@prisma/client'
|
||||
import { STAGE_WEIGHTS } from '@/lib/stages/types'
|
||||
import { VideoTemplate, TemplateCustomization, Platform } from '@/lib/templates'
|
||||
|
||||
// Stage execution order
|
||||
export const STAGE_ORDER: StageName[] = [
|
||||
@@ -51,6 +52,91 @@ export async function createPipeline(topic: string, description?: string): Promi
|
||||
})
|
||||
}
|
||||
|
||||
// Create pipeline with template configuration
|
||||
export interface TemplatePipelineOptions {
|
||||
description?: string
|
||||
customization?: TemplateCustomization
|
||||
platforms?: Platform[]
|
||||
targetDuration?: number
|
||||
}
|
||||
|
||||
export async function createPipelineWithTemplate(
|
||||
topic: string,
|
||||
template: VideoTemplate,
|
||||
options: TemplatePipelineOptions = {}
|
||||
): Promise<Pipeline & { stages: Stage[] }> {
|
||||
// Merge template with customization
|
||||
const mergedConfig = {
|
||||
voice: { ...template.voice, ...options.customization?.voice },
|
||||
visuals: { ...template.visuals, ...options.customization?.visuals },
|
||||
audio: { ...template.audio, ...options.customization?.audio },
|
||||
layout: { ...template.layout, ...options.customization?.layout },
|
||||
structure: { ...template.structure, ...options.customization?.structure },
|
||||
platforms: options.platforms || template.platforms,
|
||||
targetDuration: options.targetDuration,
|
||||
}
|
||||
|
||||
return prisma.pipeline.create({
|
||||
data: {
|
||||
topic,
|
||||
description: options.description,
|
||||
status: 'DRAFT',
|
||||
currentStage: 'RESEARCH',
|
||||
templateId: template.id,
|
||||
metadata: mergedConfig as any,
|
||||
stages: {
|
||||
create: STAGE_ORDER.map(name => ({
|
||||
name,
|
||||
status: 'PENDING',
|
||||
// Store relevant config for each stage
|
||||
input: getStageInputFromTemplate(name, mergedConfig) as any,
|
||||
}))
|
||||
}
|
||||
},
|
||||
include: { stages: true }
|
||||
})
|
||||
}
|
||||
|
||||
// Extract stage-specific config from template
|
||||
function getStageInputFromTemplate(stageName: StageName, config: any): Record<string, unknown> {
|
||||
switch (stageName) {
|
||||
case 'RESEARCH':
|
||||
return {
|
||||
structure: config.structure,
|
||||
targetDuration: config.targetDuration,
|
||||
}
|
||||
case 'SCRIPT':
|
||||
return {
|
||||
structure: config.structure,
|
||||
voice: config.voice,
|
||||
}
|
||||
case 'VOICE':
|
||||
return {
|
||||
voice: config.voice,
|
||||
}
|
||||
case 'MUSIC':
|
||||
return {
|
||||
audio: config.audio,
|
||||
}
|
||||
case 'VISUAL':
|
||||
return {
|
||||
visuals: config.visuals,
|
||||
}
|
||||
case 'EDITOR':
|
||||
return {
|
||||
layout: config.layout,
|
||||
visuals: config.visuals,
|
||||
}
|
||||
case 'PUBLISH':
|
||||
return {
|
||||
platforms: config.platforms,
|
||||
layout: config.layout,
|
||||
}
|
||||
default:
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
// Start pipeline execution
|
||||
export async function startPipeline(pipelineId: string): Promise<Pipeline> {
|
||||
return prisma.pipeline.update({
|
||||
|
||||
Reference in New Issue
Block a user