079project 1.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.
Files changed (67) hide show
  1. package/GroupStarter.cjs +647 -0
  2. package/LICENSE +165 -0
  3. package/PropagateSignalUseJsWorker.js +92 -0
  4. package/README.md +102 -0
  5. package/Redis-8.0.3-Windows-x64-cygwin-with-Service/README.md +52 -0
  6. package/Redis-8.0.3-Windows-x64-cygwin-with-Service/README.zh_CN.md +59 -0
  7. package/Redis-8.0.3-Windows-x64-cygwin-with-Service/RedisService.exe +0 -0
  8. package/Redis-8.0.3-Windows-x64-cygwin-with-Service/cygcrypto-3.dll +0 -0
  9. package/Redis-8.0.3-Windows-x64-cygwin-with-Service/cyggcc_s-seh-1.dll +0 -0
  10. package/Redis-8.0.3-Windows-x64-cygwin-with-Service/cygssl-3.dll +0 -0
  11. package/Redis-8.0.3-Windows-x64-cygwin-with-Service/cygstdc++-6.dll +0 -0
  12. package/Redis-8.0.3-Windows-x64-cygwin-with-Service/cygwin1.dll +0 -0
  13. package/Redis-8.0.3-Windows-x64-cygwin-with-Service/cygz.dll +0 -0
  14. package/Redis-8.0.3-Windows-x64-cygwin-with-Service/dump.rdb +0 -0
  15. package/Redis-8.0.3-Windows-x64-cygwin-with-Service/install_redis_service.bat +100 -0
  16. package/Redis-8.0.3-Windows-x64-cygwin-with-Service/redis-benchmark.exe +0 -0
  17. package/Redis-8.0.3-Windows-x64-cygwin-with-Service/redis-check-aof.exe +0 -0
  18. package/Redis-8.0.3-Windows-x64-cygwin-with-Service/redis-check-rdb.exe +0 -0
  19. package/Redis-8.0.3-Windows-x64-cygwin-with-Service/redis-cli.exe +0 -0
  20. package/Redis-8.0.3-Windows-x64-cygwin-with-Service/redis-full.conf +376 -0
  21. package/Redis-8.0.3-Windows-x64-cygwin-with-Service/redis-sentinel.exe +0 -0
  22. package/Redis-8.0.3-Windows-x64-cygwin-with-Service/redis-server.exe +0 -0
  23. package/Redis-8.0.3-Windows-x64-cygwin-with-Service/redis.conf +2348 -0
  24. package/Redis-8.0.3-Windows-x64-cygwin-with-Service/sentinel.conf +361 -0
  25. package/Redis-8.0.3-Windows-x64-cygwin-with-Service/start.bat +4 -0
  26. package/Redis-8.0.3-Windows-x64-cygwin-with-Service/uninstall_redis_service.bat +30 -0
  27. package/boot.py +51 -0
  28. package/chat_Client.js +29 -0
  29. package/controller.cjs +118 -0
  30. package/enhancedForwarder.js +378 -0
  31. package/forwarder.js +1456 -0
  32. package/groupmanager.cjs +143 -0
  33. package/howToStart.txt +8 -0
  34. package/lemma.csv +210 -0
  35. package/load.py +35 -0
  36. package/mainManager.cjs +81 -0
  37. package/mainStarter.cjs +535 -0
  38. package/main_Serve.cjs +2745 -0
  39. package/main_Study.cjs +3230 -0
  40. package/memeMergeWorker.cjs +55 -0
  41. package/model_RNN.py +117 -0
  42. package/note.txt +5 -0
  43. package/notebook.txt +8 -0
  44. package/npminstall-debug.log +206 -0
  45. package/package.json +48 -0
  46. package/public/chat_straight.html +90 -0
  47. package/public/index.html +247 -0
  48. package/public/indexmain.html +136 -0
  49. package/public/monitor.html +194 -0
  50. package/robots/wikitext-something.txt +25 -0
  51. package/runtime.proto +24 -0
  52. package/runtime_data.json +766294 -0
  53. package/serializer_seq2seq.h5 +0 -0
  54. package/start.js +46 -0
  55. package/tests/test_FIrststep1.txt +1224 -0
  56. package/tests/test_FIrststep2.txt +2956 -0
  57. package/tests/test_FIrststep3.txt +1224 -0
  58. package/tests/test_FIrststep4.txt +1396 -0
  59. package/tests/test_FIrststep5.txt +2852 -0
  60. package/tests/test_FIrststep6.txt +1516 -0
  61. package/tests/test_FirstStep7.txt +1748 -0
  62. package/tests/test_Firstsetp8.txt +2672 -0
  63. package/tokenizer.json +1 -0
  64. package/vocabularySplitter.js +253 -0
  65. package/wikitext/.gitattributes +27 -0
  66. package/wikitext/README.md +344 -0
  67. package/wikitext/describtion.txt +1 -0
package/forwarder.js ADDED
@@ -0,0 +1,1456 @@
1
+ const axios = require('axios');
2
+ const express = require('express');
3
+ const bodyParser = require('body-parser');
4
+ const os = require('os');
5
+ const SERIALIZER_API = 'http://localhost:5008/api/serialize';
6
+ const app = express();
7
+ app.use(bodyParser.json());
8
+ app.use(bodyParser.urlencoded({ extended: true }));
9
+ // 在所有路由定义的最上方添加静态文件服务
10
+ let WAITING_TIMEOUT = 40000;
11
+
12
+ // 静态文件服务
13
+ app.use(express.static('public'));
14
+
15
+ // 添加前端界面HTML
16
+ app.get('/', (req, res) => {
17
+ res.send(`
18
+ <!DOCTYPE html>
19
+ <html lang="zh-CN">
20
+ <head>
21
+ <meta charset="UTF-8">
22
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
23
+ <title>Phoenix 079 控制面板</title>
24
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet">
25
+ <style>
26
+ body {
27
+ padding-top: 20px;
28
+ background-color: #f8f9fa;
29
+ }
30
+ .card {
31
+ margin-bottom: 20px;
32
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
33
+ }
34
+ .chat-messages {
35
+ height: 300px;
36
+ overflow-y: auto;
37
+ padding: 15px;
38
+ background-color: #f8f9fa;
39
+ border-radius: 5px;
40
+ margin-bottom: 15px;
41
+ }
42
+ .message {
43
+ padding: 10px 15px;
44
+ border-radius: 18px;
45
+ margin-bottom: 10px;
46
+ max-width: 80%;
47
+ }
48
+ .user-message {
49
+ background-color: #007bff;
50
+ color: white;
51
+ margin-left: auto;
52
+ }
53
+ .ai-message {
54
+ background-color: #e9ecef;
55
+ color: #212529;
56
+ }
57
+ .param-slider {
58
+ width: 100%;
59
+ }
60
+ .status-indicator {
61
+ width: 15px;
62
+ height: 15px;
63
+ border-radius: 50%;
64
+ display: inline-block;
65
+ margin-right: 5px;
66
+ }
67
+ .status-online {
68
+ background-color: #28a745;
69
+ }
70
+ .status-offline {
71
+ background-color: #dc3545;
72
+ }
73
+ .snapshot-list {
74
+ max-height: 300px;
75
+ overflow-y: auto;
76
+ }
77
+ .snapshot-item {
78
+ cursor: pointer;
79
+ }
80
+ .snapshot-item:hover {
81
+ background-color: #f1f1f1;
82
+ }
83
+ .nav-tabs .nav-link {
84
+ cursor: pointer;
85
+ }
86
+ </style>
87
+ </head>
88
+ <body>
89
+ <div class="container">
90
+ <header class="mb-4">
91
+ <h1 class="text-center">Phoenix 079 控制面板</h1>
92
+ <p class="text-center text-muted">模因网络协同分布式AI系统</p>
93
+ </header>
94
+
95
+ <ul class="nav nav-tabs mb-3" id="mainTabs" role="tablist">
96
+ <li class="nav-item" role="presentation">
97
+ <button class="nav-link active" id="chat-tab" data-bs-toggle="tab" data-bs-target="#chat-panel" type="button" role="tab" aria-controls="chat-panel" aria-selected="true">对话</button>
98
+ </li>
99
+ <li class="nav-item" role="presentation">
100
+ <button class="nav-link" id="params-tab" data-bs-toggle="tab" data-bs-target="#params-panel" type="button" role="tab" aria-controls="params-panel" aria-selected="false">参数配置</button>
101
+ </li>
102
+ <li class="nav-item" role="presentation">
103
+ <button class="nav-link" id="snapshots-tab" data-bs-toggle="tab" data-bs-target="#snapshots-panel" type="button" role="tab" aria-controls="snapshots-panel" aria-selected="false">快照管理</button>
104
+ </li>
105
+ <li class="nav-item" role="presentation">
106
+ <button class="nav-link" id="monitor-tab" data-bs-toggle="tab" data-bs-target="#monitor-panel" type="button" role="tab" aria-controls="monitor-panel" aria-selected="false">系统监控</button>
107
+ </li>
108
+ </ul>
109
+
110
+ <div class="tab-content" id="mainTabsContent">
111
+ <!-- 对话面板 -->
112
+ <div class="tab-pane fade show active" id="chat-panel" role="tabpanel" aria-labelledby="chat-tab">
113
+ <div class="card">
114
+ <div class="card-body">
115
+ <h5 class="card-title">对话界面</h5>
116
+ <div class="chat-messages" id="chatMessages">
117
+ <div class="message ai-message">
118
+ 系统已就绪,请输入消息...
119
+ </div>
120
+ </div>
121
+ <form id="chatForm">
122
+ <div class="input-group">
123
+ <input type="text" id="messageInput" class="form-control" placeholder="输入消息..." required>
124
+ <button type="submit" class="btn btn-primary">发送</button>
125
+ </div>
126
+ </form>
127
+ </div>
128
+ </div>
129
+
130
+ <div class="card">
131
+ <div class="card-header">
132
+ 详细响应
133
+ </div>
134
+ <div class="card-body">
135
+ <div class="row">
136
+ <div class="col-md-4">
137
+ <div class="card mb-2">
138
+ <div class="card-body">
139
+ <h6 class="card-subtitle mb-2 text-muted">AI实例1</h6>
140
+ <p id="ai1Response" class="card-text">-</p>
141
+ </div>
142
+ </div>
143
+ </div>
144
+ <div class="col-md-4">
145
+ <div class="card mb-2">
146
+ <div class="card-body">
147
+ <h6 class="card-subtitle mb-2 text-muted">AI实例2</h6>
148
+ <p id="ai2Response" class="card-text">-</p>
149
+ </div>
150
+ </div>
151
+ </div>
152
+ <div class="col-md-4">
153
+ <div class="card mb-2">
154
+ <div class="card-body">
155
+ <h6 class="card-subtitle mb-2 text-muted">AI实例3</h6>
156
+ <p id="ai3Response" class="card-text">-</p>
157
+ </div>
158
+ </div>
159
+ </div>
160
+ </div>
161
+ <div class="card mt-3">
162
+ <div class="card-body">
163
+ <h6 class="card-subtitle mb-2 text-muted">协同决策结果</h6>
164
+ <p id="finalResponse" class="card-text">-</p>
165
+ </div>
166
+ </div>
167
+ </div>
168
+ </div>
169
+ </div>
170
+
171
+ <!-- 参数配置面板 -->
172
+ <div class="tab-pane fade" id="params-panel" role="tabpanel" aria-labelledby="params-tab">
173
+ <div class="card">
174
+ <div class="card-body">
175
+ <h5 class="card-title">模型参数配置</h5>
176
+ <form id="paramsForm" class="mt-4">
177
+ <div class="mb-3">
178
+ <label for="decayFactorRange" class="form-label">信号衰减系数 (0.1-1.0)</label>
179
+ <div class="d-flex align-items-center">
180
+ <input type="range" class="form-range me-2 param-slider" id="decayFactorRange" min="0.1" max="1.0" step="0.05" value="0.5">
181
+ <span id="decayFactorValue">0.5</span>
182
+ </div>
183
+ <small class="text-muted">较小的值使信号传播更远,较大的值使传播更局部</small>
184
+ </div>
185
+ <!-- iteration -->
186
+ <div class="mb-3">
187
+ <label for="iterationRange" class="form-label">学习轮数 (1-20)</label>
188
+ <div class="d-flex align-items-center">
189
+ <input type="range" class="form-range me-2 param-slider" id="iterationRange" min="1" max="20" step="1" value="5">
190
+ <span id="iterationValue">5</span>
191
+ </div>
192
+ </div>
193
+ <!-- threshold -->
194
+ <div class="mb-3">
195
+ <label for="thresholdRange" class="form-label">注意力阈值 (1-10)</label>
196
+ <div class="d-flex align-items-center">
197
+ <input type="range" class="form-range me-2 param-slider" id="thresholdRange" min="1" max="10" step="1" value="3">
198
+ <span id="thresholdValue">3</span>
199
+ </div>
200
+ </div>
201
+ <!-- decay -->
202
+ <div class="mb-3">
203
+ <label for="decayRange" class="form-label">注意力衰减 (0.1-5)</label>
204
+ <div class="d-flex align-items-center">
205
+ <input type="range" class="form-range me-2 param-slider" id="decayRange" min="0.1" max="5" step="0.1" value="1">
206
+ <span id="decayValue">1</span>
207
+ </div>
208
+ </div>
209
+ <!-- decayK -->
210
+ <div class="mb-3">
211
+ <label for="decayKRange" class="form-label">信号衰减K (0.1-5)</label>
212
+ <div class="d-flex align-items-center">
213
+ <input type="range" class="form-range me-2 param-slider" id="decayKRange" min="0.1" max="5" step="0.1" value="1">
214
+ <span id="decayKValue">1</span>
215
+ </div>
216
+ </div>
217
+ <!-- maxLen -->
218
+ <div class="mb-3">
219
+ <label for="maxLenRange" class="form-label">最大输出长度 (4-64)</label>
220
+ <div class="d-flex align-items-center">
221
+ <input type="range" class="form-range me-2 param-slider" id="maxLenRange" min="4" max="64" step="1" value="16">
222
+ <span id="maxLenValue">16</span>
223
+ </div>
224
+ </div>
225
+ <!-- edgeWeight -->
226
+ <div class="mb-3">
227
+ <label for="edgeWeightRange" class="form-label">默认边权重 (0.1-5)</label>
228
+ <div class="d-flex align-items-center">
229
+ <input type="range" class="form-range me-2 param-slider" id="edgeWeightRange" min="0.1" max="5" step="0.1" value="1">
230
+ <span id="edgeWeightValue">1</span>
231
+ </div>
232
+ </div>
233
+ <div class="mb-3">
234
+ <label for="maxMemeWordsRange" class="form-label">最大模因词数 (10-500)</label>
235
+ <div class="d-flex align-items-center">
236
+ <input type="range" class="form-range me-2 param-slider" id="maxMemeWordsRange" min="10" max="500" step="10" value="100">
237
+ <span id="maxMemeWordsValue">100</span>
238
+ </div>
239
+ <small class="text-muted">单个模因最大允许包含的词数</small>
240
+ </div>
241
+
242
+ <div class="mb-3">
243
+ <label for="minOverlapThresholdRange" class="form-label">最小重叠阈值 (1-10)</label>
244
+ <div class="d-flex align-items-center">
245
+ <input type="range" class="form-range me-2 param-slider" id="minOverlapThresholdRange" min="1" max="10" step="1" value="2">
246
+ <span id="minOverlapThresholdValue">2</span>
247
+ </div>
248
+ <small class="text-muted">合并模因需要的最小共同词数</small>
249
+ </div>
250
+
251
+ <div class="mb-3">
252
+ <label for="maliciousThresholdRange" class="form-label">恶性模因阈值 (0.5-0.95)</label>
253
+ <div class="d-flex align-items-center">
254
+ <input type="range" class="form-range me-2 param-slider" id="maliciousThresholdRange" min="0.5" max="0.95" step="0.05" value="0.7">
255
+ <span id="maliciousThresholdValue">0.7</span>
256
+ </div>
257
+ <small class="text-muted">判定为恶性模因的最小评分阈值</small>
258
+ </div>
259
+
260
+ <div class="mb-3">
261
+ <label for="learningIterationsRange" class="form-label">自主学习迭代数 (1-10)</label>
262
+ <div class="d-flex align-items-center">
263
+ <input type="range" class="form-range me-2 param-slider" id="learningIterationsRange" min="1" max="10" step="1" value="3">
264
+ <span id="learningIterationsValue">3</span>
265
+ </div>
266
+ <small class="text-muted">每轮自主学习的迭代次数</small>
267
+ </div>
268
+ <!-- CommunicateCount -->
269
+ <div class="mb-3">
270
+ <label for="communicateCountRange" class="form-label">阵列交互轮数 (0-3)</label>
271
+ <div class="d-flex align-items-center">
272
+ <input type="range" class="form-range me-2 param-slider" id="communicateCountRange" min="0" max="3" step="1" value="1">
273
+ <span id="communicateCountValue">1</span>
274
+ </div>
275
+ <small class="text-muted">星火阵列交互轮数,越高越深度协作</small>
276
+ </div>
277
+ <div class="mb-3">
278
+ <label for="waitingTime" class="form-label">AI响应等待时间 (毫秒)</label>
279
+ <div class="d-flex align-items-center">
280
+ <input type="range" class="form-range me-2 param-slider" id="waitingTime" min="1000" max="60000" step="500" value="10000">
281
+ <span id="waitingTimeValue">10000</span>
282
+ </div>
283
+ </div>
284
+ <div class="d-flex justify-content-between mt-4">
285
+ <button type="submit" class="btn btn-primary">应用参数</button>
286
+ <button type="button" id="resetParams" class="btn btn-secondary">重置默认值</button>
287
+ </div>
288
+
289
+ </form>
290
+ </div>
291
+ </div>
292
+ </div>
293
+
294
+ <!-- 快照管理面板 -->
295
+ <div class="tab-pane fade" id="snapshots-panel" role="tabpanel" aria-labelledby="snapshots-tab">
296
+ <div class="card">
297
+ <div class="card-body">
298
+ <div class="d-flex justify-content-between align-items-center mb-3">
299
+ <h5 class="card-title mb-0">快照管理</h5>
300
+ <div>
301
+ <button id="refreshSnapshots" class="btn btn-sm btn-outline-secondary me-2">刷新列表</button>
302
+ <button id="createSnapshot" class="btn btn-sm btn-primary">创建快照</button>
303
+ </div>
304
+ </div>
305
+
306
+ <div class="list-group snapshot-list" id="snapshotsList">
307
+ <div class="d-flex justify-content-center p-3">
308
+ <div class="spinner-border text-primary" role="status">
309
+ <span class="visually-hidden">Loading...</span>
310
+ </div>
311
+ </div>
312
+ </div>
313
+ </div>
314
+ </div>
315
+ </div>
316
+
317
+ <!-- 系统监控面板 -->
318
+ <div class="tab-pane fade" id="monitor-panel" role="tabpanel" aria-labelledby="monitor-tab">
319
+ <div class="card">
320
+ <div class="card-body">
321
+ <div class="d-flex justify-content-between align-items-center mb-3">
322
+ <h5 class="card-title mb-0">系统状态监控</h5>
323
+ <button id="refreshStatus" class="btn btn-sm btn-outline-secondary">刷新状态</button>
324
+ </div>
325
+
326
+ <div class="row">
327
+ <div class="col-md-6">
328
+ <div class="card mb-3">
329
+ <div class="card-header">转发器状态</div>
330
+ <div class="card-body">
331
+ <table class="table table-sm">
332
+ <tbody>
333
+ <tr>
334
+ <th scope="row">运行状态</th>
335
+ <td id="forwarderStatus">-</td>
336
+ </tr>
337
+ <tr>
338
+ <th scope="row">运行时间</th>
339
+ <td id="forwarderUptime">-</td>
340
+ </tr>
341
+ <tr>
342
+ <th scope="row">总请求数</th>
343
+ <td id="totalRequests">-</td>
344
+ </tr>
345
+ <tr>
346
+ <th scope="row">成功/失败</th>
347
+ <td id="successFailedRequests">-</td>
348
+ </tr>
349
+ </tbody>
350
+ </table>
351
+ </div>
352
+ </div>
353
+
354
+ <div class="card mb-3">
355
+ <div class="card-header">系统资源</div>
356
+ <div class="card-body">
357
+ <table class="table table-sm">
358
+ <tbody>
359
+ <tr>
360
+ <th scope="row">CPU核心数</th>
361
+ <td id="cpuCores">-</td>
362
+ </tr>
363
+ <tr>
364
+ <th scope="row">平均负载</th>
365
+ <td id="loadAvg">-</td>
366
+ </tr>
367
+ <tr>
368
+ <th scope="row">内存使用</th>
369
+ <td id="memoryUsage">-</td>
370
+ </tr>
371
+ <tr>
372
+ <th scope="row">系统平台</th>
373
+ <td id="platform">-</td>
374
+ </tr>
375
+ </tbody>
376
+ </table>
377
+ </div>
378
+ </div>
379
+ </div>
380
+
381
+ <div class="col-md-6">
382
+ <div class="card mb-3">
383
+ <div class="card-header">AI实例状态</div>
384
+ <div class="card-body" id="aiInstancesStatus">
385
+ <div class="d-flex justify-content-center">
386
+ <div class="spinner-border text-primary" role="status">
387
+ <span class="visually-hidden">Loading...</span>
388
+ </div>
389
+ </div>
390
+ </div>
391
+ </div>
392
+
393
+ <div class="card">
394
+ <div class="card-header">响应时间统计</div>
395
+ <div class="card-body">
396
+ <table class="table table-sm">
397
+ <thead>
398
+ <tr>
399
+ <th scope="col">AI实例</th>
400
+ <th scope="col">平均响应时间</th>
401
+ </tr>
402
+ </thead>
403
+ <tbody id="responseTimesTable">
404
+ <tr><td colspan="2">加载中...</td></tr>
405
+ </tbody>
406
+ </table>
407
+ </div>
408
+ </div>
409
+ </div>
410
+ </div>
411
+ </div>
412
+ </div>
413
+ </div>
414
+ </div>
415
+
416
+ <footer class="mt-4 text-center text-muted">
417
+ <p>Phoenix 079 控制面板 &copy; 2025</p>
418
+ </footer>
419
+ </div>
420
+
421
+ <!-- 创建快照模态框 -->
422
+ <div class="modal fade" id="createSnapshotModal" tabindex="-1" aria-labelledby="createSnapshotModalLabel" aria-hidden="true">
423
+ <div class="modal-dialog">
424
+ <div class="modal-content">
425
+ <div class="modal-header">
426
+ <h5 class="modal-title" id="createSnapshotModalLabel">创建新快照</h5>
427
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
428
+ </div>
429
+ <div class="modal-body">
430
+ <div class="mb-3">
431
+ <label for="snapshotName" class="form-label">快照名称</label>
432
+ <input type="text" class="form-control" id="snapshotName" placeholder="输入快照名称...">
433
+ </div>
434
+ </div>
435
+ <div class="modal-footer">
436
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
437
+ <button type="button" class="btn btn-primary" id="confirmCreateSnapshot">创建</button>
438
+ </div>
439
+ </div>
440
+ </div>
441
+ </div>
442
+
443
+ <!-- 恢复快照确认模态框 -->
444
+ <div class="modal fade" id="restoreSnapshotModal" tabindex="-1" aria-labelledby="restoreSnapshotModalLabel" aria-hidden="true">
445
+ <div class="modal-dialog">
446
+ <div class="modal-content">
447
+ <div class="modal-header">
448
+ <h5 class="modal-title" id="restoreSnapshotModalLabel">恢复快照</h5>
449
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
450
+ </div>
451
+ <div class="modal-body">
452
+ <p>你确定要恢复到此快照状态吗?当前的系统状态将会丢失。</p>
453
+ <p><strong>快照ID: </strong><span id="restoreSnapshotId"></span></p>
454
+ <p><strong>创建时间: </strong><span id="restoreSnapshotDate"></span></p>
455
+ </div>
456
+ <div class="modal-footer">
457
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
458
+ <button type="button" class="btn btn-danger" id="confirmRestoreSnapshot">确认恢复</button>
459
+ </div>
460
+ </div>
461
+ </div>
462
+ </div>
463
+
464
+ <!-- 删除快照确认模态框 -->
465
+ <div class="modal fade" id="deleteSnapshotModal" tabindex="-1" aria-labelledby="deleteSnapshotModalLabel" aria-hidden="true">
466
+ <div class="modal-dialog">
467
+ <div class="modal-content">
468
+ <div class="modal-header">
469
+ <h5 class="modal-title" id="deleteSnapshotModalLabel">删除快照</h5>
470
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
471
+ </div>
472
+ <div class="modal-body">
473
+ <p>你确定要删除此快照吗?此操作不可逆。</p>
474
+ <p><strong>快照ID: </strong><span id="deleteSnapshotId"></span></p>
475
+ <p><strong>创建时间: </strong><span id="deleteSnapshotDate"></span></p>
476
+ </div>
477
+ <div class="modal-footer">
478
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
479
+ <button type="button" class="btn btn-danger" id="confirmDeleteSnapshot">确认删除</button>
480
+ </div>
481
+ </div>
482
+ </div>
483
+ </div>
484
+
485
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script>
486
+ <script>
487
+ document.addEventListener('DOMContentLoaded', function() {
488
+ // 初始化页面
489
+ loadModelParams();
490
+ loadSystemStatus();
491
+ loadSnapshots();
492
+
493
+ // 对话表单提交
494
+ document.getElementById('chatForm').addEventListener('submit', function(event) {
495
+ event.preventDefault();
496
+ sendMessage();
497
+ });
498
+
499
+ // 参数表单事件
500
+ document.getElementById('paramsForm').addEventListener('submit', function(event) {
501
+ event.preventDefault();
502
+ updateModelParams();
503
+ });
504
+
505
+ document.getElementById('resetParams').addEventListener('click', function() {
506
+ resetModelParams();
507
+ });
508
+
509
+ // 绑定滑块值显示
510
+ bindSliderEvents();
511
+
512
+ // 系统监控刷新
513
+ document.getElementById('refreshStatus').addEventListener('click', function() {
514
+ loadSystemStatus();
515
+ });
516
+
517
+ // 快照相关事件
518
+ document.getElementById('createSnapshot').addEventListener('click', function() {
519
+ showCreateSnapshotModal();
520
+ });
521
+
522
+ document.getElementById('refreshSnapshots').addEventListener('click', function() {
523
+ loadSnapshots();
524
+ });
525
+
526
+ document.getElementById('confirmCreateSnapshot').addEventListener('click', function() {
527
+ createSnapshot();
528
+ });
529
+
530
+ document.getElementById('confirmRestoreSnapshot').addEventListener('click', function() {
531
+ restoreSnapshot();
532
+ });
533
+
534
+ document.getElementById('confirmDeleteSnapshot').addEventListener('click', function() {
535
+ deleteSnapshot();
536
+ });
537
+ });
538
+
539
+ // 发送消息
540
+ function sendMessage() {
541
+ const messageInput = document.getElementById('messageInput');
542
+ const message = messageInput.value.trim();
543
+ // 添加用户消息到聊天窗口
544
+
545
+ if (message) {
546
+ appendMessage('user', message);
547
+ messageInput.value = '';
548
+
549
+ // 显示加载状态
550
+ appendMessage('ai', '<div class="spinner-border spinner-border-sm" role="status"></div> 思考中...');
551
+
552
+ // 发送请求到API
553
+ fetch('/api/chat', {
554
+ method: 'POST',
555
+ headers: {
556
+ 'Content-Type': 'application/json',
557
+ },
558
+ body: JSON.stringify({ message }),
559
+ })
560
+ .then(response => response.json())
561
+ .then(data => {
562
+ // 移除加载消息
563
+ document.getElementById('chatMessages').lastElementChild.remove();
564
+
565
+ // 添加AI回复
566
+ appendMessage('ai', data.response);
567
+
568
+ // 更新详细响应
569
+ if (data.details && data.details.length >= 3) {
570
+ document.getElementById('ai1Response').textContent = data.details[0] || '-';
571
+ document.getElementById('ai2Response').textContent = data.details[1] || '-';
572
+ document.getElementById('ai3Response').textContent = data.details[2] || '-';
573
+ document.getElementById('finalResponse').textContent = data.response || '-';
574
+ }
575
+ })
576
+ .catch(error => {
577
+ // 移除加载消息
578
+ document.getElementById('chatMessages').lastElementChild.remove();
579
+
580
+ // 显示错误
581
+ appendMessage('ai', '抱歉,发生了错误: ' + error.message);
582
+ console.error('Error:', error);
583
+ });
584
+ }
585
+ }
586
+
587
+ // 添加消息到聊天窗口
588
+ function appendMessage(type, content) {
589
+ const chatMessages = document.getElementById('chatMessages');
590
+ const messageDiv = document.createElement('div');
591
+ messageDiv.className = type === 'user' ? 'message user-message' : 'message ai-message';
592
+ messageDiv.innerHTML = content;
593
+ chatMessages.appendChild(messageDiv);
594
+ chatMessages.scrollTop = chatMessages.scrollHeight;
595
+ }
596
+
597
+ // 加载模型参数
598
+ function loadModelParams() {
599
+ fetch('/api/model/params')
600
+ .then(response => response.json())
601
+ .then(data => {
602
+ if (data && data.current) {
603
+ updateParamSliders(data.current);
604
+ }
605
+ })
606
+ .catch(error => console.error('Error loading model parameters:', error));
607
+ }
608
+
609
+ // 更新参数滑块值
610
+ // 修复 updateParamSliders 函数
611
+ function updateParamSliders(params) {
612
+ if (params.decayFactor !== undefined) {
613
+ document.getElementById('decayFactorRange').value = params.decayFactor;
614
+ document.getElementById('decayFactorValue').textContent = params.decayFactor;
615
+ }
616
+
617
+ if (params.maxMemeWords !== undefined) {
618
+ document.getElementById('maxMemeWordsRange').value = params.maxMemeWords;
619
+ document.getElementById('maxMemeWordsValue').textContent = params.maxMemeWords;
620
+ }
621
+
622
+ if (params.minOverlapThreshold !== undefined) {
623
+ document.getElementById('minOverlapThresholdRange').value = params.minOverlapThreshold;
624
+ document.getElementById('minOverlapThresholdValue').textContent = params.minOverlapThreshold;
625
+ }
626
+
627
+ if (params.maliciousThreshold !== undefined) {
628
+ document.getElementById('maliciousThresholdRange').value = params.maliciousThreshold;
629
+ document.getElementById('maliciousThresholdValue').textContent = params.maliciousThreshold;
630
+ }
631
+
632
+ if (params.learningIterations !== undefined) {
633
+ document.getElementById('learningIterationsRange').value = params.learningIterations;
634
+ document.getElementById('learningIterationsValue').textContent = params.learningIterations;
635
+ }
636
+
637
+ if (params.communicateCount !== undefined) {
638
+ document.getElementById('communicateCountRange').value = params.communicateCount;
639
+ document.getElementById('communicateCountValue').textContent = params.communicateCount;
640
+ }
641
+
642
+ if (params.iteration !== undefined) {
643
+ document.getElementById('iterationRange').value = params.iteration;
644
+ document.getElementById('iterationValue').textContent = params.iteration;
645
+ }
646
+
647
+ if (params.threshold !== undefined) {
648
+ document.getElementById('thresholdRange').value = params.threshold;
649
+ document.getElementById('thresholdValue').textContent = params.threshold;
650
+ }
651
+
652
+ if (params.decay !== undefined) {
653
+ document.getElementById('decayRange').value = params.decay;
654
+ document.getElementById('decayValue').textContent = params.decay;
655
+ }
656
+
657
+ if (params.decayK !== undefined) {
658
+ document.getElementById('decayKRange').value = params.decayK;
659
+ document.getElementById('decayKValue').textContent = params.decayK;
660
+ }
661
+
662
+ if (params.maxLen !== undefined) {
663
+ document.getElementById('maxLenRange').value = params.maxLen;
664
+ document.getElementById('maxLenValue').textContent = params.maxLen;
665
+ }
666
+
667
+ if (params.edgeWeight !== undefined) {
668
+ document.getElementById('edgeWeightRange').value = params.edgeWeight;
669
+ document.getElementById('edgeWeightValue').textContent = params.edgeWeight;
670
+ }
671
+ if (params.waitingTime !== undefined) {
672
+ document.getElementById('waitingTimeRange').value = params.waitingTime;
673
+ document.getElementById('waitingTimeValue').textContent = params.waitingTime;
674
+ }
675
+ } // 添加这个花括号
676
+
677
+ function bindSliderEvents() {
678
+ const sliders = [
679
+ { range: 'decayFactorRange', value: 'decayFactorValue' },
680
+ { range: 'maxMemeWordsRange', value: 'maxMemeWordsValue' },
681
+ { range: 'minOverlapThresholdRange', value: 'minOverlapThresholdValue' },
682
+ { range: 'maliciousThresholdRange', value: 'maliciousThresholdValue' },
683
+ { range: 'learningIterationsRange', value: 'learningIterationsValue' },
684
+ { range: 'iterationRange', value: 'iterationValue' },
685
+ { range: 'thresholdRange', value: 'thresholdValue' },
686
+ { range: 'decayRange', value: 'decayValue' },
687
+ { range: 'decayKRange', value: 'decayKValue' },
688
+ { range: 'maxLenRange', value: 'maxLenValue' },
689
+ { range: 'edgeWeightRange', value: 'edgeWeightValue' },
690
+ { range: 'communicateCountRange', value: 'communicateCountValue' },
691
+ { range: 'waitingTime', value: 'waitingTimeValue' } // 添加waitingTime滑块绑定
692
+ ];
693
+
694
+ sliders.forEach(slider => {
695
+ const rangeElement = document.getElementById(slider.range);
696
+ const valueElement = document.getElementById(slider.value);
697
+
698
+ if (rangeElement && valueElement) { // 添加元素存在检查
699
+ rangeElement.addEventListener('input', function() {
700
+ valueElement.textContent = this.value;
701
+ });
702
+ } else {
703
+ console.warn(\`找不到滑块元素: \${slider.range} 或 \${slider.value}\`);
704
+ }
705
+ });
706
+ }
707
+ // 更新模型参数
708
+ function updateModelParams() {
709
+ const params = {
710
+ decayFactor: parseFloat(document.getElementById('decayFactorRange').value),
711
+ maxMemeWords: parseInt(document.getElementById('maxMemeWordsRange').value),
712
+ minOverlapThreshold: parseInt(document.getElementById('minOverlapThresholdRange').value),
713
+ maliciousThreshold: parseFloat(document.getElementById('maliciousThresholdRange').value),
714
+ learningIterations: parseInt(document.getElementById('learningIterationsRange').value),
715
+ iteration: parseInt(document.getElementById('iterationRange').value),
716
+ threshold: parseInt(document.getElementById('thresholdRange').value),
717
+ decay: parseFloat(document.getElementById('decayRange').value),
718
+ decayK: parseFloat(document.getElementById('decayKRange').value),
719
+ maxLen: parseInt(document.getElementById('maxLenRange').value),
720
+ edgeWeight: parseFloat(document.getElementById('edgeWeightRange').value),
721
+ communicateCount: parseInt(document.getElementById('communicateCountRange').value),
722
+ waitingTime: parseInt(document.getElementById('waitingTime').value) // 添加这个参数
723
+ };
724
+
725
+ fetch('/api/model/params', {
726
+ method: 'POST',
727
+ headers: {
728
+ 'Content-Type': 'application/json',
729
+ },
730
+ body: JSON.stringify(params),
731
+ })
732
+ .then(response => response.json())
733
+ .then(data => {
734
+ if (data.success) {
735
+ alert('参数更新成功!');
736
+ } else {
737
+ alert('参数更新失败: ' + (data.error || '未知错误'));
738
+ }
739
+ })
740
+ .catch(error => {
741
+ console.error('Error updating parameters:', error);
742
+ alert('更新参数时发生错误: ' + error.message);
743
+ });
744
+ }
745
+
746
+ // 重置模型参数
747
+ function resetModelParams() {
748
+ fetch('/api/model/params/reset', {
749
+ method: 'POST',
750
+ headers: {
751
+ 'Content-Type': 'application/json',
752
+ }
753
+ })
754
+ .then(response => response.json())
755
+ .then(data => {
756
+ if (data.success) {
757
+ updateParamSliders(data.params);
758
+ alert('参数已重置为默认值!');
759
+ } else {
760
+ alert('参数重置失败: ' + (data.error || '未知错误'));
761
+ }
762
+ })
763
+ .catch(error => {
764
+ console.error('Error resetting parameters:', error);
765
+ alert('重置参数时发生错误: ' + error.message);
766
+ });
767
+ }
768
+
769
+ // 加载系统状态
770
+ function loadSystemStatus() {
771
+ fetch('/api/system/status')
772
+ .then(response => response.json())
773
+ .then(data => {
774
+ updateSystemStatus(data);
775
+ })
776
+ .catch(error => console.error('Error loading system status:', error));
777
+ }
778
+
779
+ function updateParamSliders(params) {
780
+ // 定义一个更新滑块的辅助函数
781
+ function updateSlider(paramName, sliderId, valueId) {
782
+ if (params[paramName] !== undefined) {
783
+ const sliderElement = document.getElementById(sliderId);
784
+ const valueElement = document.getElementById(valueId);
785
+
786
+ if (sliderElement && valueElement) {
787
+ sliderElement.value = params[paramName];
788
+ valueElement.textContent = params[paramName];
789
+ } else {
790
+ console.warn(\`无法更新参数 \${paramName}: 元素不存在\`);
791
+ }
792
+ }
793
+ }
794
+
795
+ // 统一更新所有滑块
796
+ updateSlider('decayFactor', 'decayFactorRange', 'decayFactorValue');
797
+ updateSlider('maxMemeWords', 'maxMemeWordsRange', 'maxMemeWordsValue');
798
+ updateSlider('minOverlapThreshold', 'minOverlapThresholdRange', 'minOverlapThresholdValue');
799
+ updateSlider('maliciousThreshold', 'maliciousThresholdRange', 'maliciousThresholdValue');
800
+ updateSlider('learningIterations', 'learningIterationsRange', 'learningIterationsValue');
801
+ updateSlider('communicateCount', 'communicateCountRange', 'communicateCountValue');
802
+ updateSlider('iteration', 'iterationRange', 'iterationValue');
803
+ updateSlider('threshold', 'thresholdRange', 'thresholdValue');
804
+ updateSlider('decay', 'decayRange', 'decayValue');
805
+ updateSlider('decayK', 'decayKRange', 'decayKValue');
806
+ updateSlider('maxLen', 'maxLenRange', 'maxLenValue');
807
+ updateSlider('edgeWeight', 'edgeWeightRange', 'edgeWeightValue');
808
+ updateSlider('waitingTime', 'waitingTime', 'waitingTimeValue'); // 修正这里的ID
809
+ }
810
+
811
+ // 更新AI实例状态
812
+ function updateAIInstancesStatus(instances) {
813
+ const container = document.getElementById('aiInstancesStatus');
814
+ container.innerHTML = '';
815
+
816
+ if (instances.length === 0) {
817
+ container.innerHTML = '<p class="text-center">无法获取AI实例状态</p>';
818
+ return;
819
+ }
820
+
821
+ const table = document.createElement('table');
822
+ table.className = 'table table-sm';
823
+
824
+ const thead = document.createElement('thead');
825
+ thead.innerHTML = '<tr><th>实例端口</th><th>状态</th><th>详情</th></tr>';
826
+
827
+ const tbody = document.createElement('tbody');
828
+
829
+ instances.forEach(instance => {
830
+ const row = document.createElement('tr');
831
+ row.innerHTML = \`
832
+ <td>\${instance.port}</td>
833
+ <td>
834
+ <span class="status-indicator \${instance.online ? 'status-online' : 'status-offline'}"></span>
835
+ \${instance.online ? '在线' : '离线'}
836
+ </td>
837
+ <td>\${instance.online ? '正常运行' : (instance.error || '连接失败')}</td>
838
+ \`;
839
+ tbody.appendChild(row);
840
+ });
841
+
842
+ table.appendChild(thead);
843
+ table.appendChild(tbody);
844
+ container.appendChild(table);
845
+ }
846
+
847
+ // 更新响应时间
848
+ function updateResponseTimes(times) {
849
+ const container = document.getElementById('responseTimesTable');
850
+ container.innerHTML = '';
851
+
852
+ const ports = Object.keys(times).sort();
853
+
854
+ if (ports.length === 0) {
855
+ container.innerHTML = '<tr><td colspan="2">无响应时间数据</td></tr>';
856
+ return;
857
+ }
858
+
859
+ ports.forEach(port => {
860
+ const row = document.createElement('tr');
861
+ row.innerHTML = \`
862
+ <td>端口 \${port}</td>
863
+ <td>\${times[port] ? times[port] + 'ms' : '无数据'}</td>
864
+ \`;
865
+ container.appendChild(row);
866
+ });
867
+ }
868
+
869
+ // 加载快照列表
870
+ function loadSnapshots() {
871
+ const container = document.getElementById('snapshotsList');
872
+ container.innerHTML = '<div class="d-flex justify-content-center p-3"><div class="spinner-border text-primary" role="status"></div></div>';
873
+
874
+ fetch('/api/snapshots')
875
+ .then(response => response.json())
876
+ .then(data => {
877
+ container.innerHTML = '';
878
+
879
+ if (!data.success || !data.snapshots || data.snapshots.length === 0) {
880
+ container.innerHTML = '<div class="alert alert-info">暂无快照</div>';
881
+ return;
882
+ }
883
+
884
+ data.snapshots.forEach(snapshot => {
885
+ const item = document.createElement('div');
886
+ item.className = 'list-group-item list-group-item-action snapshot-item';
887
+ item.innerHTML = \`
888
+ <div class="d-flex w-100 justify-content-between">
889
+ <h5 class="mb-1">\${snapshot.name}</h5>
890
+ <small>\${new Date(snapshot.timestamp).toLocaleString()}</small>
891
+ </div>
892
+ <p class="mb-1">快照ID: \${snapshot.id}</p>
893
+ <div class="d-flex justify-content-end">
894
+ <button class="btn btn-sm btn-outline-primary me-2 restore-snapshot" data-id="\${snapshot.id}" data-name="\${snapshot.name}" data-date="\${snapshot.date}">恢复</button>
895
+ <button class="btn btn-sm btn-outline-danger delete-snapshot" data-id="\${snapshot.id}" data-name="\${snapshot.name}" data-date="\${snapshot.date}">删除</button>
896
+ </div>
897
+ \`;
898
+ container.appendChild(item);
899
+ });
900
+
901
+ // 绑定快照操作事件
902
+ document.querySelectorAll('.restore-snapshot').forEach(btn => {
903
+ btn.addEventListener('click', function() {
904
+ showRestoreSnapshotModal(this.dataset.id, this.dataset.date);
905
+ });
906
+ });
907
+
908
+ document.querySelectorAll('.delete-snapshot').forEach(btn => {
909
+ btn.addEventListener('click', function() {
910
+ showDeleteSnapshotModal(this.dataset.id, this.dataset.date);
911
+ });
912
+ });
913
+ })
914
+ .catch(error => {
915
+ console.error('Error loading snapshots:', error);
916
+ container.innerHTML = '<div class="alert alert-danger">加载快照失败</div>';
917
+ });
918
+ }
919
+
920
+ // 显示创建快照模态框
921
+ function showCreateSnapshotModal() {
922
+ document.getElementById('snapshotName').value = '';
923
+ const modal = new bootstrap.Modal(document.getElementById('createSnapshotModal'));
924
+ modal.show();
925
+ }
926
+
927
+ // 显示恢复快照确认模态框
928
+ function showRestoreSnapshotModal(id, date) {
929
+ document.getElementById('restoreSnapshotId').textContent = id;
930
+ document.getElementById('restoreSnapshotDate').textContent = date;
931
+
932
+ const modal = new bootstrap.Modal(document.getElementById('restoreSnapshotModal'));
933
+ modal.show();
934
+ }
935
+
936
+ // 显示删除快照确认模态框
937
+ function showDeleteSnapshotModal(id, date) {
938
+ document.getElementById('deleteSnapshotId').textContent = id;
939
+ document.getElementById('deleteSnapshotDate').textContent = date;
940
+
941
+ const modal = new bootstrap.Modal(document.getElementById('deleteSnapshotModal'));
942
+ modal.show();
943
+ }
944
+
945
+ // 创建快照
946
+ function createSnapshot() {
947
+ const name = document.getElementById('snapshotName').value.trim();
948
+
949
+ fetch('/api/snapshots/create', {
950
+ method: 'POST',
951
+ headers: {
952
+ 'Content-Type': 'application/json',
953
+ },
954
+ body: JSON.stringify({ name: name || undefined }),
955
+ })
956
+ .then(response => response.json())
957
+ .then(data => {
958
+ bootstrap.Modal.getInstance(document.getElementById('createSnapshotModal')).hide();
959
+
960
+ if (data.success) {
961
+ alert('快照创建成功!');
962
+ loadSnapshots();
963
+ } else {
964
+ alert('创建快照失败: ' + (data.error || '未知错误'));
965
+ }
966
+ })
967
+ .catch(error => {
968
+ console.error('Error creating snapshot:', error);
969
+ alert('创建快照时发生错误: ' + error.message);
970
+ });
971
+ }
972
+
973
+ // 恢复快照
974
+ function restoreSnapshot() {
975
+ const id = document.getElementById('restoreSnapshotId').textContent;
976
+
977
+ fetch(\`/api/snapshots/restore/\${id}\`, {
978
+ method: 'POST',
979
+ })
980
+ .then(response => response.json())
981
+ .then(data => {
982
+ bootstrap.Modal.getInstance(document.getElementById('restoreSnapshotModal')).hide();
983
+
984
+ if (data.success) {
985
+ alert('快照恢复成功!');
986
+ } else {
987
+ alert('恢复快照失败: ' + (data.error || '未知错误'));
988
+ }
989
+ })
990
+ .catch(error => {
991
+ console.error('Error restoring snapshot:', error);
992
+ alert('恢复快照时发生错误: ' + error.message);
993
+ });
994
+ }
995
+
996
+ // 删除快照
997
+ function deleteSnapshot() {
998
+ const id = document.getElementById('deleteSnapshotId').textContent;
999
+
1000
+ fetch(\`/api/snapshots/\${id}\`, {
1001
+ method: 'DELETE',
1002
+ })
1003
+ .then(response => response.json())
1004
+ .then(data => {
1005
+ bootstrap.Modal.getInstance(document.getElementById('deleteSnapshotModal')).hide();
1006
+
1007
+ if (data.success) {
1008
+ alert('快照删除成功!');
1009
+ loadSnapshots();
1010
+ } else {
1011
+ alert('删除快照失败: ' + (data.error || '未知错误'));
1012
+ }
1013
+ })
1014
+ .catch(error => {
1015
+ console.error('Error deleting snapshot:', error);
1016
+ alert('删除快照时发生错误: ' + error.message);
1017
+ });
1018
+ }
1019
+
1020
+ // 格式化秒数为可读时间
1021
+ function formatSeconds(seconds) {
1022
+ if (!seconds) return '-';
1023
+
1024
+ const days = Math.floor(seconds / 86400);
1025
+ const hours = Math.floor((seconds % 86400) / 3600);
1026
+ const minutes = Math.floor((seconds % 3600) / 60);
1027
+ const secs = seconds % 60;
1028
+
1029
+ let result = '';
1030
+ if (days > 0) result += days + '天 ';
1031
+ if (hours > 0) result += hours + '小时 ';
1032
+ if (minutes > 0) result += minutes + '分 ';
1033
+ if (secs > 0) result += secs + '秒';
1034
+
1035
+ return result.trim();
1036
+ }
1037
+ </script>
1038
+ </body>
1039
+ </html>
1040
+ `);
1041
+ });
1042
+
1043
+ // 目标端口列表
1044
+ const AI_PORTS = [
1045
+ process.env.AI_PORT_A || process.argv[3],
1046
+ process.env.AI_PORT_B || process.argv[4],
1047
+ process.env.AI_PORT_C || process.argv[5]
1048
+ ];
1049
+ const Study_Port=process.env.AI_STUDY_PORT || process.argv[6]; // 学习模块端口
1050
+
1051
+ const systemStats = {
1052
+ requestsTotal: 0,
1053
+ requestsSuccess: 0,
1054
+ requestsFailed: 0,
1055
+ startTime: Date.now(),
1056
+ lastRequestTime: null,
1057
+ aiResponseTimes: { [process.argv[3]]: [], [process.argv[4]]: [], [process.argv[5]]: [] },
1058
+ lastErrors: []
1059
+ };
1060
+
1061
+ // 新增:统一从各后端响应中提取文本
1062
+ function extractText(resp) {
1063
+ if (!resp) return '';
1064
+ if (typeof resp === 'string') return resp;
1065
+ if (typeof resp === 'object') {
1066
+ if (typeof resp.response === 'string') return resp.response; // 优先解析 response 字段
1067
+ if (typeof resp.top === 'string') return resp.top;
1068
+ if (Array.isArray(resp.responses)) {
1069
+ const first = resp.responses.find(x => typeof x === 'string' || typeof x?.response === 'string');
1070
+ return first ? (typeof first === 'string' ? first : first.response) : '';
1071
+ }
1072
+ return String(resp || '');
1073
+ }
1074
+ return String(resp);
1075
+ }
1076
+ // 模型默认参数
1077
+ const modelDefaults = {
1078
+ decayFactor: 0.5,
1079
+ maxMemeWords: 100,
1080
+ minOverlapThreshold: 2,
1081
+ maliciousThreshold: 0.7,
1082
+ learningIterations: 3,
1083
+ communicateCount: 1 // 新增
1084
+ };
1085
+
1086
+ // 当前应用的模型参数
1087
+ const currentModelParams = { ...modelDefaults };
1088
+
1089
+ /**
1090
+ * 生成扰动输入 - 随机移除一个词
1091
+ */
1092
+ function perturb(arr) {
1093
+ if (arr.length <= 2) return arr;
1094
+ const idx = Math.floor(Math.random() * arr.length);
1095
+ return arr.filter((_, i) => i !== idx);
1096
+ }
1097
+
1098
+ /**
1099
+ * 向指定AI实例发送请求并测量响应时间
1100
+ */
1101
+ // 修改 requestAI,返回 null 表示离线
1102
+ // 修改 requestAI 函数,增加重试和超时处理
1103
+ async function requestAI(port, message, retries = 3) {
1104
+ const url = `http://localhost:${port}/api/chat`;
1105
+ for (let attempt = 1; attempt <= retries; attempt++) {
1106
+ try {
1107
+ const start = Date.now();
1108
+ const response = await axios.post(url, { message }, { timeout: 10000 }); // 10秒超时
1109
+ const latency = Date.now() - start;
1110
+ systemStats.aiResponseTimes[port] = systemStats.aiResponseTimes[port] || [];
1111
+ systemStats.aiResponseTimes[port].push(latency);
1112
+ return response.data;
1113
+ } catch (error) {
1114
+ console.warn(`[WARN] 请求 AI 实例 ${port} 失败 (尝试 ${attempt}/${retries}): ${error.message}`);
1115
+ if (attempt === retries) {
1116
+ systemStats.lastErrors.push({ port, error: error.message });
1117
+ return null; // 返回 null 表示服务不可用
1118
+ }
1119
+ await new Promise(resolve => setTimeout(resolve, 1000)); // 重试前等待 1 秒
1120
+ }
1121
+ }
1122
+ }
1123
+
1124
+
1125
+ app.post('/api/chat', async (req, res) => {
1126
+ const { message } = req.body;
1127
+ if (!message) return res.status(400).json({ error: 'No message' });
1128
+
1129
+ systemStats.requestsTotal++;
1130
+ systemStats.lastRequestTime = Date.now();
1131
+
1132
+ const communicateCount = currentModelParams.communicateCount !== undefined ? currentModelParams.communicateCount : 1;
1133
+
1134
+ const words = message.toLowerCase().split(' ').filter(w => w.length > 0);
1135
+ let inputs = [words, perturb(words), perturb(words)];
1136
+ let results = [];
1137
+
1138
+ try {
1139
+ // 星火阵列多轮交互(每一轮并发请求3个AI)
1140
+ for (let round = 0; round <= communicateCount; round++) {
1141
+ results = await Promise.all(
1142
+ AI_PORTS.map((port, i) => requestAI(port, (inputs[i] || words).join(' ')))
1143
+ );
1144
+
1145
+ // 下一轮输入为本轮输出分词(先提取文本,再 split)
1146
+ if (round < communicateCount) {
1147
+ console.log(results);
1148
+ inputs = results.map(r => extractText(r).toLowerCase().split(/\s+/).filter(Boolean));
1149
+ }
1150
+ }
1151
+
1152
+ // 只保留有效响应(提取为字符串)
1153
+ const texts = results.map(extractText).filter(t => typeof t === 'string' && t.length > 0);
1154
+ if (texts.length === 0) {
1155
+ systemStats.requestsFailed++;
1156
+ return res.status(502).json({ error: '所有AI实例均不可用或无响应' });
1157
+ }
1158
+
1159
+ // 统计词频
1160
+ const freq = {};
1161
+ texts.forEach(t => t.split(/\s+/).forEach(w => { if (w) freq[w] = (freq[w] || 0) + 1; }));
1162
+ const sorted = Object.entries(freq).sort((a, b) => b[1] - a[1]);
1163
+ const vocabOut = sorted.slice(0, 16).map(([w]) => w);
1164
+
1165
+ let final;
1166
+ try {
1167
+ const resp = await axios.post(SERIALIZER_API, { words: vocabOut });
1168
+ final = resp.data?.sentence || vocabOut.join(' ');
1169
+ } catch (e) {
1170
+ final = vocabOut.join(' ');
1171
+ console.warn('[SERIALIZER] Python API调用失败,输出原始词表:', e.message);
1172
+ }
1173
+
1174
+ systemStats.requestsSuccess++;
1175
+ res.json({ response: final, details: texts });
1176
+ } catch (err) {
1177
+ systemStats.requestsFailed++;
1178
+ res.status(500).json({ error: err.message });
1179
+ }
1180
+ });
1181
+
1182
+ //==============================================================================
1183
+ // 新增功能:模型参数调节
1184
+ //==============================================================================
1185
+
1186
+ // 获取当前模型参数
1187
+ app.get('/api/model/params', (req, res) => {
1188
+ res.json({
1189
+ current: currentModelParams,
1190
+ defaults: modelDefaults
1191
+ });
1192
+ });
1193
+
1194
+ // 更新模型参数
1195
+ // 更新模型参数
1196
+ app.post('/api/model/params', async (req, res) => {
1197
+ try {
1198
+ const updates = req.body;
1199
+ // ...existing code...
1200
+
1201
+ Object.assign(currentModelParams, updates);
1202
+ if (updates.waitingTime !== undefined) {
1203
+ WAITING_TIMEOUT = updates.waitingTime;
1204
+ }
1205
+
1206
+ // 修复:正确组合 Promise.all,包含 Study_Port
1207
+ const updatePromises = [
1208
+ ...AI_PORTS.map(port =>
1209
+ axios.post(`http://localhost:${port}/api/model/params`, currentModelParams, { timeout: 5000 })
1210
+ .then(() => ({ port, success: true }))
1211
+ .catch(error => ({ port, success: false, error: error.message }))
1212
+ )
1213
+ ];
1214
+ if (Study_Port) {
1215
+ updatePromises.push(
1216
+ axios.post(`http://localhost:${Study_Port}/api/model/params`, currentModelParams, { timeout: 5000 })
1217
+ .then(() => ({ port: Study_Port, success: true }))
1218
+ .catch(error => ({ port: Study_Port, success: false, error: error.message }))
1219
+ );
1220
+ }
1221
+ const results = await Promise.all(updatePromises);
1222
+
1223
+ console.log('[PARAMS] 参数更新结果:', results);
1224
+ res.json({ success: results.every(r => r.success), params: currentModelParams, results });
1225
+ } catch (err) {
1226
+ console.error('[ERROR] 参数更新失败:', err);
1227
+ res.status(500).json({ error: err.message });
1228
+ }
1229
+ });
1230
+ // 重置模型参数到默认值
1231
+ app.post('/api/model/params/reset', async (req, res) => {
1232
+ try {
1233
+ Object.assign(currentModelParams, modelDefaults);
1234
+
1235
+ const resetPromises = [
1236
+ ...AI_PORTS.map(port =>
1237
+ axios.post(`http://localhost:${port}/api/model/params/reset`)
1238
+ .then(() => ({ port, success: true }))
1239
+ .catch(error => ({ port, success: false, error: error.message }))
1240
+ )
1241
+ ];
1242
+ if (Study_Port) {
1243
+ resetPromises.push(
1244
+ axios.post(`http://localhost:${Study_Port}/api/model/params/reset`)
1245
+ .then(() => ({ port: Study_Port, success: true }))
1246
+ .catch(error => ({ port: Study_Port, success: false, error: error.message }))
1247
+ );
1248
+ }
1249
+
1250
+ const results = await Promise.all(resetPromises);
1251
+ res.json({ success: results.every(r => r.success), params: currentModelParams, results });
1252
+ } catch (err) {
1253
+ res.status(500).json({ error: err.message });
1254
+ }
1255
+ });
1256
+ //==============================================================================
1257
+ // 新增功能:快照管理
1258
+ //==============================================================================
1259
+
1260
+ // 获取快照列表 (只从主控制器AI_PORTS[0]获取)
1261
+ app.get('/api/snapshots', async (req, res) => {
1262
+ try {
1263
+ const response = await axios.get(`http://localhost:${AI_PORTS[0]}/api/snapshot/list`);
1264
+ res.json(response.data);
1265
+ } catch (err) {
1266
+ res.status(500).json({ success: false, error: err.message });
1267
+ }
1268
+ });
1269
+
1270
+ // 创建新快照
1271
+ app.post('/api/snapshots/create', async (req, res) => {
1272
+ try {
1273
+ const { name } = req.body || {};
1274
+ const response = await axios.post(`http://localhost:${AI_PORTS[0]}/api/snapshot/create`, { name });
1275
+ res.json(response.data);
1276
+ } catch (err) {
1277
+ res.status(500).json({ success: false, error: err.message });
1278
+ }
1279
+ });
1280
+
1281
+ // 恢复快照
1282
+ app.post('/api/snapshots/restore/:id', async (req, res) => {
1283
+ try {
1284
+ const { id } = req.params;
1285
+ const response = await axios.post(`http://localhost:${AI_PORTS[0]}/api/snapshot/restore/${id}`);
1286
+ res.json(response.data);
1287
+ } catch (err) {
1288
+ res.status(500).json({ success: false, error: err.message });
1289
+ }
1290
+ });
1291
+
1292
+ // 删除快照
1293
+ app.delete('/api/snapshots/:id', async (req, res) => {
1294
+ try {
1295
+ const { id } = req.params;
1296
+ const response = await axios.delete(`http://localhost:${AI_PORTS[0]}/api/snapshot/${id}`);
1297
+ res.json(response.data);
1298
+ } catch (err) {
1299
+ res.status(500).json({ success: false, error: err.message });
1300
+ }
1301
+ });
1302
+
1303
+ //==============================================================================
1304
+ // 新增功能:系统监控
1305
+ //==============================================================================
1306
+
1307
+ // 系统状态监控API
1308
+ app.get('/api/system/status', async (req, res) => {
1309
+ try {
1310
+ // 获取后端AI实例的状态
1311
+ const aiStatuses = await Promise.all(
1312
+ AI_PORTS.map(port =>
1313
+ axios.get(`http://localhost:${port}/api/status`)
1314
+ .then(resp => ({ port, status: resp.data, online: true }))
1315
+ .catch(error => ({ port, online: false, error: error.message }))
1316
+ )
1317
+ );
1318
+
1319
+ // 计算平均响应时间
1320
+ const avgResponseTimes = {};
1321
+ Object.entries(systemStats.aiResponseTimes).forEach(([port, times]) => {
1322
+ if (times.length > 0) {
1323
+ avgResponseTimes[port] = Math.round(times.reduce((sum, time) => sum + time, 0) / times.length);
1324
+ } else {
1325
+ avgResponseTimes[port] = null;
1326
+ }
1327
+ });
1328
+
1329
+ // 系统运行时间
1330
+ const uptime = Math.round((Date.now() - systemStats.startTime) / 1000);
1331
+
1332
+ // 系统资源
1333
+ const systemInfo = {
1334
+ cpus: os.cpus().length,
1335
+ memory: {
1336
+ total: Math.round(os.totalmem() / (1024 * 1024)),
1337
+ free: Math.round(os.freemem() / (1024 * 1024))
1338
+ },
1339
+ loadAvg: os.loadavg(),
1340
+ platform: os.platform(),
1341
+ uptime: os.uptime()
1342
+ };
1343
+
1344
+ res.json({
1345
+ forwarder: {
1346
+ status: 'running',
1347
+ uptime,
1348
+ requests: {
1349
+ total: systemStats.requestsTotal,
1350
+ success: systemStats.requestsSuccess,
1351
+ failed: systemStats.requestsFailed
1352
+ },
1353
+ avgResponseTimes,
1354
+ lastRequestTime: systemStats.lastRequestTime ? new Date(systemStats.lastRequestTime).toISOString() : null,
1355
+ lastErrors: systemStats.lastErrors.slice(-5)
1356
+ },
1357
+ aiInstances: aiStatuses,
1358
+ system: systemInfo,
1359
+ modelParams: currentModelParams
1360
+ });
1361
+ } catch (err) {
1362
+ res.status(500).json({ error: err.message });
1363
+ }
1364
+ });
1365
+
1366
+ // 获取各AI实例的详细信息
1367
+ app.get('/api/system/ai/:port', async (req, res) => {
1368
+ const { port } = req.params;
1369
+
1370
+ if (!AI_PORTS.includes(parseInt(port))) {
1371
+ return res.status(400).json({ error: '无效的AI端口' });
1372
+ }
1373
+
1374
+ try {
1375
+ const statusPromise = axios.get(`http://localhost:${port}/api/status`);
1376
+
1377
+ // 这里可以添加其他详细信息的API调用
1378
+ const [statusResp] = await Promise.all([statusPromise]);
1379
+
1380
+ res.json({
1381
+ port,
1382
+ status: statusResp.data,
1383
+ avgResponseTime: calculateAvgResponseTime(port),
1384
+ });
1385
+ } catch (err) {
1386
+ res.status(500).json({
1387
+ port,
1388
+ error: err.message,
1389
+ online: false
1390
+ });
1391
+ }
1392
+ });
1393
+
1394
+ // 辅助函数:计算平均响应时间
1395
+ function calculateAvgResponseTime(port) {
1396
+ const times = systemStats.aiResponseTimes[port] || [];
1397
+ if (times.length === 0) return null;
1398
+ return Math.round(times.reduce((sum, time) => sum + time, 0) / times.length);
1399
+ }
1400
+
1401
+ //==============================================================================
1402
+ // 启动控制器服务
1403
+ //==============================================================================
1404
+
1405
+ const PORT = process.env.CONTROLLER_PORT || process.argv[2];
1406
+ app.listen(PORT, async () => {
1407
+ console.log(`┌───────────────────────────────────────────────────┐`);
1408
+ console.log(`│ Phoenix 079 Controller 转发控制器 v1.2 已启动 │`);
1409
+ console.log(`│ 监听端口: ${PORT.toString().padEnd(41)} │`);
1410
+ console.log(`│ AI实例: ${AI_PORTS.join(', ').padEnd(41)} │`);
1411
+ console.log(`└───────────────────────────────────────────────────┘`);
1412
+
1413
+ // 尝试连接所有AI实例,检查状态
1414
+ await Promise.all(AI_PORTS.map(async port => {
1415
+ try {
1416
+ await axios.get(`http://localhost:${port}/api/status`, { timeout: 2000 });
1417
+ console.log(`[READY] AI实例端口${port}已连接`);
1418
+ } catch (error) {
1419
+ console.warn(`[WARN] AI实例端口${port}未连接: ${error.message}`);
1420
+ }
1421
+ }));
1422
+
1423
+ console.log('转发控制器就绪,等待请求...');
1424
+ });
1425
+ // 健康检查函数,定期检查下层服务是否可用
1426
+ async function healthCheck() {
1427
+ for (const port of AI_PORTS) {
1428
+ try {
1429
+ await axios.get(`http://localhost:${port}/health`, { timeout: 5000 });
1430
+ console.log(`[HEALTH] AI 实例 ${port} 正常`);
1431
+ } catch (error) {
1432
+ console.warn(`[HEALTH] AI 实例 ${port} 不可用: ${error.message}`);
1433
+ }
1434
+ }
1435
+ }
1436
+
1437
+ // 定期执行健康检查
1438
+ setInterval(healthCheck, 60000); // 每分钟检查一次
1439
+ // 健康检查函数,定期检查所有AI实例
1440
+ setInterval(async () => {
1441
+ console.log('[HEALTH] 执行健康检查...');
1442
+ const results = await Promise.all(
1443
+ AI_PORTS.map(port =>
1444
+ axios.get(`http://localhost:${port}/api/status`, { timeout: 2000 })
1445
+ .then(() => ({ port, status: 'online' }))
1446
+ .catch(error => ({ port, status: 'offline', error: error.message }))
1447
+ )
1448
+ );
1449
+
1450
+ const offline = results.filter(r => r.status === 'offline');
1451
+ if (offline.length > 0) {
1452
+ console.warn(`[WARN] ${offline.length}个AI实例离线:`, offline.map(r => r.port).join(', '));
1453
+ } else {
1454
+ console.log('[HEALTH] 所有AI实例正常运行');
1455
+ }
1456
+ }, 60000); // 每分钟检查一次