Skip to main content
Glama
config.ts17.4 kB
/** * Config Routes * * REST API endpoints for system configuration management. * Requirements: 10.1, 10.2 */ import { Router, type Request, type Response } from "express"; import { z } from "zod"; import type { CognitiveCore } from "../cognitive-core.js"; import { asyncHandler, ValidationApiError } from "../middleware/error-handler.js"; import { buildSuccessResponse } from "../types/api-response.js"; /** * Helper to extract request ID from request */ function getRequestId(req: Request): string | undefined { return (req as Request & { requestId?: string }).requestId; } /** * Helper to parse Zod validation errors into field errors */ function parseZodErrors(error: z.ZodError): Record<string, string> { const fieldErrors: Record<string, string> = {}; for (const issue of error.issues) { const path = issue.path.join(".") || "request"; fieldErrors[path] = issue.message; } return fieldErrors; } /** * Cognitive mode options */ type CognitiveMode = "balanced" | "analytical" | "creative" | "intuitive" | "deliberative"; /** * Memory limits configuration */ interface MemoryLimits { /** Maximum memories per user */ maxMemoriesPerUser: number; /** Maximum memory content length in characters */ maxContentLength: number; /** Maximum batch size for batch operations */ maxBatchSize: number; /** Memory retention period in days (0 = infinite) */ retentionDays: number; } /** * Processing parameters configuration */ interface ProcessingParameters { /** Embedding dimension size */ embeddingDimension: number; /** Minimum similarity threshold for recall */ minSimilarityThreshold: number; /** Maximum parallel reasoning streams */ maxParallelStreams: number; /** Reasoning timeout in milliseconds */ reasoningTimeoutMs: number; /** Default recall limit */ defaultRecallLimit: number; } /** * Feature flags configuration */ interface FeatureFlags { /** Enable parallel reasoning */ parallelReasoningEnabled: boolean; /** Enable emotion detection */ emotionDetectionEnabled: boolean; /** Enable bias detection */ biasDetectionEnabled: boolean; /** Enable memory consolidation */ memoryConsolidationEnabled: boolean; /** Enable waypoint graph */ waypointGraphEnabled: boolean; } /** * Full system configuration * Requirements: 10.1 */ interface SystemConfig { /** Current cognitive processing mode */ cognitiveMode: CognitiveMode; /** Memory system limits */ memoryLimits: MemoryLimits; /** Processing parameters */ processingParameters: ProcessingParameters; /** Feature flags */ featureFlags: FeatureFlags; } /** * Response type for GET /api/v1/config * Requirements: 10.1 */ interface ConfigGetResponse extends SystemConfig { /** Configuration version for tracking changes */ version: string; /** Last updated timestamp */ lastUpdated: string; } /** * Response type for PUT /api/v1/config * Requirements: 10.2 */ interface ConfigUpdateResponse { /** Updated configuration */ config: SystemConfig; /** Fields that were updated */ updatedFields: string[]; /** Whether a restart is required for changes to take effect */ restartRequired: boolean; /** Configuration version after update */ version: string; } /** * In-memory configuration store * In production, this would be backed by a database or configuration service */ class ConfigStore { private config: SystemConfig; private version: number = 1; private lastUpdated: Date = new Date(); constructor() { // Initialize with default configuration this.config = this.getDefaultConfig(); } /** * Get default configuration values */ private getDefaultConfig(): SystemConfig { return { cognitiveMode: "balanced", memoryLimits: { maxMemoriesPerUser: 100000, maxContentLength: 100000, maxBatchSize: 100, retentionDays: 0, // 0 = infinite retention }, processingParameters: { embeddingDimension: 768, minSimilarityThreshold: 0.5, maxParallelStreams: 4, reasoningTimeoutMs: 30000, defaultRecallLimit: 10, }, featureFlags: { parallelReasoningEnabled: true, emotionDetectionEnabled: true, biasDetectionEnabled: true, memoryConsolidationEnabled: true, waypointGraphEnabled: true, }, }; } /** * Get current configuration */ getConfig(): ConfigGetResponse { return { ...this.config, version: `v${this.version}`, lastUpdated: this.lastUpdated.toISOString(), }; } /** * Update configuration with partial values * Returns the updated fields and whether restart is required */ updateConfig(updates: Partial<SystemConfig>): { config: SystemConfig; updatedFields: string[]; restartRequired: boolean; version: string; } { const updatedFields: string[] = []; let restartRequired = false; // Track which fields are being updated if ( updates.cognitiveMode !== undefined && updates.cognitiveMode !== this.config.cognitiveMode ) { this.config.cognitiveMode = updates.cognitiveMode; updatedFields.push("cognitiveMode"); } if (updates.memoryLimits !== undefined) { const memoryUpdates = this.updateMemoryLimits(updates.memoryLimits); updatedFields.push(...memoryUpdates.fields); if (memoryUpdates.restartRequired) { restartRequired = true; } } if (updates.processingParameters !== undefined) { const processingUpdates = this.updateProcessingParameters(updates.processingParameters); updatedFields.push(...processingUpdates.fields); if (processingUpdates.restartRequired) { restartRequired = true; } } if (updates.featureFlags !== undefined) { const featureUpdates = this.updateFeatureFlags(updates.featureFlags); updatedFields.push(...featureUpdates.fields); } // Update version and timestamp if any changes were made if (updatedFields.length > 0) { this.version++; this.lastUpdated = new Date(); } return { config: { ...this.config }, updatedFields, restartRequired, version: `v${this.version}`, }; } /** * Update memory limits */ private updateMemoryLimits(updates: Partial<MemoryLimits>): { fields: string[]; restartRequired: boolean; } { const fields: string[] = []; let restartRequired = false; if ( updates.maxMemoriesPerUser !== undefined && updates.maxMemoriesPerUser !== this.config.memoryLimits.maxMemoriesPerUser ) { this.config.memoryLimits.maxMemoriesPerUser = updates.maxMemoriesPerUser; fields.push("memoryLimits.maxMemoriesPerUser"); } if ( updates.maxContentLength !== undefined && updates.maxContentLength !== this.config.memoryLimits.maxContentLength ) { this.config.memoryLimits.maxContentLength = updates.maxContentLength; fields.push("memoryLimits.maxContentLength"); } if ( updates.maxBatchSize !== undefined && updates.maxBatchSize !== this.config.memoryLimits.maxBatchSize ) { this.config.memoryLimits.maxBatchSize = updates.maxBatchSize; fields.push("memoryLimits.maxBatchSize"); } if ( updates.retentionDays !== undefined && updates.retentionDays !== this.config.memoryLimits.retentionDays ) { this.config.memoryLimits.retentionDays = updates.retentionDays; fields.push("memoryLimits.retentionDays"); // Changing retention policy requires restart to apply to existing memories restartRequired = true; } return { fields, restartRequired }; } /** * Update processing parameters */ private updateProcessingParameters(updates: Partial<ProcessingParameters>): { fields: string[]; restartRequired: boolean; } { const fields: string[] = []; let restartRequired = false; if ( updates.embeddingDimension !== undefined && updates.embeddingDimension !== this.config.processingParameters.embeddingDimension ) { this.config.processingParameters.embeddingDimension = updates.embeddingDimension; fields.push("processingParameters.embeddingDimension"); // Changing embedding dimension requires restart and re-embedding restartRequired = true; } if ( updates.minSimilarityThreshold !== undefined && updates.minSimilarityThreshold !== this.config.processingParameters.minSimilarityThreshold ) { this.config.processingParameters.minSimilarityThreshold = updates.minSimilarityThreshold; fields.push("processingParameters.minSimilarityThreshold"); } if ( updates.maxParallelStreams !== undefined && updates.maxParallelStreams !== this.config.processingParameters.maxParallelStreams ) { this.config.processingParameters.maxParallelStreams = updates.maxParallelStreams; fields.push("processingParameters.maxParallelStreams"); } if ( updates.reasoningTimeoutMs !== undefined && updates.reasoningTimeoutMs !== this.config.processingParameters.reasoningTimeoutMs ) { this.config.processingParameters.reasoningTimeoutMs = updates.reasoningTimeoutMs; fields.push("processingParameters.reasoningTimeoutMs"); } if ( updates.defaultRecallLimit !== undefined && updates.defaultRecallLimit !== this.config.processingParameters.defaultRecallLimit ) { this.config.processingParameters.defaultRecallLimit = updates.defaultRecallLimit; fields.push("processingParameters.defaultRecallLimit"); } return { fields, restartRequired }; } /** * Update feature flags */ private updateFeatureFlags(updates: Partial<FeatureFlags>): { fields: string[]; restartRequired: boolean; } { const fields: string[] = []; if ( updates.parallelReasoningEnabled !== undefined && updates.parallelReasoningEnabled !== this.config.featureFlags.parallelReasoningEnabled ) { this.config.featureFlags.parallelReasoningEnabled = updates.parallelReasoningEnabled; fields.push("featureFlags.parallelReasoningEnabled"); } if ( updates.emotionDetectionEnabled !== undefined && updates.emotionDetectionEnabled !== this.config.featureFlags.emotionDetectionEnabled ) { this.config.featureFlags.emotionDetectionEnabled = updates.emotionDetectionEnabled; fields.push("featureFlags.emotionDetectionEnabled"); } if ( updates.biasDetectionEnabled !== undefined && updates.biasDetectionEnabled !== this.config.featureFlags.biasDetectionEnabled ) { this.config.featureFlags.biasDetectionEnabled = updates.biasDetectionEnabled; fields.push("featureFlags.biasDetectionEnabled"); } if ( updates.memoryConsolidationEnabled !== undefined && updates.memoryConsolidationEnabled !== this.config.featureFlags.memoryConsolidationEnabled ) { this.config.featureFlags.memoryConsolidationEnabled = updates.memoryConsolidationEnabled; fields.push("featureFlags.memoryConsolidationEnabled"); } if ( updates.waypointGraphEnabled !== undefined && updates.waypointGraphEnabled !== this.config.featureFlags.waypointGraphEnabled ) { this.config.featureFlags.waypointGraphEnabled = updates.waypointGraphEnabled; fields.push("featureFlags.waypointGraphEnabled"); } return { fields, restartRequired: false }; } /** * Reset configuration to defaults (for testing) */ resetToDefaults(): void { this.config = this.getDefaultConfig(); this.version = 1; this.lastUpdated = new Date(); } } // Singleton config store instance const configStore = new ConfigStore(); // Export for testing export { configStore, ConfigStore }; export type { CognitiveMode, ConfigGetResponse, ConfigUpdateResponse, FeatureFlags, MemoryLimits, ProcessingParameters, SystemConfig, }; /** * Zod schema for memory limits validation */ const memoryLimitsSchema = z.object({ maxMemoriesPerUser: z .number() .int() .min(1, "maxMemoriesPerUser must be at least 1") .max(10000000, "maxMemoriesPerUser cannot exceed 10,000,000") .optional(), maxContentLength: z .number() .int() .min(10, "maxContentLength must be at least 10") .max(1000000, "maxContentLength cannot exceed 1,000,000") .optional(), maxBatchSize: z .number() .int() .min(1, "maxBatchSize must be at least 1") .max(1000, "maxBatchSize cannot exceed 1,000") .optional(), retentionDays: z .number() .int() .min(0, "retentionDays cannot be negative") .max(3650, "retentionDays cannot exceed 10 years (3650 days)") .optional(), }); /** * Zod schema for processing parameters validation */ const processingParametersSchema = z.object({ embeddingDimension: z .number() .int() .refine((val) => [384, 512, 768, 1024, 1536].includes(val), { message: "embeddingDimension must be one of: 384, 512, 768, 1024, 1536", }) .optional(), minSimilarityThreshold: z .number() .min(0, "minSimilarityThreshold must be at least 0") .max(1, "minSimilarityThreshold cannot exceed 1") .optional(), maxParallelStreams: z .number() .int() .min(1, "maxParallelStreams must be at least 1") .max(8, "maxParallelStreams cannot exceed 8") .optional(), reasoningTimeoutMs: z .number() .int() .min(1000, "reasoningTimeoutMs must be at least 1000ms") .max(300000, "reasoningTimeoutMs cannot exceed 300,000ms (5 minutes)") .optional(), defaultRecallLimit: z .number() .int() .min(1, "defaultRecallLimit must be at least 1") .max(1000, "defaultRecallLimit cannot exceed 1,000") .optional(), }); /** * Zod schema for feature flags validation */ const featureFlagsSchema = z.object({ parallelReasoningEnabled: z.boolean().optional(), emotionDetectionEnabled: z.boolean().optional(), biasDetectionEnabled: z.boolean().optional(), memoryConsolidationEnabled: z.boolean().optional(), waypointGraphEnabled: z.boolean().optional(), }); /** * Zod schema for config update request validation * Requirements: 10.2 */ const configUpdateRequestSchema = z.object({ cognitiveMode: z .enum(["balanced", "analytical", "creative", "intuitive", "deliberative"], { errorMap: () => ({ message: "cognitiveMode must be one of: balanced, analytical, creative, intuitive, deliberative", }), }) .optional(), memoryLimits: memoryLimitsSchema.optional(), processingParameters: processingParametersSchema.optional(), featureFlags: featureFlagsSchema.optional(), }); /** * Handler for GET /api/v1/config * Requirements: 10.1 * * Returns current cognitive mode, memory limits, processing parameters, * and enabled feature flags. */ function createConfigGetHandler( _cognitiveCore: CognitiveCore ): (req: Request, res: Response, next: import("express").NextFunction) => void { return asyncHandler(async (req: Request, res: Response): Promise<void> => { const requestId = getRequestId(req); const startTime = Date.now(); // Get current configuration const config = configStore.getConfig(); res.status(200).json(buildSuccessResponse(config, { requestId, startTime })); }); } /** * Handler for PUT /api/v1/config * Requirements: 10.2 * * Updates configuration with partial values and indicates if restart is required. */ function createConfigUpdateHandler( _cognitiveCore: CognitiveCore ): (req: Request, res: Response, next: import("express").NextFunction) => void { return asyncHandler(async (req: Request, res: Response): Promise<void> => { const requestId = getRequestId(req); const startTime = Date.now(); // Validate request body const parseResult = configUpdateRequestSchema.safeParse(req.body); if (!parseResult.success) { throw new ValidationApiError(parseZodErrors(parseResult.error)); } const updates = parseResult.data; // Check if any updates were provided if (Object.keys(updates).length === 0) { throw new ValidationApiError({ request: "At least one configuration field must be provided for update", }); } // Update configuration // Cast to Partial<SystemConfig> since Zod schema validates the structure const result = configStore.updateConfig(updates as Partial<SystemConfig>); // Build response const responseData: ConfigUpdateResponse = { config: result.config, updatedFields: result.updatedFields, restartRequired: result.restartRequired, version: result.version, }; res.status(200).json(buildSuccessResponse(responseData, { requestId, startTime })); }); } /** * Create config routes * * @param cognitiveCore - Shared cognitive core instance * @returns Express router with config endpoints */ export function createConfigRoutes(cognitiveCore: CognitiveCore): Router { const router = Router(); // GET /api/v1/config - Get current configuration // Requirements: 10.1 router.get("/", createConfigGetHandler(cognitiveCore)); // PUT /api/v1/config - Update configuration // Requirements: 10.2 router.put("/", createConfigUpdateHandler(cognitiveCore)); return router; }

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/keyurgolani/ThoughtMcp'

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