Files

1 line
97 KiB
JSON

{"repo": "bniket2107/salesfunnel", "files": {"server/src/services/aiService.js": {"eb94370b7149711a910f159ae0f3333230decd4c": {"t": 1785746668.84642, "n": 20028, "h": "56bc6aedc3edbe7b", "c": "/**\n * AI Service - Handles communication with various AI providers\n * Supports: OpenAI, Ollama, Gemini, Hypereal, DeepSeek (configurable via AI_PROVIDER env var)\n */\n\nconst AI_PROVIDER = process.env.AI_PROVIDER || 'openai';\n\n// OpenAI Configuration\nconst OPENAI_API_KEY = process.env.OPENAI_API_KEY;\nconst OPENAI_MODEL = process.env.OPENAI_MODEL || 'gpt-4.1-mini';\nconst OPENAI_BASE_URL = 'https://api.openai.com/v1';\n\n// DeepSeek Configuration\nconst DEEPSEEK_API_KEY = process.env.DEEPSEEK_API_KEY;\nconst DEEPSEEK_MODEL = process.env.DEEPSEEK_MODEL || 'deepseek-chat';\nconst DEEPSEEK_BASE_URL = 'https://api.deepseek.com/v1';\n\n// Ollama Configuration\nconst OLLAMA_BASE_URL = process.env.OLLAMA_BASE_URL || 'http://localhost:11434';\nconst OLLAMA_MODEL = process.env.OLLAMA_MODEL || 'llama3';\nconst OLLAMA_API_KEY = process.env.OLLAMA_API_KEY;\nconst OLLAMA_TIMEOUT = parseInt(process.env.OLLAMA_TIMEOUT) || 60000;\n\n// Gemini Configuration\nconst GEMINI_API_KEY = process.env.GEMINI_API_KEY;\nconst GEMINI_MODEL = process.env.GEMINI_MODEL || 'gemini-2.5-flash';\n\n// Hypereal AI Configuration\nconst HYPEREAL_API_KEY = process.env.HYPEREAL_API_KEY;\nconst HYPEREAL_MODEL = process.env.HYPEREAL_MODEL || 'claude-sonnet-4-6';\nconst HYPEREAL_BASE_URL = process.env.HYPEREAL_BASE_URL || 'https://api.hypereal.tech/v1';\n\n/**\n * System prompt for content generation\n */\nconst CONTENT_GENERATION_SYSTEM_PROMPT = `You are an expert Performance Marketer and Copywriter specializing in creating high-converting content.\n\nYour job is to generate professional, engaging content based on the provided framework and context.\n\nRules:\n- Follow the framework structure strictly\n- Make content specific and actionable\n- Use emotional hooks and persuasive language\n- Optimize for the specified platform\n- Include the hook, body, and call-to-action\n- Use proper formatting and structure\n- Do NOT include any meta-commentary or explanations\n- Output ONLY the generated content, ready to use`;\n\n/**\n * Generate content using OpenAI\n */\nasync function generateWithOpenAI(systemPrompt, userPrompt) {\n if (!OPENAI_API_KEY) {\n throw new Error('OpenAI API key not configured');\n }\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 60000);\n\n try {\n const response = await fetch(`${OPENAI_BASE_URL}/chat/completions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${OPENAI_API_KEY}`\n },\n body: JSON.stringify({\n model: OPENAI_MODEL,\n messages: [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: userPrompt }\n ],\n temperature: 0.7,\n max_tokens: 2000\n }),\n signal: controller.signal\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const error = await response.json();\n throw new Error(error.error?.message || `OpenAI API error: ${response.status}`);\n }\n\n const data = await response.json();\n\n if (!data.choices || !data.choices[0] || !data.choices[0].message) {\n throw new Error('Invalid response from OpenAI');\n }\n\n return data.choices[0].message.content.trim();\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error.name === 'AbortError') {\n throw new Error('OpenAI request timed out');\n }\n\n throw error;\n }\n}\n\n/**\n * Generate content using Ollama\n */\n// async function generateWithOllama(systemPrompt, userPrompt) {\n// const controller = new AbortController();\n// const timeoutId = setTimeout(() => controller.abort(), OLLAMA_TIMEOUT);\n\n// try {\n// // Prepare headers - add Authorization if API key is configured\n// const headers = {\n// 'Content-Type': 'application/json'\n// };\n\n// if (OLLAMA_API_KEY) {\n// headers['Authorization'] = `Bearer ${OLLAMA_API_KEY}`;\n// }\n\n// const response = await fetch(`${OLLAMA_BASE_URL}/api/generate`, {\n// method: 'POST',\n// headers,\n// body: JSON.stringify({\n// model: OLLAMA_MODEL,\n// prompt: `${systemPrompt}\\n\\n${userPrompt}`,\n// stream: false,\n// options: {\n// temperature: 0.7,\n// top_p: 0.9,\n// top_k: 40\n// }\n// }),\n// signal: controller.signal\n// });\n\n// clearTimeout(timeoutId);\n\n// if (!response.ok) {\n// const errorText = await response.text();\n// throw new Error(`Ollama API error: ${response.status} - ${errorText}`);\n// }\n\n// const data = await response.json();\n\n// if (!data.response) {\n// throw new Error('No response from Ollama');\n// }\n\n// return data.response.trim();\n// } catch (error) {\n// clearTimeout(timeoutId);\n\n// if (error.name === 'AbortError') {\n// throw new Error('Ollama request timed out');\n// }\n\n// throw error;\n// }\n// }\nasync function generateWithOllama(systemPrompt, userPrompt) {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), OLLAMA_TIMEOUT);\n\n try {\n // Prepare headers - add Authorization if API key is configured\n const headers = {\n 'Content-Type': 'application/json'\n };\n\n if (OLLAMA_API_KEY) {\n headers['Authorization'] = `Bearer ${OLLAMA_API_KEY}`;\n }\n\n // Determine the endpoint URL\n // If OLLAMA_BASE_URL already ends with /api/chat or /api/generate, use it as-is\n // Otherwise append /api/chat\n let endpointUrl = OLLAMA_BASE_URL;\n if (!endpointUrl.endsWith('/api/chat') && !endpointUrl.endsWith('/api/generate')) {\n endpointUrl = `${endpointUrl}/api/chat`;\n }\n\n const response = await fetch(endpointUrl, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n model: OLLAMA_MODEL,\n messages: [\n {\n role: \"system\",\n content: systemPrompt\n },\n {\n role: \"user\",\n content: userPrompt\n }\n ],\n stream: false,\n options: {\n temperature: 0.7,\n top_p: 0.9,\n top_k: 40\n }\n }),\n signal: controller.signal\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Ollama API error: ${response.status} - ${errorText}`);\n }\n\n const data = await response.json();\n\n if (!data.message || !data.message.content) {\n throw new Error('No response from Ollama');\n }\n\n return data.message.content.trim();\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error.name === 'AbortError') {\n throw new Error('Ollama request timed out');\n }\n\n throw error;\n }\n}\n/**\n * Generate content using Gemini\n */\nasync function generateWithGemini(systemPrompt, userPrompt) {\n if (!GEMINI_API_KEY) {\n throw new Error('Gemini API key not configured');\n }\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 60000);\n\n try {\n // Use v1beta endpoint which supports newer models\n const response = await fetch(\n `https://generativelanguage.googleapis.com/v1beta/models/${GEMINI_MODEL}:generateContent?key=${GEMINI_API_KEY}`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({\n contents: [{\n parts: [{\n text: `${systemPrompt}\\n\\n${userPrompt}`\n }]\n }],\n generationConfig: {\n temperature: 0.7,\n maxOutputTokens: 2000\n }\n }),\n signal: controller.signal\n }\n );\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const error = await response.json();\n throw new Error(error.error?.message || `Gemini API error: ${response.status}`);\n }\n\n const data = await response.json();\n\n if (!data.candidates || !data.candidates[0] || !data.candidates[0].content) {\n throw new Error('Invalid response from Gemini');\n }\n\n return data.candidates[0].content.parts[0].text.trim();\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error.name === 'AbortError') {\n throw new Error('Gemini request timed out');\n }\n\n throw error;\n }\n}\n\n/**\n * Generate content using Hypereal AI (OpenAI-compatible API)\n */\nasync function generateWithHypereal(systemPrompt, userPrompt) {\n if (!HYPEREAL_API_KEY) {\n throw new Error('Hypereal AI API key not configured');\n }\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 60000);\n\n try {\n const response = await fetch(`${HYPEREAL_BASE_URL}/chat`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${HYPEREAL_API_KEY}`\n },\n body: JSON.stringify({\n model: HYPEREAL_MODEL,\n messages: [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: userPrompt }\n ],\n temperature: 0.7,\n max_tokens: 2000,\n stream: false\n }),\n signal: controller.signal\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const error = await response.json();\n throw new Error(error.error?.message || `Hypereal API error: ${response.status}`);\n }\n\n const data = await response.json();\n\n if (!data.choices || !data.choices[0] || !data.choices[0].message) {\n throw new Error('Invalid response from Hypereal AI');\n }\n\n return data.choices[0].message.content.trim();\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error.name === 'AbortError') {\n throw new Error('Hypereal AI request timed out');\n }\n\n throw error;\n }\n}\n\n/**\n * Generate content using DeepSeek (OpenAI-compatible API)\n */\nasync function generateWithDeepSeek(systemPrompt, userPrompt) {\n if (!DEEPSEEK_API_KEY) {\n throw new Error('DeepSeek API key not configured');\n }\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 60000);\n\n try {\n const response = await fetch(`${DEEPSEEK_BASE_URL}/chat/completions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${DEEPSEEK_API_KEY}`\n },\n body: JSON.stringify({\n model: DEEPSEEK_MODEL,\n messages: [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: userPrompt }\n ],\n temperature: 0.7,\n max_tokens: 2000\n }),\n signal: controller.signal\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const error = await response.json();\n throw new Error(error.error?.message || `DeepSeek API error: ${response.status}`);\n }\n\n const data = await response.json();\n\n if (!data.choices || !data.choices[0] || !data.choices[0].message) {\n throw new Error('Invalid response from DeepSeek');\n }\n\n return data.choices[0].message.content.trim();\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error.name === 'AbortError') {\n throw new Error('DeepSeek request timed out');\n }\n\n throw error;\n }\n}\n\n/**\n * Main generate function - routes to appropriate AI provider\n * @param {string} systemPrompt - System prompt\n * @param {string} userPrompt - User prompt\n * @param {string} provider - Optional provider override (ollama, gemini, openai, hypereal, deepseek)\n */\nasync function generateContent(systemPrompt, userPrompt, provider = null) {\n const activeProvider = provider || AI_PROVIDER.toLowerCase();\n\n switch (activeProvider) {\n case 'openai':\n return generateWithOpenAI(systemPrompt, userPrompt);\n case 'ollama':\n return generateWithOllama(systemPrompt, userPrompt);\n case 'gemini':\n return generateWithGemini(systemPrompt, userPrompt);\n case 'hypereal':\n return generateWithHypereal(systemPrompt, userPrompt);\n case 'deepseek':\n return generateWithDeepSeek(systemPrompt, userPrompt);\n default:\n // Default to Ollama\n return generateWithOllama(systemPrompt, userPrompt);\n }\n}\n\n/**\n * Generate content brief for content planner\n * @param {Object} params - Parameters\n * @param {string} params.framework - Framework name\n * @param {string} params.frameworkTemplate - Framework template\n * @param {Object} params.context - Context object\n * @param {string} params.provider - Optional AI provider override\n */\nasync function generateContentBrief({ framework, frameworkTemplate, context, provider = null }) {\n // Build the user prompt with context\n const userPrompt = buildContentBriefPrompt(framework, frameworkTemplate, context);\n\n const systemPrompt = `You are an expert Content Planner and Copywriter specializing in ${framework} framework.\n\nYour task is to create a comprehensive content brief based on the provided framework template and context.\n\nIMPORTANT RULES:\n1. Follow the framework structure exactly as provided\n2. Replace ALL placeholders with actual content from the context\n3. Make the content specific to the brand, audience, and platform\n4. Use emotional hooks and persuasive language\n5. Include actionable CTAs\n6. Format the output clearly with headers and sections\n7. Do NOT include any meta-commentary - only the actual content\n8. Output should be ready to copy-paste and use directly\n\nFRAMEWORK: ${framework}\n\nGenerate the content brief now:`;\n\n try {\n const content = await generateContent(systemPrompt, userPrompt, provider);\n return content;\n } catch (error) {\n console.error('Error generating content brief:', error);\n throw error;\n }\n}\n\n/**\n * Build the content brief prompt with all context\n */\nfunction buildContentBriefPrompt(framework, frameworkTemplate, context) {\n let prompt = `FRAMEWORK TEMPLATE:\\n${frameworkTemplate}\\n\\n`;\n\n prompt += `PROJECT & TASK CONTEXT:\\n`;\n prompt += `=====================\\n\\n`;\n\n // Project Info\n if (context.projectName) {\n prompt += `Project: ${context.projectName}\\n`;\n }\n if (context.businessName) {\n prompt += `Brand/Business: ${context.businessName}\\n`;\n }\n if (context.industry) {\n prompt += `Industry: ${context.industry}\\n`;\n }\n\n prompt += `\\nTARGET AUDIENCE:\\n`;\n prompt += `----------------\\n`;\n if (context.targetAudience) {\n prompt += `Target Audience: ${context.targetAudience}\\n`;\n }\n if (context.avatar?.description) {\n prompt += `Avatar Description: ${context.avatar.description}\\n`;\n }\n if (context.avatar?.ageRange) {\n prompt += `Age Range: ${context.avatar.ageRange}\\n`;\n }\n if (context.avatar?.gender) {\n prompt += `Gender: ${context.avatar.gender}\\n`;\n }\n\n // Pain Points\n if (context.painPoints && context.painPoints.length > 0) {\n prompt += `\\nPAIN POINTS:\\n`;\n prompt += `------------\\n`;\n context.painPoints.forEach((point, i) => {\n prompt += `${i + 1}. ${point}\\n`;\n });\n }\n\n // Desires\n if (context.desires && context.desires.length > 0) {\n prompt += `\\nDESIRES:\\n`;\n prompt += `--------\\n`;\n context.desires.forEach((desire, i) => {\n prompt += `${i + 1}. ${desire}\\n`;\n });\n }\n\n // Platform & Funnel\n prompt += `\\nPLATFORM & FUNNEL:\\n`;\n prompt += `------------------\\n`;\n if (context.platform) {\n prompt += `Platform: ${context.platform}\\n`;\n }\n if (context.funnelStage) {\n prompt += `Funnel Stage: ${context.funnelStage}\\n`;\n }\n if (context.creativeType) {\n prompt += `Creative Type: ${context.creativeType}\\n`;\n }\n\n // Strategy Elements\n prompt += `\\nSTRATEGY ELEMENTS:\\n`;\n prompt += `------------------\\n`;\n if (context.hook) {\n prompt += `Hook: ${context.hook}\\n`;\n }\n if (context.headline) {\n prompt += `Headline: ${context.headline}\\n`;\n }\n if (context.cta) {\n prompt += `Call to Action: ${context.cta}\\n`;\n }\n if (context.creativeAngle) {\n prompt += `Creative Angle: ${context.creativeAngle}\\n`;\n }\n if (context.messaging) {\n prompt += `Messaging: ${context.messaging}\\n`;\n }\n\n // Offer\n if (context.offer) {\n prompt += `\\nOFFER:\\n`;\n prompt += `-------\\n`;\n prompt += `${context.offer}\\n`;\n }\n\n // Task Specifics\n if (context.taskTitle) {\n prompt += `\\nTASK:\\n`;\n prompt += `------\\n`;\n prompt += `Task: ${context.taskTitle}\\n`;\n }\n if (context.taskType) {\n prompt += `Type: ${context.taskType}\\n`;\n }\n\n // Approved Content (for Graphic Designers and Video Editors)\n if (context.approvedContent && context.approvedContent.trim()) {\n prompt += `\\n\\n*** APPROVED CONTENT (MUST USE THIS EXACT CONTENT) ***\\n`;\n prompt += `======================================================\\n\\n`;\n prompt += `The following content has been approved by the Content Planner. You MUST incorporate this exact content into your design brief:\\n\\n`;\n prompt += context.approvedContent;\n prompt += `\\n\\n*** END APPROVED CONTENT ***\\n`;\n }\n\n prompt += `\\n\\nGENERATE THE CONTENT BRIEF NOW using the ${framework} framework structure. Make sure all placeholders are replaced with actual content from the context above.`;\n\n return prompt;\n}\n\n/**\n * Check AI provider health\n */\nasync function checkAIHealth() {\n try {\n switch (AI_PROVIDER.toLowerCase()) {\n case 'openai':\n if (!OPENAI_API_KEY) {\n return { available: false, error: 'OpenAI API key not configured' };\n }\n // Simple test request\n const response = await fetch(`${OPENAI_BASE_URL}/models`, {\n headers: {\n 'Authorization': `Bearer ${OPENAI_API_KEY}`\n }\n });\n if (!response.ok) {\n return { available: false, error: 'OpenAI API key is invalid' };\n }\n return {\n available: true,\n provider: 'openai',\n model: OPENAI_MODEL\n };\n\n case 'ollama':\n // Prepare headers - add Authorization if API key is configured\n const ollamaHeaders = {};\n if (OLLAMA_API_KEY) {\n ollamaHeaders['Authorization'] = `Bearer ${OLLAMA_API_KEY}`;\n }\n const ollamaResponse = await fetch(`${OLLAMA_BASE_URL}/api/tags`, {\n headers: ollamaHeaders\n });\n if (!ollamaResponse.ok) {\n return { available: false, error: 'Ollama not responding' };\n }\n const ollamaData = await ollamaResponse.json();\n return {\n available: true,\n provider: 'ollama',\n model: OLLAMA_MODEL,\n models: ollamaData.models?.map(m => m.name) || []\n };\n\n case 'gemini':\n if (!GEMINI_API_KEY) {\n return { available: false, error: 'Gemini API key not configured' };\n }\n // Test Gemini API with v1beta endpoint\n try {\n const geminiTest = await fetch(\n `https://generativelanguage.googleapis.com/v1beta/models?key=${GEMINI_API_KEY}`\n );\n if (!geminiTest.ok) {\n return { available: false, error: 'Gemini API key is invalid' };\n }\n return {\n available: true,\n provider: 'gemini',\n model: GEMINI_MODEL\n };\n } catch (e) {\n return { available: false, error: 'Cannot connect to Gemini API' };\n }\n\n case 'hypereal':\n if (!HYPEREAL_API_KEY) {\n return { available: false, error: 'Hypereal AI API key not configured' };\n }\n return {\n available: true,\n provider: 'hypereal',\n model: HYPEREAL_MODEL\n };\n\n case 'deepseek':\n if (!DEEPSEEK_API_KEY) {\n return { available: false, error: 'DeepSeek API key not configured' };\n }\n return {\n available: true,\n provider: 'deepseek',\n model: DEEPSEEK_MODEL\n };\n\n default:\n return { available: false, error: 'Unknown AI provider configured' };\n }\n } catch (error) {\n return {\n available: false,\n error: `Cannot connect to ${AI_PROVIDER}: ${error.message}`\n };\n }\n}\n\nmodule.exports = {\n generateContent,\n generateContentBrief,\n checkAIHealth,\n AI_PROVIDER,\n CONTENT_GENERATION_SYSTEM_PROMPT\n};"}, "c39d0585829a24295aa7d39df7094e336581338a": {"t": 1785766714.5826352, "n": 20028, "h": "56bc6aedc3edbe7b", "c": "/**\n * AI Service - Handles communication with various AI providers\n * Supports: OpenAI, Ollama, Gemini, Hypereal, DeepSeek (configurable via AI_PROVIDER env var)\n */\n\nconst AI_PROVIDER = process.env.AI_PROVIDER || 'openai';\n\n// OpenAI Configuration\nconst OPENAI_API_KEY = process.env.OPENAI_API_KEY;\nconst OPENAI_MODEL = process.env.OPENAI_MODEL || 'gpt-4.1-mini';\nconst OPENAI_BASE_URL = 'https://api.openai.com/v1';\n\n// DeepSeek Configuration\nconst DEEPSEEK_API_KEY = process.env.DEEPSEEK_API_KEY;\nconst DEEPSEEK_MODEL = process.env.DEEPSEEK_MODEL || 'deepseek-chat';\nconst DEEPSEEK_BASE_URL = 'https://api.deepseek.com/v1';\n\n// Ollama Configuration\nconst OLLAMA_BASE_URL = process.env.OLLAMA_BASE_URL || 'http://localhost:11434';\nconst OLLAMA_MODEL = process.env.OLLAMA_MODEL || 'llama3';\nconst OLLAMA_API_KEY = process.env.OLLAMA_API_KEY;\nconst OLLAMA_TIMEOUT = parseInt(process.env.OLLAMA_TIMEOUT) || 60000;\n\n// Gemini Configuration\nconst GEMINI_API_KEY = process.env.GEMINI_API_KEY;\nconst GEMINI_MODEL = process.env.GEMINI_MODEL || 'gemini-2.5-flash';\n\n// Hypereal AI Configuration\nconst HYPEREAL_API_KEY = process.env.HYPEREAL_API_KEY;\nconst HYPEREAL_MODEL = process.env.HYPEREAL_MODEL || 'claude-sonnet-4-6';\nconst HYPEREAL_BASE_URL = process.env.HYPEREAL_BASE_URL || 'https://api.hypereal.tech/v1';\n\n/**\n * System prompt for content generation\n */\nconst CONTENT_GENERATION_SYSTEM_PROMPT = `You are an expert Performance Marketer and Copywriter specializing in creating high-converting content.\n\nYour job is to generate professional, engaging content based on the provided framework and context.\n\nRules:\n- Follow the framework structure strictly\n- Make content specific and actionable\n- Use emotional hooks and persuasive language\n- Optimize for the specified platform\n- Include the hook, body, and call-to-action\n- Use proper formatting and structure\n- Do NOT include any meta-commentary or explanations\n- Output ONLY the generated content, ready to use`;\n\n/**\n * Generate content using OpenAI\n */\nasync function generateWithOpenAI(systemPrompt, userPrompt) {\n if (!OPENAI_API_KEY) {\n throw new Error('OpenAI API key not configured');\n }\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 60000);\n\n try {\n const response = await fetch(`${OPENAI_BASE_URL}/chat/completions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${OPENAI_API_KEY}`\n },\n body: JSON.stringify({\n model: OPENAI_MODEL,\n messages: [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: userPrompt }\n ],\n temperature: 0.7,\n max_tokens: 2000\n }),\n signal: controller.signal\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const error = await response.json();\n throw new Error(error.error?.message || `OpenAI API error: ${response.status}`);\n }\n\n const data = await response.json();\n\n if (!data.choices || !data.choices[0] || !data.choices[0].message) {\n throw new Error('Invalid response from OpenAI');\n }\n\n return data.choices[0].message.content.trim();\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error.name === 'AbortError') {\n throw new Error('OpenAI request timed out');\n }\n\n throw error;\n }\n}\n\n/**\n * Generate content using Ollama\n */\n// async function generateWithOllama(systemPrompt, userPrompt) {\n// const controller = new AbortController();\n// const timeoutId = setTimeout(() => controller.abort(), OLLAMA_TIMEOUT);\n\n// try {\n// // Prepare headers - add Authorization if API key is configured\n// const headers = {\n// 'Content-Type': 'application/json'\n// };\n\n// if (OLLAMA_API_KEY) {\n// headers['Authorization'] = `Bearer ${OLLAMA_API_KEY}`;\n// }\n\n// const response = await fetch(`${OLLAMA_BASE_URL}/api/generate`, {\n// method: 'POST',\n// headers,\n// body: JSON.stringify({\n// model: OLLAMA_MODEL,\n// prompt: `${systemPrompt}\\n\\n${userPrompt}`,\n// stream: false,\n// options: {\n// temperature: 0.7,\n// top_p: 0.9,\n// top_k: 40\n// }\n// }),\n// signal: controller.signal\n// });\n\n// clearTimeout(timeoutId);\n\n// if (!response.ok) {\n// const errorText = await response.text();\n// throw new Error(`Ollama API error: ${response.status} - ${errorText}`);\n// }\n\n// const data = await response.json();\n\n// if (!data.response) {\n// throw new Error('No response from Ollama');\n// }\n\n// return data.response.trim();\n// } catch (error) {\n// clearTimeout(timeoutId);\n\n// if (error.name === 'AbortError') {\n// throw new Error('Ollama request timed out');\n// }\n\n// throw error;\n// }\n// }\nasync function generateWithOllama(systemPrompt, userPrompt) {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), OLLAMA_TIMEOUT);\n\n try {\n // Prepare headers - add Authorization if API key is configured\n const headers = {\n 'Content-Type': 'application/json'\n };\n\n if (OLLAMA_API_KEY) {\n headers['Authorization'] = `Bearer ${OLLAMA_API_KEY}`;\n }\n\n // Determine the endpoint URL\n // If OLLAMA_BASE_URL already ends with /api/chat or /api/generate, use it as-is\n // Otherwise append /api/chat\n let endpointUrl = OLLAMA_BASE_URL;\n if (!endpointUrl.endsWith('/api/chat') && !endpointUrl.endsWith('/api/generate')) {\n endpointUrl = `${endpointUrl}/api/chat`;\n }\n\n const response = await fetch(endpointUrl, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n model: OLLAMA_MODEL,\n messages: [\n {\n role: \"system\",\n content: systemPrompt\n },\n {\n role: \"user\",\n content: userPrompt\n }\n ],\n stream: false,\n options: {\n temperature: 0.7,\n top_p: 0.9,\n top_k: 40\n }\n }),\n signal: controller.signal\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Ollama API error: ${response.status} - ${errorText}`);\n }\n\n const data = await response.json();\n\n if (!data.message || !data.message.content) {\n throw new Error('No response from Ollama');\n }\n\n return data.message.content.trim();\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error.name === 'AbortError') {\n throw new Error('Ollama request timed out');\n }\n\n throw error;\n }\n}\n/**\n * Generate content using Gemini\n */\nasync function generateWithGemini(systemPrompt, userPrompt) {\n if (!GEMINI_API_KEY) {\n throw new Error('Gemini API key not configured');\n }\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 60000);\n\n try {\n // Use v1beta endpoint which supports newer models\n const response = await fetch(\n `https://generativelanguage.googleapis.com/v1beta/models/${GEMINI_MODEL}:generateContent?key=${GEMINI_API_KEY}`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({\n contents: [{\n parts: [{\n text: `${systemPrompt}\\n\\n${userPrompt}`\n }]\n }],\n generationConfig: {\n temperature: 0.7,\n maxOutputTokens: 2000\n }\n }),\n signal: controller.signal\n }\n );\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const error = await response.json();\n throw new Error(error.error?.message || `Gemini API error: ${response.status}`);\n }\n\n const data = await response.json();\n\n if (!data.candidates || !data.candidates[0] || !data.candidates[0].content) {\n throw new Error('Invalid response from Gemini');\n }\n\n return data.candidates[0].content.parts[0].text.trim();\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error.name === 'AbortError') {\n throw new Error('Gemini request timed out');\n }\n\n throw error;\n }\n}\n\n/**\n * Generate content using Hypereal AI (OpenAI-compatible API)\n */\nasync function generateWithHypereal(systemPrompt, userPrompt) {\n if (!HYPEREAL_API_KEY) {\n throw new Error('Hypereal AI API key not configured');\n }\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 60000);\n\n try {\n const response = await fetch(`${HYPEREAL_BASE_URL}/chat`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${HYPEREAL_API_KEY}`\n },\n body: JSON.stringify({\n model: HYPEREAL_MODEL,\n messages: [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: userPrompt }\n ],\n temperature: 0.7,\n max_tokens: 2000,\n stream: false\n }),\n signal: controller.signal\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const error = await response.json();\n throw new Error(error.error?.message || `Hypereal API error: ${response.status}`);\n }\n\n const data = await response.json();\n\n if (!data.choices || !data.choices[0] || !data.choices[0].message) {\n throw new Error('Invalid response from Hypereal AI');\n }\n\n return data.choices[0].message.content.trim();\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error.name === 'AbortError') {\n throw new Error('Hypereal AI request timed out');\n }\n\n throw error;\n }\n}\n\n/**\n * Generate content using DeepSeek (OpenAI-compatible API)\n */\nasync function generateWithDeepSeek(systemPrompt, userPrompt) {\n if (!DEEPSEEK_API_KEY) {\n throw new Error('DeepSeek API key not configured');\n }\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 60000);\n\n try {\n const response = await fetch(`${DEEPSEEK_BASE_URL}/chat/completions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${DEEPSEEK_API_KEY}`\n },\n body: JSON.stringify({\n model: DEEPSEEK_MODEL,\n messages: [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: userPrompt }\n ],\n temperature: 0.7,\n max_tokens: 2000\n }),\n signal: controller.signal\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const error = await response.json();\n throw new Error(error.error?.message || `DeepSeek API error: ${response.status}`);\n }\n\n const data = await response.json();\n\n if (!data.choices || !data.choices[0] || !data.choices[0].message) {\n throw new Error('Invalid response from DeepSeek');\n }\n\n return data.choices[0].message.content.trim();\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error.name === 'AbortError') {\n throw new Error('DeepSeek request timed out');\n }\n\n throw error;\n }\n}\n\n/**\n * Main generate function - routes to appropriate AI provider\n * @param {string} systemPrompt - System prompt\n * @param {string} userPrompt - User prompt\n * @param {string} provider - Optional provider override (ollama, gemini, openai, hypereal, deepseek)\n */\nasync function generateContent(systemPrompt, userPrompt, provider = null) {\n const activeProvider = provider || AI_PROVIDER.toLowerCase();\n\n switch (activeProvider) {\n case 'openai':\n return generateWithOpenAI(systemPrompt, userPrompt);\n case 'ollama':\n return generateWithOllama(systemPrompt, userPrompt);\n case 'gemini':\n return generateWithGemini(systemPrompt, userPrompt);\n case 'hypereal':\n return generateWithHypereal(systemPrompt, userPrompt);\n case 'deepseek':\n return generateWithDeepSeek(systemPrompt, userPrompt);\n default:\n // Default to Ollama\n return generateWithOllama(systemPrompt, userPrompt);\n }\n}\n\n/**\n * Generate content brief for content planner\n * @param {Object} params - Parameters\n * @param {string} params.framework - Framework name\n * @param {string} params.frameworkTemplate - Framework template\n * @param {Object} params.context - Context object\n * @param {string} params.provider - Optional AI provider override\n */\nasync function generateContentBrief({ framework, frameworkTemplate, context, provider = null }) {\n // Build the user prompt with context\n const userPrompt = buildContentBriefPrompt(framework, frameworkTemplate, context);\n\n const systemPrompt = `You are an expert Content Planner and Copywriter specializing in ${framework} framework.\n\nYour task is to create a comprehensive content brief based on the provided framework template and context.\n\nIMPORTANT RULES:\n1. Follow the framework structure exactly as provided\n2. Replace ALL placeholders with actual content from the context\n3. Make the content specific to the brand, audience, and platform\n4. Use emotional hooks and persuasive language\n5. Include actionable CTAs\n6. Format the output clearly with headers and sections\n7. Do NOT include any meta-commentary - only the actual content\n8. Output should be ready to copy-paste and use directly\n\nFRAMEWORK: ${framework}\n\nGenerate the content brief now:`;\n\n try {\n const content = await generateContent(systemPrompt, userPrompt, provider);\n return content;\n } catch (error) {\n console.error('Error generating content brief:', error);\n throw error;\n }\n}\n\n/**\n * Build the content brief prompt with all context\n */\nfunction buildContentBriefPrompt(framework, frameworkTemplate, context) {\n let prompt = `FRAMEWORK TEMPLATE:\\n${frameworkTemplate}\\n\\n`;\n\n prompt += `PROJECT & TASK CONTEXT:\\n`;\n prompt += `=====================\\n\\n`;\n\n // Project Info\n if (context.projectName) {\n prompt += `Project: ${context.projectName}\\n`;\n }\n if (context.businessName) {\n prompt += `Brand/Business: ${context.businessName}\\n`;\n }\n if (context.industry) {\n prompt += `Industry: ${context.industry}\\n`;\n }\n\n prompt += `\\nTARGET AUDIENCE:\\n`;\n prompt += `----------------\\n`;\n if (context.targetAudience) {\n prompt += `Target Audience: ${context.targetAudience}\\n`;\n }\n if (context.avatar?.description) {\n prompt += `Avatar Description: ${context.avatar.description}\\n`;\n }\n if (context.avatar?.ageRange) {\n prompt += `Age Range: ${context.avatar.ageRange}\\n`;\n }\n if (context.avatar?.gender) {\n prompt += `Gender: ${context.avatar.gender}\\n`;\n }\n\n // Pain Points\n if (context.painPoints && context.painPoints.length > 0) {\n prompt += `\\nPAIN POINTS:\\n`;\n prompt += `------------\\n`;\n context.painPoints.forEach((point, i) => {\n prompt += `${i + 1}. ${point}\\n`;\n });\n }\n\n // Desires\n if (context.desires && context.desires.length > 0) {\n prompt += `\\nDESIRES:\\n`;\n prompt += `--------\\n`;\n context.desires.forEach((desire, i) => {\n prompt += `${i + 1}. ${desire}\\n`;\n });\n }\n\n // Platform & Funnel\n prompt += `\\nPLATFORM & FUNNEL:\\n`;\n prompt += `------------------\\n`;\n if (context.platform) {\n prompt += `Platform: ${context.platform}\\n`;\n }\n if (context.funnelStage) {\n prompt += `Funnel Stage: ${context.funnelStage}\\n`;\n }\n if (context.creativeType) {\n prompt += `Creative Type: ${context.creativeType}\\n`;\n }\n\n // Strategy Elements\n prompt += `\\nSTRATEGY ELEMENTS:\\n`;\n prompt += `------------------\\n`;\n if (context.hook) {\n prompt += `Hook: ${context.hook}\\n`;\n }\n if (context.headline) {\n prompt += `Headline: ${context.headline}\\n`;\n }\n if (context.cta) {\n prompt += `Call to Action: ${context.cta}\\n`;\n }\n if (context.creativeAngle) {\n prompt += `Creative Angle: ${context.creativeAngle}\\n`;\n }\n if (context.messaging) {\n prompt += `Messaging: ${context.messaging}\\n`;\n }\n\n // Offer\n if (context.offer) {\n prompt += `\\nOFFER:\\n`;\n prompt += `-------\\n`;\n prompt += `${context.offer}\\n`;\n }\n\n // Task Specifics\n if (context.taskTitle) {\n prompt += `\\nTASK:\\n`;\n prompt += `------\\n`;\n prompt += `Task: ${context.taskTitle}\\n`;\n }\n if (context.taskType) {\n prompt += `Type: ${context.taskType}\\n`;\n }\n\n // Approved Content (for Graphic Designers and Video Editors)\n if (context.approvedContent && context.approvedContent.trim()) {\n prompt += `\\n\\n*** APPROVED CONTENT (MUST USE THIS EXACT CONTENT) ***\\n`;\n prompt += `======================================================\\n\\n`;\n prompt += `The following content has been approved by the Content Planner. You MUST incorporate this exact content into your design brief:\\n\\n`;\n prompt += context.approvedContent;\n prompt += `\\n\\n*** END APPROVED CONTENT ***\\n`;\n }\n\n prompt += `\\n\\nGENERATE THE CONTENT BRIEF NOW using the ${framework} framework structure. Make sure all placeholders are replaced with actual content from the context above.`;\n\n return prompt;\n}\n\n/**\n * Check AI provider health\n */\nasync function checkAIHealth() {\n try {\n switch (AI_PROVIDER.toLowerCase()) {\n case 'openai':\n if (!OPENAI_API_KEY) {\n return { available: false, error: 'OpenAI API key not configured' };\n }\n // Simple test request\n const response = await fetch(`${OPENAI_BASE_URL}/models`, {\n headers: {\n 'Authorization': `Bearer ${OPENAI_API_KEY}`\n }\n });\n if (!response.ok) {\n return { available: false, error: 'OpenAI API key is invalid' };\n }\n return {\n available: true,\n provider: 'openai',\n model: OPENAI_MODEL\n };\n\n case 'ollama':\n // Prepare headers - add Authorization if API key is configured\n const ollamaHeaders = {};\n if (OLLAMA_API_KEY) {\n ollamaHeaders['Authorization'] = `Bearer ${OLLAMA_API_KEY}`;\n }\n const ollamaResponse = await fetch(`${OLLAMA_BASE_URL}/api/tags`, {\n headers: ollamaHeaders\n });\n if (!ollamaResponse.ok) {\n return { available: false, error: 'Ollama not responding' };\n }\n const ollamaData = await ollamaResponse.json();\n return {\n available: true,\n provider: 'ollama',\n model: OLLAMA_MODEL,\n models: ollamaData.models?.map(m => m.name) || []\n };\n\n case 'gemini':\n if (!GEMINI_API_KEY) {\n return { available: false, error: 'Gemini API key not configured' };\n }\n // Test Gemini API with v1beta endpoint\n try {\n const geminiTest = await fetch(\n `https://generativelanguage.googleapis.com/v1beta/models?key=${GEMINI_API_KEY}`\n );\n if (!geminiTest.ok) {\n return { available: false, error: 'Gemini API key is invalid' };\n }\n return {\n available: true,\n provider: 'gemini',\n model: GEMINI_MODEL\n };\n } catch (e) {\n return { available: false, error: 'Cannot connect to Gemini API' };\n }\n\n case 'hypereal':\n if (!HYPEREAL_API_KEY) {\n return { available: false, error: 'Hypereal AI API key not configured' };\n }\n return {\n available: true,\n provider: 'hypereal',\n model: HYPEREAL_MODEL\n };\n\n case 'deepseek':\n if (!DEEPSEEK_API_KEY) {\n return { available: false, error: 'DeepSeek API key not configured' };\n }\n return {\n available: true,\n provider: 'deepseek',\n model: DEEPSEEK_MODEL\n };\n\n default:\n return { available: false, error: 'Unknown AI provider configured' };\n }\n } catch (error) {\n return {\n available: false,\n error: `Cannot connect to ${AI_PROVIDER}: ${error.message}`\n };\n }\n}\n\nmodule.exports = {\n generateContent,\n generateContentBrief,\n checkAIHealth,\n AI_PROVIDER,\n CONTENT_GENERATION_SYSTEM_PROMPT\n};"}, "15e48482c8e091b580b8e0ee4b95b8f8b989c98f": {"t": 1785766715.9172182, "n": 19498, "h": "5577ffa95c29ddba", "c": "/**\n * AI Service - Handles communication with various AI providers\n * Supports: OpenAI, Ollama, Gemini, Hypereal, DeepSeek (configurable via AI_PROVIDER env var)\n */\n\nconst AI_PROVIDER = process.env.AI_PROVIDER || 'openai';\n\n// OpenAI Configuration\nconst OPENAI_API_KEY = process.env.OPENAI_API_KEY;\nconst OPENAI_MODEL = process.env.OPENAI_MODEL || 'gpt-4.1-mini';\nconst OPENAI_BASE_URL = 'https://api.openai.com/v1';\n\n// DeepSeek Configuration\nconst DEEPSEEK_API_KEY = process.env.DEEPSEEK_API_KEY;\nconst DEEPSEEK_MODEL = process.env.DEEPSEEK_MODEL || 'deepseek-chat';\nconst DEEPSEEK_BASE_URL = 'https://api.deepseek.com/v1';\n\n// Ollama Configuration\nconst OLLAMA_BASE_URL = process.env.OLLAMA_BASE_URL || 'http://localhost:11434';\nconst OLLAMA_MODEL = process.env.OLLAMA_MODEL || 'llama3';\nconst OLLAMA_API_KEY = process.env.OLLAMA_API_KEY;\nconst OLLAMA_TIMEOUT = parseInt(process.env.OLLAMA_TIMEOUT) || 60000;\n\n// Gemini Configuration\nconst GEMINI_API_KEY = process.env.GEMINI_API_KEY;\nconst GEMINI_MODEL = process.env.GEMINI_MODEL || 'gemini-2.5-flash';\n\n// Hypereal AI Configuration\nconst HYPEREAL_API_KEY = process.env.HYPEREAL_API_KEY;\nconst HYPEREAL_MODEL = process.env.HYPEREAL_MODEL || 'claude-sonnet-4-6';\nconst HYPEREAL_BASE_URL = process.env.HYPEREAL_BASE_URL || 'https://api.hypereal.tech/v1';\n\n/**\n * System prompt for content generation\n */\nconst CONTENT_GENERATION_SYSTEM_PROMPT = `You are an expert Performance Marketer and Copywriter specializing in creating high-converting content.\n\nYour job is to generate professional, engaging content based on the provided framework and context.\n\nRules:\n- Follow the framework structure strictly\n- Make content specific and actionable\n- Use emotional hooks and persuasive language\n- Optimize for the specified platform\n- Include the hook, body, and call-to-action\n- Use proper formatting and structure\n- Do NOT include any meta-commentary or explanations\n- Output ONLY the generated content, ready to use`;\n\n/**\n * Generate content using OpenAI\n */\nasync function generateWithOpenAI(systemPrompt, userPrompt) {\n if (!OPENAI_API_KEY) {\n throw new Error('OpenAI API key not configured');\n }\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 60000);\n\n try {\n const response = await fetch(`${OPENAI_BASE_URL}/chat/completions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${OPENAI_API_KEY}`\n },\n body: JSON.stringify({\n model: OPENAI_MODEL,\n messages: [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: userPrompt }\n ],\n temperature: 0.7,\n max_tokens: 2000\n }),\n signal: controller.signal\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const error = await response.json();\n throw new Error(error.error?.message || `OpenAI API error: ${response.status}`);\n }\n\n const data = await response.json();\n\n if (!data.choices || !data.choices[0] || !data.choices[0].message) {\n throw new Error('Invalid response from OpenAI');\n }\n\n return data.choices[0].message.content.trim();\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error.name === 'AbortError') {\n throw new Error('OpenAI request timed out');\n }\n\n throw error;\n }\n}\n\n/**\n * Generate content using Ollama\n */\n// async function generateWithOllama(systemPrompt, userPrompt) {\n// const controller = new AbortController();\n// const timeoutId = setTimeout(() => controller.abort(), OLLAMA_TIMEOUT);\n\n// try {\n// // Prepare headers - add Authorization if API key is configured\n// const headers = {\n// 'Content-Type': 'application/json'\n// };\n\n// if (OLLAMA_API_KEY) {\n// headers['Authorization'] = `Bearer ${OLLAMA_API_KEY}`;\n// }\n\n// const response = await fetch(`${OLLAMA_BASE_URL}/api/generate`, {\n// method: 'POST',\n// headers,\n// body: JSON.stringify({\n// model: OLLAMA_MODEL,\n// prompt: `${systemPrompt}\\n\\n${userPrompt}`,\n// stream: false,\n// options: {\n// temperature: 0.7,\n// top_p: 0.9,\n// top_k: 40\n// }\n// }),\n// signal: controller.signal\n// });\n\n// clearTimeout(timeoutId);\n\n// if (!response.ok) {\n// const errorText = await response.text();\n// throw new Error(`Ollama API error: ${response.status} - ${errorText}`);\n// }\n\n// const data = await response.json();\n\n// if (!data.response) {\n// throw new Error('No response from Ollama');\n// }\n\n// return data.response.trim();\n// } catch (error) {\n// clearTimeout(timeoutId);\n\n// if (error.name === 'AbortError') {\n// throw new Error('Ollama request timed out');\n// }\n\n// throw error;\n// }\n// }\nasync function generateWithOllama(systemPrompt, userPrompt) {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), OLLAMA_TIMEOUT);\n\n try {\n // Prepare headers - add Authorization if API key is configured\n const headers = {\n 'Content-Type': 'application/json'\n };\n\n if (OLLAMA_API_KEY) {\n headers['Authorization'] = `Bearer ${OLLAMA_API_KEY}`;\n }\n\n // Determine the endpoint URL\n // If OLLAMA_BASE_URL already ends with /api/chat or /api/generate, use it as-is\n // Otherwise append /api/chat\n let endpointUrl = OLLAMA_BASE_URL;\n if (!endpointUrl.endsWith('/api/chat') && !endpointUrl.endsWith('/api/generate')) {\n endpointUrl = `${endpointUrl}/api/chat`;\n }\n\n const response = await fetch(endpointUrl, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n model: OLLAMA_MODEL,\n messages: [\n {\n role: \"system\",\n content: systemPrompt\n },\n {\n role: \"user\",\n content: userPrompt\n }\n ],\n stream: false,\n options: {\n temperature: 0.7,\n top_p: 0.9,\n top_k: 40\n }\n }),\n signal: controller.signal\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Ollama API error: ${response.status} - ${errorText}`);\n }\n\n const data = await response.json();\n\n if (!data.message || !data.message.content) {\n throw new Error('No response from Ollama');\n }\n\n return data.message.content.trim();\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error.name === 'AbortError') {\n throw new Error('Ollama request timed out');\n }\n\n throw error;\n }\n}\n/**\n * Generate content using Gemini\n */\nasync function generateWithGemini(systemPrompt, userPrompt) {\n if (!GEMINI_API_KEY) {\n throw new Error('Gemini API key not configured');\n }\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 60000);\n\n try {\n // Use v1beta endpoint which supports newer models\n const response = await fetch(\n `https://generativelanguage.googleapis.com/v1beta/models/${GEMINI_MODEL}:generateContent?key=${GEMINI_API_KEY}`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({\n contents: [{\n parts: [{\n text: `${systemPrompt}\\n\\n${userPrompt}`\n }]\n }],\n generationConfig: {\n temperature: 0.7,\n maxOutputTokens: 2000\n }\n }),\n signal: controller.signal\n }\n );\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const error = await response.json();\n throw new Error(error.error?.message || `Gemini API error: ${response.status}`);\n }\n\n const data = await response.json();\n\n if (!data.candidates || !data.candidates[0] || !data.candidates[0].content) {\n throw new Error('Invalid response from Gemini');\n }\n\n return data.candidates[0].content.parts[0].text.trim();\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error.name === 'AbortError') {\n throw new Error('Gemini request timed out');\n }\n\n throw error;\n }\n}\n\n/**\n * Generate content using Hypereal AI (OpenAI-compatible API)\n */\nasync function generateWithHypereal(systemPrompt, userPrompt) {\n if (!HYPEREAL_API_KEY) {\n throw new Error('Hypereal AI API key not configured');\n }\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 60000);\n\n try {\n const response = await fetch(`${HYPEREAL_BASE_URL}/chat`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${HYPEREAL_API_KEY}`\n },\n body: JSON.stringify({\n model: HYPEREAL_MODEL,\n messages: [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: userPrompt }\n ],\n temperature: 0.7,\n max_tokens: 2000,\n stream: false\n }),\n signal: controller.signal\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const error = await response.json();\n throw new Error(error.error?.message || `Hypereal API error: ${response.status}`);\n }\n\n const data = await response.json();\n\n if (!data.choices || !data.choices[0] || !data.choices[0].message) {\n throw new Error('Invalid response from Hypereal AI');\n }\n\n return data.choices[0].message.content.trim();\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error.name === 'AbortError') {\n throw new Error('Hypereal AI request timed out');\n }\n\n throw error;\n }\n}\n\n/**\n * Generate content using DeepSeek (OpenAI-compatible API)\n */\nasync function generateWithDeepSeek(systemPrompt, userPrompt) {\n if (!DEEPSEEK_API_KEY) {\n throw new Error('DeepSeek API key not configured');\n }\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 60000);\n\n try {\n const response = await fetch(`${DEEPSEEK_BASE_URL}/chat/completions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${DEEPSEEK_API_KEY}`\n },\n body: JSON.stringify({\n model: DEEPSEEK_MODEL,\n messages: [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: userPrompt }\n ],\n temperature: 0.7,\n max_tokens: 2000\n }),\n signal: controller.signal\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const error = await response.json();\n throw new Error(error.error?.message || `DeepSeek API error: ${response.status}`);\n }\n\n const data = await response.json();\n\n if (!data.choices || !data.choices[0] || !data.choices[0].message) {\n throw new Error('Invalid response from DeepSeek');\n }\n\n return data.choices[0].message.content.trim();\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error.name === 'AbortError') {\n throw new Error('DeepSeek request timed out');\n }\n\n throw error;\n }\n}\n\n/**\n * Main generate function - routes to appropriate AI provider\n * @param {string} systemPrompt - System prompt\n * @param {string} userPrompt - User prompt\n * @param {string} provider - Optional provider override (ollama, gemini, openai, hypereal, deepseek)\n */\nasync function generateContent(systemPrompt, userPrompt, provider = null) {\n const activeProvider = provider || AI_PROVIDER.toLowerCase();\n\n switch (activeProvider) {\n case 'openai':\n return generateWithOpenAI(systemPrompt, userPrompt);\n case 'ollama':\n return generateWithOllama(systemPrompt, userPrompt);\n case 'gemini':\n return generateWithGemini(systemPrompt, userPrompt);\n case 'hypereal':\n return generateWithHypereal(systemPrompt, userPrompt);\n case 'deepseek':\n return generateWithDeepSeek(systemPrompt, userPrompt);\n default:\n // Default to Ollama\n return generateWithOllama(systemPrompt, userPrompt);\n }\n}\n\n/**\n * Generate content brief for content planner\n * @param {Object} params - Parameters\n * @param {string} params.framework - Framework name\n * @param {string} params.frameworkTemplate - Framework template\n * @param {Object} params.context - Context object\n * @param {string} params.provider - Optional AI provider override\n */\nasync function generateContentBrief({ framework, frameworkTemplate, context, provider = null }) {\n // Build the user prompt with context\n const userPrompt = buildContentBriefPrompt(framework, frameworkTemplate, context);\n\n const systemPrompt = `You are an expert Content Planner and Copywriter specializing in ${framework} framework.\n\nYour task is to create a comprehensive content brief based on the provided framework template and context.\n\nIMPORTANT RULES:\n1. Follow the framework structure exactly as provided\n2. Replace ALL placeholders with actual content from the context\n3. Make the content specific to the brand, audience, and platform\n4. Use emotional hooks and persuasive language\n5. Include actionable CTAs\n6. Format the output clearly with headers and sections\n7. Do NOT include any meta-commentary - only the actual content\n8. Output should be ready to copy-paste and use directly\n\nFRAMEWORK: ${framework}\n\nGenerate the content brief now:`;\n\n try {\n const content = await generateContent(systemPrompt, userPrompt, provider);\n return content;\n } catch (error) {\n console.error('Error generating content brief:', error);\n throw error;\n }\n}\n\n/**\n * Build the content brief prompt with all context\n */\nfunction buildContentBriefPrompt(framework, frameworkTemplate, context) {\n let prompt = `FRAMEWORK TEMPLATE:\\n${frameworkTemplate}\\n\\n`;\n\n prompt += `PROJECT & TASK CONTEXT:\\n`;\n prompt += `=====================\\n\\n`;\n\n // Project Info\n if (context.projectName) {\n prompt += `Project: ${context.projectName}\\n`;\n }\n if (context.businessName) {\n prompt += `Brand/Business: ${context.businessName}\\n`;\n }\n if (context.industry) {\n prompt += `Industry: ${context.industry}\\n`;\n }\n\n prompt += `\\nTARGET AUDIENCE:\\n`;\n prompt += `----------------\\n`;\n if (context.targetAudience) {\n prompt += `Target Audience: ${context.targetAudience}\\n`;\n }\n if (context.avatar?.description) {\n prompt += `Avatar Description: ${context.avatar.description}\\n`;\n }\n if (context.avatar?.ageRange) {\n prompt += `Age Range: ${context.avatar.ageRange}\\n`;\n }\n if (context.avatar?.gender) {\n prompt += `Gender: ${context.avatar.gender}\\n`;\n }\n\n // Pain Points\n if (context.painPoints && context.painPoints.length > 0) {\n prompt += `\\nPAIN POINTS:\\n`;\n prompt += `------------\\n`;\n context.painPoints.forEach((point, i) => {\n prompt += `${i + 1}. ${point}\\n`;\n });\n }\n\n // Desires\n if (context.desires && context.desires.length > 0) {\n prompt += `\\nDESIRES:\\n`;\n prompt += `--------\\n`;\n context.desires.forEach((desire, i) => {\n prompt += `${i + 1}. ${desire}\\n`;\n });\n }\n\n // Platform & Funnel\n prompt += `\\nPLATFORM & FUNNEL:\\n`;\n prompt += `------------------\\n`;\n if (context.platform) {\n prompt += `Platform: ${context.platform}\\n`;\n }\n if (context.funnelStage) {\n prompt += `Funnel Stage: ${context.funnelStage}\\n`;\n }\n if (context.creativeType) {\n prompt += `Creative Type: ${context.creativeType}\\n`;\n }\n\n // Strategy Elements\n prompt += `\\nSTRATEGY ELEMENTS:\\n`;\n prompt += `------------------\\n`;\n if (context.hook) {\n prompt += `Hook: ${context.hook}\\n`;\n }\n if (context.headline) {\n prompt += `Headline: ${context.headline}\\n`;\n }\n if (context.cta) {\n prompt += `Call to Action: ${context.cta}\\n`;\n }\n if (context.creativeAngle) {\n prompt += `Creative Angle: ${context.creativeAngle}\\n`;\n }\n if (context.messaging) {\n prompt += `Messaging: ${context.messaging}\\n`;\n }\n\n // Offer\n if (context.offer) {\n prompt += `\\nOFFER:\\n`;\n prompt += `-------\\n`;\n prompt += `${context.offer}\\n`;\n }\n\n // Task Specifics\n if (context.taskTitle) {\n prompt += `\\nTASK:\\n`;\n prompt += `------\\n`;\n prompt += `Task: ${context.taskTitle}\\n`;\n }\n if (context.taskType) {\n prompt += `Type: ${context.taskType}\\n`;\n }\n\n prompt += `\\n\\nGENERATE THE CONTENT BRIEF NOW using the ${framework} framework structure. Make sure all placeholders are replaced with actual content from the context above.`;\n\n return prompt;\n}\n\n/**\n * Check AI provider health\n */\nasync function checkAIHealth() {\n try {\n switch (AI_PROVIDER.toLowerCase()) {\n case 'openai':\n if (!OPENAI_API_KEY) {\n return { available: false, error: 'OpenAI API key not configured' };\n }\n // Simple test request\n const response = await fetch(`${OPENAI_BASE_URL}/models`, {\n headers: {\n 'Authorization': `Bearer ${OPENAI_API_KEY}`\n }\n });\n if (!response.ok) {\n return { available: false, error: 'OpenAI API key is invalid' };\n }\n return {\n available: true,\n provider: 'openai',\n model: OPENAI_MODEL\n };\n\n case 'ollama':\n // Prepare headers - add Authorization if API key is configured\n const ollamaHeaders = {};\n if (OLLAMA_API_KEY) {\n ollamaHeaders['Authorization'] = `Bearer ${OLLAMA_API_KEY}`;\n }\n const ollamaResponse = await fetch(`${OLLAMA_BASE_URL}/api/tags`, {\n headers: ollamaHeaders\n });\n if (!ollamaResponse.ok) {\n return { available: false, error: 'Ollama not responding' };\n }\n const ollamaData = await ollamaResponse.json();\n return {\n available: true,\n provider: 'ollama',\n model: OLLAMA_MODEL,\n models: ollamaData.models?.map(m => m.name) || []\n };\n\n case 'gemini':\n if (!GEMINI_API_KEY) {\n return { available: false, error: 'Gemini API key not configured' };\n }\n // Test Gemini API with v1beta endpoint\n try {\n const geminiTest = await fetch(\n `https://generativelanguage.googleapis.com/v1beta/models?key=${GEMINI_API_KEY}`\n );\n if (!geminiTest.ok) {\n return { available: false, error: 'Gemini API key is invalid' };\n }\n return {\n available: true,\n provider: 'gemini',\n model: GEMINI_MODEL\n };\n } catch (e) {\n return { available: false, error: 'Cannot connect to Gemini API' };\n }\n\n case 'hypereal':\n if (!HYPEREAL_API_KEY) {\n return { available: false, error: 'Hypereal AI API key not configured' };\n }\n return {\n available: true,\n provider: 'hypereal',\n model: HYPEREAL_MODEL\n };\n\n case 'deepseek':\n if (!DEEPSEEK_API_KEY) {\n return { available: false, error: 'DeepSeek API key not configured' };\n }\n return {\n available: true,\n provider: 'deepseek',\n model: DEEPSEEK_MODEL\n };\n\n default:\n return { available: false, error: 'Unknown AI provider configured' };\n }\n } catch (error) {\n return {\n available: false,\n error: `Cannot connect to ${AI_PROVIDER}: ${error.message}`\n };\n }\n}\n\nmodule.exports = {\n generateContent,\n generateContentBrief,\n checkAIHealth,\n AI_PROVIDER,\n CONTENT_GENERATION_SYSTEM_PROMPT\n};"}, "a7694e8f0185852234c768c77b31b3b788398ec1": {"t": 1785766717.2398243, "n": 18003, "h": "aab423f0e60f9d57", "c": "/**\n * AI Service - Handles communication with various AI providers\n * Supports: OpenAI, Ollama, Gemini, Hypereal (configurable via AI_PROVIDER env var)\n */\n\nconst AI_PROVIDER = process.env.AI_PROVIDER || 'openai';\n\n// OpenAI Configuration\nconst OPENAI_API_KEY = process.env.OPENAI_API_KEY;\nconst OPENAI_MODEL = process.env.OPENAI_MODEL || 'gpt-3.5-turbo';\nconst OPENAI_BASE_URL = 'https://api.openai.com/v1';\n\n// Ollama Configuration\nconst OLLAMA_BASE_URL = process.env.OLLAMA_BASE_URL || 'http://localhost:11434';\nconst OLLAMA_MODEL = process.env.OLLAMA_MODEL || 'llama3';\nconst OLLAMA_API_KEY = process.env.OLLAMA_API_KEY;\nconst OLLAMA_TIMEOUT = parseInt(process.env.OLLAMA_TIMEOUT) || 60000;\n\n// Gemini Configuration\nconst GEMINI_API_KEY = process.env.GEMINI_API_KEY;\nconst GEMINI_MODEL = process.env.GEMINI_MODEL || 'gemini-2.5-flash';\n\n// Hypereal AI Configuration\nconst HYPEREAL_API_KEY = process.env.HYPEREAL_API_KEY;\nconst HYPEREAL_MODEL = process.env.HYPEREAL_MODEL || 'claude-sonnet-4-6';\nconst HYPEREAL_BASE_URL = process.env.HYPEREAL_BASE_URL || 'https://api.hypereal.tech/v1';\n\n/**\n * System prompt for content generation\n */\nconst CONTENT_GENERATION_SYSTEM_PROMPT = `You are an expert Performance Marketer and Copywriter specializing in creating high-converting content.\n\nYour job is to generate professional, engaging content based on the provided framework and context.\n\nRules:\n- Follow the framework structure strictly\n- Make content specific and actionable\n- Use emotional hooks and persuasive language\n- Optimize for the specified platform\n- Include the hook, body, and call-to-action\n- Use proper formatting and structure\n- Do NOT include any meta-commentary or explanations\n- Output ONLY the generated content, ready to use`;\n\n/**\n * Generate content using OpenAI\n */\nasync function generateWithOpenAI(systemPrompt, userPrompt) {\n if (!OPENAI_API_KEY) {\n throw new Error('OpenAI API key not configured');\n }\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 60000);\n\n try {\n const response = await fetch(`${OPENAI_BASE_URL}/chat/completions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${OPENAI_API_KEY}`\n },\n body: JSON.stringify({\n model: OPENAI_MODEL,\n messages: [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: userPrompt }\n ],\n temperature: 0.7,\n max_tokens: 2000\n }),\n signal: controller.signal\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const error = await response.json();\n throw new Error(error.error?.message || `OpenAI API error: ${response.status}`);\n }\n\n const data = await response.json();\n\n if (!data.choices || !data.choices[0] || !data.choices[0].message) {\n throw new Error('Invalid response from OpenAI');\n }\n\n return data.choices[0].message.content.trim();\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error.name === 'AbortError') {\n throw new Error('OpenAI request timed out');\n }\n\n throw error;\n }\n}\n\n/**\n * Generate content using Ollama\n */\n// async function generateWithOllama(systemPrompt, userPrompt) {\n// const controller = new AbortController();\n// const timeoutId = setTimeout(() => controller.abort(), OLLAMA_TIMEOUT);\n\n// try {\n// // Prepare headers - add Authorization if API key is configured\n// const headers = {\n// 'Content-Type': 'application/json'\n// };\n\n// if (OLLAMA_API_KEY) {\n// headers['Authorization'] = `Bearer ${OLLAMA_API_KEY}`;\n// }\n\n// const response = await fetch(`${OLLAMA_BASE_URL}/api/generate`, {\n// method: 'POST',\n// headers,\n// body: JSON.stringify({\n// model: OLLAMA_MODEL,\n// prompt: `${systemPrompt}\\n\\n${userPrompt}`,\n// stream: false,\n// options: {\n// temperature: 0.7,\n// top_p: 0.9,\n// top_k: 40\n// }\n// }),\n// signal: controller.signal\n// });\n\n// clearTimeout(timeoutId);\n\n// if (!response.ok) {\n// const errorText = await response.text();\n// throw new Error(`Ollama API error: ${response.status} - ${errorText}`);\n// }\n\n// const data = await response.json();\n\n// if (!data.response) {\n// throw new Error('No response from Ollama');\n// }\n\n// return data.response.trim();\n// } catch (error) {\n// clearTimeout(timeoutId);\n\n// if (error.name === 'AbortError') {\n// throw new Error('Ollama request timed out');\n// }\n\n// throw error;\n// }\n// }\nasync function generateWithOllama(systemPrompt, userPrompt) {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), OLLAMA_TIMEOUT);\n\n try {\n // Prepare headers - add Authorization if API key is configured\n const headers = {\n 'Content-Type': 'application/json'\n };\n\n if (OLLAMA_API_KEY) {\n headers['Authorization'] = `Bearer ${OLLAMA_API_KEY}`;\n }\n\n // Determine the endpoint URL\n // If OLLAMA_BASE_URL already ends with /api/chat or /api/generate, use it as-is\n // Otherwise append /api/chat\n let endpointUrl = OLLAMA_BASE_URL;\n if (!endpointUrl.endsWith('/api/chat') && !endpointUrl.endsWith('/api/generate')) {\n endpointUrl = `${endpointUrl}/api/chat`;\n }\n\n const response = await fetch(endpointUrl, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n model: OLLAMA_MODEL,\n messages: [\n {\n role: \"system\",\n content: systemPrompt\n },\n {\n role: \"user\",\n content: userPrompt\n }\n ],\n stream: false,\n options: {\n temperature: 0.7,\n top_p: 0.9,\n top_k: 40\n }\n }),\n signal: controller.signal\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Ollama API error: ${response.status} - ${errorText}`);\n }\n\n const data = await response.json();\n\n if (!data.message || !data.message.content) {\n throw new Error('No response from Ollama');\n }\n\n return data.message.content.trim();\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error.name === 'AbortError') {\n throw new Error('Ollama request timed out');\n }\n\n throw error;\n }\n}\n/**\n * Generate content using Gemini\n */\nasync function generateWithGemini(systemPrompt, userPrompt) {\n if (!GEMINI_API_KEY) {\n throw new Error('Gemini API key not configured');\n }\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 60000);\n\n try {\n // Use v1beta endpoint which supports newer models\n const response = await fetch(\n `https://generativelanguage.googleapis.com/v1beta/models/${GEMINI_MODEL}:generateContent?key=${GEMINI_API_KEY}`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({\n contents: [{\n parts: [{\n text: `${systemPrompt}\\n\\n${userPrompt}`\n }]\n }],\n generationConfig: {\n temperature: 0.7,\n maxOutputTokens: 2000\n }\n }),\n signal: controller.signal\n }\n );\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const error = await response.json();\n throw new Error(error.error?.message || `Gemini API error: ${response.status}`);\n }\n\n const data = await response.json();\n\n if (!data.candidates || !data.candidates[0] || !data.candidates[0].content) {\n throw new Error('Invalid response from Gemini');\n }\n\n return data.candidates[0].content.parts[0].text.trim();\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error.name === 'AbortError') {\n throw new Error('Gemini request timed out');\n }\n\n throw error;\n }\n}\n\n/**\n * Generate content using Hypereal AI (OpenAI-compatible API)\n */\nasync function generateWithHypereal(systemPrompt, userPrompt) {\n if (!HYPEREAL_API_KEY) {\n throw new Error('Hypereal AI API key not configured');\n }\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 60000);\n\n try {\n const response = await fetch(`${HYPEREAL_BASE_URL}/chat`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${HYPEREAL_API_KEY}`\n },\n body: JSON.stringify({\n model: HYPEREAL_MODEL,\n messages: [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: userPrompt }\n ],\n temperature: 0.7,\n max_tokens: 2000,\n stream: false\n }),\n signal: controller.signal\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const error = await response.json();\n throw new Error(error.error?.message || `Hypereal API error: ${response.status}`);\n }\n\n const data = await response.json();\n\n if (!data.choices || !data.choices[0] || !data.choices[0].message) {\n throw new Error('Invalid response from Hypereal AI');\n }\n\n return data.choices[0].message.content.trim();\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error.name === 'AbortError') {\n throw new Error('Hypereal AI request timed out');\n }\n\n throw error;\n }\n}\n\n/**\n * Main generate function - routes to appropriate AI provider\n * @param {string} systemPrompt - System prompt\n * @param {string} userPrompt - User prompt\n * @param {string} provider - Optional provider override (ollama, gemini, openai, hypereal)\n */\nasync function generateContent(systemPrompt, userPrompt, provider = null) {\n const activeProvider = provider || AI_PROVIDER.toLowerCase();\n\n switch (activeProvider) {\n case 'openai':\n return generateWithOpenAI(systemPrompt, userPrompt);\n case 'ollama':\n return generateWithOllama(systemPrompt, userPrompt);\n case 'gemini':\n return generateWithGemini(systemPrompt, userPrompt);\n case 'hypereal':\n return generateWithHypereal(systemPrompt, userPrompt);\n default:\n // Default to Ollama\n return generateWithOllama(systemPrompt, userPrompt);\n }\n}\n\n/**\n * Generate content brief for content planner\n * @param {Object} params - Parameters\n * @param {string} params.framework - Framework name\n * @param {string} params.frameworkTemplate - Framework template\n * @param {Object} params.context - Context object\n * @param {string} params.provider - Optional AI provider override\n */\nasync function generateContentBrief({ framework, frameworkTemplate, context, provider = null }) {\n // Build the user prompt with context\n const userPrompt = buildContentBriefPrompt(framework, frameworkTemplate, context);\n\n const systemPrompt = `You are an expert Content Planner and Copywriter specializing in ${framework} framework.\n\nYour task is to create a comprehensive content brief based on the provided framework template and context.\n\nIMPORTANT RULES:\n1. Follow the framework structure exactly as provided\n2. Replace ALL placeholders with actual content from the context\n3. Make the content specific to the brand, audience, and platform\n4. Use emotional hooks and persuasive language\n5. Include actionable CTAs\n6. Format the output clearly with headers and sections\n7. Do NOT include any meta-commentary - only the actual content\n8. Output should be ready to copy-paste and use directly\n\nFRAMEWORK: ${framework}\n\nGenerate the content brief now:`;\n\n try {\n const content = await generateContent(systemPrompt, userPrompt, provider);\n return content;\n } catch (error) {\n console.error('Error generating content brief:', error);\n throw error;\n }\n}\n\n/**\n * Build the content brief prompt with all context\n */\nfunction buildContentBriefPrompt(framework, frameworkTemplate, context) {\n let prompt = `FRAMEWORK TEMPLATE:\\n${frameworkTemplate}\\n\\n`;\n\n prompt += `PROJECT & TASK CONTEXT:\\n`;\n prompt += `=====================\\n\\n`;\n\n // Project Info\n if (context.projectName) {\n prompt += `Project: ${context.projectName}\\n`;\n }\n if (context.businessName) {\n prompt += `Brand/Business: ${context.businessName}\\n`;\n }\n if (context.industry) {\n prompt += `Industry: ${context.industry}\\n`;\n }\n\n prompt += `\\nTARGET AUDIENCE:\\n`;\n prompt += `----------------\\n`;\n if (context.targetAudience) {\n prompt += `Target Audience: ${context.targetAudience}\\n`;\n }\n if (context.avatar?.description) {\n prompt += `Avatar Description: ${context.avatar.description}\\n`;\n }\n if (context.avatar?.ageRange) {\n prompt += `Age Range: ${context.avatar.ageRange}\\n`;\n }\n if (context.avatar?.gender) {\n prompt += `Gender: ${context.avatar.gender}\\n`;\n }\n\n // Pain Points\n if (context.painPoints && context.painPoints.length > 0) {\n prompt += `\\nPAIN POINTS:\\n`;\n prompt += `------------\\n`;\n context.painPoints.forEach((point, i) => {\n prompt += `${i + 1}. ${point}\\n`;\n });\n }\n\n // Desires\n if (context.desires && context.desires.length > 0) {\n prompt += `\\nDESIRES:\\n`;\n prompt += `--------\\n`;\n context.desires.forEach((desire, i) => {\n prompt += `${i + 1}. ${desire}\\n`;\n });\n }\n\n // Platform & Funnel\n prompt += `\\nPLATFORM & FUNNEL:\\n`;\n prompt += `------------------\\n`;\n if (context.platform) {\n prompt += `Platform: ${context.platform}\\n`;\n }\n if (context.funnelStage) {\n prompt += `Funnel Stage: ${context.funnelStage}\\n`;\n }\n if (context.creativeType) {\n prompt += `Creative Type: ${context.creativeType}\\n`;\n }\n\n // Strategy Elements\n prompt += `\\nSTRATEGY ELEMENTS:\\n`;\n prompt += `------------------\\n`;\n if (context.hook) {\n prompt += `Hook: ${context.hook}\\n`;\n }\n if (context.headline) {\n prompt += `Headline: ${context.headline}\\n`;\n }\n if (context.cta) {\n prompt += `Call to Action: ${context.cta}\\n`;\n }\n if (context.creativeAngle) {\n prompt += `Creative Angle: ${context.creativeAngle}\\n`;\n }\n if (context.messaging) {\n prompt += `Messaging: ${context.messaging}\\n`;\n }\n\n // Offer\n if (context.offer) {\n prompt += `\\nOFFER:\\n`;\n prompt += `-------\\n`;\n prompt += `${context.offer}\\n`;\n }\n\n // Task Specifics\n if (context.taskTitle) {\n prompt += `\\nTASK:\\n`;\n prompt += `------\\n`;\n prompt += `Task: ${context.taskTitle}\\n`;\n }\n if (context.taskType) {\n prompt += `Type: ${context.taskType}\\n`;\n }\n\n // Approved Content (for Graphic Designers and Video Editors)\n if (context.approvedContent && context.approvedContent.trim()) {\n prompt += `\\n\\n*** APPROVED CONTENT (MUST USE THIS EXACT CONTENT) ***\\n`;\n prompt += `======================================================\\n\\n`;\n prompt += `The following content has been approved by the Content Planner. You MUST incorporate this exact content into your design brief:\\n\\n`;\n prompt += context.approvedContent;\n prompt += `\\n\\n*** END APPROVED CONTENT ***\\n`;\n }\n\n prompt += `\\n\\nGENERATE THE CONTENT BRIEF NOW using the ${framework} framework structure. Make sure all placeholders are replaced with actual content from the context above.`;\n\n return prompt;\n}\n\n/**\n * Check AI provider health\n */\nasync function checkAIHealth() {\n try {\n switch (AI_PROVIDER.toLowerCase()) {\n case 'openai':\n if (!OPENAI_API_KEY) {\n return { available: false, error: 'OpenAI API key not configured' };\n }\n // Simple test request\n const response = await fetch(`${OPENAI_BASE_URL}/models`, {\n headers: {\n 'Authorization': `Bearer ${OPENAI_API_KEY}`\n }\n });\n if (!response.ok) {\n return { available: false, error: 'OpenAI API key is invalid' };\n }\n return {\n available: true,\n provider: 'openai',\n model: OPENAI_MODEL\n };\n\n case 'ollama':\n // Prepare headers - add Authorization if API key is configured\n const ollamaHeaders = {};\n if (OLLAMA_API_KEY) {\n ollamaHeaders['Authorization'] = `Bearer ${OLLAMA_API_KEY}`;\n }\n const ollamaResponse = await fetch(`${OLLAMA_BASE_URL}/api/tags`, {\n headers: ollamaHeaders\n });\n if (!ollamaResponse.ok) {\n return { available: false, error: 'Ollama not responding' };\n }\n const ollamaData = await ollamaResponse.json();\n return {\n available: true,\n provider: 'ollama',\n model: OLLAMA_MODEL,\n models: ollamaData.models?.map(m => m.name) || []\n };\n\n case 'gemini':\n if (!GEMINI_API_KEY) {\n return { available: false, error: 'Gemini API key not configured' };\n }\n // Test Gemini API with v1beta endpoint\n try {\n const geminiTest = await fetch(\n `https://generativelanguage.googleapis.com/v1beta/models?key=${GEMINI_API_KEY}`\n );\n if (!geminiTest.ok) {\n return { available: false, error: 'Gemini API key is invalid' };\n }\n return {\n available: true,\n provider: 'gemini',\n model: GEMINI_MODEL\n };\n } catch (e) {\n return { available: false, error: 'Cannot connect to Gemini API' };\n }\n\n case 'hypereal':\n if (!HYPEREAL_API_KEY) {\n return { available: false, error: 'Hypereal AI API key not configured' };\n }\n return {\n available: true,\n provider: 'hypereal',\n model: HYPEREAL_MODEL\n };\n\n default:\n return { available: false, error: 'Unknown AI provider configured' };\n }\n } catch (error) {\n return {\n available: false,\n error: `Cannot connect to ${AI_PROVIDER}: ${error.message}`\n };\n }\n}\n\nmodule.exports = {\n generateContent,\n generateContentBrief,\n checkAIHealth,\n AI_PROVIDER,\n CONTENT_GENERATION_SYSTEM_PROMPT\n};"}, "b1d4c113e4bcf217ad406de3e0478191a910fe10": {"t": 1785766718.5335975, "n": 17473, "h": "d368e0b23eba8e0e", "c": "/**\n * AI Service - Handles communication with various AI providers\n * Supports: OpenAI, Ollama, Gemini, Hypereal (configurable via AI_PROVIDER env var)\n */\n\nconst AI_PROVIDER = process.env.AI_PROVIDER || 'openai';\n\n// OpenAI Configuration\nconst OPENAI_API_KEY = process.env.OPENAI_API_KEY;\nconst OPENAI_MODEL = process.env.OPENAI_MODEL || 'gpt-3.5-turbo';\nconst OPENAI_BASE_URL = 'https://api.openai.com/v1';\n\n// Ollama Configuration\nconst OLLAMA_BASE_URL = process.env.OLLAMA_BASE_URL || 'http://localhost:11434';\nconst OLLAMA_MODEL = process.env.OLLAMA_MODEL || 'llama3';\nconst OLLAMA_API_KEY = process.env.OLLAMA_API_KEY;\nconst OLLAMA_TIMEOUT = parseInt(process.env.OLLAMA_TIMEOUT) || 60000;\n\n// Gemini Configuration\nconst GEMINI_API_KEY = process.env.GEMINI_API_KEY;\nconst GEMINI_MODEL = process.env.GEMINI_MODEL || 'gemini-2.5-flash';\n\n// Hypereal AI Configuration\nconst HYPEREAL_API_KEY = process.env.HYPEREAL_API_KEY;\nconst HYPEREAL_MODEL = process.env.HYPEREAL_MODEL || 'claude-sonnet-4-6';\nconst HYPEREAL_BASE_URL = process.env.HYPEREAL_BASE_URL || 'https://api.hypereal.tech/v1';\n\n/**\n * System prompt for content generation\n */\nconst CONTENT_GENERATION_SYSTEM_PROMPT = `You are an expert Performance Marketer and Copywriter specializing in creating high-converting content.\n\nYour job is to generate professional, engaging content based on the provided framework and context.\n\nRules:\n- Follow the framework structure strictly\n- Make content specific and actionable\n- Use emotional hooks and persuasive language\n- Optimize for the specified platform\n- Include the hook, body, and call-to-action\n- Use proper formatting and structure\n- Do NOT include any meta-commentary or explanations\n- Output ONLY the generated content, ready to use`;\n\n/**\n * Generate content using OpenAI\n */\nasync function generateWithOpenAI(systemPrompt, userPrompt) {\n if (!OPENAI_API_KEY) {\n throw new Error('OpenAI API key not configured');\n }\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 60000);\n\n try {\n const response = await fetch(`${OPENAI_BASE_URL}/chat/completions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${OPENAI_API_KEY}`\n },\n body: JSON.stringify({\n model: OPENAI_MODEL,\n messages: [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: userPrompt }\n ],\n temperature: 0.7,\n max_tokens: 2000\n }),\n signal: controller.signal\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const error = await response.json();\n throw new Error(error.error?.message || `OpenAI API error: ${response.status}`);\n }\n\n const data = await response.json();\n\n if (!data.choices || !data.choices[0] || !data.choices[0].message) {\n throw new Error('Invalid response from OpenAI');\n }\n\n return data.choices[0].message.content.trim();\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error.name === 'AbortError') {\n throw new Error('OpenAI request timed out');\n }\n\n throw error;\n }\n}\n\n/**\n * Generate content using Ollama\n */\n// async function generateWithOllama(systemPrompt, userPrompt) {\n// const controller = new AbortController();\n// const timeoutId = setTimeout(() => controller.abort(), OLLAMA_TIMEOUT);\n\n// try {\n// // Prepare headers - add Authorization if API key is configured\n// const headers = {\n// 'Content-Type': 'application/json'\n// };\n\n// if (OLLAMA_API_KEY) {\n// headers['Authorization'] = `Bearer ${OLLAMA_API_KEY}`;\n// }\n\n// const response = await fetch(`${OLLAMA_BASE_URL}/api/generate`, {\n// method: 'POST',\n// headers,\n// body: JSON.stringify({\n// model: OLLAMA_MODEL,\n// prompt: `${systemPrompt}\\n\\n${userPrompt}`,\n// stream: false,\n// options: {\n// temperature: 0.7,\n// top_p: 0.9,\n// top_k: 40\n// }\n// }),\n// signal: controller.signal\n// });\n\n// clearTimeout(timeoutId);\n\n// if (!response.ok) {\n// const errorText = await response.text();\n// throw new Error(`Ollama API error: ${response.status} - ${errorText}`);\n// }\n\n// const data = await response.json();\n\n// if (!data.response) {\n// throw new Error('No response from Ollama');\n// }\n\n// return data.response.trim();\n// } catch (error) {\n// clearTimeout(timeoutId);\n\n// if (error.name === 'AbortError') {\n// throw new Error('Ollama request timed out');\n// }\n\n// throw error;\n// }\n// }\nasync function generateWithOllama(systemPrompt, userPrompt) {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), OLLAMA_TIMEOUT);\n\n try {\n // Prepare headers - add Authorization if API key is configured\n const headers = {\n 'Content-Type': 'application/json'\n };\n\n if (OLLAMA_API_KEY) {\n headers['Authorization'] = `Bearer ${OLLAMA_API_KEY}`;\n }\n\n // Determine the endpoint URL\n // If OLLAMA_BASE_URL already ends with /api/chat or /api/generate, use it as-is\n // Otherwise append /api/chat\n let endpointUrl = OLLAMA_BASE_URL;\n if (!endpointUrl.endsWith('/api/chat') && !endpointUrl.endsWith('/api/generate')) {\n endpointUrl = `${endpointUrl}/api/chat`;\n }\n\n const response = await fetch(endpointUrl, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n model: OLLAMA_MODEL,\n messages: [\n {\n role: \"system\",\n content: systemPrompt\n },\n {\n role: \"user\",\n content: userPrompt\n }\n ],\n stream: false,\n options: {\n temperature: 0.7,\n top_p: 0.9,\n top_k: 40\n }\n }),\n signal: controller.signal\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Ollama API error: ${response.status} - ${errorText}`);\n }\n\n const data = await response.json();\n\n if (!data.message || !data.message.content) {\n throw new Error('No response from Ollama');\n }\n\n return data.message.content.trim();\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error.name === 'AbortError') {\n throw new Error('Ollama request timed out');\n }\n\n throw error;\n }\n}\n/**\n * Generate content using Gemini\n */\nasync function generateWithGemini(systemPrompt, userPrompt) {\n if (!GEMINI_API_KEY) {\n throw new Error('Gemini API key not configured');\n }\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 60000);\n\n try {\n // Use v1beta endpoint which supports newer models\n const response = await fetch(\n `https://generativelanguage.googleapis.com/v1beta/models/${GEMINI_MODEL}:generateContent?key=${GEMINI_API_KEY}`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({\n contents: [{\n parts: [{\n text: `${systemPrompt}\\n\\n${userPrompt}`\n }]\n }],\n generationConfig: {\n temperature: 0.7,\n maxOutputTokens: 2000\n }\n }),\n signal: controller.signal\n }\n );\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const error = await response.json();\n throw new Error(error.error?.message || `Gemini API error: ${response.status}`);\n }\n\n const data = await response.json();\n\n if (!data.candidates || !data.candidates[0] || !data.candidates[0].content) {\n throw new Error('Invalid response from Gemini');\n }\n\n return data.candidates[0].content.parts[0].text.trim();\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error.name === 'AbortError') {\n throw new Error('Gemini request timed out');\n }\n\n throw error;\n }\n}\n\n/**\n * Generate content using Hypereal AI (OpenAI-compatible API)\n */\nasync function generateWithHypereal(systemPrompt, userPrompt) {\n if (!HYPEREAL_API_KEY) {\n throw new Error('Hypereal AI API key not configured');\n }\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 60000);\n\n try {\n const response = await fetch(`${HYPEREAL_BASE_URL}/chat`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${HYPEREAL_API_KEY}`\n },\n body: JSON.stringify({\n model: HYPEREAL_MODEL,\n messages: [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: userPrompt }\n ],\n temperature: 0.7,\n max_tokens: 2000,\n stream: false\n }),\n signal: controller.signal\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n const error = await response.json();\n throw new Error(error.error?.message || `Hypereal API error: ${response.status}`);\n }\n\n const data = await response.json();\n\n if (!data.choices || !data.choices[0] || !data.choices[0].message) {\n throw new Error('Invalid response from Hypereal AI');\n }\n\n return data.choices[0].message.content.trim();\n } catch (error) {\n clearTimeout(timeoutId);\n\n if (error.name === 'AbortError') {\n throw new Error('Hypereal AI request timed out');\n }\n\n throw error;\n }\n}\n\n/**\n * Main generate function - routes to appropriate AI provider\n * @param {string} systemPrompt - System prompt\n * @param {string} userPrompt - User prompt\n * @param {string} provider - Optional provider override (ollama, gemini, openai, hypereal)\n */\nasync function generateContent(systemPrompt, userPrompt, provider = null) {\n const activeProvider = provider || AI_PROVIDER.toLowerCase();\n\n switch (activeProvider) {\n case 'openai':\n return generateWithOpenAI(systemPrompt, userPrompt);\n case 'ollama':\n return generateWithOllama(systemPrompt, userPrompt);\n case 'gemini':\n return generateWithGemini(systemPrompt, userPrompt);\n case 'hypereal':\n return generateWithHypereal(systemPrompt, userPrompt);\n default:\n // Default to Ollama\n return generateWithOllama(systemPrompt, userPrompt);\n }\n}\n\n/**\n * Generate content brief for content planner\n * @param {Object} params - Parameters\n * @param {string} params.framework - Framework name\n * @param {string} params.frameworkTemplate - Framework template\n * @param {Object} params.context - Context object\n * @param {string} params.provider - Optional AI provider override\n */\nasync function generateContentBrief({ framework, frameworkTemplate, context, provider = null }) {\n // Build the user prompt with context\n const userPrompt = buildContentBriefPrompt(framework, frameworkTemplate, context);\n\n const systemPrompt = `You are an expert Content Planner and Copywriter specializing in ${framework} framework.\n\nYour task is to create a comprehensive content brief based on the provided framework template and context.\n\nIMPORTANT RULES:\n1. Follow the framework structure exactly as provided\n2. Replace ALL placeholders with actual content from the context\n3. Make the content specific to the brand, audience, and platform\n4. Use emotional hooks and persuasive language\n5. Include actionable CTAs\n6. Format the output clearly with headers and sections\n7. Do NOT include any meta-commentary - only the actual content\n8. Output should be ready to copy-paste and use directly\n\nFRAMEWORK: ${framework}\n\nGenerate the content brief now:`;\n\n try {\n const content = await generateContent(systemPrompt, userPrompt, provider);\n return content;\n } catch (error) {\n console.error('Error generating content brief:', error);\n throw error;\n }\n}\n\n/**\n * Build the content brief prompt with all context\n */\nfunction buildContentBriefPrompt(framework, frameworkTemplate, context) {\n let prompt = `FRAMEWORK TEMPLATE:\\n${frameworkTemplate}\\n\\n`;\n\n prompt += `PROJECT & TASK CONTEXT:\\n`;\n prompt += `=====================\\n\\n`;\n\n // Project Info\n if (context.projectName) {\n prompt += `Project: ${context.projectName}\\n`;\n }\n if (context.businessName) {\n prompt += `Brand/Business: ${context.businessName}\\n`;\n }\n if (context.industry) {\n prompt += `Industry: ${context.industry}\\n`;\n }\n\n prompt += `\\nTARGET AUDIENCE:\\n`;\n prompt += `----------------\\n`;\n if (context.targetAudience) {\n prompt += `Target Audience: ${context.targetAudience}\\n`;\n }\n if (context.avatar?.description) {\n prompt += `Avatar Description: ${context.avatar.description}\\n`;\n }\n if (context.avatar?.ageRange) {\n prompt += `Age Range: ${context.avatar.ageRange}\\n`;\n }\n if (context.avatar?.gender) {\n prompt += `Gender: ${context.avatar.gender}\\n`;\n }\n\n // Pain Points\n if (context.painPoints && context.painPoints.length > 0) {\n prompt += `\\nPAIN POINTS:\\n`;\n prompt += `------------\\n`;\n context.painPoints.forEach((point, i) => {\n prompt += `${i + 1}. ${point}\\n`;\n });\n }\n\n // Desires\n if (context.desires && context.desires.length > 0) {\n prompt += `\\nDESIRES:\\n`;\n prompt += `--------\\n`;\n context.desires.forEach((desire, i) => {\n prompt += `${i + 1}. ${desire}\\n`;\n });\n }\n\n // Platform & Funnel\n prompt += `\\nPLATFORM & FUNNEL:\\n`;\n prompt += `------------------\\n`;\n if (context.platform) {\n prompt += `Platform: ${context.platform}\\n`;\n }\n if (context.funnelStage) {\n prompt += `Funnel Stage: ${context.funnelStage}\\n`;\n }\n if (context.creativeType) {\n prompt += `Creative Type: ${context.creativeType}\\n`;\n }\n\n // Strategy Elements\n prompt += `\\nSTRATEGY ELEMENTS:\\n`;\n prompt += `------------------\\n`;\n if (context.hook) {\n prompt += `Hook: ${context.hook}\\n`;\n }\n if (context.headline) {\n prompt += `Headline: ${context.headline}\\n`;\n }\n if (context.cta) {\n prompt += `Call to Action: ${context.cta}\\n`;\n }\n if (context.creativeAngle) {\n prompt += `Creative Angle: ${context.creativeAngle}\\n`;\n }\n if (context.messaging) {\n prompt += `Messaging: ${context.messaging}\\n`;\n }\n\n // Offer\n if (context.offer) {\n prompt += `\\nOFFER:\\n`;\n prompt += `-------\\n`;\n prompt += `${context.offer}\\n`;\n }\n\n // Task Specifics\n if (context.taskTitle) {\n prompt += `\\nTASK:\\n`;\n prompt += `------\\n`;\n prompt += `Task: ${context.taskTitle}\\n`;\n }\n if (context.taskType) {\n prompt += `Type: ${context.taskType}\\n`;\n }\n\n prompt += `\\n\\nGENERATE THE CONTENT BRIEF NOW using the ${framework} framework structure. Make sure all placeholders are replaced with actual content from the context above.`;\n\n return prompt;\n}\n\n/**\n * Check AI provider health\n */\nasync function checkAIHealth() {\n try {\n switch (AI_PROVIDER.toLowerCase()) {\n case 'openai':\n if (!OPENAI_API_KEY) {\n return { available: false, error: 'OpenAI API key not configured' };\n }\n // Simple test request\n const response = await fetch(`${OPENAI_BASE_URL}/models`, {\n headers: {\n 'Authorization': `Bearer ${OPENAI_API_KEY}`\n }\n });\n if (!response.ok) {\n return { available: false, error: 'OpenAI API key is invalid' };\n }\n return {\n available: true,\n provider: 'openai',\n model: OPENAI_MODEL\n };\n\n case 'ollama':\n // Prepare headers - add Authorization if API key is configured\n const ollamaHeaders = {};\n if (OLLAMA_API_KEY) {\n ollamaHeaders['Authorization'] = `Bearer ${OLLAMA_API_KEY}`;\n }\n const ollamaResponse = await fetch(`${OLLAMA_BASE_URL}/api/tags`, {\n headers: ollamaHeaders\n });\n if (!ollamaResponse.ok) {\n return { available: false, error: 'Ollama not responding' };\n }\n const ollamaData = await ollamaResponse.json();\n return {\n available: true,\n provider: 'ollama',\n model: OLLAMA_MODEL,\n models: ollamaData.models?.map(m => m.name) || []\n };\n\n case 'gemini':\n if (!GEMINI_API_KEY) {\n return { available: false, error: 'Gemini API key not configured' };\n }\n // Test Gemini API with v1beta endpoint\n try {\n const geminiTest = await fetch(\n `https://generativelanguage.googleapis.com/v1beta/models?key=${GEMINI_API_KEY}`\n );\n if (!geminiTest.ok) {\n return { available: false, error: 'Gemini API key is invalid' };\n }\n return {\n available: true,\n provider: 'gemini',\n model: GEMINI_MODEL\n };\n } catch (e) {\n return { available: false, error: 'Cannot connect to Gemini API' };\n }\n\n case 'hypereal':\n if (!HYPEREAL_API_KEY) {\n return { available: false, error: 'Hypereal AI API key not configured' };\n }\n return {\n available: true,\n provider: 'hypereal',\n model: HYPEREAL_MODEL\n };\n\n default:\n return { available: false, error: 'Unknown AI provider configured' };\n }\n } catch (error) {\n return {\n available: false,\n error: `Cannot connect to ${AI_PROVIDER}: ${error.message}`\n };\n }\n}\n\nmodule.exports = {\n generateContent,\n generateContentBrief,\n checkAIHealth,\n AI_PROVIDER,\n CONTENT_GENERATION_SYSTEM_PROMPT\n};"}}}}