UNPKG

optivise

Version:

Optivise - The Ultimate Optimizely Development Assistant with AI-powered features, zero-config setup, and comprehensive development support

413 lines (388 loc) 15.2 kB
/** * Documentation Indexing Service * Automatically indexes Optimizely documentation into ChromaDB for AI-powered search */ import { chromaDBService } from '../integrations/chromadb-client.js'; import { openAIClient } from '../integrations/openai-client.js'; export class DocumentationIndexingService { logger; indexingProgress = { totalDocuments: 0, processedDocuments: 0, failedDocuments: 0, status: 'idle' }; // Sample documentation sources for different Optimizely products documentationSources = [ // Configured Commerce { url: 'https://docs.optimizely.com/configured-commerce/docs/handler-chain-pattern', product: 'configured-commerce', contentType: 'documentation', title: 'Handler Chain Pattern', tags: ['architecture', 'patterns', 'handlers'] }, { url: 'https://docs.optimizely.com/configured-commerce/docs/extension-development', product: 'configured-commerce', contentType: 'documentation', title: 'Extension Development Guide', tags: ['development', 'extensions', 'customization'] }, { url: 'https://docs.optimizely.com/configured-commerce/docs/blueprint-development', product: 'configured-commerce', contentType: 'documentation', title: 'Blueprint Development', tags: ['frontend', 'blueprints', 'development'] }, // CMS PaaS { url: 'https://docs.optimizely.com/content-management-system/docs/content-types', product: 'cms-paas', contentType: 'documentation', title: 'Content Types Overview', tags: ['content-types', 'modeling', 'cms'] }, { url: 'https://docs.optimizely.com/content-management-system/docs/mvc-templates', product: 'cms-paas', contentType: 'documentation', title: 'MVC Templates and Controllers', tags: ['mvc', 'templates', 'development'] }, // Web Experimentation { url: 'https://docs.optimizely.com/web-experimentation/docs/javascript-sdk', product: 'web-experimentation', contentType: 'documentation', title: 'JavaScript SDK Reference', tags: ['sdk', 'javascript', 'implementation'] }, { url: 'https://docs.optimizely.com/web-experimentation/docs/experiment-setup', product: 'web-experimentation', contentType: 'tutorial', title: 'Setting up Experiments', tags: ['experiments', 'setup', 'getting-started'] }, // Feature Experimentation { url: 'https://docs.optimizely.com/feature-experimentation/docs/node-sdk', product: 'feature-experimentation', contentType: 'documentation', title: 'Node.js SDK Documentation', tags: ['sdk', 'nodejs', 'feature-flags'] }, // DXP { url: 'https://docs.optimizely.com/digital-experience-platform/docs/personalization', product: 'dxp', contentType: 'documentation', title: 'Personalization and Visitor Groups', tags: ['personalization', 'targeting', 'visitor-groups'] } ]; constructor(logger) { this.logger = logger; } /** * Start indexing all documentation sources */ async indexAllDocumentation() { if (!chromaDBService.isAvailable() || !openAIClient.isAvailable()) { this.logger.warn('ChromaDB or OpenAI not available - cannot index documentation'); return false; } try { this.indexingProgress = { totalDocuments: this.documentationSources.length, processedDocuments: 0, failedDocuments: 0, status: 'indexing', startTime: new Date() }; this.logger.info('Starting documentation indexing', { totalSources: this.documentationSources.length }); const chunks = []; for (const source of this.documentationSources) { try { this.indexingProgress.currentDocument = source.title || source.url; this.logger.debug(`Processing: ${source.title || source.url}`); const documentChunks = await this.processDocumentSource(source); chunks.push(...documentChunks); this.indexingProgress.processedDocuments++; } catch (error) { this.logger.error(`Failed to process document: ${source.url}`, error); this.indexingProgress.failedDocuments++; } } // Add all chunks to ChromaDB if (chunks.length > 0) { const success = await chromaDBService.addDocuments(chunks); if (!success) { throw new Error('Failed to add documents to ChromaDB'); } } this.indexingProgress.status = 'completed'; this.indexingProgress.endTime = new Date(); this.logger.info('Documentation indexing completed', { totalProcessed: this.indexingProgress.processedDocuments, totalFailed: this.indexingProgress.failedDocuments, chunksCreated: chunks.length, duration: this.indexingProgress.endTime.getTime() - this.indexingProgress.startTime.getTime() }); return true; } catch (error) { this.indexingProgress.status = 'failed'; this.indexingProgress.endTime = new Date(); this.logger.error('Documentation indexing failed', error); return false; } } /** * Process a single documentation source and create chunks */ async processDocumentSource(source) { try { // For demo purposes, create sample content chunks // In a real implementation, this would fetch and parse the actual documentation const sampleContent = this.generateSampleContent(source); const chunks = []; // Split content into chunks (simulate chunking strategy) const contentChunks = this.chunkContent(sampleContent, 500); // 500 chars per chunk for (let i = 0; i < contentChunks.length; i++) { const chunkContent = contentChunks[i]; if (!chunkContent) continue; const chunkId = `${source.product}_${Date.now()}_${i}`; const chunk = { id: chunkId, content: chunkContent, metadata: { product: source.product, url: source.url, title: source.title || `${source.product} Documentation`, section: `Section ${i + 1}`, lastUpdated: new Date().toISOString(), contentType: source.contentType, tags: source.tags || [] } }; chunks.push(chunk); } return chunks; } catch (error) { this.logger.error(`Failed to process document source: ${source.url}`, error); return []; } } /** * Generate sample content for demonstration * In production, this would fetch real documentation content */ generateSampleContent(source) { const productName = source.product.replace('-', ' ').replace(/\b\w/g, l => l.toUpperCase()); switch (source.product) { case 'configured-commerce': return ` # ${source.title || 'Configured Commerce Documentation'} ${productName} provides a robust e-commerce platform with extensible architecture. ## Handler Chain Pattern The handler chain pattern is fundamental to extending commerce functionality. Each handler in the chain processes requests and can modify or extend behavior. ### Key Components: - HandlerChainManager: Orchestrates handler execution - IHandlerFactory: Creates handler instances - Custom handlers: Implement business logic ### Best Practices: - Always call base.Execute() when overriding handlers - Use dependency injection for handler dependencies - Implement proper error handling in custom handlers - Follow the single responsibility principle ### Code Example: \`\`\`csharp public class CustomOrderHandler : HandlerBase<ProcessOrderParameter, ProcessOrderResult> { public override ProcessOrderResult Execute(ProcessOrderParameter parameter) { // Custom logic here var result = base.Execute(parameter); // Additional processing return result; } } \`\`\` This pattern ensures maintainability and extensibility in commerce applications.`; case 'cms-paas': return ` # ${source.title || 'CMS Documentation'} ${productName} enables content management with strongly-typed models and MVC architecture. ## Content Types Content types define the structure and properties of your content. Use the ContentType attribute to register types. ### Creating Content Types: - Inherit from PageData or BlockData - Use Display attributes for editor labels - Implement validation with DataAnnotations - Group properties using GroupName ### Best Practices: - Use strongly-typed models - Implement IContentRepository for data access - Cache content appropriately - Follow naming conventions ### Example: \`\`\`csharp [ContentType(DisplayName = "Article Page", GUID = "GUID-HERE")] public class ArticlePage : PageData { [Display(Name = "Heading", GroupName = SystemTabNames.Content)] public virtual string Heading { get; set; } [Display(Name = "Body", GroupName = SystemTabNames.Content)] public virtual XhtmlString Body { get; set; } } \`\`\``; case 'web-experimentation': return ` # ${source.title || 'Web Experimentation Documentation'} ${productName} enables A/B testing and experimentation on web applications. ## JavaScript SDK The JavaScript SDK provides methods to activate experiments and track events. ### Key Methods: - activate(): Get variation for user - track(): Send conversion events - isFeatureEnabled(): Check feature flags - getVariation(): Get specific variation ### Implementation: \`\`\`javascript import optimizely from '@optimizely/optimizely-sdk'; const optimizelyClient = optimizely.createInstance({ datafile: 'your-datafile-url' }); // Activate experiment const variation = optimizelyClient.activate('experiment_key', 'user_id'); // Track conversion optimizelyClient.track('conversion_event', 'user_id'); \`\`\` ### Best Practices: - Initialize client once and reuse - Handle errors gracefully - Use consistent user IDs - Track meaningful events`; default: return ` # ${source.title || 'Optimizely Documentation'} ${productName} documentation and implementation guide. ## Overview This documentation covers the key concepts and implementation patterns for ${productName}. ### Getting Started: 1. Install required packages 2. Configure your environment 3. Implement basic functionality 4. Test and deploy ### Best Practices: - Follow the official guidelines - Use recommended patterns - Implement proper error handling - Write comprehensive tests This serves as a foundation for building with ${productName}.`; } } /** * Split content into manageable chunks */ chunkContent(content, maxChunkSize) { const chunks = []; const paragraphs = content.split('\n\n'); let currentChunk = ''; for (const paragraph of paragraphs) { if (currentChunk.length + paragraph.length > maxChunkSize && currentChunk.length > 0) { chunks.push(currentChunk.trim()); currentChunk = paragraph; } else { currentChunk += (currentChunk ? '\n\n' : '') + paragraph; } } if (currentChunk.trim()) { chunks.push(currentChunk.trim()); } return chunks.filter(chunk => chunk.length > 50); // Filter out very short chunks } /** * Index specific products only */ async indexProductDocumentation(products) { const filteredSources = this.documentationSources.filter(source => products.includes(source.product)); if (filteredSources.length === 0) { this.logger.warn('No documentation sources found for specified products', { products }); return false; } // Temporarily replace sources for targeted indexing const originalSources = [...this.documentationSources]; this.documentationSources = filteredSources; const result = await this.indexAllDocumentation(); // Restore original sources this.documentationSources = originalSources; return result; } /** * Get indexing progress */ getIndexingProgress() { return { ...this.indexingProgress }; } /** * Check if indexing is in progress */ isIndexing() { return this.indexingProgress.status === 'indexing'; } /** * Clear all indexed documentation */ async clearIndex() { if (!chromaDBService.isAvailable()) { return false; } try { const collections = ['commerce', 'cms-paas', 'cms-saas', 'experimentation', 'dxp', 'platform']; for (const product of collections) { await chromaDBService.clearCollection(product); } this.logger.info('Documentation index cleared'); return true; } catch (error) { this.logger.error('Failed to clear documentation index', error); return false; } } /** * Get indexing statistics */ async getIndexingStats() { if (!chromaDBService.isAvailable()) { return null; } try { const stats = await chromaDBService.getCollectionStats(); return { collections: stats, totalDocuments: Object.values(stats).reduce((sum, stat) => sum + (stat.documentCount || 0), 0), lastIndexed: this.indexingProgress.endTime, indexingStatus: this.indexingProgress.status }; } catch (error) { this.logger.error('Failed to get indexing stats', error); return null; } } } // Singleton instance for global use export const documentationIndexingService = new DocumentationIndexingService(console // Will be replaced with proper logger when used ); //# sourceMappingURL=documentation-indexing-service.js.map