fix(configure_mykey): 备份命名标准化 + 保留既有 LLM 配置

This commit is contained in:
WindCat committed 2026-05-19 22:59:57 +08:00
1 parent cf129162d5
commit 964f51dacb
2 files changed
+98 -19

No files matched your search

+91 -19
View File
@@ -7,6 +7,7 @@ GenericAgent — 交互式初始化向导 (configure.py)
python configure.py python configure.py
""" """
import ast
import os import os
import sys import sys
import re import re
@@ -1068,7 +1069,7 @@ def generate_mykey(llm_cfgs, platform_configs):
def _write_config_fields(lines, cfg): def _write_config_fields(lines, cfg):
"""写入配置字典的键值对(缩进的 'key': value, 格式)""" """写入配置字典的键值对(缩进的 'key': value, 格式)"""
for key in ['name', 'apikey', 'apibase', 'model', 'api_mode', for key in ['name', 'type', 'apikey', 'apibase', 'model', 'api_mode',
'fake_cc_system_prompt', 'thinking_type', 'thinking_budget_tokens', 'fake_cc_system_prompt', 'thinking_type', 'thinking_budget_tokens',
'reasoning_effort', 'max_tokens', 'max_retries', 'connect_timeout', 'reasoning_effort', 'max_tokens', 'max_retries', 'connect_timeout',
'read_timeout', 'temperature', 'context_win', 'read_timeout', 'temperature', 'context_win',
@@ -1101,7 +1102,7 @@ def _write_platform_value(lines, key, val):
def _parse_existing_mykey(): def _parse_existing_mykey():
"""解析已有 mykey.py,返回 (model_names, platform_infos) """解析已有 mykey.py,返回 (model_names, platform_infos)
llm_cfgs: [{'name': str, 'type': str, ...}] — 模型配置字典列表 model_names: [str] — 模型名列表
platform_infos: [{'id': str, 'vars': [{'key': str, 'val': ...}]}] — 平台信息 platform_infos: [{'id': str, 'vars': [{'key': str, 'val': ...}]}] — 平台信息
解析失败时返回 ([], []) 解析失败时返回 ([], [])
""" """
@@ -1117,21 +1118,81 @@ def _parse_existing_mykey():
if m: if m:
model_names = re.findall(r"'([^']+)'", m.group(1)) model_names = re.findall(r"'([^']+)'", m.group(1))
# 解析平台变量 → 平台 ID # 先收集所有已知平台 env var key → 判断值类型
platform_id_map = { all_env_var_keys = {}
'tg_bot_token': 'telegram', 'qq_app_id': 'qq', platform_env_keys = {} # pid -> [var_key]
'fs_app_id': 'feishu', 'wecom_bot_id': 'wecom', for p in PLATFORMS:
'dingtalk_client_id': 'dingtalk', 'dc_bot_token': 'discord', pid = p['id']
} platform_env_keys.setdefault(pid, [])
for var in p.get('env_vars', []):
vkey = var['key']
all_env_var_keys[vkey] = var
platform_env_keys[pid].append(vkey)
# 逐平台解析所有已知变量
platform_infos = [] platform_infos = []
for var_key, pid in platform_id_map.items(): for pid, env_keys in platform_env_keys.items():
m_var = re.search(rf"^{var_key}\s*=\s*'([^']*)'", content, re.MULTILINE) vars_found = []
if m_var: for vkey in env_keys:
platform_infos.append({'id': pid, 'vars': [{'key': var_key, 'val': m_var.group(1)}]}) var_def = all_env_var_keys[vkey]
val = None
if var_def.get('is_list'):
# 匹配 `xxx = [...]`
m_var = re.search(rf"^{vkey}\s*=\s*(\[[^\]]*\])", content, re.MULTILINE)
if m_var:
try:
val = ast.literal_eval(m_var.group(1))
except (ValueError, SyntaxError):
pass
else:
# 匹配 `xxx = '...'`
m_var = re.search(rf"^{vkey}\s*=\s*'([^']*)'", content, re.MULTILINE)
if m_var:
val = m_var.group(1)
if val is not None:
vars_found.append({'key': vkey, 'val': val})
if vars_found:
platform_infos.append({'id': pid, 'vars': vars_found})
return model_names, platform_infos return model_names, platform_infos
def _parse_existing_llm_cfgs():
"""解析已有 mykey.py,返回完整 LLM 配置字典列表 [{name, apikey, ...}]
解析失败时返回 []
"""
if not os.path.exists(MYKPY_PATH):
return []
with open(MYKPY_PATH, 'r', encoding='utf-8') as f:
content = f.read()
cfgs = []
# 匹配所有 `xxx = { ... }` 顶层字典赋值
# 用简单状态机: 找 `\w+ = {` 然后匹配花括号
pattern = re.compile(r'^(\w+)\s*=\s*\{', re.MULTILINE)
for m in pattern.finditer(content):
brace_start = m.end() - 1 # '{' 的位置
depth = 1
i = brace_start + 1
while i < len(content) and depth > 0:
if content[i] == '{':
depth += 1
elif content[i] == '}':
depth -= 1
i += 1
if depth == 0:
dict_text = content[m.end():i - 1]
try:
d = ast.literal_eval('{' + dict_text + '}')
if isinstance(d, dict) and 'name' in d:
cfgs.append(d)
except (ValueError, SyntaxError):
continue
return cfgs
def _backup_with_name(model_names, platform_ids): def _backup_with_name(model_names, platform_ids):
"""按 mykey+模型名+机器人名 格式备份旧 mykey.py""" """按 mykey+模型名+机器人名 格式备份旧 mykey.py"""
parts = ['mykey'] parts = ['mykey']
@@ -1142,6 +1203,8 @@ def _backup_with_name(model_names, platform_ids):
if pid_clean not in parts: if pid_clean not in parts:
parts.append(pid_clean) parts.append(pid_clean)
safe_name = '_'.join(parts) safe_name = '_'.join(parts)
if safe_name == 'mykey':
safe_name = 'mykey_backup' # 避免和源文件同名
if len(safe_name) > 100: if len(safe_name) > 100:
safe_name = safe_name[:100] safe_name = safe_name[:100]
backup_path = os.path.join(PROJECT_ROOT, f'{safe_name}.py') backup_path = os.path.join(PROJECT_ROOT, f'{safe_name}.py')
@@ -1189,7 +1252,7 @@ def main():
if mode == 'new': if mode == 'new':
backup_path = _backup_with_name(model_names, [p['id'] for p in platform_infos]) backup_path = _backup_with_name(model_names, [p['id'] for p in platform_infos])
print(f" {C['green']}✓ 旧配置已备份至:{C['reset']} {C['dim']}{backup}{C['reset']}") print(f" {C['green']}✓ 旧配置已备份至:{C['reset']} {C['dim']}{backup_path}{C['reset']}")
is_new = True is_new = True
else: else:
is_modify = True is_modify = True
@@ -1212,8 +1275,12 @@ def main():
config_dict = {v['key']: v['val'] for v in pi['vars']} config_dict = {v['key']: v['val'] for v in pi['vars']}
platform_configs.append({'platform': p, 'config': config_dict}) platform_configs.append({'platform': p, 'config': config_dict})
elif scope == 'platform' and model_names: elif scope == 'platform' and model_names:
print(f"\n {C['yellow']}⚠ 只修改平台时若未提供 LLM 配置将无法使用。{C['reset']}") old_cfgs = _parse_existing_llm_cfgs()
cprint(f" 建议两项都重新配置。", 'dim') if old_cfgs:
llm_cfgs = old_cfgs
print(f"\n {C['green']}✓ 已保留现有 LLM 配置: {', '.join(c['name'] for c in old_cfgs)}{C['reset']}")
else:
print(f"\n {C['yellow']}⚠ 保留 LLM 配置失败,将生成空配置。建议两项都重新配置。{C['reset']}")
if not is_modify: if not is_modify:
if is_new: if is_new:
@@ -1245,6 +1312,12 @@ def main():
platform_configs, platform_deps = configure_platforms() platform_configs, platform_deps = configure_platforms()
if ask_yesno("是否继续配置 LLM 模型?", default=True): if ask_yesno("是否继续配置 LLM 模型?", default=True):
llm_cfgs = _do_llm() llm_cfgs = _do_llm()
elif os.path.exists(MYKPY_PATH):
# 新建+仅平台:从备份保留旧 LLM 配置
old_cfgs = _parse_existing_llm_cfgs()
if old_cfgs:
llm_cfgs = old_cfgs
print(f"\n {C['green']}✓ 已保留备份中的 LLM 配置: {', '.join(c['name'] for c in old_cfgs)}{C['reset']}")
# ── 生成 mykey.py ── # ── 生成 mykey.py ──
if not llm_cfgs and not platform_configs: if not llm_cfgs and not platform_configs:
@@ -1253,10 +1326,9 @@ def main():
content = generate_mykey(llm_cfgs, platform_configs) content = generate_mykey(llm_cfgs, platform_configs)
# 备份旧文件 # 备份旧文件(修改模式不备份,直接在原文件修改)
if os.path.exists(MYKPY_PATH): if os.path.exists(MYKPY_PATH) and not is_modify and not is_new:
backup = os.path.join(PROJECT_ROOT, f'mykey.py.bak.{datetime.now().strftime("%Y%m%d_%H%M%S")}') backup = _backup_with_name(model_names, [p['id'] for p in platform_infos])
shutil.copy2(MYKPY_PATH, backup)
print(f"\n {C['green']}✓ 旧配置已备份至:{C['reset']} {C['dim']}{backup}{C['reset']}") print(f"\n {C['green']}✓ 旧配置已备份至:{C['reset']} {C['dim']}{backup}{C['reset']}")
# 写入 # 写入
+7
View File
@@ -65,6 +65,11 @@ COMMANDS = {
"desc": "启动终端图形界面(Textual),适合纯终端环境或 SSH", "desc": "启动终端图形界面(Textual),适合纯终端环境或 SSH",
"cmd": ["python", "{FRONTENDS}/tuiapp.py"], "cmd": ["python", "{FRONTENDS}/tuiapp.py"],
}, },
"tui2": {
"help": "启动终端 TUI v2 (tuiapp_v2)",
"desc": "启动增强版终端图形界面(Textual v2),更多功能更好的体验",
"cmd": ["python", "{FRONTENDS}/tuiapp_v2.py"],
},
"cli": { "cli": {
"help": "启动 CLI 对话 (agentmain)", "help": "启动 CLI 对话 (agentmain)",
"desc": "启动命令行交互对话模式,最轻量的使用方式", "desc": "启动命令行交互对话模式,最轻量的使用方式",
@@ -151,6 +156,8 @@ def main():
ga gui 启动桌面 GUI ga gui 启动桌面 GUI
ga web 启动 Web 增强版 ga web 启动 Web 增强版
ga web --native 启动 Web 基础版(桌面壳) ga web --native 启动 Web 基础版(桌面壳)
ga tui 启动终端 TUI (v1)
ga tui2 启动终端 TUI (v2 增强版)
ga pet 启动桌面宠物 v2 ga pet 启动桌面宠物 v2
ga launch 启动 webview 桌面壳 ga launch 启动 webview 桌面壳
ga list 列出所有命令 ga list 列出所有命令