UNPKG

prprompts-flutter-generator

Version:

AI-powered Flutter development with full automation + official extension support - Generate 32 security-audited guides & auto-implement in 2-3 hours. NEW v5.1: Official Claude Code plugin with hooks, Gemini TOML commands, Qwen MCP settings. Features: Comp

185 lines (166 loc) 4.69 kB
/** * Abstract AI Client Interface * Base class for all AI providers (Claude, Qwen, Gemini) */ class AIClient { constructor(config) { if (this.constructor === AIClient) { throw new Error('AIClient is abstract and cannot be instantiated directly'); } this.config = { apiKey: config.apiKey || process.env.AI_API_KEY, model: config.model || 'default', maxTokens: config.maxTokens || 4000, temperature: config.temperature || 0.3, timeout: config.timeout || 30000, retryAttempts: config.retryAttempts || 3, retryDelay: config.retryDelay || 1000, ...config }; this.cache = new Map(); this.tokenUsage = { totalTokens: 0, promptTokens: 0, completionTokens: 0, requestCount: 0 }; } /** * Enhance generated Flutter code * @param {string} code - Generated Flutter code * @param {Object} context - Conversion context * @returns {Promise<Object>} Enhanced code and changelog */ async enhance(code, context) { throw new Error('enhance() must be implemented by subclass'); } /** * Optimize widget structure * @param {Object} widgetTree - Widget tree structure * @returns {Promise<Object>} Optimization suggestions */ async optimize(widgetTree) { throw new Error('optimize() must be implemented by subclass'); } /** * Generate suggestions for improvements * @param {string} code - Code to analyze * @param {Object} context - Analysis context * @returns {Promise<Array>} List of suggestions */ async suggest(code, context) { throw new Error('suggest() must be implemented by subclass'); } /** * Validate generated code * @param {string} code - Code to validate * @param {Object} options - Validation options * @returns {Promise<Object>} Validation results */ async validate(code, options = {}) { throw new Error('validate() must be implemented by subclass'); } /** * Make API request with retry logic * @protected */ async _makeRequestWithRetry(requestFn, retryCount = 0) { try { const result = await this._withTimeout(requestFn(), this.config.timeout); this.tokenUsage.requestCount++; return result; } catch (error) { if (retryCount < this.config.retryAttempts) { const delay = this.config.retryDelay * Math.pow(2, retryCount); await this._sleep(delay); return this._makeRequestWithRetry(requestFn, retryCount + 1); } throw error; } } /** * Check cache for existing response * @protected */ _getCached(key) { const cached = this.cache.get(key); if (cached && Date.now() - cached.timestamp < 3600000) { // 1 hour TTL return cached.data; } return null; } /** * Store response in cache * @protected */ _setCache(key, data) { this.cache.set(key, { data, timestamp: Date.now() }); } /** * Generate cache key from prompt * @protected */ _getCacheKey(prompt, context = {}) { const contextStr = JSON.stringify(context); return `${this.constructor.name}:${prompt.substring(0, 100)}:${contextStr}`.replace(/\s+/g, '_'); } /** * Wrap promise with timeout * @protected */ _withTimeout(promise, ms) { return Promise.race([ promise, new Promise((_, reject) => setTimeout(() => reject(new Error('Request timeout')), ms) ) ]); } /** * Sleep utility * @protected */ _sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } /** * Parse JSON from AI response (handles markdown code blocks) * @protected */ _parseJSON(response) { try { // Remove markdown code blocks let cleaned = response .replace(/```json\s*/g, '') .replace(/```\s*/g, '') .trim(); // Try direct parse try { return JSON.parse(cleaned); } catch (e) { // Try to extract JSON object/array const jsonMatch = cleaned.match(/[\{\[][\s\S]*[\}\]]/); if (jsonMatch) { return JSON.parse(jsonMatch[0]); } throw e; } } catch (error) { throw new Error(`Failed to parse AI response as JSON: ${error.message}`); } } /** * Get token usage statistics * @returns {Object} Token usage stats */ getTokenUsage() { return { ...this.tokenUsage }; } /** * Clear cache */ clearCache() { this.cache.clear(); } } module.exports = AIClient;