aura-ai
Version:
AI-powered marketing strategist CLI tool for developers
106 lines (93 loc) • 3.28 kB
JavaScript
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();