VIBN Frontend for Coolify deployment
This commit is contained in:
70
lib/ai/marketing-agent.ts
Normal file
70
lib/ai/marketing-agent.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import { z } from 'zod';
|
||||
import type { LlmClient } from '@/lib/ai/llm-client';
|
||||
import { GeminiLlmClient } from '@/lib/ai/gemini-client';
|
||||
import { clamp, nowIso, loadPhaseContainers, persistPhaseArtifacts } from '@/lib/server/projects';
|
||||
import type { MarketingModel } from '@/lib/types/marketing';
|
||||
|
||||
const HomepageMessagingSchema = z.object({
|
||||
headline: z.string().nullable(),
|
||||
subheadline: z.string().nullable(),
|
||||
bullets: z.array(z.string()).default([]),
|
||||
});
|
||||
|
||||
const MarketingModelSchema = z.object({
|
||||
projectId: z.string(),
|
||||
icp: z.array(z.string()).default([]),
|
||||
positioning: z.string().nullable(),
|
||||
homepageMessaging: HomepageMessagingSchema,
|
||||
initialChannels: z.array(z.string()).default([]),
|
||||
launchAngles: z.array(z.string()).default([]),
|
||||
overallConfidence: z.number().min(0).max(1),
|
||||
});
|
||||
|
||||
export async function runMarketingPlanning(
|
||||
projectId: string,
|
||||
llmClient?: LlmClient,
|
||||
): Promise<MarketingModel> {
|
||||
const { phaseData } = await loadPhaseContainers(projectId);
|
||||
const canonical = phaseData.canonicalProductModel;
|
||||
if (!canonical) {
|
||||
throw new Error('Canonical product model missing. Run buildCanonicalProductModel first.');
|
||||
}
|
||||
|
||||
const llm = llmClient ?? new GeminiLlmClient();
|
||||
const systemPrompt =
|
||||
'You are a SaaS marketing strategist. Given the canonical product model, produce ICP, positioning, homepage messaging, and launch ideas as strict JSON.';
|
||||
|
||||
const marketing = await llm.structuredCall<MarketingModel>({
|
||||
model: 'gemini',
|
||||
systemPrompt,
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: [
|
||||
'Canonical product model JSON:',
|
||||
'```json',
|
||||
JSON.stringify(canonical, null, 2),
|
||||
'```',
|
||||
'Respond ONLY with valid JSON that matches the required schema.',
|
||||
].join('\n'),
|
||||
},
|
||||
],
|
||||
schema: MarketingModelSchema,
|
||||
temperature: 0.2,
|
||||
});
|
||||
|
||||
await persistPhaseArtifacts(projectId, (phaseData, phaseScores, phaseHistory) => {
|
||||
phaseData.marketingPlan = marketing;
|
||||
phaseScores.marketing = {
|
||||
overallCompletion: clamp(marketing.homepageMessaging.bullets.length ? 0.7 : 0.5),
|
||||
overallConfidence: marketing.overallConfidence,
|
||||
updatedAt: nowIso(),
|
||||
};
|
||||
phaseHistory.push({ phase: 'marketing', status: 'completed', timestamp: nowIso() });
|
||||
return { phaseData, phaseScores, phaseHistory, nextPhase: 'marketing_ready' };
|
||||
});
|
||||
|
||||
return marketing;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user