import Anthropic from "@anthropic-ai/sdk"; import { z } from "zod"; import type { UnifiedProduct } from "./match-products"; const ReviewOutputSchema = z.object({ summary: z.string(), pros: z.array(z.string()).min(3).max(5), cons: z.array(z.string()).min(2).max(4), bestValue: z.string(), }); export type ReviewOutput = z.infer; export async function generateReview( product: UnifiedProduct, client: Anthropic, ): Promise { const priceList = product.prices .map((p) => `${p.store}: $${p.price.toFixed(2)}`) .join(", "); const prompt = `You are a consumer product reviewer writing for a shopping comparison app called Palace Cart. Based on the following product data, generate a review analysis. Product: ${product.name} Category: ${product.category} Rating: ${product.rating}/5 (${product.reviewCount.toLocaleString()} reviews) Prices: ${priceList} ${product.description ? `Description: ${product.description.slice(0, 500)}` : ""} Generate a JSON response with these exact fields: - "summary": A 3-4 sentence review summary. Write like a knowledgeable friend giving honest advice. Reference what the rating and review volume suggest about the product. Mention the key strength and main tradeoff. Do not invent specific claims about features not mentioned. - "pros": Array of 3-5 genuine product strengths. - "cons": Array of 2-4 honest drawbacks or tradeoffs. - "bestValue": One sentence identifying the best deal among the listed retailers, including the price. If all prices are the same, note that and mention when deals typically appear. Respond ONLY with valid JSON. No markdown, no explanation.`; const response = await client.messages.create({ model: "claude-sonnet-4-20250514", max_tokens: 800, messages: [{ role: "user", content: prompt }], }); const text = response.content[0].type === "text" ? response.content[0].text : ""; const parsed = JSON.parse(text); return ReviewOutputSchema.parse(parsed); } export async function generateAllReviews( products: UnifiedProduct[], apiKey: string, ): Promise> { const client = new Anthropic({ apiKey }); const results = new Map(); for (const product of products) { try { const review = await generateReview(product, client); results.set(product.id, review); console.log(`[ai] Generated review for ${product.name}`); } catch (err) { console.warn(`[ai] Failed for ${product.name}: ${err}`); results.set(product.id, { summary: `The ${product.name} has a ${product.rating}/5 rating from ${product.reviewCount.toLocaleString()} reviews, making it a well-regarded choice in the ${product.category} category.`, pros: ["Well-reviewed by customers", "Competitive pricing", "Popular choice in its category"], cons: ["Research specific features before buying", "Compare with alternatives"], bestValue: product.prices.length > 0 ? `${product.prices.find((p) => p.isBest)?.store ?? product.prices[0].store} at $${(product.prices.find((p) => p.isBest)?.price ?? product.prices[0].price).toFixed(2)}.` : "Check multiple retailers for the best price.", }); } } return results; }