diff --git a/src/lib/llm/providers/openai.ts b/src/lib/llm/providers/openai.ts index f3116af..f4eadd4 100644 --- a/src/lib/llm/providers/openai.ts +++ b/src/lib/llm/providers/openai.ts @@ -5,6 +5,7 @@ import type { DimensionResult, ItemResearch, } from "../types"; +import type { SearchResult } from "./tavily"; const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY, @@ -140,3 +141,75 @@ Provide a comprehensive comparison with scores, pros/cons, and a recommendation. `Failed to generate comparison after ${MAX_RETRIES} attempts: ${lastError?.message}` ); } + +export async function generateComparisonWithResearch( + request: ComparisonRequest, + searchResults: Record +): Promise { + const allResults = Object.values(searchResults).flat(); + if (allResults.length === 0) { + return generateComparison(request); + } + + let researchContext = "Web research data:\n\n"; + for (const [itemName, results] of Object.entries(searchResults)) { + if (results.length === 0) continue; + researchContext += `=== ${itemName} ===\n`; + for (const r of results) { + researchContext += `- ${r.title}: ${r.content}\n Source: ${r.url}\n`; + } + researchContext += "\n"; + } + + const userPrompt = `Compare the following items: ${request.items.join(", ")} +${request.query ? `Focus: ${request.query}` : ""} +${request.dimensions?.length ? `Specific dimensions to include: ${request.dimensions.join(", ")}` : ""} + +${researchContext} + +Use the web research data above to provide factual, data-driven insights. Reference specific data points in your analysis. Provide a comprehensive comparison with scores, pros/cons, and a recommendation.`; + + let lastError: Error | null = null; + + for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) { + try { + const response = await client.chat.completions.create({ + model: "gpt-4o-mini", + messages: [ + { role: "system", content: SYSTEM_PROMPT }, + { + role: "system", + content: + "You have access to real web search results. Use them to ground your comparison in factual data. Cite specific findings from the research when scoring and analyzing items.", + }, + { role: "user", content: userPrompt }, + ], + response_format: { type: "json_object" }, + temperature: 0.3, + }); + + const content = response.choices[0]?.message?.content; + if (!content) { + throw new Error("Empty response from OpenAI"); + } + + const parsed: unknown = JSON.parse(content); + + if (!validateComparisonResult(parsed)) { + throw new Error("Invalid comparison result structure from OpenAI"); + } + + return parsed; + } catch (error) { + lastError = error instanceof Error ? error : new Error(String(error)); + + if (attempt < MAX_RETRIES) { + await sleep(RETRY_DELAY_MS * attempt); + } + } + } + + throw new Error( + `Failed to generate comparison with research after ${MAX_RETRIES} attempts: ${lastError?.message}` + ); +}