UNPKG

aura-ai

Version:

AI-powered marketing strategist CLI tool for developers

106 lines (93 loc) 3.28 kB
import OpenAI from 'openai'; import dotenv from 'dotenv'; import path from 'path'; import { fileURLToPath } from 'url'; import { dirname } from 'path'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); // Load environment variables from .env.local at project root dotenv.config({ path: path.resolve(process.cwd(), '.env.local') }); /** * AI-powered question parser for extracting options from markdown questions */ export class AIQuestionParser { constructor() { // Only initialize OpenAI client if API key is available if (process.env.OPENAI_API_KEY) { this.client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY }); } else { this.client = null; } } /** * Parse a question and extract potential options using AI * @param {string} questionText - The question text * @returns {Promise<Object>} Parsed question with options and explainer if applicable */ async parseQuestion(questionText) { try { // Check if client is available if (!this.client) { console.log('⚠️ OpenAI client not initialized, using basic explainers'); return { hasOptions: false, options: [], explainer: '' }; } // Check if question already has options in parentheses const parenMatch = questionText.match(/\(([^)]+)\)/); if (parenMatch) { const optionsText = parenMatch[1]; const options = optionsText.split(',').map(opt => opt.trim()); return { hasOptions: true, options: options.map(opt => ({ label: opt.charAt(0).toUpperCase() + opt.slice(1), // Capitalize first letter value: opt.toLowerCase().replace(/\s+/g, '_') })), explainer: '' }; } // For questions without parentheses, just return text input return { hasOptions: false, options: [], explainer: '' }; } catch (error) { // If AI fails, just return no options return { hasOptions: false, options: [], explainer: '' }; } } /** * Process all questions in markdown content * @param {Array} questions - Array of question objects * @returns {Promise<Array>} Enhanced questions with options (keeps explainer from markdown) */ async processQuestions(questions) { const enhancedQuestions = []; for (const question of questions) { // Use originalQuestion if available, otherwise use question const questionText = question.originalQuestion || question.question; const parsed = await this.parseQuestion(questionText); // Keep explainer from markdown, don't override enhancedQuestions.push({ ...question, // Keep original type and options if they exist type: question.options && question.options.length > 0 ? 'select' : parsed.hasOptions ? 'select' : 'text', options: question.options && question.options.length > 0 ? question.options : parsed.options, // Keep the explainer from markdown explainer: question.explainer || '' }); } return enhancedQuestions; } } export const aiParser = new AIQuestionParser();