import re PATH = '/data/ai/audio/server/dist/services/llm/index.js' with open(PATH, 'r', encoding='utf-8') as f: src = f.read() OLD = """function resolveProviderKey(modelId) { const cfg = config_1.config.models.getModel?.(modelId); if (cfg?.vendor) { // 关键: 用 vendor+apiKey 指纹区分同一个 vendor 的不同 key (例如 minimax Key1 vs Key2) // 这样 fallback 不会把两个独立 key 当成同一个 provider 转一圈就停 if (cfg.apiKey) { return cfg.vendor + ':' + cfg.apiKey.slice(-12); } return cfg.vendor; } // fallback: 从 registry 查找 const registry = (0, provider_registry_1.getLlmRegistry)(); for (const node of registry.listEnabled()) { if (node.provider.hasModel(modelId)) return node.provider.vendor; } return modelId; }""" NEW = """function resolveProviderKey(modelId) { // 关键: 优先用 registry 查找(保留 vendorKey 区分), 而不是 config.models.getModel // 因为 getModel 只返回第一个匹配的, minimax Key1 和 Key2 都提供同名 modelId 时 // 永远只会返回 Key1, 导致 Key2 被误判为 Key1 已经试过, fallback 转一圈就停 const registry = (0, provider_registry_1.getLlmRegistry)(); for (const node of registry.listEnabled()) { if (node.provider.hasModel(modelId)) { // 用 vendorKey + apiKey 指纹, 即使两个 key 同 vendor 也能区分 const apiKey = node.provider._apiKey || ''; if (apiKey) { return node.provider.vendor + ':' + apiKey.slice(-12); } return node.provider.vendor; } } // 最后 fallback: 直接看 models.json const cfg = config_1.config.models.getModel?.(modelId); return cfg?.vendor || modelId; }""" if OLD not in src: print("ERROR: OLD not found") raise SystemExit(1) src = src.replace(OLD, NEW) with open(PATH, 'w', encoding='utf-8') as f: f.write(src) print("OK: resolveProviderKey v2 patched (uses registry first)")