hikma-engine
Version:
Code Knowledge Graph Indexer - A sophisticated TypeScript-based indexer that transforms Git repositories into multi-dimensional knowledge stores for AI agents
394 lines (393 loc) • 16.2 kB
JavaScript
/**
* @file Centralized configuration management for hikma-engine.
* Manages database connections, AI model settings, file patterns, and environment-specific configurations.
*/
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.ConfigManager = void 0;
exports.initializeConfig = initializeConfig;
exports.getConfig = getConfig;
const path = __importStar(require("path"));
/**
* Default configuration for the hikma-engine.
*/
const defaultConfig = {
database: {
sqlite: {
path: './data/metadata.db',
vectorExtension: './extensions/vec0.dylib',
},
},
ai: {
embedding: {
model: 'mixedbread-ai/mxbai-embed-large-v1',
batchSize: 32,
provider: 'python',
localEndpoint: 'http://localhost:1234',
},
summary: {
model: 'Xenova/distilgpt2',
maxTokens: 256,
},
rag: {
model: 'Qwen/Qwen2.5-Coder-1.5B-Instruct', // Fallback model for Python provider - main LLM config is in ai.llmProvider.server.model
},
llmProvider: {
provider: 'server',
timeout: 300000,
retryAttempts: 3,
retryDelay: 1000,
server: {
apiUrl: 'http://localhost:1234/v1/chat/completions',
apiKey: 'not-needed',
model: 'qwen/qwen3-coder-30b',
maxTokens: 4096,
temperature: 0.1,
},
},
},
indexing: {
filePatterns: [
'**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx',
'**/*.py', '**/*.java', '**/*.go',
'**/*.c', '**/*.cpp', '**/*.h', '**/*.hpp',
'**/*.cs', '**/*.rb', '**/*.php',
'**/*.html', '**/*.css', '**/*.scss', '**/*.less',
'**/*.json', '**/*.xml', '**/*.yaml', '**/*.yml',
'**/*.md', '**/*.rst', '**/*.txt',
],
ignorePatterns: [
'**/node_modules/**',
'**/dist/**',
'**/build/**',
'**/.git/**',
'**/*.min.js',
'**/*.min.css',
],
maxFileSize: 1024 * 1024, // 1MB
supportedLanguages: [
'typescript', 'javascript', 'python', 'java', 'go',
'c', 'cpp', 'csharp', 'ruby', 'php', 'html', 'css',
],
},
logging: {
level: 'info',
enableConsole: true,
enableFile: false,
},
};
/**
* Configuration manager class that handles loading and merging configurations
* from various sources (defaults, environment variables, config files).
*/
class ConfigManager {
constructor(projectRoot) {
this.projectRoot = projectRoot;
this.config = this.loadConfig();
}
/**
* Loads configuration from various sources and merges them.
* Priority: Environment variables > Config file > Defaults
*/
loadConfig() {
let config = { ...defaultConfig };
// Resolve relative paths to absolute paths based on project root
config.database.sqlite.path = path.resolve(this.projectRoot, config.database.sqlite.path);
if (config.database.sqlite.vectorExtension) {
config.database.sqlite.vectorExtension = path.resolve(this.projectRoot, config.database.sqlite.vectorExtension);
}
// Override with environment variables if present
if (process.env.HIKMA_SQLITE_PATH) {
config.database.sqlite.path = path.resolve(this.projectRoot, process.env.HIKMA_SQLITE_PATH);
}
if (process.env.HIKMA_SQLITE_VEC_EXTENSION) {
config.database.sqlite.vectorExtension = path.resolve(this.projectRoot, process.env.HIKMA_SQLITE_VEC_EXTENSION);
}
// Override AI model configuration
if (process.env.HIKMA_EMBEDDING_MODEL) {
config.ai.embedding.model = process.env.HIKMA_EMBEDDING_MODEL;
}
if (process.env.HIKMA_RAG_MODEL) {
config.ai.rag.model = process.env.HIKMA_RAG_MODEL;
}
// Override embedding provider configuration
if (process.env.HIKMA_EMBEDDING_PROVIDER) {
const provider = process.env.HIKMA_EMBEDDING_PROVIDER;
if (['local', 'transformers', 'python', 'server'].includes(provider)) {
config.ai.embedding.provider = provider;
}
}
// Server embedding provider configuration
if (process.env.HIKMA_EMBEDDING_SERVER_API_URL ||
process.env.HIKMA_EMBEDDING_SERVER_API_KEY ||
process.env.HIKMA_EMBEDDING_SERVER_MODEL) {
if (!config.ai.embedding.server) {
config.ai.embedding.server = {
apiUrl: 'http://localhost:11434',
model: 'mxbai-embed-large:latest',
};
}
if (process.env.HIKMA_EMBEDDING_SERVER_API_URL) {
config.ai.embedding.server.apiUrl = process.env.HIKMA_EMBEDDING_SERVER_API_URL;
}
if (process.env.HIKMA_EMBEDDING_SERVER_API_KEY) {
config.ai.embedding.server.apiKey = process.env.HIKMA_EMBEDDING_SERVER_API_KEY;
}
if (process.env.HIKMA_EMBEDDING_SERVER_MODEL) {
config.ai.embedding.server.model = process.env.HIKMA_EMBEDDING_SERVER_MODEL;
}
}
// Override LLM provider configuration
if (process.env.HIKMA_ENGINE_LLM_PROVIDER) {
const provider = process.env.HIKMA_ENGINE_LLM_PROVIDER;
if (provider === 'python' || provider === 'server') {
config.ai.llmProvider.provider = provider;
}
}
if (process.env.HIKMA_ENGINE_LLM_TIMEOUT) {
const timeout = parseInt(process.env.HIKMA_ENGINE_LLM_TIMEOUT, 10);
if (!isNaN(timeout) && timeout > 0) {
config.ai.llmProvider.timeout = timeout;
}
}
if (process.env.HIKMA_ENGINE_LLM_RETRY_ATTEMPTS) {
const retryAttempts = parseInt(process.env.HIKMA_ENGINE_LLM_RETRY_ATTEMPTS, 10);
if (!isNaN(retryAttempts) && retryAttempts >= 0) {
config.ai.llmProvider.retryAttempts = retryAttempts;
}
}
if (process.env.HIKMA_ENGINE_LLM_RETRY_DELAY) {
const retryDelay = parseInt(process.env.HIKMA_ENGINE_LLM_RETRY_DELAY, 10);
if (!isNaN(retryDelay) && retryDelay >= 0) {
config.ai.llmProvider.retryDelay = retryDelay;
}
}
// Server-specific configuration
if (process.env.HIKMA_ENGINE_LLM_SERVER_API_URL ||
process.env.HIKMA_ENGINE_LLM_SERVER_API_KEY ||
process.env.HIKMA_ENGINE_LLM_SERVER_MODEL) {
if (!config.ai.llmProvider.server) {
config.ai.llmProvider.server = {
apiUrl: 'http://localhost:1234',
apiKey: '',
// model: 'openai/gpt-oss-20b',
model: 'qwen/qwen3-coder-30b' // Fallback configuration - main config is in defaultConfig.ai.llmProvider.server.model
};
}
if (process.env.HIKMA_ENGINE_LLM_SERVER_API_URL) {
config.ai.llmProvider.server.apiUrl = process.env.HIKMA_ENGINE_LLM_SERVER_API_URL;
}
if (process.env.HIKMA_ENGINE_LLM_SERVER_API_KEY) {
config.ai.llmProvider.server.apiKey = process.env.HIKMA_ENGINE_LLM_SERVER_API_KEY;
}
if (process.env.HIKMA_ENGINE_LLM_SERVER_MODEL) {
config.ai.llmProvider.server.model = process.env.HIKMA_ENGINE_LLM_SERVER_MODEL;
}
if (process.env.HIKMA_ENGINE_LLM_SERVER_MAX_TOKENS) {
const maxTokens = parseInt(process.env.HIKMA_ENGINE_LLM_SERVER_MAX_TOKENS, 10);
if (!isNaN(maxTokens) && maxTokens > 0) {
config.ai.llmProvider.server.maxTokens = maxTokens;
}
}
if (process.env.HIKMA_ENGINE_LLM_SERVER_TEMPERATURE) {
const temperature = parseFloat(process.env.HIKMA_ENGINE_LLM_SERVER_TEMPERATURE);
if (!isNaN(temperature) && temperature >= 0 && temperature <= 2) {
config.ai.llmProvider.server.temperature = temperature;
}
}
}
// Python-specific configuration
if (process.env.HIKMA_ENGINE_LLM_PYTHON_MODEL) {
if (!config.ai.llmProvider.python) {
config.ai.llmProvider.python = {
model: 'Qwen/Qwen2.5-Coder-1.5B-Instruct',
maxResults: 8,
};
}
config.ai.llmProvider.python.model = process.env.HIKMA_ENGINE_LLM_PYTHON_MODEL;
}
if (process.env.HIKMA_ENGINE_LLM_PYTHON_MAX_RESULTS) {
const maxResults = parseInt(process.env.HIKMA_ENGINE_LLM_PYTHON_MAX_RESULTS, 10);
if (!isNaN(maxResults) && maxResults > 0) {
if (!config.ai.llmProvider.python) {
config.ai.llmProvider.python = {
model: 'Qwen/Qwen2.5-Coder-1.5B-Instruct',
maxResults: 8,
};
}
config.ai.llmProvider.python.maxResults = maxResults;
}
}
// Support both HIKMA_LOG_LEVEL (CLI) and HIKMA_API_LOG_LEVEL (API) environment variables
if (process.env.HIKMA_LOG_LEVEL || process.env.HIKMA_API_LOG_LEVEL) {
config.logging.level = (process.env.HIKMA_LOG_LEVEL || process.env.HIKMA_API_LOG_LEVEL);
}
return config;
}
/**
* Gets the complete configuration object.
*/
getConfig() {
return this.config;
}
/**
* Gets database configuration.
*/
getDatabaseConfig() {
return this.config.database;
}
/**
* Gets AI configuration.
*/
getAIConfig() {
return this.config.ai;
}
/**
* Gets indexing configuration.
*/
getIndexingConfig() {
return this.config.indexing;
}
/**
* Gets logging configuration.
*/
getLoggingConfig() {
return this.config.logging;
}
/**
* Updates configuration at runtime (useful for testing or dynamic configuration).
*/
updateConfig(updates) {
this.config = { ...this.config, ...updates };
}
/**
* Validates the LLM provider configuration.
* @throws Error if configuration is invalid
*/
validateLLMProviderConfig() {
const llmConfig = this.config.ai.llmProvider;
// Validate common configuration
if (typeof llmConfig.timeout !== 'number' || llmConfig.timeout <= 0) {
throw new Error('LLM provider timeout must be a positive number');
}
if (typeof llmConfig.retryAttempts !== 'number' || llmConfig.retryAttempts < 0) {
throw new Error('LLM provider retry attempts must be a non-negative number');
}
if (typeof llmConfig.retryDelay !== 'number' || llmConfig.retryDelay < 0) {
throw new Error('LLM provider retry delay must be a non-negative number');
}
// Validate provider-specific configuration
if (llmConfig.provider === 'server') {
if (!llmConfig.server) {
throw new Error('Server configuration is required when provider is set to "server"');
}
const serverConfig = llmConfig.server;
if (!serverConfig.apiUrl || typeof serverConfig.apiUrl !== 'string') {
throw new Error('Server API URL is required and must be a valid string');
}
if (!serverConfig.apiKey || typeof serverConfig.apiKey !== 'string') {
throw new Error('Server API key is required and must be a valid string');
}
if (!serverConfig.model || typeof serverConfig.model !== 'string') {
throw new Error('Server model is required and must be a valid string');
}
// Validate URL format
try {
new URL(serverConfig.apiUrl);
}
catch {
throw new Error('Server API URL must be a valid URL');
}
// Validate API key format (relaxed for local/self-hosted endpoints)
const isLocalEndpoint = serverConfig.apiUrl.includes('localhost') ||
serverConfig.apiUrl.includes('127.0.0.1') ||
serverConfig.apiUrl.includes('0.0.0.0');
if (!isLocalEndpoint && !serverConfig.apiKey.startsWith('sk-') && !serverConfig.apiKey.startsWith('org-')) {
throw new Error('Server API key must start with "sk-" or "org-" for external APIs');
}
// Validate optional parameters
if (serverConfig.maxTokens !== undefined) {
if (typeof serverConfig.maxTokens !== 'number' || serverConfig.maxTokens <= 0) {
throw new Error('Server max tokens must be a positive number');
}
}
if (serverConfig.temperature !== undefined) {
if (typeof serverConfig.temperature !== 'number' ||
serverConfig.temperature < 0 ||
serverConfig.temperature > 2) {
throw new Error('Server temperature must be a number between 0 and 2');
}
}
}
else if (llmConfig.provider === 'python') {
if (llmConfig.python) {
const pythonConfig = llmConfig.python;
if (!pythonConfig.model || typeof pythonConfig.model !== 'string') {
throw new Error('Python model must be a valid string');
}
if (typeof pythonConfig.maxResults !== 'number' || pythonConfig.maxResults <= 0) {
throw new Error('Python max results must be a positive number');
}
}
}
else {
throw new Error(`Unsupported LLM provider: ${llmConfig.provider}`);
}
}
}
exports.ConfigManager = ConfigManager;
/**
* Global configuration instance (singleton pattern).
* Initialize this once at application startup.
*/
let globalConfigManager = null;
/**
* Initializes the global configuration manager.
*/
function initializeConfig(projectRoot) {
globalConfigManager = new ConfigManager(projectRoot);
return globalConfigManager;
}
/**
* Gets the global configuration manager instance.
* Throws an error if not initialized.
*/
function getConfig() {
if (!globalConfigManager) {
throw new Error('Configuration not initialized. Call initializeConfig() first.');
}
return globalConfigManager;
}
;