import OpenAI from "openai"; import { zodTextFormat } from "openai/helpers/zod"; import { competitorAnalysisSchema, type CompetitorAnalysis, type SectorRequest } from "@/lib/schema"; const DEFAULT_OPENAI_MODEL = "gpt-5.4-mini"; let cachedClient: OpenAI | null = null; function getOpenAIClient() { const apiKey = process.env.OPENAI_API_KEY; if (!apiKey) { throw new Error("Missing OPENAI_API_KEY environment variable."); } cachedClient ??= new OpenAI({ apiKey }); return cachedClient; } function buildPrompt(request: SectorRequest) { const context = [ `Sector: ${request.sector}`, request.geography ? `Geography: ${request.geography}` : null, request.customerType ? `Customer type: ${request.customerType}` : null, request.focus ? `Strategic focus: ${request.focus}` : null ] .filter(Boolean) .join("\n"); return `${context} Produce a competitive landscape analysis from the perspective of a new market entrant. Keep the output grounded, specific, and commercially useful. Do not include markdown, preamble, or citations in the JSON fields. Make sure there are 4 to 6 key players, 3 to 5 emerging trends, and 2 to 3 white-space opportunities. Always include at least 2 concrete uncertainties in data_confidence.flagged_uncertainties.`; } export async function generateCompetitorAnalysis( request: SectorRequest ): Promise { const client = getOpenAIClient(); const model = process.env.OPENAI_MODEL || DEFAULT_OPENAI_MODEL; const response = await client.responses.parse({ model, reasoning: { effort: "medium" }, max_output_tokens: 2200, instructions: "You are an AI-powered competitive intelligence engine for early-stage startups. Return only content that matches the requested schema. Keep each field concise and decision-useful for a new entrant.", input: buildPrompt(request), text: { format: zodTextFormat(competitorAnalysisSchema, "competitor_analysis"), verbosity: "medium" } }); if (!response.output_parsed) { throw new Error("OpenAI returned no structured analysis payload."); } return competitorAnalysisSchema.parse(response.output_parsed); }