diff --git a/apps/rowboat/app/actions/assistant-templates.actions.ts b/apps/rowboat/app/actions/assistant-templates.actions.ts index 428738ac..5b163a2e 100644 --- a/apps/rowboat/app/actions/assistant-templates.actions.ts +++ b/apps/rowboat/app/actions/assistant-templates.actions.ts @@ -3,7 +3,7 @@ import { z } from 'zod'; import { authCheck } from "./auth.actions"; import { MongoDBAssistantTemplatesRepository } from '@/src/infrastructure/repositories/mongodb.assistant-templates.repository'; -import { ensureLibraryTemplatesSeeded } from '@/app/lib/assistant_templates_seed'; +import { prebuiltTemplates } from '@/app/lib/prebuilt-cards'; import { auth0 } from '@/app/lib/auth0'; import { USE_AUTH } from '@/app/lib/feature_flags'; @@ -41,8 +41,7 @@ const CreateTemplateSchema = z.object({ export async function listAssistantTemplates(request: z.infer) { const user = await authCheck(); - // Ensure library JSONs are seeded into the unified collection (idempotent) - await ensureLibraryTemplatesSeeded(); + // No longer seeding all templates upfront - using lazy seeding instead const params = ListTemplatesSchema.parse(request); @@ -81,6 +80,54 @@ export async function listAssistantTemplates(request: z.infer tag.startsWith('prebuilt:') || tag === '__library__'); + + if (isPrebuiltTemplate) { + // For prebuilt templates, get the original JSON and apply fresh transformation + const prebuiltTag = template.tags.find(tag => tag.startsWith('prebuilt:')); + if (prebuiltTag) { + const prebuiltKey = prebuiltTag.replace('prebuilt:', ''); + const originalTemplate = prebuiltTemplates[prebuiltKey as keyof typeof prebuiltTemplates]; + + if (originalTemplate) { + // Apply model transformation to the original template + const defaultModel = process.env.PROVIDER_DEFAULT_MODEL || 'gpt-4.1'; + const transformedWorkflow = JSON.parse(JSON.stringify(originalTemplate)); + + // Transform blank agent models to use the default model + if (transformedWorkflow.agents && Array.isArray(transformedWorkflow.agents)) { + transformedWorkflow.agents.forEach((agent: any) => { + if (agent.model === '') { + agent.model = defaultModel; + console.log(`[PrebuiltTemplate] Applied model ${defaultModel} to agent ${agent.name} in template ${prebuiltKey}`); + } + }); + } + + // Return the transformed template with database metadata but fresh workflow + const result = { + ...template, + workflow: transformedWorkflow + }; + + return serializeTemplate(result); + } + } + } + + return serializeTemplate(template); +} + export async function getAssistantTemplateCategories() { const user = await authCheck(); @@ -88,15 +135,6 @@ export async function getAssistantTemplateCategories() { return { items: categories }; } -export async function getAssistantTemplate(id: string) { - const user = await authCheck(); - - const item = await repo.fetch(id); - if (!item) { - throw new Error('Template not found'); - } - return serializeTemplate(item); -} export async function createAssistantTemplate(data: z.infer) { const user = await authCheck(); diff --git a/apps/rowboat/app/lib/assistant_templates_seed.ts b/apps/rowboat/app/lib/assistant_templates_seed.ts index d577f3a4..862a2380 100644 --- a/apps/rowboat/app/lib/assistant_templates_seed.ts +++ b/apps/rowboat/app/lib/assistant_templates_seed.ts @@ -1,12 +1,17 @@ import { db } from "@/app/lib/mongodb"; import { prebuiltTemplates } from "@/app/lib/prebuilt-cards"; +// Cache to track which templates have been seeded +const seededTemplates = new Set(); + // idempotent seed: creates library (prebuilt) templates in DB if missing // Uses name+authorName match to avoid duplicates; tags include a stable prebuilt key export async function ensureLibraryTemplatesSeeded(): Promise { try { const collection = db.collection("assistant_templates"); const now = new Date().toISOString(); + + console.log('[PrebuiltTemplates] Starting template seeding...'); const entries = Object.entries(prebuiltTemplates); for (const [prebuiltKey, tpl] of entries) { @@ -17,7 +22,10 @@ export async function ensureLibraryTemplatesSeeded(): Promise { // check if already present (by name + authorName Rowboat and special tag) const existing = await collection.findOne({ name, authorName: "Rowboat", tags: { $in: [ `prebuilt:${prebuiltKey}`, "__library__" ] } }); - if (existing) continue; + if (existing) { + // Skip updating existing templates - we use original JSON at runtime + continue; + } const doc = { name, @@ -49,4 +57,98 @@ export async function ensureLibraryTemplatesSeeded(): Promise { } } +// Lazy seed: only seed a specific template when it's requested +export async function ensureTemplateSeeded(prebuiltKey: string): Promise { + if (seededTemplates.has(prebuiltKey)) { + return; // Already seeded + } + + const tpl = prebuiltTemplates[prebuiltKey as keyof typeof prebuiltTemplates]; + if (!tpl) { + console.warn(`[PrebuiltTemplates] Template not found: ${prebuiltKey}`); + return; + } + + try { + const collection = db.collection("assistant_templates"); + const now = new Date().toISOString(); + const name = (tpl as any).name || prebuiltKey; + + // Check if already exists + const existing = await collection.findOne({ + name, + authorName: "Rowboat", + tags: { $in: [ `prebuilt:${prebuiltKey}`, "__library__" ] } + }); + + if (existing) { + // Update existing template with current model configuration + const defaultModel = process.env.PROVIDER_DEFAULT_MODEL || 'gpt-4.1'; + const updatedWorkflow = JSON.parse(JSON.stringify(tpl)); + + // Apply model transformation + if (updatedWorkflow.agents && Array.isArray(updatedWorkflow.agents)) { + updatedWorkflow.agents.forEach((agent: any) => { + if (agent.model === '') { + agent.model = defaultModel; + } + }); + } + + await collection.updateOne( + { _id: existing._id }, + { + $set: { + workflow: updatedWorkflow, + lastUpdatedAt: now, + } + } + ); + console.log(`[PrebuiltTemplates] Updated template: ${name}`); + } else { + // Create new template with model transformation + const defaultModel = process.env.PROVIDER_DEFAULT_MODEL || 'gpt-4.1'; + const transformedWorkflow = JSON.parse(JSON.stringify(tpl)); + + // Apply model transformation + if (transformedWorkflow.agents && Array.isArray(transformedWorkflow.agents)) { + transformedWorkflow.agents.forEach((agent: any) => { + if (agent.model === '') { + agent.model = defaultModel; + } + }); + } + + const doc = { + name, + description: (tpl as any).description || "", + category: (tpl as any).category || "Other", + authorId: "rowboat-system", + authorName: "Rowboat", + authorEmail: undefined, + isAnonymous: false, + workflow: transformedWorkflow, + tags: ["__library__", `prebuilt:${prebuiltKey}`].filter(Boolean), + publishedAt: now, + lastUpdatedAt: now, + downloadCount: 0, + likeCount: 0, + featured: false, + isPublic: true, + likes: [] as string[], + copilotPrompt: (tpl as any).copilotPrompt || undefined, + thumbnailUrl: undefined, + source: 'library' as const, + } as const; + + await collection.insertOne(doc as any); + console.log(`[PrebuiltTemplates] Created template: ${name}`); + } + + seededTemplates.add(prebuiltKey); + } catch (err) { + console.error(`[PrebuiltTemplates] Error seeding template ${prebuiltKey}:`, err); + } +} +