fix_ha_full.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. PATH = '/data/ai/audio/server/dist/services/llm/index.js'
  2. with open(PATH, 'r', encoding='utf-8') as f:
  3. src = f.read()
  4. # 1. 注入 AsyncLocalStorage require (在 require 区块)
  5. need_als = "const { AsyncLocalStorage } = require('async_hooks');" in src or 'AsyncLocalStorage' in src
  6. if not need_als:
  7. # 找到 require('async_hooks') 或在顶部 require 后插入
  8. if "require('async_hooks')" not in src:
  9. # 在文件最顶部 require 区域插入
  10. src = src.replace(
  11. "Object.defineProperty(exports, '__esModule', { value: true });",
  12. "Object.defineProperty(exports, '__esModule', { value: true });\nconst { AsyncLocalStorage: _ALS } = require('async_hooks');\nconst _triedNodesStorage = new _ALS();\nfunction _runWithTriedNodes(triedSet, fn) { return _triedNodesStorage.run(triedSet, fn); }\nfunction _currentTriedNodes() { return _triedNodesStorage.getStore() || new Set(); }",
  13. 1
  14. )
  15. # 2. 改造 findVendorForModel:跳过 tried 节点
  16. OLD_FIND = """function findVendorForModel(modelId) {
  17. const registry = (0, provider_registry_1.getLlmRegistry)();
  18. return registry.listAvailable().find(n => n.provider.hasModel(modelId));
  19. }"""
  20. NEW_FIND = """function findVendorForModel(modelId) {
  21. const registry = (0, provider_registry_1.getLlmRegistry)();
  22. const tried = _currentTriedNodes();
  23. // 关键: 跳过当前 call scope 里已 tried 的 node (避免 minimax Key1/Key2 同 modelId 时永远命中第一个)
  24. for (const n of registry.listAvailable()) {
  25. if (!n.provider.hasModel(modelId)) continue;
  26. const fp = n.provider.vendor + ':' + (n.provider._apiKey || '').slice(-12);
  27. if (tried.has(fp)) continue;
  28. return n;
  29. }
  30. // 全部 tried 过, 返回第一个匹配的 (会触发上层 break)
  31. return registry.listAvailable().find(n => n.provider.hasModel(modelId)) || null;
  32. }"""
  33. if OLD_FIND not in src:
  34. print("ERROR: findVendorForModel pattern not found")
  35. raise SystemExit(1)
  36. src = src.replace(OLD_FIND, NEW_FIND)
  37. # 3. 改造 invokeWithRetry:用 AsyncLocalStorage 包裹 fn 调用
  38. # 找到 "for (let attempt = 0; attempt < exports.HA_RETRY_CONFIG.maxRetriesPerProvider; attempt++)" 这一段
  39. OLD_RETRY_LOOP = """ for (let attempt = 0; attempt < exports.HA_RETRY_CONFIG.maxRetriesPerProvider; attempt++) {
  40. try {
  41. return await fn(cur);
  42. }"""
  43. NEW_RETRY_LOOP = """ for (let attempt = 0; attempt < exports.HA_RETRY_CONFIG.maxRetriesPerProvider; attempt++) {
  44. // 用 AsyncLocalStorage 传递 tried set, 让 findVendorForModel 跳过已试 node
  45. const node = resolveNodeForModel(cur, tried);
  46. if (!node) break;
  47. const fp = node.provider.vendor + ':' + (node.provider._apiKey || '').slice(-12);
  48. tried.set(fp, { model: cur });
  49. try {
  50. const result = await _runWithTriedNodes(new Set(tried.keys()), () => fn(cur));
  51. return result;
  52. }"""
  53. if OLD_RETRY_LOOP not in src:
  54. print("ERROR: retry loop pattern not found")
  55. raise SystemExit(1)
  56. src = src.replace(OLD_RETRY_LOOP, NEW_RETRY_LOOP)
  57. # 4. 加 resolveNodeForModel 函数(如果不存在)
  58. if 'function resolveNodeForModel' not in src:
  59. HELPER = """
  60. /** 根据 modelId + tried set 找到下一个未尝试的 provider node */
  61. function resolveNodeForModel(modelId, tried) {
  62. const registry = (0, provider_registry_1.getLlmRegistry)();
  63. for (const n of registry.listAvailable()) {
  64. if (!n.provider.hasModel(modelId)) continue;
  65. const fp = n.provider.vendor + ':' + (n.provider._apiKey || '').slice(-12);
  66. if (!tried.has(fp)) return n;
  67. }
  68. return null;
  69. }
  70. """
  71. # 插在 resolveProviderKey 之后
  72. src = src.replace("function resolveProviderKey(modelId) {", HELPER + "\nfunction resolveProviderKey(modelId) {", 1)
  73. # 5. 简化 invokeWithRetry 的循环头(移除老的 tried.has(provider) 检查,因为现在用 resolveNodeForModel 决定)
  74. # 找到 "if (tried.has(provider)) break;"
  75. OLD_BREAK = """ const provider = resolveProviderKey(cur);
  76. if (tried.has(provider))
  77. break;"""
  78. NEW_BREAK = """ const provider = resolveProviderKey(cur);
  79. // tried check is now done in resolveNodeForModel below; keep this as a guard
  80. if (tried.has(provider))
  81. continue;"""
  82. if OLD_BREAK not in src:
  83. print("WARN: tried.has(provider) break pattern not found, skipping")
  84. else:
  85. src = src.replace(OLD_BREAK, NEW_BREAK)
  86. with open(PATH, 'w', encoding='utf-8') as f:
  87. f.write(src)
  88. print("OK: full HA chain fix applied")