English
technician1 commited on
Commit
37ed9a9
·
verified ·
1 Parent(s): 0374172

Upload ChatIPCv2.cpp

Browse files
Files changed (1) hide show
  1. ChatIPCv2.cpp +713 -0
ChatIPCv2.cpp ADDED
@@ -0,0 +1,713 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #include <algorithm>
2
+ #include <cstdint>
3
+ #include <cstdio>
4
+ #include <fstream>
5
+ #include <iostream>
6
+ #include <mutex>
7
+ #include <optional>
8
+ #include <sstream>
9
+ #include <stdexcept>
10
+ #include <string>
11
+ #include <unordered_map>
12
+ #include <unordered_set>
13
+ #include <vector>
14
+
15
+ #ifdef _OPENMP
16
+ #include <omp.h>
17
+ #else
18
+ inline int omp_get_max_threads(){ return 1; }
19
+ #endif
20
+
21
+ using namespace std;
22
+
23
+ static vector<string> tokenize_whitespace(const string &s){
24
+ istringstream iss(s);
25
+ vector<string> out;
26
+ string t;
27
+ while (iss >> t) out.push_back(t);
28
+ return out;
29
+ }
30
+
31
+ using StrPtr = const string*;
32
+ using TokenId = uint32_t;
33
+ static constexpr TokenId TOKEN_ID_INVALID = 0xFFFFFFFFu;
34
+
35
+ static inline size_t popcount_u64(uint64_t x){
36
+ #if defined(_MSC_VER)
37
+ return static_cast<size_t>(__popcnt64(x));
38
+ #else
39
+ return static_cast<size_t>(__builtin_popcountll(static_cast<unsigned long long>(x)));
40
+ #endif
41
+ }
42
+
43
+ struct StringInterner {
44
+ unordered_set<string> pool;
45
+ unordered_map<string, TokenId> id_by_string;
46
+ vector<const string*> string_by_id;
47
+ mutable mutex m;
48
+
49
+ const string* intern(const string &s){
50
+ lock_guard<mutex> lk(m);
51
+
52
+ auto [it, inserted] = pool.emplace(s);
53
+ if (inserted){
54
+ const TokenId id = static_cast<TokenId>(string_by_id.size());
55
+ id_by_string.emplace(*it, id);
56
+ string_by_id.push_back(&*it);
57
+ }
58
+
59
+ return &*it;
60
+ }
61
+
62
+ TokenId id_of(const string &s) const {
63
+ lock_guard<mutex> lk(m);
64
+
65
+ auto it = id_by_string.find(s);
66
+ return (it == id_by_string.end()) ? TOKEN_ID_INVALID : it->second;
67
+ }
68
+
69
+ TokenId id_of(const string *p) const {
70
+ return p ? id_of(*p) : TOKEN_ID_INVALID;
71
+ }
72
+
73
+ size_t size() const {
74
+ lock_guard<mutex> lk(m);
75
+ return string_by_id.size();
76
+ }
77
+ };
78
+
79
+ static inline void bitset_set(uint64_t *bits, size_t words, TokenId id){
80
+ if (id == TOKEN_ID_INVALID) return;
81
+ const size_t idx = static_cast<size_t>(id >> 6);
82
+ if (idx >= words) return;
83
+ bits[idx] |= (1ULL << (id & 63u));
84
+ }
85
+
86
+ static inline size_t bitset_count(const uint64_t *bits, size_t words){
87
+ size_t total = 0;
88
+ for (size_t i = 0; i < words; ++i) total += popcount_u64(bits[i]);
89
+ return total;
90
+ }
91
+
92
+ using NextSet = vector<StrPtr>;
93
+
94
+ struct NgramHash {
95
+ size_t operator()(const vector<StrPtr>& v) const noexcept {
96
+ size_t seed = v.size();
97
+ for(auto& p : v) {
98
+ seed ^= hash<StrPtr>()(p) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
99
+ }
100
+ return seed;
101
+ }
102
+ };
103
+
104
+ struct KnowledgeBase {
105
+ StringInterner interner;
106
+ // Modified to use a vector of StrPtr (N-gram) as the key
107
+ unordered_map<vector<StrPtr>, NextSet, NgramHash> next;
108
+ mutable mutex m;
109
+
110
+ void add_ngram(const vector<StrPtr>& ctx, StrPtr v){
111
+ lock_guard<mutex> lk(m);
112
+ auto &vec = next[ctx];
113
+ for (auto p : vec) if (p == v) return;
114
+ vec.push_back(v);
115
+ }
116
+
117
+ optional<NextSet> lookup_ngram(const vector<StrPtr>& ctx) const {
118
+ lock_guard<mutex> lk(m);
119
+ auto it = next.find(ctx);
120
+ if (it == next.end()) return nullopt;
121
+ return it->second;
122
+ }
123
+
124
+ // Fast read-only lookup returning direct pointer to avoid mutex locking and vector copies
125
+ const NextSet* lookup_ngram_fast(const vector<StrPtr>& ctx) const {
126
+ auto it = next.find(ctx);
127
+ return (it != next.end()) ? &(it->second) : nullptr;
128
+ }
129
+ };
130
+
131
+ static void learn_tokens_ngram(KnowledgeBase &kb, const vector<string>& tokens, int n_gram_size) {
132
+ vector<StrPtr> window;
133
+ for (const auto& tok : tokens) {
134
+ StrPtr tp = kb.interner.intern(tok);
135
+ for (size_t j = 0; j < window.size(); ++j) {
136
+ vector<StrPtr> ctx(window.begin() + j, window.end());
137
+ kb.add_ngram(ctx, tp);
138
+ }
139
+ window.push_back(tp);
140
+ if (window.size() > static_cast<size_t>(n_gram_size)) window.erase(window.begin());
141
+ }
142
+ }
143
+
144
+ static vector<StrPtr>
145
+ intern_tokens(KnowledgeBase &kb, const vector<string> &tokens)
146
+ {
147
+ vector<StrPtr> out;
148
+ out.reserve(tokens.size());
149
+ for (const auto &t : tokens) out.push_back(kb.interner.intern(t));
150
+ return out;
151
+ }
152
+
153
+
154
+ static const NextSet* lookup_ngram_with_backoff(
155
+ const KnowledgeBase &kb,
156
+ const vector<StrPtr> &ctx_path,
157
+ int n_gram_size)
158
+ {
159
+ int cur_len = min(static_cast<int>(ctx_path.size()), n_gram_size);
160
+ while (cur_len > 0) {
161
+ vector<StrPtr> window(ctx_path.end() - cur_len, ctx_path.end());
162
+ const NextSet* res = kb.lookup_ngram_fast(window);
163
+ if (res && !res->empty()) {
164
+ return res;
165
+ }
166
+ cur_len--;
167
+ }
168
+ return nullptr;
169
+ }
170
+
171
+ struct CandState {
172
+ StrPtr orig_cand;
173
+ vector<vector<StrPtr>> frontier;
174
+ double total_score;
175
+ };
176
+
177
+ static string choose_candidate(
178
+ const NextSet &cands,
179
+ const vector<StrPtr> &history_ptrs,
180
+ const KnowledgeBase &kb,
181
+ const unordered_map<string, int> &recent_counts,
182
+ double repeat_penalty,
183
+ int n_gram_size)
184
+ {
185
+ if (cands.empty()) return string();
186
+ if (cands.size() == 1) return *cands[0];
187
+
188
+ // 1. Build historical context bitset once
189
+ const size_t words = max<size_t>(1, (kb.interner.size() + 63u) / 64u);
190
+ vector<uint64_t> agg_words(words, 0ULL);
191
+
192
+ for (StrPtr t : history_ptrs) {
193
+ TokenId tid = kb.interner.id_of(t);
194
+ if (tid != TOKEN_ID_INVALID) bitset_set(agg_words.data(), words, tid);
195
+ }
196
+ const size_t agg_count = bitset_count(agg_words.data(), words);
197
+
198
+ // Build base context tail from history (up to N-1 tokens)
199
+ vector<StrPtr> base_ctx;
200
+ int base_len = min(static_cast<int>(history_ptrs.size()), n_gram_size - 1);
201
+ if (base_len > 0) {
202
+ base_ctx.assign(history_ptrs.end() - base_len, history_ptrs.end());
203
+ }
204
+
205
+ // 2. Initialize iterative state for ALL original candidates
206
+ vector<CandState> tied_states(cands.size());
207
+ for (size_t i = 0; i < cands.size(); ++i) {
208
+ tied_states[i].orig_cand = cands[i];
209
+
210
+ // Initial frontier path for candidate = base_ctx + candidate
211
+ vector<StrPtr> init_path = base_ctx;
212
+ init_path.push_back(cands[i]);
213
+ if (init_path.size() > static_cast<size_t>(n_gram_size)) {
214
+ init_path.erase(init_path.begin(), init_path.end() - n_gram_size);
215
+ }
216
+
217
+ tied_states[i].frontier.push_back(move(init_path));
218
+ tied_states[i].total_score = 0.0;
219
+ }
220
+
221
+ // 3. Iterative deepening BFS up to n_gram_size across all candidates
222
+ for (int depth = 1; depth <= n_gram_size; ++depth) {
223
+
224
+ #ifdef _OPENMP
225
+ #pragma omp parallel for schedule(dynamic)
226
+ #endif
227
+ for (ptrdiff_t i = 0; i < static_cast<ptrdiff_t>(tied_states.size()); ++i) {
228
+ double depth_max_score = -1.0;
229
+ vector<vector<StrPtr>> next_frontier;
230
+
231
+ for (const auto &path : tied_states[i].frontier) {
232
+ // Perform n-gram backoff lookup using the full path context
233
+ const NextSet* opt_next = lookup_ngram_with_backoff(kb, path, n_gram_size);
234
+ if (!opt_next || opt_next->empty()) continue;
235
+
236
+ size_t count = 0;
237
+ size_t inter = 0;
238
+
239
+ // O(1) bitwise lookups bypass allocating new bitsets
240
+ for (StrPtr nxt : *opt_next) {
241
+ TokenId nid = kb.interner.id_of(nxt);
242
+ if (nid != TOKEN_ID_INVALID) {
243
+ count++;
244
+ if ((agg_words[nid >> 6] & (1ULL << (nid & 63u))) != 0) {
245
+ inter++;
246
+ }
247
+ }
248
+ }
249
+
250
+ const size_t uni = agg_count + count - inter;
251
+ const double score = uni ? static_cast<double>(inter) / static_cast<double>(uni) : 0.0;
252
+
253
+ if (score > depth_max_score) {
254
+ depth_max_score = score;
255
+ }
256
+
257
+ // Append new context paths to next frontier if under max depth
258
+ if (depth < n_gram_size) {
259
+ for (StrPtr nxt : *opt_next) {
260
+ vector<StrPtr> next_path = path;
261
+ next_path.push_back(nxt);
262
+ if (next_path.size() > static_cast<size_t>(n_gram_size)) {
263
+ next_path.erase(next_path.begin(), next_path.end() - n_gram_size);
264
+ }
265
+ next_frontier.push_back(move(next_path));
266
+ }
267
+ }
268
+ }
269
+
270
+ // Apply repetition penalty on the base token level at depth 1
271
+ if (depth == 1) {
272
+ const string &tok = *(tied_states[i].orig_cand);
273
+ auto rc_it = recent_counts.find(tok);
274
+ const int cnt = (rc_it == recent_counts.end()) ? 0 : rc_it->second;
275
+ if (depth_max_score > -0.5) {
276
+ depth_max_score -= repeat_penalty * static_cast<double>(cnt);
277
+ } else {
278
+ depth_max_score = -repeat_penalty * static_cast<double>(cnt);
279
+ }
280
+ } else {
281
+ if (depth_max_score < 0.0) {
282
+ depth_max_score = 0.0;
283
+ }
284
+ }
285
+
286
+ // Accumulate depth score into candidate's total score
287
+ tied_states[i].total_score += depth_max_score;
288
+
289
+ // Deduplicate next frontier to prevent exponential path expansion
290
+ if (next_frontier.size() > 1) {
291
+ sort(next_frontier.begin(), next_frontier.end());
292
+ next_frontier.erase(unique(next_frontier.begin(), next_frontier.end()), next_frontier.end());
293
+ }
294
+ tied_states[i].frontier = move(next_frontier);
295
+ }
296
+ }
297
+
298
+ // 4. Final Selection: Identify highest total score across all depths
299
+ double best_total = -1e9;
300
+ for (const auto &st : tied_states) {
301
+ if (st.total_score > best_total) {
302
+ best_total = st.total_score;
303
+ }
304
+ }
305
+
306
+ // Collect all candidates tied for the best total score
307
+ vector<StrPtr> tied_cands;
308
+ for (const auto &st : tied_states) {
309
+ if (st.total_score >= best_total - 1e-9) {
310
+ tied_cands.push_back(st.orig_cand);
311
+ }
312
+ }
313
+
314
+ // Break remaining ties alphabetically
315
+ string best_tok = *(tied_cands[0]);
316
+ for (size_t i = 1; i < tied_cands.size(); ++i) {
317
+ if (*(tied_cands[i]) < best_tok) {
318
+ best_tok = *(tied_cands[i]);
319
+ }
320
+ }
321
+
322
+ return best_tok;
323
+ }
324
+
325
+ static vector<string> construct_response(KnowledgeBase &kb,
326
+ const vector<string> &prompt_toks,
327
+ size_t response_maxlen,
328
+ double repeat_penalty,
329
+ int n_gram_size,
330
+ bool allow_path_loops)
331
+ {
332
+ vector<string> resp;
333
+ if (prompt_toks.empty() || response_maxlen == 0) return resp;
334
+
335
+ auto prompt_ptrs = intern_tokens(kb, prompt_toks);
336
+ unordered_map<string, int> recent_counts;
337
+
338
+ // The context combines prompt and generated text
339
+ vector<StrPtr> context = prompt_ptrs;
340
+
341
+ auto would_create_2_cycle = [&](const string &cand) -> bool {
342
+ if (resp.size() < 3) return false;
343
+ return cand == resp[resp.size() - 2] &&
344
+ resp.back() == resp[resp.size() - 3];
345
+ };
346
+
347
+ for (size_t step = 0; step < response_maxlen; ++step){
348
+ NextSet candidates;
349
+ bool found = false;
350
+
351
+ int current_n = min(static_cast<int>(context.size()), n_gram_size);
352
+ while (current_n > 0) {
353
+ vector<StrPtr> search_ctx(context.end() - current_n, context.end());
354
+ auto opt = kb.lookup_ngram(search_ctx);
355
+ if (opt && !opt->empty()) {
356
+ candidates = *opt;
357
+ found = true;
358
+ break;
359
+ }
360
+ current_n--;
361
+ }
362
+
363
+ if (!found || candidates.empty()) break;
364
+
365
+ if (candidates.size() == 1){
366
+ string only = *candidates[0];
367
+ if (!allow_path_loops && recent_counts[only] > 0) break;
368
+ resp.push_back(only);
369
+ StrPtr ptr = kb.interner.intern(only);
370
+ context.push_back(ptr);
371
+ recent_counts[only] += 1;
372
+ cout << only << ' ' << flush;
373
+ continue;
374
+ }
375
+
376
+ string chosen = choose_candidate(
377
+ candidates, context, kb, recent_counts, repeat_penalty, n_gram_size
378
+ );
379
+
380
+ if (chosen.empty()) break;
381
+ if (!allow_path_loops && would_create_2_cycle(chosen)) break;
382
+
383
+ resp.push_back(chosen);
384
+ StrPtr chosen_ptr = kb.interner.intern(chosen);
385
+ context.push_back(chosen_ptr);
386
+ recent_counts[chosen] += 1;
387
+ cout << chosen << ' ' << flush;
388
+ }
389
+
390
+ return resp;
391
+ }
392
+
393
+ static void learn_from_file(KnowledgeBase &kb, const string &fname, int n_gram_size){
394
+ ifstream ifs(fname);
395
+ if (!ifs) return;
396
+ vector<string> tokens;
397
+ string tok;
398
+ while (ifs >> tok) tokens.push_back(tok);
399
+ learn_tokens_ngram(kb, tokens, n_gram_size);
400
+ }
401
+
402
+ static void learn_files_parallel(KnowledgeBase &kb, const vector<string> &files, int n_gram_size){
403
+ #pragma omp parallel for schedule(dynamic)
404
+ for (ptrdiff_t i=0;i<static_cast<ptrdiff_t>(files.size());++i){
405
+ learn_from_file(kb, files[(size_t)i], n_gram_size);
406
+ }
407
+ }
408
+
409
+ static constexpr uint64_t KB_MAGIC = 0x434850434B535641ULL;
410
+ static constexpr uint64_t KB_VERSION = 2ULL;
411
+
412
+ static void write_u64(ostream &os, uint64_t v) {
413
+ do {
414
+ unsigned char byte = v & 0x7F;
415
+ v >>= 7;
416
+ if (v != 0) byte |= 0x80;
417
+ os.put(static_cast<char>(byte));
418
+ } while (v != 0);
419
+ if (!os) throw runtime_error("write_u64 failed");
420
+ }
421
+
422
+ static uint64_t read_u64(istream &is) {
423
+ uint64_t v = 0;
424
+ unsigned int shift = 0;
425
+ unsigned char byte;
426
+ do {
427
+ if (!is.read(reinterpret_cast<char*>(&byte), 1)) {
428
+ throw runtime_error("read_u64 failed");
429
+ }
430
+ v |= (static_cast<uint64_t>(byte & 0x7F) << shift);
431
+ shift += 7;
432
+ } while (byte & 0x80);
433
+ return v;
434
+ }
435
+
436
+ static void write_string(ostream &os, const string &s){
437
+ write_u64(os, static_cast<uint64_t>(s.size()));
438
+ if (!s.empty()){
439
+ os.write(s.data(), static_cast<streamsize>(s.size()));
440
+ if(!os) throw runtime_error("write_string failed");
441
+ }
442
+ }
443
+
444
+ static string read_string(istream &is){
445
+ uint64_t n = read_u64(is);
446
+ //if (n > (1ULL << 30)) throw runtime_error("save file is corrupted: string too large");
447
+
448
+ string s;
449
+ s.resize(static_cast<size_t>(n));
450
+
451
+ if (n != 0){
452
+ is.read(&s[0], static_cast<streamsize>(n));
453
+ if(!is) throw runtime_error("read_string failed");
454
+ }
455
+ return s;
456
+ }
457
+
458
+ static void save_kb_binary(const KnowledgeBase &kb, const string &fname){
459
+ const string temp = fname + ".tmp";
460
+
461
+ {
462
+ ofstream ofs(temp.c_str(), ios::binary | ios::trunc);
463
+ if (!ofs) throw runtime_error("cannot open temp save file");
464
+
465
+ vector<string> pool;
466
+ pool.reserve(kb.interner.pool.size());
467
+ for (const auto &s : kb.interner.pool) pool.push_back(s);
468
+
469
+ sort(pool.begin(), pool.end());
470
+
471
+ unordered_map<string, uint64_t> id;
472
+ id.reserve(pool.size());
473
+ for (uint64_t i = 0; i < static_cast<uint64_t>(pool.size()); ++i){
474
+ id.emplace(pool[(size_t)i], i);
475
+ }
476
+
477
+ write_u64(ofs, KB_MAGIC);
478
+ write_u64(ofs, KB_VERSION);
479
+
480
+ write_u64(ofs, static_cast<uint64_t>(pool.size()));
481
+ for (const auto &s : pool) write_string(ofs, s);
482
+
483
+ write_u64(ofs, static_cast<uint64_t>(kb.next.size()));
484
+ for (const auto &pr : kb.next){
485
+ write_u64(ofs, static_cast<uint64_t>(pr.first.size()));
486
+ for (StrPtr ctx_tok : pr.first){
487
+ write_u64(ofs, id.at(*ctx_tok));
488
+ }
489
+ write_u64(ofs, static_cast<uint64_t>(pr.second.size()));
490
+ for (StrPtr nxt : pr.second){
491
+ write_u64(ofs, id.at(*nxt));
492
+ }
493
+ }
494
+
495
+ ofs.flush();
496
+ if (!ofs) throw runtime_error("failed while writing temp save file");
497
+ }
498
+
499
+ remove(fname.c_str());
500
+ if (rename(temp.c_str(), fname.c_str()) != 0){
501
+ remove(temp.c_str());
502
+ throw runtime_error("failed to commit save file");
503
+ }
504
+ }
505
+
506
+ static void load_kb_binary(KnowledgeBase &kb, const string &fname){
507
+ ifstream ifs(fname, ios::binary);
508
+ if (!ifs) throw runtime_error("cannot open load file");
509
+
510
+ const uint64_t magic = read_u64(ifs);
511
+ if (magic != KB_MAGIC) throw runtime_error("bad save file magic");
512
+
513
+ const uint64_t version = read_u64(ifs);
514
+ if (version != KB_VERSION) throw runtime_error("unsupported save file version");
515
+
516
+ const uint64_t N = read_u64(ifs);
517
+ //if (N > (1ULL << 26)) throw runtime_error("save file is corrupted: pool too large");
518
+
519
+ vector<string> strings;
520
+ strings.reserve(static_cast<size_t>(N));
521
+
522
+ for (uint64_t i = 0; i < N; ++i){
523
+ strings.push_back(read_string(ifs));
524
+ }
525
+
526
+ kb.interner.pool.clear();
527
+ kb.interner.pool.reserve(static_cast<size_t>(N));
528
+
529
+ vector<StrPtr> ptrs;
530
+ ptrs.reserve(static_cast<size_t>(N));
531
+ for (const auto &s : strings){
532
+ ptrs.push_back(kb.interner.intern(s));
533
+ }
534
+
535
+ // Rebuild next
536
+ const uint64_t E = read_u64(ifs);
537
+ //if (E > (1ULL << 26)) throw runtime_error("save file is corrupted: graph too large");
538
+
539
+ {
540
+ lock_guard<mutex> lk(kb.m);
541
+ kb.next.clear();
542
+ kb.next.reserve(static_cast<size_t>(E));
543
+ }
544
+
545
+ for (uint64_t i = 0; i < E; ++i){
546
+ const uint64_t ctx_size = read_u64(ifs);
547
+ //if (ctx_size > (1ULL << 16)) throw runtime_error("save file is corrupted: n-gram context too large");
548
+
549
+ vector<StrPtr> ctx;
550
+ ctx.reserve(static_cast<size_t>(ctx_size));
551
+ for(uint64_t c = 0; c < ctx_size; ++c) {
552
+ const uint64_t c_idx = read_u64(ifs);
553
+ if (c_idx >= ptrs.size()) throw runtime_error("save file is corrupted: bad context key");
554
+ ctx.push_back(ptrs[(size_t)c_idx]);
555
+ }
556
+
557
+ const uint64_t M = read_u64(ifs);
558
+ //if (M > (1ULL << 26)) throw runtime_error("save file is corrupted: graph degree too large");
559
+
560
+ NextSet vec;
561
+ vec.reserve(static_cast<size_t>(M));
562
+
563
+ for (uint64_t j = 0; j < M; ++j){
564
+ const uint64_t v_idx = read_u64(ifs);
565
+ if (v_idx >= ptrs.size()) throw runtime_error("save file is corrupted: bad graph value");
566
+ vec.push_back(ptrs[(size_t)v_idx]);
567
+ }
568
+
569
+ {
570
+ lock_guard<mutex> lk(kb.m);
571
+ kb.next.emplace(move(ctx), move(vec));
572
+ }
573
+ }
574
+ }
575
+
576
+ static void print_kb_info(const string &fname) {
577
+ ifstream ifs(fname, ios::binary);
578
+ if (!ifs) {
579
+ cerr << "Error: cannot open knowledge-base file " << fname << "\n";
580
+ return;
581
+ }
582
+
583
+ cout << "Reckoning knowledge-base " << fname << " info. ";
584
+
585
+ try {
586
+ const uint64_t magic = read_u64(ifs);
587
+ if (magic != KB_MAGIC) throw runtime_error("bad save file magic");
588
+
589
+ const uint64_t version = read_u64(ifs);
590
+ if (version != KB_VERSION) throw runtime_error("unsupported save file version");
591
+
592
+ const uint64_t N = read_u64(ifs);
593
+ //if (N > (1ULL << 26)) throw runtime_error("save file is corrupted: pool too large");
594
+
595
+ // Dynamically read and skip strings since lengths are now variable-length encoded
596
+ for (uint64_t i = 0; i < N; ++i){
597
+ uint64_t len = read_u64(ifs);
598
+ //if (len > (1ULL << 30)) throw runtime_error("save file is corrupted: string too large");
599
+ if (len > 0) ifs.seekg(static_cast<streamoff>(len), ios_base::cur);
600
+ }
601
+
602
+ const uint64_t E = read_u64(ifs);
603
+ //if (E > (1ULL << 26)) throw runtime_error("save file is corrupted: graph too large");
604
+
605
+ uint64_t max_ngram = 0;
606
+ for (uint64_t i = 0; i < E; ++i){
607
+ const uint64_t ctx_size = read_u64(ifs);
608
+ //if (ctx_size > (1ULL << 16)) throw runtime_error("save file is corrupted: n-gram context too large");
609
+
610
+ if (ctx_size > max_ngram) max_ngram = ctx_size;
611
+
612
+ // Sequentially read (and skip) context keys using LEB128 stream parsing
613
+ for(uint64_t c = 0; c < ctx_size; ++c) {
614
+ read_u64(ifs);
615
+ }
616
+
617
+ const uint64_t M = read_u64(ifs);
618
+ //if (M > (1ULL << 26)) throw runtime_error("save file is corrupted: graph degree too large");
619
+
620
+ // Sequentially read (and skip) next-node values
621
+ for(uint64_t j = 0; j < M; ++j){
622
+ read_u64(ifs);
623
+ }
624
+ }
625
+
626
+ cout << "The " << fname << " n-gram max size is " << max_ngram << "\n";
627
+
628
+ } catch (const exception &e) {
629
+ cerr << "Error reading knowledge-base info: " << e.what() << "\n";
630
+ }
631
+ }
632
+
633
+ static void print_commands(const char *p){
634
+ cout << p << " command-line interface options:\n";
635
+ cout << " --response-max-length N Set maximum number of token(s) in a response. Default number is 500\n";
636
+ cout << " --save-kb FILE Save the knowledge-base graph to a binary file.\n";
637
+ cout << " --load-kb FILE Load a previously saved knowledge-base graph from a binary file.\n";
638
+ cout << " --info-kb FILE Show the dictionary-depth and n-gram max size of a knowledge-base file.\n";
639
+ cout << " --static-kb Disable learning prompt(s) and response(s) during interactive session.\n";
640
+ cout << " --n-gram-max-size N Set maximum size of the n-gram where N is the size. Default size is 3\n";
641
+ cout << " --learn f1 f2 ... Learn from text file(s) to update the knowledge-base graph.\n";
642
+ cout << " --repeat-penalty P Set penalty for repeated tokens in response. Default penalty is 0.7 (higher penalty reduce repetition).\n";
643
+ cout << " --allow-path-loops Allow finite loops when the knowledge-base graph contains a circular single-path reference.\n";
644
+ cout << " --activate-agi Activate the Artificial General Intelligence (AGI) features.\n";
645
+ cout << " --help Show " << p << " command-line interface options.\n";
646
+ }
647
+
648
+ int main(int argc, char **argv){
649
+ size_t response_maxlen = 500;
650
+ string savefile;
651
+ string load_kb;
652
+ int n_gram_size = 3;
653
+ double repeat_penalty = 0.7; // default λ
654
+ vector<string> learn_files;
655
+ bool learn_chat = true;
656
+ bool allow_path_loops = false;
657
+
658
+ for (int i=1;i<argc;++i){
659
+ string a = argv[i];
660
+ if (a=="--help"){ print_commands(argv[0]); return 0; }
661
+ if (a=="--response-max-length" && i+1<argc){ response_maxlen = stoul(argv[++i]); continue; }
662
+ if (a=="--save-kb" && i+1<argc){ savefile = argv[++i]; continue; }
663
+ if (a=="--load-kb" && i+1<argc){ load_kb = argv[++i]; continue; }
664
+ if (a=="--info-kb" && i+1<argc){ print_kb_info(argv[++i]); return 0; }
665
+ if (a=="--static-kb"){ learn_chat = false; continue; }
666
+ if (a=="--n-gram-max-size" && i+1<argc){ n_gram_size = max(1, stoi(argv[++i])); continue; }
667
+ if (a=="--learn"){ while(i+1<argc && argv[i+1][0] != '-') learn_files.push_back(argv[++i]); continue; }
668
+ if (a=="--repeat-penalty" && i+1<argc){ repeat_penalty = stod(argv[++i]); continue; }
669
+ if (a=="--allow-path-loops") { allow_path_loops = true; continue; }
670
+ if (a=="--activate-agi"){
671
+ cerr << "AGI subscription fee is $1,000 CAD per month. To pay the fee, contact Caleb Nwokocha via email cnwokocha@proton.me\n";
672
+ return 0;
673
+ }
674
+
675
+ cerr << "Error: Unknown or incomplete command-line option '" << a << "'\n";
676
+ print_commands(argv[0]);
677
+ return 1;
678
+ }
679
+
680
+ KnowledgeBase kb;
681
+
682
+ if (!load_kb.empty()){
683
+ try { print_kb_info(load_kb);
684
+ cerr << "Loading knowledge base " << load_kb << " using values from " << argv[0] << " command-line interface options: --n-gram-max-size " << n_gram_size <<
685
+ ", --response-max-length " << response_maxlen << ", and --repeat-penalty " << repeat_penalty << "\n";
686
+ cerr << "To change the value(s), use " << argv[0] << " command-line interface option(s).\n";
687
+ load_kb_binary(kb, load_kb); cerr << "Loaded knowledge base: " << load_kb << "\n"; }
688
+ catch (const exception &e){ cerr << "Error: " << e.what() << "\n"; }
689
+ }
690
+
691
+ if (!learn_files.empty()){
692
+ cerr << "Learning from file/s (" << learn_files.size() << ") using threads=" << omp_get_max_threads() << "\n";
693
+ learn_files_parallel(kb, learn_files, n_gram_size);
694
+ }
695
+
696
+ string line;
697
+ cout << "Ready. Enter prompts.\n";
698
+ while (cout << "> " , getline(cin, line)){
699
+ if (line.empty()){ cout << "\n"; continue; }
700
+ auto prompt_toks = tokenize_whitespace(line);
701
+ if (learn_chat) {learn_tokens_ngram(kb, prompt_toks, n_gram_size);}
702
+ auto resp = construct_response(kb, prompt_toks, response_maxlen, repeat_penalty, n_gram_size, allow_path_loops);
703
+ cout << "\n";
704
+ if (!resp.empty() && learn_chat){learn_tokens_ngram(kb, resp, n_gram_size);}
705
+ if (!savefile.empty()){
706
+ try { cerr << "Saving knowledge base: " << savefile << "\n";
707
+ save_kb_binary(kb, savefile); cerr << "Saved knowledge base: " << savefile << "\n"; }
708
+ catch (const exception &e){ cerr << "Error: " << e.what() << "\n"; }
709
+ }
710
+ }
711
+
712
+ return 0;
713
+ }