diff --git a/core/application/config_file_watcher.go b/core/application/config_file_watcher.go index 8eb26355d132..c985c0d8012d 100644 --- a/core/application/config_file_watcher.go +++ b/core/application/config_file_watcher.go @@ -189,10 +189,11 @@ func readRuntimeSettingsJson(startupAppConfig config.ApplicationConfig) fileHand handler := func(fileContent []byte, appConfig *config.ApplicationConfig) error { xlog.Debug("processing runtime_settings.json") - // Determine if settings came from env vars by comparing with startup config - // startupAppConfig contains the original values set from env vars at startup. - // If current values match startup values, they came from env vars (or defaults). - // We apply file settings only if current values match startup values (meaning not from env vars). + // Determine if settings were changed via API after startup. + // startupAppConfig contains values set from env vars at startup. + // If current value matches startup value, it was NOT changed via API, + // so it's safe to apply file settings. If it differs, it was changed + // via API and should NOT be overwritten by the file. envWatchdogIdle := appConfig.WatchDogIdle == startupAppConfig.WatchDogIdle envWatchdogBusy := appConfig.WatchDogBusy == startupAppConfig.WatchDogBusy envWatchdogIdleTimeout := appConfig.WatchDogIdleTimeout == startupAppConfig.WatchDogIdleTimeout @@ -219,6 +220,16 @@ func readRuntimeSettingsJson(startupAppConfig config.ApplicationConfig) fileHand envForceEvictionWhenBusy := appConfig.ForceEvictionWhenBusy == startupAppConfig.ForceEvictionWhenBusy envLRUEvictionMaxRetries := appConfig.LRUEvictionMaxRetries == startupAppConfig.LRUEvictionMaxRetries envLRUEvictionRetryInterval := appConfig.LRUEvictionRetryInterval == startupAppConfig.LRUEvictionRetryInterval + envAgentPoolEnabled := appConfig.AgentPool.Enabled == startupAppConfig.AgentPool.Enabled + envAgentPoolDefaultModel := appConfig.AgentPool.DefaultModel == startupAppConfig.AgentPool.DefaultModel + envAgentPoolEmbeddingModel := appConfig.AgentPool.EmbeddingModel == startupAppConfig.AgentPool.EmbeddingModel + envAgentPoolMaxChunkingSize := appConfig.AgentPool.MaxChunkingSize == startupAppConfig.AgentPool.MaxChunkingSize + envAgentPoolChunkOverlap := appConfig.AgentPool.ChunkOverlap == startupAppConfig.AgentPool.ChunkOverlap + envAgentPoolEnableLogs := appConfig.AgentPool.EnableLogs == startupAppConfig.AgentPool.EnableLogs + envAgentPoolCollectionDBPath := appConfig.AgentPool.CollectionDBPath == startupAppConfig.AgentPool.CollectionDBPath + envAgentPoolVectorEngine := appConfig.AgentPool.VectorEngine == startupAppConfig.AgentPool.VectorEngine + envAgentPoolDatabaseURL := appConfig.AgentPool.DatabaseURL == startupAppConfig.AgentPool.DatabaseURL + envAgentPoolAgentHubURL := appConfig.AgentPool.AgentHubURL == startupAppConfig.AgentPool.AgentHubURL if len(fileContent) > 0 { var settings config.RuntimeSettings @@ -227,20 +238,20 @@ func readRuntimeSettingsJson(startupAppConfig config.ApplicationConfig) fileHand return err } - // Apply file settings only if they don't match startup values (i.e., not from env vars) - if settings.WatchdogIdleEnabled != nil && !envWatchdogIdle { + // Apply file settings only if current values match startup values (i.e., not changed via API) + if settings.WatchdogIdleEnabled != nil && envWatchdogIdle { appConfig.WatchDogIdle = *settings.WatchdogIdleEnabled if appConfig.WatchDogIdle { appConfig.WatchDog = true } } - if settings.WatchdogBusyEnabled != nil && !envWatchdogBusy { + if settings.WatchdogBusyEnabled != nil && envWatchdogBusy { appConfig.WatchDogBusy = *settings.WatchdogBusyEnabled if appConfig.WatchDogBusy { appConfig.WatchDog = true } } - if settings.WatchdogIdleTimeout != nil && !envWatchdogIdleTimeout { + if settings.WatchdogIdleTimeout != nil && envWatchdogIdleTimeout { dur, err := time.ParseDuration(*settings.WatchdogIdleTimeout) if err == nil { appConfig.WatchDogIdleTimeout = dur @@ -248,7 +259,7 @@ func readRuntimeSettingsJson(startupAppConfig config.ApplicationConfig) fileHand xlog.Warn("invalid watchdog idle timeout in runtime_settings.json", "error", err, "timeout", *settings.WatchdogIdleTimeout) } } - if settings.WatchdogBusyTimeout != nil && !envWatchdogBusyTimeout { + if settings.WatchdogBusyTimeout != nil && envWatchdogBusyTimeout { dur, err := time.ParseDuration(*settings.WatchdogBusyTimeout) if err == nil { appConfig.WatchDogBusyTimeout = dur @@ -257,11 +268,11 @@ func readRuntimeSettingsJson(startupAppConfig config.ApplicationConfig) fileHand } } // Handle MaxActiveBackends (new) and SingleBackend (deprecated) - if settings.MaxActiveBackends != nil && !envMaxActiveBackends { + if settings.MaxActiveBackends != nil && envMaxActiveBackends { appConfig.MaxActiveBackends = *settings.MaxActiveBackends // For backward compatibility, also set SingleBackend if MaxActiveBackends == 1 appConfig.SingleBackend = (*settings.MaxActiveBackends == 1) - } else if settings.SingleBackend != nil && !envSingleBackend { + } else if settings.SingleBackend != nil && envSingleBackend { // Legacy: SingleBackend maps to MaxActiveBackends = 1 appConfig.SingleBackend = *settings.SingleBackend if *settings.SingleBackend { @@ -270,22 +281,22 @@ func readRuntimeSettingsJson(startupAppConfig config.ApplicationConfig) fileHand appConfig.MaxActiveBackends = 0 } } - if settings.MemoryReclaimerEnabled != nil && !envMemoryReclaimerEnabled { + if settings.MemoryReclaimerEnabled != nil && envMemoryReclaimerEnabled { appConfig.MemoryReclaimerEnabled = *settings.MemoryReclaimerEnabled if appConfig.MemoryReclaimerEnabled { appConfig.WatchDog = true // Memory reclaimer requires watchdog } } - if settings.MemoryReclaimerThreshold != nil && !envMemoryReclaimerThreshold { + if settings.MemoryReclaimerThreshold != nil && envMemoryReclaimerThreshold { appConfig.MemoryReclaimerThreshold = *settings.MemoryReclaimerThreshold } - if settings.ForceEvictionWhenBusy != nil && !envForceEvictionWhenBusy { + if settings.ForceEvictionWhenBusy != nil && envForceEvictionWhenBusy { appConfig.ForceEvictionWhenBusy = *settings.ForceEvictionWhenBusy } - if settings.LRUEvictionMaxRetries != nil && !envLRUEvictionMaxRetries { + if settings.LRUEvictionMaxRetries != nil && envLRUEvictionMaxRetries { appConfig.LRUEvictionMaxRetries = *settings.LRUEvictionMaxRetries } - if settings.LRUEvictionRetryInterval != nil && !envLRUEvictionRetryInterval { + if settings.LRUEvictionRetryInterval != nil && envLRUEvictionRetryInterval { dur, err := time.ParseDuration(*settings.LRUEvictionRetryInterval) if err == nil { appConfig.LRUEvictionRetryInterval = dur @@ -293,46 +304,46 @@ func readRuntimeSettingsJson(startupAppConfig config.ApplicationConfig) fileHand xlog.Warn("invalid LRU eviction retry interval in runtime_settings.json", "error", err, "interval", *settings.LRUEvictionRetryInterval) } } - if settings.Threads != nil && !envThreads { + if settings.Threads != nil && envThreads { appConfig.Threads = *settings.Threads } - if settings.ContextSize != nil && !envContextSize { + if settings.ContextSize != nil && envContextSize { appConfig.ContextSize = *settings.ContextSize } - if settings.F16 != nil && !envF16 { + if settings.F16 != nil && envF16 { appConfig.F16 = *settings.F16 } - if settings.Debug != nil && !envDebug { + if settings.Debug != nil && envDebug { appConfig.Debug = *settings.Debug } - if settings.CORS != nil && !envCORS { + if settings.CORS != nil && envCORS { appConfig.CORS = *settings.CORS } - if settings.CSRF != nil && !envCSRF { + if settings.CSRF != nil && envCSRF { appConfig.DisableCSRF = *settings.CSRF } - if settings.CORSAllowOrigins != nil && !envCORSAllowOrigins { + if settings.CORSAllowOrigins != nil && envCORSAllowOrigins { appConfig.CORSAllowOrigins = *settings.CORSAllowOrigins } - if settings.P2PToken != nil && !envP2PToken { + if settings.P2PToken != nil && envP2PToken { appConfig.P2PToken = *settings.P2PToken } - if settings.P2PNetworkID != nil && !envP2PNetworkID { + if settings.P2PNetworkID != nil && envP2PNetworkID { appConfig.P2PNetworkID = *settings.P2PNetworkID } - if settings.Federated != nil && !envFederated { + if settings.Federated != nil && envFederated { appConfig.Federated = *settings.Federated } - if settings.Galleries != nil && !envGalleries { + if settings.Galleries != nil && envGalleries { appConfig.Galleries = *settings.Galleries } - if settings.BackendGalleries != nil && !envBackendGalleries { + if settings.BackendGalleries != nil && envBackendGalleries { appConfig.BackendGalleries = *settings.BackendGalleries } - if settings.AutoloadGalleries != nil && !envAutoloadGalleries { + if settings.AutoloadGalleries != nil && envAutoloadGalleries { appConfig.AutoloadGalleries = *settings.AutoloadGalleries } - if settings.AutoloadBackendGalleries != nil && !envAutoloadBackendGalleries { + if settings.AutoloadBackendGalleries != nil && envAutoloadBackendGalleries { appConfig.AutoloadBackendGalleries = *settings.AutoloadBackendGalleries } if settings.ApiKeys != nil { @@ -344,12 +355,42 @@ func readRuntimeSettingsJson(startupAppConfig config.ApplicationConfig) fileHand // Replace all runtime keys with what's in runtime_settings.json appConfig.ApiKeys = append(envKeys, runtimeKeys...) } - if settings.AgentJobRetentionDays != nil && !envAgentJobRetentionDays { + if settings.AgentJobRetentionDays != nil && envAgentJobRetentionDays { appConfig.AgentJobRetentionDays = *settings.AgentJobRetentionDays } + if settings.AgentPoolEnabled != nil && envAgentPoolEnabled { + appConfig.AgentPool.Enabled = *settings.AgentPoolEnabled + } + if settings.AgentPoolDefaultModel != nil && envAgentPoolDefaultModel { + appConfig.AgentPool.DefaultModel = *settings.AgentPoolDefaultModel + } + if settings.AgentPoolEmbeddingModel != nil && envAgentPoolEmbeddingModel { + appConfig.AgentPool.EmbeddingModel = *settings.AgentPoolEmbeddingModel + } + if settings.AgentPoolMaxChunkingSize != nil && envAgentPoolMaxChunkingSize { + appConfig.AgentPool.MaxChunkingSize = *settings.AgentPoolMaxChunkingSize + } + if settings.AgentPoolChunkOverlap != nil && envAgentPoolChunkOverlap { + appConfig.AgentPool.ChunkOverlap = *settings.AgentPoolChunkOverlap + } + if settings.AgentPoolEnableLogs != nil && envAgentPoolEnableLogs { + appConfig.AgentPool.EnableLogs = *settings.AgentPoolEnableLogs + } + if settings.AgentPoolCollectionDBPath != nil && envAgentPoolCollectionDBPath { + appConfig.AgentPool.CollectionDBPath = *settings.AgentPoolCollectionDBPath + } + if settings.AgentPoolVectorEngine != nil && envAgentPoolVectorEngine { + appConfig.AgentPool.VectorEngine = *settings.AgentPoolVectorEngine + } + if settings.AgentPoolDatabaseURL != nil && envAgentPoolDatabaseURL { + appConfig.AgentPool.DatabaseURL = *settings.AgentPoolDatabaseURL + } + if settings.AgentPoolAgentHubURL != nil && envAgentPoolAgentHubURL { + appConfig.AgentPool.AgentHubURL = *settings.AgentPoolAgentHubURL + } // If watchdog is enabled via file but not via env, ensure WatchDog flag is set - if !envWatchdogIdle && !envWatchdogBusy { + if envWatchdogIdle && envWatchdogBusy { if settings.WatchdogEnabled != nil && *settings.WatchdogEnabled { appConfig.WatchDog = true } diff --git a/core/application/startup.go b/core/application/startup.go index 728c3c97221e..e0254da498c9 100644 --- a/core/application/startup.go +++ b/core/application/startup.go @@ -263,7 +263,7 @@ func New(opts ...config.AppOption) (*Application, error) { // This applies file settings with env var precedence (env vars take priority) // Note: startupConfigCopy was already created above, so it has the original env var values if options.DynamicConfigsDir != "" { - loadRuntimeSettingsFromFile(options) + loadRuntimeSettingsFromFile(options, &startupConfigCopy) } application.ModelLoader().SetBackendLoggingEnabled(options.EnableBackendLogging) @@ -336,21 +336,9 @@ func startWatcher(options *config.ApplicationConfig) { // loadRuntimeSettingsFromFile loads settings from runtime_settings.json with env var precedence // This function is called at startup, before env vars are applied via AppOptions. // Since env vars are applied via AppOptions in run.go, we need to check if they're set. -// We do this by checking if the current options values differ from defaults, which would -// indicate they were set from env vars. However, a simpler approach is to just apply -// file settings here, and let the AppOptions (which are applied after this) override them. -// But actually, this is called AFTER AppOptions are applied in New(), so we need to check env vars. -// The cleanest solution: Store original values before applying file, or check if values match -// what would be set from env vars. For now, we'll apply file settings and they'll be -// overridden by AppOptions if env vars were set (but AppOptions are already applied). -// Actually, this function is called in New() before AppOptions are fully processed for watchdog. -// Let's check the call order: New() -> loadRuntimeSettingsFromFile() -> initializeWatchdog() -// But AppOptions are applied in NewApplicationConfig() which is called first. -// So at this point, options already has values from env vars. We should compare against -// defaults to see if env vars were set. But we don't have defaults stored. -// Simplest: Just apply file settings. If env vars were set, they're already in options. -// The file watcher handler will handle runtime changes properly by comparing with startupAppConfig. -func loadRuntimeSettingsFromFile(options *config.ApplicationConfig) { +// We do this by comparing current values with startupConfig (values before file loading). +// If current value matches startup value, it wasn't set from env var, so we can apply file. +func loadRuntimeSettingsFromFile(options *config.ApplicationConfig, startupConfig *config.ApplicationConfig) { settingsFile := filepath.Join(options.DynamicConfigsDir, "runtime_settings.json") fileContent, err := os.ReadFile(settingsFile) if err != nil { @@ -370,16 +358,14 @@ func loadRuntimeSettingsFromFile(options *config.ApplicationConfig) { } // At this point, options already has values from env vars (via AppOptions in run.go). - // To avoid env var duplication, we determine if env vars were set by checking if - // current values differ from defaults. Defaults are: false for bools, 0 for durations. - // If current value is at default, it likely wasn't set from env var, so we can apply file. - // If current value is non-default, it was likely set from env var, so we preserve it. - // Note: This means env vars explicitly setting to false/0 won't be distinguishable from defaults, - // but that's an acceptable limitation to avoid env var duplication. + // startupConfig is a snapshot taken right after AppOptions were applied, so at this point + // options == startupConfig for all fields. The comparison ensures file settings are applied + // consistently with the file watcher logic in config_file_watcher.go. + // Note: At startup, file settings always override env vars. After startup, the file watcher + // only applies changes when values haven't been modified via API. if settings.WatchdogIdleEnabled != nil { - // Only apply if current value is default (false), suggesting it wasn't set from env var - if !options.WatchDogIdle { + if options.WatchDogIdle == startupConfig.WatchDogIdle { options.WatchDogIdle = *settings.WatchdogIdleEnabled if options.WatchDogIdle { options.WatchDog = true @@ -387,7 +373,7 @@ func loadRuntimeSettingsFromFile(options *config.ApplicationConfig) { } } if settings.WatchdogBusyEnabled != nil { - if !options.WatchDogBusy { + if options.WatchDogBusy == startupConfig.WatchDogBusy { options.WatchDogBusy = *settings.WatchdogBusyEnabled if options.WatchDogBusy { options.WatchDog = true @@ -395,8 +381,7 @@ func loadRuntimeSettingsFromFile(options *config.ApplicationConfig) { } } if settings.WatchdogIdleTimeout != nil { - // Only apply if current value is default (0), suggesting it wasn't set from env var - if options.WatchDogIdleTimeout == 0 { + if options.WatchDogIdleTimeout == startupConfig.WatchDogIdleTimeout { dur, err := time.ParseDuration(*settings.WatchdogIdleTimeout) if err == nil { options.WatchDogIdleTimeout = dur @@ -406,7 +391,7 @@ func loadRuntimeSettingsFromFile(options *config.ApplicationConfig) { } } if settings.WatchdogBusyTimeout != nil { - if options.WatchDogBusyTimeout == 0 { + if options.WatchDogBusyTimeout == startupConfig.WatchDogBusyTimeout { dur, err := time.ParseDuration(*settings.WatchdogBusyTimeout) if err == nil { options.WatchDogBusyTimeout = dur @@ -416,27 +401,24 @@ func loadRuntimeSettingsFromFile(options *config.ApplicationConfig) { } } if settings.WatchdogInterval != nil { - if options.WatchDogInterval == 0 { + if options.WatchDogInterval == startupConfig.WatchDogInterval { dur, err := time.ParseDuration(*settings.WatchdogInterval) if err == nil { options.WatchDogInterval = dur } else { xlog.Warn("invalid watchdog interval in runtime_settings.json", "error", err, "interval", *settings.WatchdogInterval) - options.WatchDogInterval = model.DefaultWatchdogInterval } } } // Handle MaxActiveBackends (new) and SingleBackend (deprecated) if settings.MaxActiveBackends != nil { - // Only apply if current value is default (0), suggesting it wasn't set from env var - if options.MaxActiveBackends == 0 { + if options.MaxActiveBackends == startupConfig.MaxActiveBackends { options.MaxActiveBackends = *settings.MaxActiveBackends // For backward compatibility, also set SingleBackend if MaxActiveBackends == 1 options.SingleBackend = (*settings.MaxActiveBackends == 1) } } else if settings.SingleBackend != nil { - // Legacy: SingleBackend maps to MaxActiveBackends = 1 - if !options.SingleBackend { + if options.SingleBackend == startupConfig.SingleBackend { options.SingleBackend = *settings.SingleBackend if *settings.SingleBackend { options.MaxActiveBackends = 1 @@ -444,8 +426,7 @@ func loadRuntimeSettingsFromFile(options *config.ApplicationConfig) { } } if settings.MemoryReclaimerEnabled != nil { - // Only apply if current value is default (false), suggesting it wasn't set from env var - if !options.MemoryReclaimerEnabled { + if options.MemoryReclaimerEnabled == startupConfig.MemoryReclaimerEnabled { options.MemoryReclaimerEnabled = *settings.MemoryReclaimerEnabled if options.MemoryReclaimerEnabled { options.WatchDog = true // Memory reclaimer requires watchdog @@ -453,26 +434,22 @@ func loadRuntimeSettingsFromFile(options *config.ApplicationConfig) { } } if settings.MemoryReclaimerThreshold != nil { - // Only apply if current value is default (0), suggesting it wasn't set from env var - if options.MemoryReclaimerThreshold == 0 { + if options.MemoryReclaimerThreshold == startupConfig.MemoryReclaimerThreshold { options.MemoryReclaimerThreshold = *settings.MemoryReclaimerThreshold } } if settings.ForceEvictionWhenBusy != nil { - // Only apply if current value is default (false), suggesting it wasn't set from env var - if !options.ForceEvictionWhenBusy { + if options.ForceEvictionWhenBusy == startupConfig.ForceEvictionWhenBusy { options.ForceEvictionWhenBusy = *settings.ForceEvictionWhenBusy } } if settings.LRUEvictionMaxRetries != nil { - // Only apply if current value is default (30), suggesting it wasn't set from env var - if options.LRUEvictionMaxRetries == 0 { + if options.LRUEvictionMaxRetries == startupConfig.LRUEvictionMaxRetries { options.LRUEvictionMaxRetries = *settings.LRUEvictionMaxRetries } } if settings.LRUEvictionRetryInterval != nil { - // Only apply if current value is default (1s), suggesting it wasn't set from env var - if options.LRUEvictionRetryInterval == 0 { + if options.LRUEvictionRetryInterval == startupConfig.LRUEvictionRetryInterval { dur, err := time.ParseDuration(*settings.LRUEvictionRetryInterval) if err == nil { options.LRUEvictionRetryInterval = dur @@ -482,8 +459,7 @@ func loadRuntimeSettingsFromFile(options *config.ApplicationConfig) { } } if settings.AgentJobRetentionDays != nil { - // Only apply if current value is default (0), suggesting it wasn't set from env var - if options.AgentJobRetentionDays == 0 { + if options.AgentJobRetentionDays == startupConfig.AgentJobRetentionDays { options.AgentJobRetentionDays = *settings.AgentJobRetentionDays } } @@ -495,39 +471,91 @@ func loadRuntimeSettingsFromFile(options *config.ApplicationConfig) { // P2P settings if settings.P2PToken != nil { - if options.P2PToken == "" { + if options.P2PToken == startupConfig.P2PToken { options.P2PToken = *settings.P2PToken } } if settings.P2PNetworkID != nil { - if options.P2PNetworkID == "" { + if options.P2PNetworkID == startupConfig.P2PNetworkID { options.P2PNetworkID = *settings.P2PNetworkID } } if settings.Federated != nil { - if !options.Federated { + if options.Federated == startupConfig.Federated { options.Federated = *settings.Federated } } if settings.EnableBackendLogging != nil { - if !options.EnableBackendLogging { + if options.EnableBackendLogging == startupConfig.EnableBackendLogging { options.EnableBackendLogging = *settings.EnableBackendLogging } } // Tracing settings if settings.EnableTracing != nil { - if !options.EnableTracing { + if options.EnableTracing == startupConfig.EnableTracing { options.EnableTracing = *settings.EnableTracing } } if settings.TracingMaxItems != nil { - if options.TracingMaxItems == 0 { + if options.TracingMaxItems == startupConfig.TracingMaxItems { options.TracingMaxItems = *settings.TracingMaxItems } } + // Agent Pool settings + if settings.AgentPoolEnabled != nil { + if options.AgentPool.Enabled == startupConfig.AgentPool.Enabled { + options.AgentPool.Enabled = *settings.AgentPoolEnabled + } + } + if settings.AgentPoolDefaultModel != nil { + if options.AgentPool.DefaultModel == startupConfig.AgentPool.DefaultModel { + options.AgentPool.DefaultModel = *settings.AgentPoolDefaultModel + } + } + if settings.AgentPoolEmbeddingModel != nil { + if options.AgentPool.EmbeddingModel == startupConfig.AgentPool.EmbeddingModel { + options.AgentPool.EmbeddingModel = *settings.AgentPoolEmbeddingModel + } + } + if settings.AgentPoolMaxChunkingSize != nil { + if options.AgentPool.MaxChunkingSize == startupConfig.AgentPool.MaxChunkingSize { + options.AgentPool.MaxChunkingSize = *settings.AgentPoolMaxChunkingSize + } + } + if settings.AgentPoolChunkOverlap != nil { + if options.AgentPool.ChunkOverlap == startupConfig.AgentPool.ChunkOverlap { + options.AgentPool.ChunkOverlap = *settings.AgentPoolChunkOverlap + } + } + if settings.AgentPoolEnableLogs != nil { + if options.AgentPool.EnableLogs == startupConfig.AgentPool.EnableLogs { + options.AgentPool.EnableLogs = *settings.AgentPoolEnableLogs + } + } + if settings.AgentPoolCollectionDBPath != nil { + if options.AgentPool.CollectionDBPath == startupConfig.AgentPool.CollectionDBPath { + options.AgentPool.CollectionDBPath = *settings.AgentPoolCollectionDBPath + } + } + if settings.AgentPoolVectorEngine != nil { + if options.AgentPool.VectorEngine == startupConfig.AgentPool.VectorEngine { + options.AgentPool.VectorEngine = *settings.AgentPoolVectorEngine + } + } + if settings.AgentPoolDatabaseURL != nil { + if options.AgentPool.DatabaseURL == startupConfig.AgentPool.DatabaseURL { + options.AgentPool.DatabaseURL = *settings.AgentPoolDatabaseURL + } + } + if settings.AgentPoolAgentHubURL != nil { + if options.AgentPool.AgentHubURL == startupConfig.AgentPool.AgentHubURL { + options.AgentPool.AgentHubURL = *settings.AgentPoolAgentHubURL + } + } + xlog.Debug("Runtime settings loaded from runtime_settings.json") } diff --git a/core/config/application_config.go b/core/config/application_config.go index 8d55f83a61cf..5052f4c3efff 100644 --- a/core/config/application_config.go +++ b/core/config/application_config.go @@ -898,6 +898,9 @@ func (o *ApplicationConfig) ToRuntimeSettings() RuntimeSettings { agentPoolChunkOverlap := o.AgentPool.ChunkOverlap agentPoolEnableLogs := o.AgentPool.EnableLogs agentPoolCollectionDBPath := o.AgentPool.CollectionDBPath + agentPoolVectorEngine := o.AgentPool.VectorEngine + agentPoolDatabaseURL := o.AgentPool.DatabaseURL + agentPoolAgentHubURL := o.AgentPool.AgentHubURL return RuntimeSettings{ WatchdogEnabled: &watchdogEnabled, @@ -940,6 +943,9 @@ func (o *ApplicationConfig) ToRuntimeSettings() RuntimeSettings { AgentPoolChunkOverlap: &agentPoolChunkOverlap, AgentPoolEnableLogs: &agentPoolEnableLogs, AgentPoolCollectionDBPath: &agentPoolCollectionDBPath, + AgentPoolVectorEngine: &agentPoolVectorEngine, + AgentPoolDatabaseURL: &agentPoolDatabaseURL, + AgentPoolAgentHubURL: &agentPoolAgentHubURL, } } @@ -1118,6 +1124,18 @@ func (o *ApplicationConfig) ApplyRuntimeSettings(settings *RuntimeSettings) (req o.AgentPool.CollectionDBPath = *settings.AgentPoolCollectionDBPath requireRestart = true } + if settings.AgentPoolVectorEngine != nil { + o.AgentPool.VectorEngine = *settings.AgentPoolVectorEngine + requireRestart = true + } + if settings.AgentPoolDatabaseURL != nil { + o.AgentPool.DatabaseURL = *settings.AgentPoolDatabaseURL + requireRestart = true + } + if settings.AgentPoolAgentHubURL != nil { + o.AgentPool.AgentHubURL = *settings.AgentPoolAgentHubURL + requireRestart = true + } // Note: ApiKeys requires special handling (merging with startup keys) - handled in caller diff --git a/core/config/runtime_settings.go b/core/config/runtime_settings.go index 6a4117f06490..812204f7aa87 100644 --- a/core/config/runtime_settings.go +++ b/core/config/runtime_settings.go @@ -71,4 +71,7 @@ type RuntimeSettings struct { AgentPoolChunkOverlap *int `json:"agent_pool_chunk_overlap,omitempty"` AgentPoolEnableLogs *bool `json:"agent_pool_enable_logs,omitempty"` AgentPoolCollectionDBPath *string `json:"agent_pool_collection_db_path,omitempty"` + AgentPoolVectorEngine *string `json:"agent_pool_vector_engine,omitempty"` + AgentPoolDatabaseURL *string `json:"agent_pool_database_url,omitempty"` + AgentPoolAgentHubURL *string `json:"agent_pool_agent_hub_url,omitempty"` } diff --git a/core/http/react-ui/src/pages/Settings.jsx b/core/http/react-ui/src/pages/Settings.jsx index 36f3bfaf3a04..3cda12527dc3 100644 --- a/core/http/react-ui/src/pages/Settings.jsx +++ b/core/http/react-ui/src/pages/Settings.jsx @@ -451,6 +451,15 @@ export default function Settings() { update('agent_pool_collection_db_path', e.target.value)} placeholder="Leave empty for default" /> + + update('agent_pool_vector_engine', e.target.value)} placeholder="chromem" /> + + + update('agent_pool_database_url', e.target.value)} placeholder="Leave empty for default" /> + + + update('agent_pool_agent_hub_url', e.target.value)} placeholder="https://agenthub.localai.io" /> +