UNPKG

supa-seed

Version:

A constraint-aware, framework-agnostic database seeding framework with deep PostgreSQL business logic discovery and MakerKit integration support

328 lines 11.8 kB
"use strict"; /** * Ollama Local Model Client * Phase 5, Checkpoint E1 - Local AI integration for asset generation */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.ollamaClient = exports.OllamaClient = void 0; const logger_1 = require("../core/utils/logger"); const http = __importStar(require("http")); const https = __importStar(require("https")); class OllamaClient { constructor(config) { this.isHealthy = false; this.lastHealthCheck = new Date(0); this.healthCheckInterval = 30000; // 30 seconds this.config = { baseUrl: 'http://localhost:11434', model: 'llama3.1:latest', timeout: 30000, maxRetries: 3, fallbackModels: ['llama3.1:8b', 'llama2:latest', 'mistral:latest'], ...config }; logger_1.Logger.info(`🤖 Initializing Ollama client: ${this.config.baseUrl}`); } /** * Check if Ollama is available and healthy */ async checkHealth() { const now = new Date(); // Skip frequent health checks if (this.isHealthy && (now.getTime() - this.lastHealthCheck.getTime()) < this.healthCheckInterval) { return { connected: true, availableModels: [], recommendedModel: this.config.model }; } try { // Check if Ollama is running const version = await this.getVersion(); // Get available models const models = await this.listModels(); const availableModels = models.map(m => m.name); // Find best available model const recommendedModel = this.findBestModel(availableModels); this.isHealthy = true; this.lastHealthCheck = now; logger_1.Logger.info(`🤖 Ollama connected: ${availableModels.length} models available`); return { connected: true, version, availableModels, recommendedModel }; } catch (error) { this.isHealthy = false; logger_1.Logger.warn(`🤖 Ollama connection failed: ${error.message}`); return { connected: false, availableModels: [], error: error.message }; } } /** * Generate text using Ollama */ async generate(request) { const model = request.model || this.config.model; // Health check before generation const health = await this.checkHealth(); if (!health.connected) { throw new Error(`Ollama not available: ${health.error}`); } // Ensure model is available if (!health.availableModels.includes(model)) { const fallback = this.findBestModel(health.availableModels); if (!fallback) { throw new Error(`No suitable models available. Requested: ${model}`); } logger_1.Logger.warn(`🤖 Model ${model} not available, using fallback: ${fallback}`); request.model = fallback; } const fullRequest = { model, ...request }; logger_1.Logger.debug(`🤖 Generating with ${model}: ${request.prompt.substring(0, 100)}...`); let attempt = 0; while (attempt < this.config.maxRetries) { try { const response = await this.makeRequest('/api/generate', fullRequest); logger_1.Logger.debug(`🤖 Generated ${response.response.length} characters in ${response.total_duration}ns`); return response; } catch (error) { attempt++; if (attempt >= this.config.maxRetries) { throw new Error(`Generation failed after ${attempt} attempts: ${error.message}`); } logger_1.Logger.warn(`🤖 Generation attempt ${attempt} failed, retrying: ${error.message}`); await this.delay(1000 * attempt); // Exponential backoff } } throw new Error('Generation failed: Maximum retries exceeded'); } /** * Generate structured JSON response */ async generateJSON(prompt, system, schema) { try { const response = await this.generate({ prompt, system, format: 'json', options: { temperature: 0.1, // Lower temperature for more consistent JSON } }); const parsed = JSON.parse(response.response); // Basic schema validation if provided if (schema && !this.validateSchema(parsed, schema)) { throw new Error('Generated JSON does not match expected schema'); } return parsed; } catch (error) { if (error.message.includes('JSON')) { logger_1.Logger.error(`🤖 JSON parsing failed: ${error.message}`); throw new Error(`Invalid JSON response from model: ${error.message}`); } throw error; } } /** * Get available models */ async listModels() { try { const response = await this.makeRequest('/api/tags', null, 'GET'); return response.models || []; } catch (error) { logger_1.Logger.error(`🤖 Failed to list models: ${error.message}`); return []; } } /** * Pull a model if not available */ async pullModel(name, onProgress) { try { logger_1.Logger.info(`🤖 Pulling model: ${name}`); // This would implement streaming for progress updates // For now, simplified version await this.makeRequest('/api/pull', { name }); logger_1.Logger.info(`🤖 Model pulled successfully: ${name}`); return true; } catch (error) { logger_1.Logger.error(`🤖 Failed to pull model ${name}: ${error.message}`); return false; } } /** * Get Ollama version */ async getVersion() { try { const response = await this.makeRequest('/api/version', null, 'GET'); return response.version || 'unknown'; } catch (error) { throw new Error(`Failed to get Ollama version: ${error.message}`); } } /** * Find the best available model from our preferences */ findBestModel(availableModels) { // Check configured model first if (availableModels.includes(this.config.model)) { return this.config.model; } // Check fallback models if (this.config.fallbackModels) { for (const fallback of this.config.fallbackModels) { if (availableModels.includes(fallback)) { return fallback; } } } // Return first available model return availableModels[0]; } /** * Make HTTP request to Ollama API */ async makeRequest(endpoint, data, method = 'POST') { return new Promise((resolve, reject) => { const url = new URL(endpoint, this.config.baseUrl); const isHttps = url.protocol === 'https:'; const httpModule = isHttps ? https : http; const postData = data ? JSON.stringify(data) : undefined; const options = { hostname: url.hostname, port: url.port || (isHttps ? 443 : 80), path: url.pathname + url.search, method, headers: { 'Content-Type': 'application/json', ...(postData && { 'Content-Length': Buffer.byteLength(postData) }) }, timeout: this.config.timeout }; const req = httpModule.request(options, (res) => { let body = ''; res.on('data', (chunk) => { body += chunk; }); res.on('end', () => { try { if (res.statusCode >= 200 && res.statusCode < 300) { const response = body ? JSON.parse(body) : {}; resolve(response); } else { reject(new Error(`HTTP ${res.statusCode}: ${body}`)); } } catch (error) { reject(new Error(`Failed to parse response: ${error.message}`)); } }); }); req.on('error', (error) => { reject(new Error(`Request failed: ${error.message}`)); }); req.on('timeout', () => { req.destroy(); reject(new Error(`Request timeout after ${this.config.timeout}ms`)); }); if (postData) { req.write(postData); } req.end(); }); } /** * Basic schema validation */ validateSchema(data, schema) { // Very basic validation - in production would use proper JSON schema validator if (typeof schema === 'object' && schema !== null) { for (const key in schema) { if (!(key in data)) { return false; } } } return true; } /** * Simple delay utility */ delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } /** * Get connection status */ isConnected() { return this.isHealthy; } /** * Get current configuration */ getConfig() { return { ...this.config }; } /** * Update configuration */ updateConfig(updates) { this.config = { ...this.config, ...updates }; this.isHealthy = false; // Force health check on next request logger_1.Logger.info('🤖 Ollama configuration updated'); } } exports.OllamaClient = OllamaClient; // Export singleton instance exports.ollamaClient = new OllamaClient(); //# sourceMappingURL=ollama-client.js.map