Files
avbeat-com/src/app/api/ai/article-suggestions/route.ts

78 lines
2.8 KiB
TypeScript

import { NextRequest, NextResponse } from 'next/server';
import { hybridAI } from '@/lib/ai/hybridRouter';
import {
getLegacyArticlesBySection,
getLegacyArticleBySlug,
} from '@/lib/articles/legacy-source';
export async function POST(request: NextRequest) {
try {
const { interests, readingHistory } = await request.json();
// Pull a recent slice of news for the AI to choose from. Cap at 100 so the
// prompt stays bounded.
const news = await getLegacyArticlesBySection('news', 100);
const articleIndex = news.map((a) => ({
slug: a.slug,
title: a.title,
excerpt: a.excerpt,
tags: a.tags,
category: a.category,
date: a.date,
}));
const systemPrompt = `You are a personalized content recommendation engine for AV Beat, a pro AV / live production / display tech news platform.
Given a reader's topic interests and reading history, select the 4 most relevant articles from the provided article list.
Return ONLY a valid JSON array of slugs in order of relevance. Example: ["slug-1","slug-2","slug-3","slug-4"]
Do not include any explanation or markdown — only the raw JSON array.`;
const userPrompt = `Reader interests: ${interests?.length ? interests.join(', ') : 'general pro AV and live production'}
Reading history (recently read slugs): ${readingHistory?.length ? readingHistory.join(', ') : 'none'}
Available articles:
${JSON.stringify(articleIndex, null, 2)}
Return the 4 most relevant article slugs as a JSON array.`;
const result = await hybridAI(
[
{ role: 'system', content: systemPrompt },
{ role: 'user', content: userPrompt },
],
{ maxTokens: 200, temperature: 0.3, priority: 4 }
);
const content = result.text;
let slugs: string[] = [];
try {
const match = content.match(/\[[\s\S]*?\]/);
slugs = match ? JSON.parse(match[0]) : JSON.parse(content.trim());
} catch {
const matches = content.match(/"([^"]+)"/g);
slugs = matches ? matches.map((m: string) => m.replace(/"/g, '')).slice(0, 4) : [];
}
// Resolve from the candidate set first; if a slug wasn't in the recent
// window, look it up via DB so AI hallucinations or older slugs still resolve.
const suggested = (
await Promise.all(
slugs.slice(0, 4).map(async (slug: string) =>
news.find((a) => a.slug === slug) || (await getLegacyArticleBySlug(slug))
)
)
).filter(Boolean);
if (suggested.length === 0) {
return NextResponse.json({ suggestions: news.slice(0, 4) });
}
return NextResponse.json({ suggestions: suggested });
} catch (error) {
console.error('Article suggestions error:', error);
const fallback = await getLegacyArticlesBySection('news', 4);
return NextResponse.json({ suggestions: fallback });
}
}