Skip to main content
Glama
config-manager.tsβ€’7.28 kB
/** * Configuration Manager for System Prompt Architecture * * Handles loading, parsing, and validation of system prompt configurations. * Supports both programmatic configuration and file-based configuration. */ import { promises as fs } from 'fs'; import path from 'path'; import { SystemPromptConfig, ProviderConfig, ProviderType } from './interfaces.js'; export interface ConfigLoadOptions { /** Base directory for resolving relative file paths */ baseDir?: string; /** Environment variables to replace in configuration */ envVariables?: Record<string, string>; /** Whether to validate configuration after loading */ validate?: boolean; } export class SystemPromptConfigManager { private config: SystemPromptConfig | null = null; private baseDir: string = process.cwd(); /** * Load configuration from an object */ public loadFromObject(config: SystemPromptConfig, options: ConfigLoadOptions = {}): void { this.baseDir = options.baseDir || process.cwd(); if (options.validate !== false) { this.validateConfig(config); } this.config = this.processConfiguration(config, options); } /** * Load configuration from a JSON file */ public async loadFromFile(filePath: string, options: ConfigLoadOptions = {}): Promise<void> { const fullPath = path.isAbsolute(filePath) ? filePath : path.resolve(options.baseDir || process.cwd(), filePath); this.baseDir = path.dirname(fullPath); try { const fileContent = await fs.readFile(fullPath, 'utf8'); const rawConfig = JSON.parse(fileContent); if (options.validate !== false) { this.validateConfig(rawConfig); } this.config = this.processConfiguration(rawConfig, options); } catch (error) { throw new Error( `Failed to load configuration from ${filePath}: ${error instanceof Error ? error.message : 'Unknown error'}` ); } } /** * Get the current configuration */ public getConfig(): SystemPromptConfig { if (!this.config) { throw new Error('Configuration not loaded. Call loadFromObject() or loadFromFile() first.'); } return this.config; } /** * Get providers sorted by priority (highest first) */ public getProviders(): ProviderConfig[] { const config = this.getConfig(); return [...config.providers].sort((a, b) => b.priority - a.priority); } /** * Get enabled providers sorted by priority */ public getEnabledProviders(): ProviderConfig[] { return this.getProviders().filter(provider => provider.enabled); } /** * Get a specific provider by name */ public getProvider(name: string): ProviderConfig | undefined { const config = this.getConfig(); return config.providers.find(provider => provider.name === name); } /** * Check if configuration is loaded */ public isLoaded(): boolean { return this.config !== null; } /** * Get configuration settings */ public getSettings() { const config = this.getConfig(); return config.settings; } /** * Create a default configuration */ public static createDefault(): SystemPromptConfig { return { providers: [ { name: 'built-in-instructions', type: ProviderType.STATIC, priority: 0, enabled: true, config: { content: '# Built-in System Instructions\nThis section contains built-in tool instructions and agent behavior guidelines.', }, }, ], settings: { maxGenerationTime: 5000, failOnProviderError: false, contentSeparator: '\n\n', }, }; } /** * Validate configuration structure and content */ private validateConfig(config: any): void { if (!config || typeof config !== 'object') { throw new Error('Configuration must be an object'); } // Validate providers array if (!Array.isArray(config.providers)) { throw new Error('Configuration must have a "providers" array'); } // Validate each provider config.providers.forEach((provider: any, index: number) => { this.validateProvider(provider, index); }); // Validate settings if (!config.settings || typeof config.settings !== 'object') { throw new Error('Configuration must have a "settings" object'); } this.validateSettings(config.settings); } /** * Validate a single provider configuration */ private validateProvider(provider: any, index: number): void { const prefix = `Provider at index ${index}`; if (!provider || typeof provider !== 'object') { throw new Error(`${prefix} must be an object`); } // Validate required fields if (typeof provider.name !== 'string' || !provider.name.trim()) { throw new Error(`${prefix} must have a non-empty "name" string`); } if ( typeof provider.type !== 'string' || !Object.values(ProviderType).includes(provider.type as ProviderType) ) { throw new Error( `${prefix} must have a valid "type" (${Object.values(ProviderType).join(', ')})` ); } if (typeof provider.priority !== 'number') { throw new Error(`${prefix} must have a numeric "priority"`); } if (typeof provider.enabled !== 'boolean') { throw new Error(`${prefix} must have a boolean "enabled" field`); } // Validate config field if present if ( provider.config !== undefined && (typeof provider.config !== 'object' || provider.config === null) ) { throw new Error(`${prefix} "config" must be an object if provided`); } } /** * Validate settings configuration */ private validateSettings(settings: any): void { if (typeof settings.maxGenerationTime !== 'number' || settings.maxGenerationTime <= 0) { throw new Error('Settings "maxGenerationTime" must be a positive number'); } if (typeof settings.failOnProviderError !== 'boolean') { throw new Error('Settings "failOnProviderError" must be a boolean'); } if (typeof settings.contentSeparator !== 'string') { throw new Error('Settings "contentSeparator" must be a string'); } } /** * Process configuration with environment variable replacement and path resolution */ private processConfiguration( config: SystemPromptConfig, options: ConfigLoadOptions ): SystemPromptConfig { const processed = JSON.parse(JSON.stringify(config)); // Deep clone // Process environment variables if provided if (options.envVariables) { this.replaceEnvironmentVariables(processed, options.envVariables); } // Process file paths for file-based providers - only if providers is an array if (Array.isArray(processed.providers)) { processed.providers.forEach((provider: any) => { if (provider.type === ProviderType.FILE_BASED && provider.config?.filePath) { if (!path.isAbsolute(provider.config.filePath)) { provider.config.filePath = path.resolve(this.baseDir, provider.config.filePath); } } }); } return processed; } /** * Replace environment variables in configuration */ private replaceEnvironmentVariables(obj: any, envVars: Record<string, string>): void { for (const key in obj) { if (typeof obj[key] === 'string') { // Replace ${VAR_NAME} patterns obj[key] = obj[key].replace(/\$\{([^}]+)\}/g, (match: string, varName: string) => { return envVars[varName] || match; }); } else if (typeof obj[key] === 'object' && obj[key] !== null) { this.replaceEnvironmentVariables(obj[key], envVars); } } } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/campfirein/cipher'

If you have feedback or need assistance with the MCP directory API, please join our Discord server