UNPKG

pdm-ai

Version:

PDM-AI - Transform customer feedback into structured product insights using the Jobs-to-be-Done (JTBD) methodology

260 lines (225 loc) 7.68 kB
/** * Configuration utility for PDM-AI * Handles loading and managing configurations from different sources */ import fs from 'fs-extra'; import path from 'path'; import dotenv from 'dotenv'; import os from 'os'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); class Config { constructor() { // Load .env file immediately on instantiation dotenv.config(); this.config = { project: { name: null, creationDate: null, }, llm: { apiKey: process.env.LLM_API_KEY || '', model: process.env.LLM_MODEL || 'gpt-4o', maxTokens: parseInt(process.env.LLM_MAX_TOKENS || process.env.MAX_TOKENS || '4000'), temperature: parseFloat(process.env.LLM_TEMPERATURE || process.env.TEMPERATURE || '0.7'), }, language: process.env.LANGUAGE || 'en', logLevel: process.env.LOG_LEVEL || 'info', }; // Log loaded configuration for debugging console.log(`Config initialized with language: ${this.config.language}`); this.projectRoot = null; } // Add direct property getters for commonly used properties get language() { return this.config.language; } get model() { return this.config.llm.model; } get maxTokens() { return this.config.llm.maxTokens; } get temperature() { return this.config.llm.temperature; } get llmApiKey() { return this.config.llm.apiKey; } /** * Load configuration from .env files in the following order: * 1. Project-specific .env (if provided) * 2. Global .env in user's home directory * * @param {string} [projectPath] - Optional path to project directory * @returns {boolean} - True if any configuration was loaded */ loadConfig(projectPath) { let configLoaded = false; // 1. Try project-specific .env if a project path is provided if (projectPath) { const projectEnvPath = path.join(projectPath, '.env'); if (fs.existsSync(projectEnvPath)) { this.loadEnv(projectEnvPath); configLoaded = true; } } // 2. Try global .env if needed if (!configLoaded) { const globalEnvPath = path.join(os.homedir(), '.pdm-config', '.env'); if (fs.existsSync(globalEnvPath)) { this.loadEnv(globalEnvPath); configLoaded = true; } } // Log updated configuration console.log(`Config loaded with language: ${this.config.language}`); return configLoaded; } /** * Load configuration from .env file * @param {string} envPath - Path to .env file * @returns {boolean} - True if successfully loaded */ loadEnv(envPath) { try { if (fs.existsSync(envPath)) { const result = dotenv.config({ path: envPath }); if (result.error) { throw result.error; } // Update config with new env values this.config.llm.apiKey = process.env.LLM_API_KEY || this.config.llm.apiKey; this.config.llm.model = process.env.LLM_MODEL || this.config.llm.model; this.config.llm.maxTokens = parseInt(process.env.LLM_MAX_TOKENS || process.env.MAX_TOKENS || this.config.llm.maxTokens); this.config.llm.temperature = parseFloat(process.env.LLM_TEMPERATURE || process.env.TEMPERATURE || this.config.llm.temperature); this.config.language = process.env.LANGUAGE || this.config.language; this.config.logLevel = process.env.LOG_LEVEL || this.config.logLevel; return true; } } catch (error) { console.error(`Error loading .env file: ${error.message}`); } return false; } /** * Load project configuration from .pdm directory * @param {string} projectPath - Path to project directory * @returns {boolean} - True if successfully loaded */ loadProjectConfig(projectPath) { try { this.projectRoot = projectPath; const configPath = path.join(projectPath, '.pdm', 'config.json'); if (fs.existsSync(configPath)) { const projectConfig = fs.readJsonSync(configPath); this.config.project = { ...this.config.project, ...projectConfig }; return true; } } catch (error) { console.error(`Error loading project config: ${error.message}`); } return false; } /** * Save project configuration to .pdm directory * @returns {boolean} - True if successfully saved */ saveProjectConfig() { try { if (!this.projectRoot) { throw new Error('Project root is not set'); } const pdmDir = path.join(this.projectRoot, '.pdm'); fs.ensureDirSync(pdmDir); const configPath = path.join(pdmDir, 'config.json'); fs.writeJsonSync(configPath, this.config.project, { spaces: 2 }); return true; } catch (error) { console.error(`Error saving project config: ${error.message}`); return false; } } /** * Get the current configuration * @returns {Object} - Current configuration */ getConfig() { return this.config; } /** * Update project configuration * @param {Object} projectConfig - Project configuration updates */ updateProjectConfig(projectConfig) { this.config.project = { ...this.config.project, ...projectConfig }; } /** * Generate default .env file based on .env.example * @param {string} envPath - Path to create the .env file * @returns {boolean} - True if successfully generated */ generateEnvTemplate(envPath) { try { // First, try to find the .env.example file in the package root const packageRoot = path.resolve(__dirname, '../..'); const envExamplePath = path.join(packageRoot, '.env.example'); if (fs.existsSync(envExamplePath)) { // Copy the example file to the destination fs.copyFileSync(envExamplePath, envPath); console.log(`Created .env file from .env.example. Please update it with your API key.`); return true; } else { // Fallback to hardcoded template if .env.example is not found const envTemplate = `# PDM-AI Configuration # ----------------------------- # This is a project-specific configuration file. # You must set your LLM_API_KEY below to use PDM-AI. # # Get your OpenAI API key at: https://platform.openai.com/api-keys # API key for LLM provider (REQUIRED) LLM_API_KEY= # Model to use for text generation LLM_MODEL=gpt-4o # Maximum tokens for LLM responses LLM_MAX_TOKENS=4000 # Randomness parameter (0.0-1.0) LLM_TEMPERATURE=0.7 # Language (en or ja) LANGUAGE=en # Log level (debug, info, warn, error) LOG_LEVEL=info `; fs.writeFileSync(envPath, envTemplate); console.log(`Created default .env file. Please update it with your API key.`); return true; } } catch (error) { console.error(`Error generating .env template: ${error.message}`); return false; } } /** * Set up global configuration directory and template * @returns {boolean} - True if successfully set up */ setupGlobalConfig() { try { const globalConfigDir = path.join(os.homedir(), '.pdm-config'); const globalEnvPath = path.join(globalConfigDir, '.env'); // Create directory if it doesn't exist fs.ensureDirSync(globalConfigDir); // Only create the template if it doesn't exist if (!fs.existsSync(globalEnvPath)) { this.generateEnvTemplate(globalEnvPath); } return true; } catch (error) { console.error(`Error setting up global config: ${error.message}`); return false; } } } const config = new Config(); export default config;