# config.py import configparser import os import logging # 获取根日志器,确保日志配置已在utils.py中完成 logger = logging.getLogger(__name__) CONFIG_FILE = "config.ini" # 定义默认配置,如果config.ini不存在或缺失,将使用这些值 DEFAULT_CONFIG = { "API": { "GEMINI_API_BASE_URL": "https://generativelanguage.googleapis.com/v1beta", "GEMINI_API_KEY": "YOUR_GEMINI_API_KEY_HERE", # 默认值,强烈建议用户手动修改 "DEFAULT_GEMINI_MODEL": "gemini-pro", "MEMORY_UPDATE_MODEL": "gemini-pro", }, "Application": { "CONTEXT_WINDOW_SIZE": "6", "MEMORY_RETENTION_TURNS": "18", "MAX_SHORT_TERM_EVENTS": "20", "FEATURES_DIR": "data/features", "MEMORIES_DIR": "data/memories", "CHAT_LOGS_DIR": "data/chat_logs", "APP_LOG_FILE_PATH": "logs/app.log", }, "Session": { "CURRENT_ROLE_ID": "default_role", "CURRENT_MEMORY_ID": "default_memory", }, } def load_config(): """ 从 config.ini 文件加载配置。 如果文件不存在或某些配置项缺失,则使用默认值并尝试创建/更新文件。 :return: 包含所有配置的字典。 """ config = configparser.ConfigParser() # 检查config.ini是否存在,如果不存在则创建并写入默认配置 if not os.path.exists(CONFIG_FILE): logger.info(f"config.ini 文件不存在,正在创建默认配置文件:{CONFIG_FILE}") for section, options in DEFAULT_CONFIG.items(): config.add_section(section) for key, value in options.items(): config.set(section, key, value) try: with open(CONFIG_FILE, "w", encoding="utf-8") as f: config.write(f) logger.info("默认 config.ini 文件创建成功。") except IOError as e: logger.error(f"无法写入 config.ini 文件: {e}") # 即使写入失败,也继续使用内存中的默认配置 else: logger.info(f"从 {CONFIG_FILE} 加载配置...") # 读取配置 try: config.read(CONFIG_FILE, encoding="utf-8") except Exception as e: logger.error(f"读取 config.ini 文件时发生错误: {e},将使用默认配置。") # 如果读取失败,清空 configparser 对象,让后续逻辑使用默认值 config = configparser.ConfigParser() # 将配置转换为字典格式,并确保所有默认值都存在 app_config = {} for section, options in DEFAULT_CONFIG.items(): app_config[section] = {} for key, default_value in options.items(): # 使用 get() 方法获取值,如果不存在则使用默认值 # 对于整型或布尔型配置,需要手动转换 value = config.get(section, key, fallback=default_value) # 尝试将特定配置项转换为数字类型 if section == "Application" and key in [ "CONTEXT_WINDOW_SIZE", "MEMORY_RETENTION_TURNS", "MAX_SHORT_TERM_EVENTS", ]: try: app_config[section][key] = int(value) except ValueError: logger.warning( f"配置项 {section}.{key} 的值 '{value}' 不是有效数字,使用默认值 {default_value}。" ) app_config[section][key] = int(default_value) elif section == "API" and key == "GEMINI_API_KEY": # 优先从环境变量读取 GEMINI_API_KEY env_key = os.getenv("GEMINI_API_KEY") if env_key: app_config[section][key] = env_key logger.info("已从环境变量加载 GEMINI_API_KEY。") else: app_config[section][key] = value else: app_config[section][key] = value # 如果存在config.ini但缺少某些默认值,更新它 try: updated = False for section, options in DEFAULT_CONFIG.items(): if not config.has_section(section): config.add_section(section) updated = True for key, default_value in options.items(): if not config.has_option(section, key): config.set(section, key, str(default_value)) # 确保写入的是字符串 updated = True if updated: with open(CONFIG_FILE, "w", encoding="utf-8") as f: config.write(f) logger.info("config.ini 文件已更新,补齐缺失配置项。") except IOError as e: logger.error(f"无法更新 config.ini 文件以补齐缺失配置项: {e}") logger.info("配置加载完成。") return app_config def save_config(new_config): """ 将给定的配置字典保存到 config.ini 文件。 它会读取现有配置,然后只更新 new_config 中提供的部分。 :param new_config: 包含要保存的所有配置的字典。 """ config = configparser.ConfigParser() # 首先读取现有配置,以保留未在 new_config 中提供的项 if os.path.exists(CONFIG_FILE): try: config.read(CONFIG_FILE, encoding="utf-8") except Exception as e: logger.error(f"读取现有 config.ini 文件时发生错误: {e},将只保存新配置。") for section, options in new_config.items(): if not config.has_section(section): config.add_section(section) for key, value in options.items(): # 确保将值转换为字符串,因为 configparser 存储的是字符串 config.set(section, key, str(value)) try: with open(CONFIG_FILE, "w", encoding="utf-8") as f: config.write(f) logger.info(f"配置已成功保存到 {CONFIG_FILE}") except IOError as e: logger.error(f"保存配置到 {CONFIG_FILE} 失败: {e}") # 全局变量,存储当前加载的配置 app_config = load_config() def get_config(): """获取当前加载的配置""" return app_config def set_config(new_config): """更新内存中的配置并保存到文件""" global app_config app_config = new_config save_config(app_config) logger.info("内存配置已更新并保存。")