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.
- package/GroupStarter.cjs +647 -0
- package/LICENSE +165 -0
- package/PropagateSignalUseJsWorker.js +92 -0
- package/README.md +102 -0
- package/Redis-8.0.3-Windows-x64-cygwin-with-Service/README.md +52 -0
- package/Redis-8.0.3-Windows-x64-cygwin-with-Service/README.zh_CN.md +59 -0
- package/Redis-8.0.3-Windows-x64-cygwin-with-Service/RedisService.exe +0 -0
- package/Redis-8.0.3-Windows-x64-cygwin-with-Service/cygcrypto-3.dll +0 -0
- package/Redis-8.0.3-Windows-x64-cygwin-with-Service/cyggcc_s-seh-1.dll +0 -0
- package/Redis-8.0.3-Windows-x64-cygwin-with-Service/cygssl-3.dll +0 -0
- package/Redis-8.0.3-Windows-x64-cygwin-with-Service/cygstdc++-6.dll +0 -0
- package/Redis-8.0.3-Windows-x64-cygwin-with-Service/cygwin1.dll +0 -0
- package/Redis-8.0.3-Windows-x64-cygwin-with-Service/cygz.dll +0 -0
- package/Redis-8.0.3-Windows-x64-cygwin-with-Service/dump.rdb +0 -0
- package/Redis-8.0.3-Windows-x64-cygwin-with-Service/install_redis_service.bat +100 -0
- package/Redis-8.0.3-Windows-x64-cygwin-with-Service/redis-benchmark.exe +0 -0
- package/Redis-8.0.3-Windows-x64-cygwin-with-Service/redis-check-aof.exe +0 -0
- package/Redis-8.0.3-Windows-x64-cygwin-with-Service/redis-check-rdb.exe +0 -0
- package/Redis-8.0.3-Windows-x64-cygwin-with-Service/redis-cli.exe +0 -0
- package/Redis-8.0.3-Windows-x64-cygwin-with-Service/redis-full.conf +376 -0
- package/Redis-8.0.3-Windows-x64-cygwin-with-Service/redis-sentinel.exe +0 -0
- package/Redis-8.0.3-Windows-x64-cygwin-with-Service/redis-server.exe +0 -0
- package/Redis-8.0.3-Windows-x64-cygwin-with-Service/redis.conf +2348 -0
- package/Redis-8.0.3-Windows-x64-cygwin-with-Service/sentinel.conf +361 -0
- package/Redis-8.0.3-Windows-x64-cygwin-with-Service/start.bat +4 -0
- package/Redis-8.0.3-Windows-x64-cygwin-with-Service/uninstall_redis_service.bat +30 -0
- package/boot.py +51 -0
- package/chat_Client.js +29 -0
- package/controller.cjs +118 -0
- package/enhancedForwarder.js +378 -0
- package/forwarder.js +1456 -0
- package/groupmanager.cjs +143 -0
- package/howToStart.txt +8 -0
- package/lemma.csv +210 -0
- package/load.py +35 -0
- package/mainManager.cjs +81 -0
- package/mainStarter.cjs +535 -0
- package/main_Serve.cjs +2745 -0
- package/main_Study.cjs +3230 -0
- package/memeMergeWorker.cjs +55 -0
- package/model_RNN.py +117 -0
- package/note.txt +5 -0
- package/notebook.txt +8 -0
- package/npminstall-debug.log +206 -0
- package/package.json +48 -0
- package/public/chat_straight.html +90 -0
- package/public/index.html +247 -0
- package/public/indexmain.html +136 -0
- package/public/monitor.html +194 -0
- package/robots/wikitext-something.txt +25 -0
- package/runtime.proto +24 -0
- package/runtime_data.json +766294 -0
- package/serializer_seq2seq.h5 +0 -0
- package/start.js +46 -0
- package/tests/test_FIrststep1.txt +1224 -0
- package/tests/test_FIrststep2.txt +2956 -0
- package/tests/test_FIrststep3.txt +1224 -0
- package/tests/test_FIrststep4.txt +1396 -0
- package/tests/test_FIrststep5.txt +2852 -0
- package/tests/test_FIrststep6.txt +1516 -0
- package/tests/test_FirstStep7.txt +1748 -0
- package/tests/test_Firstsetp8.txt +2672 -0
- package/tokenizer.json +1 -0
- package/vocabularySplitter.js +253 -0
- package/wikitext/.gitattributes +27 -0
- package/wikitext/README.md +344 -0
- package/wikitext/describtion.txt +1 -0
package/mainStarter.cjs
ADDED
|
@@ -0,0 +1,535 @@
|
|
|
1
|
+
const { spawn } = require('child_process');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const express = require('express');
|
|
5
|
+
const axios = require('axios');
|
|
6
|
+
// 轻量神经网络(避免 tfjs):优先 brain.js,回退 brainjs;以及 synaptic
|
|
7
|
+
let brain;
|
|
8
|
+
try { brain = require('brain.js'); } catch (e1) {
|
|
9
|
+
try { brain = require('brainjs'); } catch (e2) {
|
|
10
|
+
console.warn('[AGI] 未找到 brain.js/brainjs,使用最小空实现');
|
|
11
|
+
brain = { NeuralNetwork: class { constructor(){ } train(){ } run(i){ return Array.isArray(i) ? i : []; } } };
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
const synaptic = require('synaptic');
|
|
15
|
+
const Trainer = synaptic.Trainer;
|
|
16
|
+
// 可选分词器
|
|
17
|
+
let NAT = null; try { NAT = require('natural'); } catch {}
|
|
18
|
+
|
|
19
|
+
const plans = [
|
|
20
|
+
// 文本组
|
|
21
|
+
{
|
|
22
|
+
type: 'text',
|
|
23
|
+
groupsDir: path.join(__dirname, 'groups_text'),
|
|
24
|
+
masterPort: 9100,
|
|
25
|
+
portBase: 12001,
|
|
26
|
+
serveCount: 4
|
|
27
|
+
},
|
|
28
|
+
// 图片组
|
|
29
|
+
{
|
|
30
|
+
type: 'image',
|
|
31
|
+
groupsDir: path.join(__dirname, 'groups_image'),
|
|
32
|
+
masterPort: 9200,
|
|
33
|
+
portBase: 22001,
|
|
34
|
+
serveCount: 4
|
|
35
|
+
},
|
|
36
|
+
// 语音组
|
|
37
|
+
{
|
|
38
|
+
type: 'audio',
|
|
39
|
+
groupsDir: path.join(__dirname, 'groups_audio'),
|
|
40
|
+
masterPort: 9300,
|
|
41
|
+
portBase: 32001,
|
|
42
|
+
serveCount: 4
|
|
43
|
+
}
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
const children = [];
|
|
47
|
+
|
|
48
|
+
function ensureGroupsDir(dir, fallback) {
|
|
49
|
+
if (fs.existsSync(dir)) return true;
|
|
50
|
+
// 文本组若未准备,回退到基础 groups 做演示
|
|
51
|
+
if (fallback && fs.existsSync(fallback)) {
|
|
52
|
+
console.warn(`[WARN] ${dir} 不存在,临时使用 ${fallback} 作为替代(仅演示)`);
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
console.warn(`[WARN] 目标目录不存在: ${dir},请先用分片脚本生成对应组`);
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function startGroupStarter(plan) {
|
|
60
|
+
const ok = ensureGroupsDir(plan.groupsDir, path.join(__dirname, 'groups'));
|
|
61
|
+
if (!ok) return;
|
|
62
|
+
|
|
63
|
+
const script = path.join(__dirname, 'GroupStarter.cjs');
|
|
64
|
+
const args = [
|
|
65
|
+
script,
|
|
66
|
+
'--groups-dir', plan.groupsDir,
|
|
67
|
+
'--master-port', String(plan.masterPort),
|
|
68
|
+
'--port-base', String(plan.portBase),
|
|
69
|
+
'--serve-count', String(plan.serveCount),
|
|
70
|
+
'--group-start-delay', '3000'
|
|
71
|
+
];
|
|
72
|
+
console.log(`[MAIN-START] 启动 ${plan.type} 组: node ${args.join(' ')}`);
|
|
73
|
+
const p = spawn('node', args, { stdio: 'inherit' });
|
|
74
|
+
children.push(p);
|
|
75
|
+
p.on('close', (code) => {
|
|
76
|
+
console.log(`[MAIN-START] 组 ${plan.type} 退出, code=${code}`);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function main() {
|
|
81
|
+
plans.forEach(startGroupStarter);
|
|
82
|
+
console.log('[MAIN-START] 三大工作组启动中,稍候访问:');
|
|
83
|
+
console.log(' 文本: http://localhost:9100 图片: http://localhost:9200 语音: http://localhost:9300');
|
|
84
|
+
startAgiOrchestrator();
|
|
85
|
+
}
|
|
86
|
+
main();
|
|
87
|
+
|
|
88
|
+
process.on('SIGINT', () => {
|
|
89
|
+
console.log('[MAIN-START] 退出信号,关闭子进程...');
|
|
90
|
+
for (const c of children) {
|
|
91
|
+
try { c.kill(); } catch {}
|
|
92
|
+
}
|
|
93
|
+
process.exit(0);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// ============ 通用 AGI 编排 API(基于三大组 + brain/synaptic) ============
|
|
97
|
+
function startAgiOrchestrator() {
|
|
98
|
+
const app = express();
|
|
99
|
+
app.use(express.json({ limit: '50mb' }));
|
|
100
|
+
app.use('/public', express.static(path.join(__dirname, 'public')));
|
|
101
|
+
app.get(['/','/indexmain','/index'], (req,res)=>{
|
|
102
|
+
res.sendFile(path.join(__dirname,'public','indexmain.html'));
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const MASTER = {
|
|
106
|
+
text: 9100,
|
|
107
|
+
image: 9200,
|
|
108
|
+
audio: 9300,
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// --- 辅助:调用三大组 ---
|
|
112
|
+
async function callGroup(port, message, timeout = 45000) {
|
|
113
|
+
const url = `http://localhost:${port}/api/chat`;
|
|
114
|
+
const r = await axios.post(url, { message }, { timeout });
|
|
115
|
+
return r.data && (r.data.top || r.data.response) ? (r.data.top || r.data.response) : '';
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// --- 模型 & 预训练配置 ---
|
|
119
|
+
const MODEL_DIR = path.join(__dirname, 'logs', 'brain_synaptic');
|
|
120
|
+
const MODEL_BRAIN = path.join(MODEL_DIR, 'brain.json');
|
|
121
|
+
const MODEL_SYNA = path.join(MODEL_DIR, 'synaptic.json');
|
|
122
|
+
const MODEL_VEC = path.join(MODEL_DIR, 'vectorizer.json');
|
|
123
|
+
function ensureDir(p){ try{ fs.mkdirSync(p, { recursive: true }); } catch {} }
|
|
124
|
+
ensureDir(MODEL_DIR);
|
|
125
|
+
const CFG = {
|
|
126
|
+
WIKITEXT: path.join(__dirname, './wikitext/', 'wikitext-103-all.txt'),
|
|
127
|
+
HEAD_BYTES: Math.min(2*1024*1024, Number(process.env.WIKI_HEAD_BYTES)|| (2*1024*1024)),
|
|
128
|
+
VEC_DIM: Math.min(1024, Number(process.env.VEC_DIM)||256),
|
|
129
|
+
VEC_VOCAB_SIZE: Math.min(5000, Number(process.env.VEC_VOCAB_SIZE)||800),
|
|
130
|
+
HASH_SEED: Number(process.env.VEC_HASH_SEED)||1337,
|
|
131
|
+
BRAIN_TRAIN_SAMPLES: Math.min(1500, Number(process.env.BRAIN_TRAIN_SAMPLES)||400),
|
|
132
|
+
BRAIN_TRAIN_ITER: Math.min(600, Number(process.env.BRAIN_TRAIN_ITER)||220),
|
|
133
|
+
SYNA_TRAIN_SAMPLES: Math.min(2000, Number(process.env.SYNA_TRAIN_SAMPLES)||600),
|
|
134
|
+
SYNA_TRAIN_ITER: Math.min(8, Number(process.env.SYNA_TRAIN_ITER)||2),
|
|
135
|
+
TRAIN_ENABLE: (process.env.AGI_TRAIN||'1') !== '0',
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
function readFileHead(fp, maxBytes){
|
|
139
|
+
try{
|
|
140
|
+
if(!fs.existsSync(fp)) return '';
|
|
141
|
+
const fd = fs.openSync(fp,'r');
|
|
142
|
+
const buf = Buffer.allocUnsafe(maxBytes);
|
|
143
|
+
const n = fs.readSync(fd, buf, 0, maxBytes, 0);
|
|
144
|
+
fs.closeSync(fd);
|
|
145
|
+
return buf.slice(0, n).toString('utf8');
|
|
146
|
+
}catch{ return ''; }
|
|
147
|
+
}
|
|
148
|
+
function sampleLinesFromText(txt, max){
|
|
149
|
+
const lines = txt.split(/\r?\n/).map(s=>s.trim()).filter(Boolean);
|
|
150
|
+
const out = [];
|
|
151
|
+
for (let i=0;i<lines.length && out.length<max;i++){
|
|
152
|
+
const s = lines[i];
|
|
153
|
+
if (s.length>=8) out.push(s);
|
|
154
|
+
}
|
|
155
|
+
return out;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// === 文本清洗 / 分词 / 哈希向量化 ===
|
|
159
|
+
const styleBank = [
|
|
160
|
+
'high quality','detailed','4k','sharp focus','cinematic','hdr','photorealistic','artistic','dynamic lighting','bokeh','volumetric light','film grain','texture rich'
|
|
161
|
+
];
|
|
162
|
+
function basicClean(s) {
|
|
163
|
+
if (!s) return '';
|
|
164
|
+
let t = String(s)
|
|
165
|
+
.replace(/https?:\/\/[\w\-._~:/?#[\]@!$&'()*+,;=%]+/gi, ' ')
|
|
166
|
+
.replace(/\s+/g, ' ')
|
|
167
|
+
.trim();
|
|
168
|
+
// 限长防止爆内存
|
|
169
|
+
if (t.length > 512) t = t.slice(0, 512);
|
|
170
|
+
return t;
|
|
171
|
+
}
|
|
172
|
+
function tokenize(s){
|
|
173
|
+
const t = basicClean(s);
|
|
174
|
+
if (!t) return [];
|
|
175
|
+
if (NAT) {
|
|
176
|
+
try { const tok = new NAT.WordTokenizer(); return tok.tokenize(t).map(x=>x.toLowerCase()); } catch {}
|
|
177
|
+
}
|
|
178
|
+
return t.toLowerCase().split(/[^a-z0-9_\-\u4e00-\u9fa5]+/).filter(Boolean);
|
|
179
|
+
}
|
|
180
|
+
function hashStr(str, seed){
|
|
181
|
+
// 简易 FNV-1a 变体
|
|
182
|
+
let h = seed>>>0;
|
|
183
|
+
for (let i=0;i<str.length;i++) { h ^= str.charCodeAt(i); h = Math.imul(h, 16777619); }
|
|
184
|
+
return (h>>>0);
|
|
185
|
+
}
|
|
186
|
+
function ngrams(tokens, n){
|
|
187
|
+
const out = [];
|
|
188
|
+
for (let i=0;i<=tokens.length-n;i++) out.push(tokens.slice(i,i+n).join(' '));
|
|
189
|
+
return out;
|
|
190
|
+
}
|
|
191
|
+
// 构建/加载向量化器(固定维度 + 词表映射到哈希桶)
|
|
192
|
+
let VEC = { dim: CFG.VEC_DIM, seed: CFG.HASH_SEED, vocab: [], bucketOf:{} };
|
|
193
|
+
function loadVectorizer(){ try{ if (fs.existsSync(MODEL_VEC)) { VEC = JSON.parse(fs.readFileSync(MODEL_VEC,'utf8')); return true; } } catch{} return false; }
|
|
194
|
+
function saveVectorizer(){ try{ fs.writeFileSync(MODEL_VEC, JSON.stringify(VEC)); } catch{} }
|
|
195
|
+
function buildVectorizer(lines){
|
|
196
|
+
const freq = new Map();
|
|
197
|
+
for (const ln of lines){
|
|
198
|
+
const tks = tokenize(ln); for (const t of tks) freq.set(t, (freq.get(t)||0)+1);
|
|
199
|
+
for (const bg of ngrams(tks,2)) freq.set(bg, (freq.get(bg)||0)+1);
|
|
200
|
+
}
|
|
201
|
+
const top = Array.from(freq.entries()).sort((a,b)=>b[1]-a[1]).slice(0, CFG.VEC_VOCAB_SIZE).map(x=>x[0]);
|
|
202
|
+
VEC.vocab = top;
|
|
203
|
+
VEC.bucketOf = {};
|
|
204
|
+
for (const tok of top) VEC.bucketOf[tok] = hashStr(tok, VEC.seed) % VEC.dim;
|
|
205
|
+
saveVectorizer();
|
|
206
|
+
}
|
|
207
|
+
function ensureVectorizer(){
|
|
208
|
+
if (loadVectorizer()) return;
|
|
209
|
+
const head = readFileHead(CFG.WIKITEXT, CFG.HEAD_BYTES);
|
|
210
|
+
const lines = head ? sampleLinesFromText(head, CFG.VEC_VOCAB_SIZE*3) : [];
|
|
211
|
+
buildVectorizer(lines);
|
|
212
|
+
}
|
|
213
|
+
ensureVectorizer();
|
|
214
|
+
function textToVector(s){
|
|
215
|
+
const vec = new Array(VEC.dim).fill(0);
|
|
216
|
+
const tks = tokenize(s); if (tks.length===0) return vec;
|
|
217
|
+
const grams = tks.concat(ngrams(tks,2));
|
|
218
|
+
for (const g of grams) {
|
|
219
|
+
const b = (VEC.bucketOf[g] !== undefined) ? VEC.bucketOf[g] : (hashStr(g, VEC.seed) % VEC.dim);
|
|
220
|
+
vec[b] += 1;
|
|
221
|
+
}
|
|
222
|
+
// 归一化到 [0,1]
|
|
223
|
+
let maxv = 0; for (const v of vec) if (v>maxv) maxv=v;
|
|
224
|
+
if (maxv>0) for (let i=0;i<vec.length;i++) vec[i] = vec[i]/maxv;
|
|
225
|
+
return vec;
|
|
226
|
+
}
|
|
227
|
+
function vectorToText(vec){
|
|
228
|
+
// 取 top-k 维度,映射回对应的 vocab token(可能有碰撞,尽量去重)
|
|
229
|
+
const k = Math.min(12, Math.max(6, Math.floor(VEC.dim/32)));
|
|
230
|
+
const idx = vec.map((v,i)=>[v,i]).sort((a,b)=>b[0]-a[0]).slice(0, k).map(x=>x[1]);
|
|
231
|
+
const chosen = [];
|
|
232
|
+
const used = new Set();
|
|
233
|
+
for (const i of idx){
|
|
234
|
+
for (const tok of VEC.vocab){
|
|
235
|
+
if (VEC.bucketOf[tok] === i && !used.has(tok)) { chosen.push(tok); used.add(tok); if (chosen.length>=k) break; }
|
|
236
|
+
}
|
|
237
|
+
if (chosen.length>=k) break;
|
|
238
|
+
}
|
|
239
|
+
return chosen.join(' ');
|
|
240
|
+
}
|
|
241
|
+
// --- brainjs: 提示词增强网络(词袋向量 -> 词袋向量)---
|
|
242
|
+
const brainNet = new brain.NeuralNetwork({ hiddenLayers: [Math.max(16, Math.floor(CFG.VEC_DIM/6))] });
|
|
243
|
+
function buildBrainPairs(lines, n) {
|
|
244
|
+
const out = [];
|
|
245
|
+
for (let i=0;i<lines.length && out.length<n;i++){
|
|
246
|
+
const phrase = lines[i].split(/[\.\!\?]/)[0].trim().slice(0, 96);
|
|
247
|
+
if (!phrase || phrase.length<3) continue;
|
|
248
|
+
const s1 = styleBank[Math.floor(Math.random()*styleBank.length)];
|
|
249
|
+
const s2 = styleBank[Math.floor(Math.random()*styleBank.length)];
|
|
250
|
+
const enhanced = `${phrase}, ${s1}, ${s2}`;
|
|
251
|
+
out.push({ input: textToVector(phrase), output: textToVector(enhanced) });
|
|
252
|
+
}
|
|
253
|
+
// 若样本不足,补充一些手工模板
|
|
254
|
+
if (out.length < Math.max(10, Math.floor(n/4))) {
|
|
255
|
+
const seeds = [
|
|
256
|
+
'draw a cat','a landscape','portrait','city at night','seaside village','forest path','mountain view'
|
|
257
|
+
];
|
|
258
|
+
for (const s of seeds) {
|
|
259
|
+
const s1 = styleBank[Math.floor(Math.random()*styleBank.length)];
|
|
260
|
+
const s2 = styleBank[Math.floor(Math.random()*styleBank.length)];
|
|
261
|
+
out.push({ input: textToVector(s), output: textToVector(`${s}, ${s1}, ${s2}`) });
|
|
262
|
+
if (out.length>=n) break;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return out;
|
|
266
|
+
}
|
|
267
|
+
function loadBrain(){ try{ if (fs.existsSync(MODEL_BRAIN)) { const j = JSON.parse(fs.readFileSync(MODEL_BRAIN,'utf8')); brainNet.fromJSON(j); return true; } } catch{} return false; }
|
|
268
|
+
function saveBrain(){ try{ fs.writeFileSync(MODEL_BRAIN, JSON.stringify(brainNet.toJSON())); } catch{} }
|
|
269
|
+
function trainBrainNet() {
|
|
270
|
+
if (loadBrain()) { console.log('[AGI][brain] 已加载已存模型'); return; }
|
|
271
|
+
if (!CFG.TRAIN_ENABLE) { console.log('[AGI][brain] 跳过训练'); return; }
|
|
272
|
+
const head = readFileHead(CFG.WIKITEXT, CFG.HEAD_BYTES);
|
|
273
|
+
const lines = head ? sampleLinesFromText(head, CFG.BRAIN_TRAIN_SAMPLES*2) : [];
|
|
274
|
+
const trainData = buildBrainPairs(lines, CFG.BRAIN_TRAIN_SAMPLES);
|
|
275
|
+
try {
|
|
276
|
+
brainNet.train(trainData, { iterations: CFG.BRAIN_TRAIN_ITER, errorThresh: 0.01, log: false });
|
|
277
|
+
saveBrain();
|
|
278
|
+
console.log('[AGI][brain] 训练完成,样本', trainData.length);
|
|
279
|
+
} catch(e){ console.warn('[AGI][brain] 训练失败,使用默认小模型:', e.message);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
trainBrainNet();
|
|
283
|
+
function cosine(a,b){ let dot=0,na=0,nb=0; for(let i=0;i<a.length;i++){dot+=a[i]*b[i]; na+=a[i]*a[i]; nb+=b[i]*b[i];} if(na===0||nb===0) return 0; return dot/Math.sqrt(na*nb); }
|
|
284
|
+
function refinePromptWithBrain(prompt) {
|
|
285
|
+
try {
|
|
286
|
+
const inp = textToVector(prompt);
|
|
287
|
+
const v = brainNet.run(inp);
|
|
288
|
+
const gen = vectorToText(v);
|
|
289
|
+
const out = `${prompt} ${gen}`.trim();
|
|
290
|
+
// 质量门:余弦相似度 & 可读性
|
|
291
|
+
const sim = cosine(inp, Array.isArray(v)?v:Object.values(v));
|
|
292
|
+
if (!looksPlausibleText(out) || sim<0.1) return basicClean(prompt);
|
|
293
|
+
return basicClean(out);
|
|
294
|
+
} catch { return prompt; }
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// --- synaptic: 文本清洗/规整(微小感知器) ---
|
|
298
|
+
const Layer = synaptic.Layer; const Network = synaptic.Network;
|
|
299
|
+
const inputLayer = new Layer(CFG.VEC_DIM); const hiddenLayer = new Layer(Math.max(24, Math.floor(CFG.VEC_DIM/2))); const outputLayer = new Layer(CFG.VEC_DIM);
|
|
300
|
+
inputLayer.project(hiddenLayer); hiddenLayer.project(outputLayer);
|
|
301
|
+
const synNet = new Network({ input: inputLayer, hidden: [hiddenLayer], output: outputLayer });
|
|
302
|
+
function loadSyna(){ try{ if (fs.existsSync(MODEL_SYNA)) { const j = JSON.parse(fs.readFileSync(MODEL_SYNA,'utf8')); synNet.fromJSON(j); return true; } } catch{} return false; }
|
|
303
|
+
function saveSyna(){ try{ fs.writeFileSync(MODEL_SYNA, JSON.stringify(synNet.toJSON())); } catch{} }
|
|
304
|
+
function buildSynaSet(lines, n) {
|
|
305
|
+
const set = [];
|
|
306
|
+
for (let i=0;i<lines.length && set.length<n;i++){
|
|
307
|
+
const phrase = lines[i].slice(0,96);
|
|
308
|
+
if (phrase.length<3) continue;
|
|
309
|
+
// 构造轻度噪音输入(dropout + 小幅噪声),目标为原文(去噪自编码)
|
|
310
|
+
const clean = textToVector(phrase);
|
|
311
|
+
const noisy = clean.map(v => (Math.random()<0.15 ? 0 : Math.max(0, Math.min(1, v + (Math.random()-0.5)*0.05))));
|
|
312
|
+
set.push({ input: noisy, output: clean });
|
|
313
|
+
}
|
|
314
|
+
return set;
|
|
315
|
+
}
|
|
316
|
+
function trainSyna(){
|
|
317
|
+
if (loadSyna()) { console.log('[AGI][synaptic] 已加载已存模型'); return; }
|
|
318
|
+
if (!CFG.TRAIN_ENABLE) { console.log('[AGI][synaptic] 跳过训练'); return; }
|
|
319
|
+
const head = readFileHead(CFG.WIKITEXT, CFG.HEAD_BYTES);
|
|
320
|
+
const lines = head ? sampleLinesFromText(head, CFG.SYNA_TRAIN_SAMPLES*2) : [];
|
|
321
|
+
const trainSet = buildSynaSet(lines, CFG.SYNA_TRAIN_SAMPLES);
|
|
322
|
+
try {
|
|
323
|
+
const trainer = new Trainer(synNet);
|
|
324
|
+
trainer.train(trainSet, { iterations: CFG.SYNA_TRAIN_ITER, rate: 0.1, error: 0.01, shuffle: true, log: false });
|
|
325
|
+
saveSyna();
|
|
326
|
+
console.log('[AGI][synaptic] 训练完成,样本', trainSet.length);
|
|
327
|
+
} catch(e){ console.warn('[AGI][synaptic] 训练失败,继续使用随机初始:', e.message); }
|
|
328
|
+
}
|
|
329
|
+
trainSyna();
|
|
330
|
+
function synRun(s) {
|
|
331
|
+
const i = textToVector(s); const o = synNet.activate(i);
|
|
332
|
+
return vectorToText(o);
|
|
333
|
+
}
|
|
334
|
+
function looksPlausibleText(s) {
|
|
335
|
+
if (!s) return false;
|
|
336
|
+
const t = basicClean(s);
|
|
337
|
+
if (t.length < 3) return false;
|
|
338
|
+
const letters = (t.match(/[A-Za-z\u4e00-\u9fa5]/g) || []).length;
|
|
339
|
+
const digits = (t.match(/\d/g) || []).length;
|
|
340
|
+
const ratio = (letters + digits) / t.length;
|
|
341
|
+
return ratio >= 0.3; // 至少 30% 为字母/数字
|
|
342
|
+
}
|
|
343
|
+
function normalizeTextWithSynaptic(s) {
|
|
344
|
+
try {
|
|
345
|
+
const out = synRun(s);
|
|
346
|
+
const t = basicClean(out);
|
|
347
|
+
if (!looksPlausibleText(t)) return basicClean(s);
|
|
348
|
+
return t;
|
|
349
|
+
} catch { return basicClean(s); }
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// 1) 文本对话:走文本大组
|
|
353
|
+
app.post('/api/agi/chat', async (req, res) => {
|
|
354
|
+
try {
|
|
355
|
+
const { message } = req.body || {}; if (!message) return res.status(400).json({ error: 'message required' });
|
|
356
|
+
const refined = normalizeTextWithSynaptic(message);
|
|
357
|
+
const resp = await callGroup(MASTER.text, refined);
|
|
358
|
+
res.json({ ok: true, response: resp });
|
|
359
|
+
} catch (e) { res.status(500).json({ ok: false, error: e.message }); }
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
// 1.1) 各大组分别对话
|
|
363
|
+
app.post('/api/agi/chat-text', async (req,res)=>{
|
|
364
|
+
try { const { message } = req.body||{}; if (!message) return res.status(400).json({ error: 'message required' });
|
|
365
|
+
const refined = normalizeTextWithSynaptic(message);
|
|
366
|
+
const resp = await callGroup(MASTER.text, refined);
|
|
367
|
+
res.json({ ok:true, response: resp });
|
|
368
|
+
} catch(e){ res.status(500).json({ ok:false, error:e.message }); }
|
|
369
|
+
});
|
|
370
|
+
app.post('/api/agi/chat-image', async (req,res)=>{
|
|
371
|
+
try { const { imageUrl, message } = req.body||{}; if (!imageUrl && !message) return res.status(400).json({ error: 'imageUrl or message required' });
|
|
372
|
+
const q = basicClean(message||'');
|
|
373
|
+
const prompt = `IMG-CHAT ${imageUrl?`URL:${imageUrl} `:''}${q}`.trim();
|
|
374
|
+
const resp = await callGroup(MASTER.image, prompt, 60000);
|
|
375
|
+
res.json({ ok:true, response: resp });
|
|
376
|
+
} catch(e){ res.status(500).json({ ok:false, error:e.message }); }
|
|
377
|
+
});
|
|
378
|
+
app.post('/api/agi/chat-audio', async (req,res)=>{
|
|
379
|
+
try { const { audioUrl, message } = req.body||{}; if (!audioUrl && !message) return res.status(400).json({ error: 'audioUrl or message required' });
|
|
380
|
+
const q = basicClean(message||'');
|
|
381
|
+
const prompt = `AUDIO-CHAT ${audioUrl?`URL:${audioUrl} `:''}${q}`.trim();
|
|
382
|
+
const resp = await callGroup(MASTER.audio, prompt, 60000);
|
|
383
|
+
res.json({ ok:true, response: resp });
|
|
384
|
+
} catch(e){ res.status(500).json({ ok:false, error:e.message }); }
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
// 2) 图片描述:图片->文本(由图片组),再文本组润色
|
|
388
|
+
app.post('/api/agi/describe-image', async (req, res) => {
|
|
389
|
+
try {
|
|
390
|
+
const { imageUrl, imageHint } = req.body || {}; // 简化:通过 hint 引导
|
|
391
|
+
const captionReq = `CAPTION REQUEST ${imageUrl ? `URL:${imageUrl}` : ''} ${imageHint || ''}`.trim();
|
|
392
|
+
const caption = await callGroup(MASTER.image, captionReq, 60000);
|
|
393
|
+
const refined = await callGroup(MASTER.text, `Polish: ${caption}`, 30000);
|
|
394
|
+
res.json({ ok: true, caption, refined });
|
|
395
|
+
} catch (e) { res.status(500).json({ ok: false, error: e.message }); }
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
// 3) 文生图:文本组扩写提示 + 图片组生成
|
|
399
|
+
app.post('/api/agi/generate-image', async (req, res) => {
|
|
400
|
+
try {
|
|
401
|
+
const { prompt } = req.body || {}; if (!prompt) return res.status(400).json({ error: 'prompt required' });
|
|
402
|
+
const brainPrompt = refinePromptWithBrain(prompt);
|
|
403
|
+
const refined = await callGroup(MASTER.text, `Expand prompt for image generation: ${brainPrompt}`, 30000);
|
|
404
|
+
const finalPrompt = `${prompt}. ${refined}`;
|
|
405
|
+
const image = await callGroup(MASTER.image, `DRAW: ${finalPrompt}`, 60000);
|
|
406
|
+
res.json({ ok: true, prompt: finalPrompt, image });
|
|
407
|
+
} catch (e) { res.status(500).json({ ok: false, error: e.message }); }
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
// 4) 语音转写:语音组 ASR -> 文本组理解
|
|
411
|
+
app.post('/api/agi/transcribe', async (req, res) => {
|
|
412
|
+
try {
|
|
413
|
+
const { audioUrl, hint } = req.body || {}; if (!audioUrl) return res.status(400).json({ error: 'audioUrl required' });
|
|
414
|
+
const text = await callGroup(MASTER.audio, `ASR URL:${audioUrl} ${hint || ''}`.trim(), 60000);
|
|
415
|
+
const nlp = await callGroup(MASTER.text, text, 30000);
|
|
416
|
+
res.json({ ok: true, transcript: text, nlp });
|
|
417
|
+
} catch (e) { res.status(500).json({ ok: false, error: e.message }); }
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
// 5) 文本转语音:文本组改写 -> 语音组 TTS
|
|
421
|
+
app.post('/api/agi/tts', async (req, res) => {
|
|
422
|
+
try {
|
|
423
|
+
const { text } = req.body || {}; if (!text) return res.status(400).json({ error: 'text required' });
|
|
424
|
+
const refined = await callGroup(MASTER.text, `Make this sentence speak-friendly: ${text}`, 30000);
|
|
425
|
+
const audio = await callGroup(MASTER.audio, `TTS: ${refined}`, 60000);
|
|
426
|
+
res.json({ ok: true, text: refined, audio });
|
|
427
|
+
} catch (e) { res.status(500).json({ ok: false, error: e.message }); }
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
// === 六个互转(标准命名)===
|
|
431
|
+
// text -> image
|
|
432
|
+
app.post('/api/agi/text-to-image', async (req,res)=>{
|
|
433
|
+
try { const { text } = req.body||{}; if (!text) return res.status(400).json({ error: 'text required' });
|
|
434
|
+
const brainPrompt = refinePromptWithBrain(text);
|
|
435
|
+
const refined = await callGroup(MASTER.text, `Expand prompt for image generation: ${brainPrompt}`, 30000);
|
|
436
|
+
const finalPrompt = `${text}. ${refined}`;
|
|
437
|
+
const image = await callGroup(MASTER.image, `DRAW: ${finalPrompt}`, 60000);
|
|
438
|
+
res.json({ ok:true, prompt: finalPrompt, image });
|
|
439
|
+
} catch(e){ res.status(500).json({ ok:false, error:e.message }); }
|
|
440
|
+
});
|
|
441
|
+
// image -> text
|
|
442
|
+
app.post('/api/agi/image-to-text', async (req,res)=>{
|
|
443
|
+
try { const { imageUrl, imageHint } = req.body||{}; if (!imageUrl && !imageHint) return res.status(400).json({ error: 'imageUrl or imageHint required' });
|
|
444
|
+
const captionReq = `CAPTION REQUEST ${imageUrl?`URL:${imageUrl}`:''} ${imageHint||''}`.trim();
|
|
445
|
+
const caption = await callGroup(MASTER.image, captionReq, 60000);
|
|
446
|
+
const refined = await callGroup(MASTER.text, `Polish: ${caption}`, 30000);
|
|
447
|
+
res.json({ ok:true, caption, text: refined });
|
|
448
|
+
} catch(e){ res.status(500).json({ ok:false, error:e.message }); }
|
|
449
|
+
});
|
|
450
|
+
// text -> audio
|
|
451
|
+
app.post('/api/agi/text-to-audio', async (req,res)=>{
|
|
452
|
+
try { const { text } = req.body||{}; if (!text) return res.status(400).json({ error: 'text required' });
|
|
453
|
+
const refined = await callGroup(MASTER.text, `Make this sentence speak-friendly: ${text}`, 30000);
|
|
454
|
+
const audio = await callGroup(MASTER.audio, `TTS: ${refined}`, 60000);
|
|
455
|
+
res.json({ ok:true, text: refined, audio });
|
|
456
|
+
} catch(e){ res.status(500).json({ ok:false, error:e.message }); }
|
|
457
|
+
});
|
|
458
|
+
// audio -> text
|
|
459
|
+
app.post('/api/agi/audio-to-text', async (req,res)=>{
|
|
460
|
+
try { const { audioUrl, hint } = req.body||{}; if (!audioUrl) return res.status(400).json({ error: 'audioUrl required' });
|
|
461
|
+
const text = await callGroup(MASTER.audio, `ASR URL:${audioUrl} ${hint||''}`.trim(), 60000);
|
|
462
|
+
const nlp = await callGroup(MASTER.text, text, 30000);
|
|
463
|
+
res.json({ ok:true, transcript: text, text: nlp });
|
|
464
|
+
} catch(e){ res.status(500).json({ ok:false, error:e.message }); }
|
|
465
|
+
});
|
|
466
|
+
// image -> audio (先 caption,再 TTS)
|
|
467
|
+
app.post('/api/agi/image-to-audio', async (req,res)=>{
|
|
468
|
+
try { const { imageUrl, imageHint } = req.body||{}; if (!imageUrl && !imageHint) return res.status(400).json({ error: 'imageUrl or imageHint required' });
|
|
469
|
+
const captionReq = `CAPTION REQUEST ${imageUrl?`URL:${imageUrl}`:''} ${imageHint||''}`.trim();
|
|
470
|
+
const caption = await callGroup(MASTER.image, captionReq, 60000);
|
|
471
|
+
const refined = await callGroup(MASTER.text, `Make speak-friendly: ${caption}`, 30000);
|
|
472
|
+
const audio = await callGroup(MASTER.audio, `TTS: ${refined}`, 60000);
|
|
473
|
+
res.json({ ok:true, caption, audio });
|
|
474
|
+
} catch(e){ res.status(500).json({ ok:false, error:e.message }); }
|
|
475
|
+
});
|
|
476
|
+
// audio -> image (先 ASR,再绘图)
|
|
477
|
+
app.post('/api/agi/audio-to-image', async (req,res)=>{
|
|
478
|
+
try { const { audioUrl, hint } = req.body||{}; if (!audioUrl) return res.status(400).json({ error: 'audioUrl required' });
|
|
479
|
+
const text = await callGroup(MASTER.audio, `ASR URL:${audioUrl} ${hint||''}`.trim(), 60000);
|
|
480
|
+
const brainPrompt = refinePromptWithBrain(text);
|
|
481
|
+
const refined = await callGroup(MASTER.text, `Expand prompt for image generation: ${brainPrompt}`, 30000);
|
|
482
|
+
const finalPrompt = `${text}. ${refined}`;
|
|
483
|
+
const image = await callGroup(MASTER.image, `DRAW: ${finalPrompt}`, 60000);
|
|
484
|
+
res.json({ ok:true, prompt: finalPrompt, image });
|
|
485
|
+
} catch(e){ res.status(500).json({ ok:false, error:e.message }); }
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
// === 统一编排入口:多模态输入 -> 目标输出 ===
|
|
489
|
+
app.post('/api/agi/convert', async (req,res)=>{
|
|
490
|
+
try {
|
|
491
|
+
const { text, imageUrl, audioUrl, target } = req.body||{};
|
|
492
|
+
if (!target || !['text','image','audio'].includes(target)) return res.status(400).json({ error: 'target must be text|image|audio' });
|
|
493
|
+
|
|
494
|
+
const parts = [];
|
|
495
|
+
// 将所有输入尽量转为文本语义片段
|
|
496
|
+
if (text) parts.push(normalizeTextWithSynaptic(text));
|
|
497
|
+
if (imageUrl) {
|
|
498
|
+
const cap = await callGroup(MASTER.image, `CAPTION REQUEST URL:${imageUrl}`, 60000);
|
|
499
|
+
parts.push(cap);
|
|
500
|
+
}
|
|
501
|
+
if (audioUrl) {
|
|
502
|
+
const asr = await callGroup(MASTER.audio, `ASR URL:${audioUrl}`.trim(), 60000);
|
|
503
|
+
parts.push(asr);
|
|
504
|
+
}
|
|
505
|
+
const merged = parts.join('. ');
|
|
506
|
+
|
|
507
|
+
if (target === 'text') {
|
|
508
|
+
const out = await callGroup(MASTER.text, `Polish & merge: ${merged}`, 30000);
|
|
509
|
+
return res.json({ ok:true, text: out });
|
|
510
|
+
}
|
|
511
|
+
if (target === 'image') {
|
|
512
|
+
const brainPrompt = refinePromptWithBrain(merged);
|
|
513
|
+
const refined = await callGroup(MASTER.text, `Expand prompt for image generation: ${brainPrompt}`, 30000);
|
|
514
|
+
const finalPrompt = `${merged}. ${refined}`;
|
|
515
|
+
const image = await callGroup(MASTER.image, `DRAW: ${finalPrompt}`, 60000);
|
|
516
|
+
return res.json({ ok:true, prompt: finalPrompt, image });
|
|
517
|
+
}
|
|
518
|
+
if (target === 'audio') {
|
|
519
|
+
const speech = await callGroup(MASTER.text, `Make this sentence speak-friendly: ${merged}`, 30000);
|
|
520
|
+
const audio = await callGroup(MASTER.audio, `TTS: ${speech}`, 60000);
|
|
521
|
+
return res.json({ ok:true, text: speech, audio });
|
|
522
|
+
}
|
|
523
|
+
} catch(e){ res.status(500).json({ ok:false, error:e.message }); }
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
const PORT = Number(process.env.AGI_PORT || 9400);
|
|
527
|
+
app.listen(PORT, () => {
|
|
528
|
+
console.log(`[AGI] 编排层已启动,端口 ${PORT}`);
|
|
529
|
+
console.log(`[AGI] APIs:`);
|
|
530
|
+
console.log(' /api/agi/chat, /api/agi/chat-text, /api/agi/chat-image, /api/agi/chat-audio');
|
|
531
|
+
console.log(' /api/agi/text-to-image, /api/agi/image-to-text, /api/agi/text-to-audio, /api/agi/audio-to-text, /api/agi/image-to-audio, /api/agi/audio-to-image');
|
|
532
|
+
console.log(' /api/agi/convert');
|
|
533
|
+
console.log(` UI: http://localhost:${PORT}/indexmain`);
|
|
534
|
+
});
|
|
535
|
+
}
|