import { initDebugConfig } from './init-debug'; import type { ModelConfigManager } from './model-config-manager'; import { BOOLEAN_ENV_KEYS, GLOBAL_ENV_KEYS, NUMBER_ENV_KEYS, STRING_ENV_KEYS, } from './types'; import { ALL_ENV_KEYS, MATCH_BY_POSITION, MODEL_ENV_KEYS, type TGlobalConfig, } from './types'; /** * Collect global configs from process.env, overrideAIConfig, etc. * And provider methods to get merged config value */ export class GlobalConfigManager { private override: | { newConfig?: Partial; extendMode?: boolean; } | undefined; private keysHaveBeenRead: Record = {}; private globalModelConfigManager: ModelConfigManager | undefined = undefined; constructor() { initDebugConfig(); } /** * recalculate allEnvConfig every time because process.env can be updated any time */ public getAllEnvConfig() { const envConfig = ALL_ENV_KEYS.reduce( (p, name) => { p[name] = process.env[name]; return p; }, Object.create(null) as Record, ); if (this.override) { const { newConfig, extendMode } = this.override; if (extendMode) { return { ...envConfig, ...newConfig }; } else { return { ...newConfig }; } } else { return envConfig; } } getEnvConfigValue(key: (typeof STRING_ENV_KEYS)[number]) { const allConfig = this.getAllEnvConfig(); if (key === MATCH_BY_POSITION) { throw new Error( 'MATCH_BY_POSITION is discarded, use MIDSCENE_MODEL_FAMILY instead', ); } if (!STRING_ENV_KEYS.includes(key)) { throw new Error(`getEnvConfigValue with key ${key} is not supported.`); } const value = allConfig[key]; this.keysHaveBeenRead[key] = true; if (typeof value === 'string') { return value.trim(); } return value; } /** * read boolean only from process.env */ getEnvConfigInBoolean(key: (typeof BOOLEAN_ENV_KEYS)[number]): boolean { const allConfig = this.getAllEnvConfig(); if (!BOOLEAN_ENV_KEYS.includes(key)) { throw new Error(`getEnvConfigInBoolean with key ${key} is not supported`); } const value = allConfig[key]; this.keysHaveBeenRead[key] = true; if (!value) { return false; } if (/^(true|1)$/i.test(value)) { return true; } if (/^(false|0)$/i.test(value)) { return false; } return !!value.trim(); } /** * Read environment variable value and convert it to number. * Returns undefined if the value is not set or cannot be converted to a valid number. */ getEnvConfigValueAsNumber( key: (typeof STRING_ENV_KEYS)[number] | (typeof NUMBER_ENV_KEYS)[number], ): number | undefined { if ( !STRING_ENV_KEYS.includes(key as never) && !NUMBER_ENV_KEYS.includes(key as never) ) { throw new Error( `getEnvConfigValueAsNumber with key ${key} is not supported.`, ); } const allConfig = this.getAllEnvConfig(); const value = allConfig[key]; this.keysHaveBeenRead[key] = true; if (typeof value !== 'string') { return undefined; } const trimmed = value.trim(); if (!trimmed) { return undefined; } const numValue = Number(trimmed); return Number.isNaN(numValue) ? undefined : numValue; } registerModelConfigManager(globalModelConfigManager: ModelConfigManager) { this.globalModelConfigManager = globalModelConfigManager; } /** * @deprecated use the modelConfig param in Agent constructor instead */ overrideAIConfig( newConfig: Partial< Record< (typeof GLOBAL_ENV_KEYS)[number] | (typeof MODEL_ENV_KEYS)[number], string > >, extendMode = false, // true: merge with global config, false: override global config ) { for (const key in newConfig) { if (![...GLOBAL_ENV_KEYS, ...MODEL_ENV_KEYS].includes(key as never)) { throw new Error(`Failed to override AI config, invalid key: ${key}`); } const value = newConfig[key as keyof typeof newConfig]; if (typeof value !== 'string') { throw new Error( `Failed to override AI config, value for key ${key} must be a string, but got with type ${typeof value}`, ); } if (this.keysHaveBeenRead[key]) { console.warn( `Warning: try to override AI config with key ${key} ,but it has been read.`, ); } } const savedNewConfig = extendMode ? { ...this.override?.newConfig, ...newConfig, } : newConfig; this.override = { newConfig: { ...savedNewConfig, }, extendMode, }; if (!this.globalModelConfigManager) { throw new Error( 'globalModelConfigManager is not registered, which should not happen', ); } this.globalModelConfigManager.clearModelConfigMap(); } }