079project 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/main_Serve.cjs CHANGED
@@ -31,11 +31,28 @@ protobuf.load(runtimeProtoPath, (err, root) => {
31
31
  if (err) throw err;
32
32
  RuntimeMessage = root.lookupType('Runtime');
33
33
  });
34
- // 全局配置
34
+ function parseArgs(argv) {
35
+ const out = {};
36
+ for (let i = 0; i < argv.length; i++) {
37
+ const a = argv[i];
38
+ if (a && a.startsWith('--')) {
39
+ const k = a.slice(2);
40
+ const v = argv[i + 1] && !argv[i + 1].startsWith('--') ? argv[++i] : true;
41
+ out[k] = v;
42
+ }
43
+ }
44
+ return out;
45
+ }
46
+ const __args = parseArgs(process.argv.slice(2));
35
47
  global.config = {
36
- masterPortOfSql: 3125,
37
- masterPortOfMain: process.argv[2],
38
- emitExitport: process.argv[3] || 8641
48
+ masterPortOfSql: 3125,
49
+ masterPortOfMain: process.argv[2],
50
+ emitExitport: process.argv[3] || 8641,
51
+ groupId: Number(__args['group-id'] || -1),
52
+ forwarderPort: Number(__args['forwarder-port'] || 0),
53
+ studyPort: Number(__args['study-port'] || 0),
54
+ peerServePorts: String(__args['peers'] || '').split(',').map(s => Number(s)).filter(n => Number.isFinite(n) && n > 0),
55
+ isStudy: !!__args['study']
39
56
  };
40
57
  const modelDefaults = {
41
58
  decayFactor: 0.5,
@@ -2280,6 +2297,78 @@ async function boot() {
2280
2297
  return model;
2281
2298
  }
2282
2299
 
2300
+
2301
+ // 激活副本:从快照加载 ctrlB/ctrlC 并加入轮询
2302
+ let activeControllers = []; // 轮询数组
2303
+ let rrIdx = 0;
2304
+ function getNextController() {
2305
+ if (!activeControllers.length) return global.ctrlA;
2306
+ const c = activeControllers[rrIdx % activeControllers.length];
2307
+ rrIdx = (rrIdx + 1) % activeControllers.length;
2308
+ return c;
2309
+ }
2310
+ async function activateReplicasIfNeeded() {
2311
+ if (global.config.isStudy) return; // study 进程不启用副本扩容
2312
+ if (global.ctrlB && global.ctrlC) return; // 已激活
2313
+ try {
2314
+ const loadReplicaByName = async (name) => {
2315
+ const ctrl = new controller();
2316
+ // 为副本单独挂载快照管理器
2317
+ ctrl.snapshotManager = new SnapshotManager(ctrl.runtime);
2318
+ // 在 A 的快照目录中寻找对应 id
2319
+ const smA = global.ctrlA.snapshotManager;
2320
+ smA.loadSnapshotList();
2321
+ const snap = smA.snapshotList.find(s => s.name === name || s.id.endsWith(`_${name}`));
2322
+ if (!snap) {
2323
+ console.warn(`[REPLICA] 未找到快照: ${name},尝试现生成...`);
2324
+ await smA.createSnapshot(name);
2325
+ }
2326
+ const snap2 = smA.snapshotList.find(s => s.name === name || s.id.endsWith(`_${name}`));
2327
+ if (snap2) {
2328
+ // 直接读取该文件内容并恢复到 ctrl.runtime
2329
+ const dataStr = fs.readFileSync(snap2.path, 'utf-8');
2330
+ const data = JSON.parse(dataStr);
2331
+ // 简单恢复(与 SnapshotManager.restoreSnapshot 类似)
2332
+ ctrl.runtime.graph = new GraphDB();
2333
+ ctrl.runtime.wordGraph = new GraphDB();
2334
+ ctrl.runtime.kvm = new KVM();
2335
+ ctrl.runtime.wordAccessLog = new Map(data.wordAccessLog || []);
2336
+ if (data.memes) for (const p of data.memes) ctrl.runtime.graph.addPoint(p.pointID, p.connect);
2337
+ if (data.wordGraph) for (const p of data.wordGraph) ctrl.runtime.wordGraph.addPoint(p.pointID, p.connect);
2338
+ if (data.kvm) for (const [k, v] of data.kvm) ctrl.runtime.kvm.set(k, v);
2339
+ if (data.vocab) { ctrl.runtime.vocabManager.vocab = data.vocab; ctrl.runtime.vocabManager.updateMappings(); }
2340
+ return ctrl;
2341
+ }
2342
+ return null;
2343
+ };
2344
+
2345
+ if (!global.ctrlB) global.ctrlB = await loadReplicaByName('replica_B');
2346
+ if (!global.ctrlC) global.ctrlC = await loadReplicaByName('replica_C');
2347
+ activeControllers = [global.ctrlA, ...(global.ctrlB ? [global.ctrlB] : []), ...(global.ctrlC ? [global.ctrlC] : [])];
2348
+ console.log(`[REPLICA] 已激活副本: ${activeControllers.length} 个控制器`);
2349
+ } catch (e) {
2350
+ console.warn('[REPLICA] 激活失败:', e.message);
2351
+ }
2352
+ }
2353
+ // 对端探测/反触发:同组任一对端端口死亡则激活副本
2354
+ function installPeerFailoverMonitor() {
2355
+ const peers = global.config.peerServePorts || [];
2356
+ if (!Array.isArray(peers) || peers.length === 0) return;
2357
+ const axiosInst = axios.create({ timeout: 1500 });
2358
+ setInterval(async () => {
2359
+ try {
2360
+ const checks = await Promise.all(peers.map(p =>
2361
+ axiosInst.get(`http://127.0.0.1:${p}/health`).then(() => true).catch(() => false)
2362
+ ));
2363
+ const dead = checks.some(ok => !ok);
2364
+ if (dead) {
2365
+ console.warn('[ANTI-TRIGGER] 检测到对端 serve 进程离线,启动副本扩容...');
2366
+ await activateReplicasIfNeeded();
2367
+ }
2368
+ } catch (_) { /* 忽略一次错误 */ }
2369
+ }, 3000);
2370
+ }
2371
+
2283
2372
  // 在main函数末尾添加
2284
2373
  function setupExitHandler() {
2285
2374
  let isExiting = false;
@@ -2324,6 +2413,27 @@ function setupExitHandler() {
2324
2413
  exitHandler();
2325
2414
  });
2326
2415
  }
2416
+
2417
+ // 预缓存副本快照(不在内存常驻)
2418
+ async function preCacheReplicas() {
2419
+ try {
2420
+ if (!global.ctrlA?.snapshotManager) return;
2421
+ const sm = global.ctrlA.snapshotManager;
2422
+ sm.loadSnapshotList();
2423
+ const hasB = sm.getSnapshotList().some(s => s.name.includes('replica_B'));
2424
+ const hasC = sm.getSnapshotList().some(s => s.name.includes('replica_C'));
2425
+ if (!hasB) {
2426
+ console.log('[REPLICA] 预生成 ctrlB 快照(replica_B)...');
2427
+ await sm.createSnapshot('replica_B');
2428
+ }
2429
+ if (!hasC) {
2430
+ console.log('[REPLICA] 预生成 ctrlC 快照(replica_C)...');
2431
+ await sm.createSnapshot('replica_C');
2432
+ }
2433
+ } catch (e) {
2434
+ console.warn('[REPLICA] 预缓存失败:', e.message);
2435
+ }
2436
+ }
2327
2437
  // 添加定期垃圾回收帮助函数
2328
2438
  function optimizeMemory() {
2329
2439
  const memUsage = process.memoryUsage();
@@ -2340,24 +2450,19 @@ async function main() {
2340
2450
 
2341
2451
  let spiderPrivate = new Spider();
2342
2452
  // 创建三个全局控制器副本
2343
- const ctrlA = new controller();
2344
- const ctrlB = new controller();
2345
- const ctrlC = new controller();
2346
- global.ctrlA = ctrlA;
2347
- global.ctrlB = ctrlB;
2348
- global.ctrlC = ctrlC;
2349
- global.ctrl = ctrlA;
2350
- // 启动快照自动保存(每30分钟)
2351
- if (ctrlA.snapshotManager) {
2352
- ctrlA.snapshotManager.setupAutoSnapshot(30 * 60 * 1000);
2353
- console.log('[MAIN] 已设置自动快照,每30分钟检查一次');
2354
- }
2355
- setupExitHandler();
2356
- console.log('Starting AI system...');
2357
- // 恢复各自持久化数据
2358
- loadAll(ctrlA.runtime);
2359
- loadAll(ctrlB.runtime);
2360
- loadAll(ctrlC.runtime);
2453
+ const ctrlA = new controller();
2454
+ global.ctrlA = ctrlA;
2455
+ global.ctrl = ctrlA;
2456
+ // 启动快照自动保存(每30分钟)
2457
+ if (ctrlA.snapshotManager) {
2458
+ ctrlA.snapshotManager.setupAutoSnapshot(30 * 60 * 1000);
2459
+ console.log('[MAIN] 已设置自动快照,每30分钟检查一次');
2460
+ }
2461
+ setupExitHandler();
2462
+ console.log('Starting AI system...');
2463
+ // 恢复 ctrlA
2464
+ loadAll(ctrlA.runtime);
2465
+ await preCacheReplicas();
2361
2466
  const redisClient = redis.createClient();
2362
2467
  redisClient.connect();
2363
2468
  redisClient.on("error", function (err) {
@@ -2451,36 +2556,33 @@ async function main() {
2451
2556
  await ctrlA.handleInput('I like apple');
2452
2557
  await ctrlA.handleInput('I love orange');
2453
2558
  await ctrlA.handleInput('you are good');
2454
-
2455
- // 启动主循环(只用A副本)
2456
- ctrlA.startMainLoop();
2457
-
2458
- app.post('/api/chat', async (req, res) => {
2459
- try {
2460
- const { message } = req.body;
2461
- // 先分词,再归一化
2462
- const words = typeof message === 'string'
2463
- ? message.toLowerCase().split(/\s+/).filter(w => w.length > 0)
2464
- : [];
2465
- const normWords = spiderPrivate.lemmatizeWords(words);
2466
- const normMessage = normWords.join(' ');
2467
-
2468
- /// console.log(`[DEBUG] 收到消息: ${message}`);
2469
- /// console.log(`[DEBUG] 归一化后: ${normMessage}`);
2470
-
2471
- const response = await ctrlA.handleInput(normMessage);
2472
-
2473
- if (!response || response.trim() === '') {
2474
- console.warn('[WARN] AI响应为空,可能KVM损坏或无记忆');
2475
- }
2476
-
2477
- // console.log(`[DEBUG] 响应: ${response}`);
2478
- res.json({ response });
2479
- } catch (error) {
2480
- console.error('[ERROR] 处理请求失败:', error);
2481
- res.status(500).json({ error: error.message });
2482
- }
2483
- });
2559
+ // 启动主循环(仅 A)
2560
+ ctrlA.startMainLoop();
2561
+
2562
+ // 安装对端监控触发器
2563
+ installPeerFailoverMonitor();
2564
+
2565
+ // 替换 /api/chat 为轮询分发(A|B|C)
2566
+ app.post('/api/chat', async (req, res) => {
2567
+ try {
2568
+ const { message } = req.body || {};
2569
+ const ctrl = getNextController();
2570
+ // 分词与归一化沿用原逻辑
2571
+ const words = typeof message === 'string'
2572
+ ? message.toLowerCase().split(/\s+/).filter(w => w.length > 0)
2573
+ : [];
2574
+ const normWords = (new Spider()).lemmatizeWords(words);
2575
+ const normMessage = normWords.join(' ');
2576
+ const response = await ctrl.handleInput(normMessage);
2577
+ if (!response || response.trim() === '') {
2578
+ console.warn('[WARN] AI响应为空');
2579
+ }
2580
+ res.json({ response });
2581
+ } catch (error) {
2582
+ console.error('[ERROR] 处理请求失败:', error);
2583
+ res.status(500).json({ error: error.message });
2584
+ }
2585
+ });
2484
2586
  // 添加健康检查路由
2485
2587
  app.get('/health', (req, res) => {
2486
2588
  res.status(200).send('OK');
@@ -2580,8 +2682,7 @@ async function main() {
2580
2682
  // 参数校验(可根据需要扩展)
2581
2683
  Object.assign(currentModelParams, updates);
2582
2684
  applyModelParams(global.ctrlA?.runtime);
2583
- applyModelParams(global.ctrlB?.runtime);
2584
- applyModelParams(global.ctrlC?.runtime);
2685
+
2585
2686
  res.json({ success: true, params: currentModelParams });
2586
2687
  } catch (err) {
2587
2688
  res.status(500).json({ error: err.message });
@@ -2592,8 +2693,7 @@ async function main() {
2592
2693
  try {
2593
2694
  Object.assign(currentModelParams, modelDefaults);
2594
2695
  applyModelParams(global.ctrlA?.runtime);
2595
- applyModelParams(global.ctrlB?.runtime);
2596
- applyModelParams(global.ctrlC?.runtime);
2696
+
2597
2697
  res.json({ success: true, params: currentModelParams });
2598
2698
  } catch (err) {
2599
2699
  res.status(500).json({ error: err.message });
@@ -2722,16 +2822,7 @@ async function plainObjToRuntime(runtime, obj) {
2722
2822
  // 如果直接运行此文件,启动主函数
2723
2823
  // 如果直接运行此文件,启动主函数
2724
2824
  if (require.main === module) {
2725
- /*antiTrigger(
2726
- () => main().catch(console.error), // onContinue
2727
- () => {
2728
- // onExit: 保存数据
2729
- try {
2730
- if (global.ctrl && global.ctrl.runtime) saveAll(global.ctrl.runtime);
2731
- } catch (e) { }
2732
- }
2733
- );
2734
- */
2825
+
2735
2826
  main().catch(console.error)
2736
2827
  }
2737
2828