technician1 commited on
Commit
844305a
·
1 Parent(s): 35e8531

Upload ChatIPC.cpp

Browse files
Files changed (1) hide show
  1. ChatIPC.cpp +121 -101
ChatIPC.cpp CHANGED
@@ -468,20 +468,32 @@ struct PtrEq { bool operator()(StrPtr a, StrPtr b) const noexcept { return a =
468
 
469
  using NextSet = std::vector<StrPtr>;
470
 
 
 
 
 
 
 
 
 
 
 
471
  struct KnowledgeBase {
472
  StringInterner interner;
473
- std::unordered_map<StrPtr, NextSet, PtrHash, PtrEq> next;
474
- std::unordered_map<std::string, StrPtr> next_key_index;
475
  mutable std::mutex m;
476
 
477
  std::unordered_map<StrPtr, std::vector<StrPtr>, PtrHash, PtrEq> def_index;
478
  mutable std::mutex def_m;
479
  int def_depth = 0;
480
 
481
- void add_pair_interned(StrPtr k, StrPtr v){
 
 
 
482
  std::lock_guard<std::mutex> lk(m);
483
- next_key_index.emplace(*k, k);
484
- auto &vec = next[k];
485
  for (auto p : vec) if (p == v) return;
486
  vec.push_back(v);
487
  }
@@ -495,8 +507,7 @@ struct KnowledgeBase {
495
  }
496
 
497
  void ensure_def_for_interned(StrPtr wp){
498
- if (wp == nullptr) return;
499
- if (def_depth <= 0) return;
500
 
501
  {
502
  std::lock_guard<std::mutex> lk(def_m);
@@ -546,30 +557,26 @@ struct KnowledgeBase {
546
  }
547
  }
548
 
549
- void add_pair(const std::string &k, const std::string &v){
550
- StrPtr kp = interner.intern(k);
551
- StrPtr vp = interner.intern(v);
552
- ensure_def_for_interned(kp);
553
- ensure_def_for_interned(vp);
554
- add_pair_interned(kp, vp);
555
- }
556
-
557
- std::optional<NextSet> lookup_by_string(const std::string &k) const {
558
  std::lock_guard<std::mutex> lk(m);
559
- auto kit = next_key_index.find(k);
560
- if (kit == next_key_index.end()) return std::nullopt;
561
- auto it = next.find(kit->second);
562
  if (it == next.end()) return std::nullopt;
563
  return it->second;
564
  }
 
565
 
566
- std::optional<NextSet> lookup_by_ptr(StrPtr k) const {
567
- std::lock_guard<std::mutex> lk(m);
568
- auto it = next.find(k);
569
- if (it == next.end()) return std::nullopt;
570
- return it->second;
 
 
 
 
 
571
  }
572
- };
573
 
574
  static std::vector<StrPtr>
575
  intern_tokens(KnowledgeBase &kb, const std::vector<std::string> &tokens)
@@ -872,14 +879,18 @@ static std::string best_candidate_by_similarity(
872
  static std::vector<std::string> construct_response(KnowledgeBase &kb,
873
  const std::vector<std::string> &prompt_toks,
874
  size_t response_maxlen,
875
- double repeat_penalty)
 
876
  {
877
  std::vector<std::string> resp;
878
  if (prompt_toks.empty() || response_maxlen == 0) return resp;
879
 
880
  auto prompt_ptrs = intern_tokens(kb, prompt_toks);
881
  std::vector<StrPtr> resp_ptrs;
882
- std::unordered_map<std::string,int> recent_counts;
 
 
 
883
 
884
  auto would_create_2_cycle = [&](const std::string &cand) -> bool {
885
  if (resp.size() < 3) return false;
@@ -887,40 +898,37 @@ static std::vector<std::string> construct_response(KnowledgeBase &kb,
887
  normalize_dictionary_key(resp.back()) == normalize_dictionary_key(resp[resp.size() - 3]);
888
  };
889
 
890
- std::string last_printed;
891
-
892
  for (size_t step = 0; step < response_maxlen; ++step){
893
  NextSet candidates;
894
  bool found = false;
895
  std::string context_tok;
896
 
897
- if (step == 0){
898
- for (ssize_t p = static_cast<ssize_t>(prompt_toks.size()) - 1; p >= 0; --p){
899
- auto opt = kb.lookup_by_string(prompt_toks[(size_t)p]);
900
- if (opt){
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
901
  candidates = *opt;
902
  found = true;
903
- context_tok = prompt_toks[(size_t)p];
904
  break;
905
  }
906
  }
907
- } else {
908
- auto opt = kb.lookup_by_string(last_printed);
909
- if (opt){
910
- candidates = *opt;
911
- found = true;
912
- context_tok = last_printed;
913
- } else {
914
- for (ssize_t p = static_cast<ssize_t>(prompt_toks.size()) - 1; p >= 0; --p){
915
- auto opt2 = kb.lookup_by_string(prompt_toks[(size_t)p]);
916
- if (opt2){
917
- candidates = *opt2;
918
- found = true;
919
- context_tok = prompt_toks[(size_t)p];
920
- break;
921
- }
922
- }
923
- }
924
  }
925
 
926
  if (!found || candidates.empty()) break;
@@ -931,9 +939,10 @@ static std::vector<std::string> construct_response(KnowledgeBase &kb,
931
  if (recent_counts[only_key.empty() ? only : only_key] > 0) break;
932
 
933
  resp.push_back(only);
934
- resp_ptrs.push_back(kb.interner.intern(only));
 
 
935
  recent_counts[only_key.empty() ? only : only_key] += 1;
936
- last_printed = only;
937
  std::cout << only << ' ' << std::flush;
938
  continue;
939
  }
@@ -947,37 +956,37 @@ static std::vector<std::string> construct_response(KnowledgeBase &kb,
947
  if (would_create_2_cycle(chosen)) break;
948
 
949
  resp.push_back(chosen);
950
- resp_ptrs.push_back(kb.interner.intern(chosen));
 
 
951
 
952
  std::string chosen_key = normalize_dictionary_key(chosen);
953
  recent_counts[chosen_key.empty() ? chosen : chosen_key] += 1;
954
 
955
- last_printed = chosen;
956
  std::cout << chosen << ' ' << std::flush;
957
  }
958
 
959
  return resp;
960
  }
961
 
962
- static void learn_from_file(KnowledgeBase &kb, const std::string &fname){
963
  std::ifstream ifs(fname);
964
  if (!ifs) return;
 
965
  std::string tok;
966
- std::string prev;
967
- bool have_prev = false;
968
- while (ifs >> tok){
969
- if (have_prev) kb.add_pair(prev, tok);
970
- prev = tok; have_prev = true;
971
- }
972
  }
973
 
974
- static void learn_files_parallel(KnowledgeBase &kb, const std::vector<std::string> &files){
975
  #pragma omp parallel for schedule(dynamic)
976
- for (ptrdiff_t i=0;i<static_cast<ptrdiff_t>(files.size());++i) learn_from_file(kb, files[(size_t)i]);
 
 
977
  }
978
 
979
  static constexpr std::uint64_t KB_MAGIC = 0x434850434B535641ULL;
980
- static constexpr std::uint64_t KB_VERSION = 1ULL;
981
 
982
  static void write_u64(std::ostream &os, std::uint64_t v){
983
  os.write(reinterpret_cast<const char*>(&v), sizeof(v));
@@ -1001,7 +1010,7 @@ static void write_string(std::ostream &os, const std::string &s){
1001
 
1002
  static std::string read_string(std::istream &is){
1003
  std::uint64_t n = read_u64(is);
1004
- if (n > (1ULL << 30)) throw std::runtime_error("corrupt save file: string too large");
1005
 
1006
  std::string s;
1007
  s.resize(static_cast<size_t>(n));
@@ -1041,7 +1050,10 @@ static void save_kb_binary(const KnowledgeBase &kb, const std::string &fname){
1041
 
1042
  write_u64(ofs, static_cast<std::uint64_t>(kb.next.size()));
1043
  for (const auto &pr : kb.next){
1044
- write_u64(ofs, id.at(*pr.first));
 
 
 
1045
  write_u64(ofs, static_cast<std::uint64_t>(pr.second.size()));
1046
  for (StrPtr nxt : pr.second){
1047
  write_u64(ofs, id.at(*nxt));
@@ -1081,7 +1093,7 @@ static void load_kb_binary(KnowledgeBase &kb, const std::string &fname, int cli_
1081
  const std::uint64_t file_def_depth = read_u64(ifs);
1082
 
1083
  const std::uint64_t N = read_u64(ifs);
1084
- if (N > (1ULL << 26)) throw std::runtime_error("corrupt save file: pool too large");
1085
 
1086
  std::vector<std::string> strings;
1087
  strings.reserve(static_cast<size_t>(N));
@@ -1101,43 +1113,47 @@ static void load_kb_binary(KnowledgeBase &kb, const std::string &fname, int cli_
1101
 
1102
  // Rebuild next
1103
  const std::uint64_t E = read_u64(ifs);
1104
- if (E > (1ULL << 26)) throw std::runtime_error("corrupt save file: graph too large");
1105
 
1106
  {
1107
  std::lock_guard<std::mutex> lk(kb.m);
1108
  kb.next.clear();
1109
- kb.next_key_index.clear();
1110
  kb.next.reserve(static_cast<size_t>(E));
1111
- kb.next_key_index.reserve(static_cast<size_t>(E));
1112
  }
1113
 
1114
  for (std::uint64_t i = 0; i < E; ++i){
1115
- const std::uint64_t key_idx = read_u64(ifs);
1116
- const std::uint64_t M = read_u64(ifs);
 
 
 
 
 
 
 
 
1117
 
1118
- if (key_idx >= ptrs.size()) throw std::runtime_error("corrupt save file: bad graph key");
1119
- if (M > (1ULL << 26)) throw std::runtime_error("corrupt save file: graph degree too large");
1120
 
1121
- StrPtr key_ptr = ptrs[(size_t)key_idx];
1122
  NextSet vec;
1123
  vec.reserve(static_cast<size_t>(M));
1124
 
1125
  for (std::uint64_t j = 0; j < M; ++j){
1126
  const std::uint64_t v_idx = read_u64(ifs);
1127
- if (v_idx >= ptrs.size()) throw std::runtime_error("corrupt save file: bad graph value");
1128
  vec.push_back(ptrs[(size_t)v_idx]);
1129
  }
1130
 
1131
  {
1132
  std::lock_guard<std::mutex> lk(kb.m);
1133
- kb.next.emplace(key_ptr, std::move(vec));
1134
- kb.next_key_index.emplace(*key_ptr, key_ptr);
1135
  }
1136
  }
1137
 
1138
  // Rebuild def_index from file
1139
  const std::uint64_t K = read_u64(ifs);
1140
- if (K > (1ULL << 26)) throw std::runtime_error("corrupt save file: def_index too large");
1141
 
1142
  {
1143
  std::lock_guard<std::mutex> lk(kb.def_m);
@@ -1150,15 +1166,15 @@ static void load_kb_binary(KnowledgeBase &kb, const std::string &fname, int cli_
1150
  const std::uint64_t key_idx = read_u64(ifs);
1151
  const std::uint64_t M = read_u64(ifs);
1152
 
1153
- if (key_idx >= ptrs.size()) throw std::runtime_error("corrupt save file: bad def key");
1154
- if (M > (1ULL << 26)) throw std::runtime_error("corrupt save file: def list too large");
1155
 
1156
  std::vector<StrPtr> toks;
1157
  toks.reserve(static_cast<size_t>(M));
1158
 
1159
  for (std::uint64_t j = 0; j < M; ++j){
1160
  const std::uint64_t v_idx = read_u64(ifs);
1161
- if (v_idx >= ptrs.size()) throw std::runtime_error("corrupt save file: bad def value");
1162
  toks.push_back(ptrs[(size_t)v_idx]);
1163
  }
1164
 
@@ -1184,9 +1200,16 @@ static void load_kb_binary(KnowledgeBase &kb, const std::string &fname, int cli_
1184
  {
1185
  std::lock_guard<std::mutex> lk(kb.m);
1186
  for (const auto &pr : kb.next){
1187
- if (seen.insert(pr.first).second) targets.push_back(pr.first);
 
 
 
 
 
1188
  for (StrPtr v : pr.second){
1189
- if (seen.insert(v).second) targets.push_back(v);
 
 
1190
  }
1191
  }
1192
  }
@@ -1201,20 +1224,22 @@ static void load_kb_binary(KnowledgeBase &kb, const std::string &fname, int cli_
1201
  static void print_usage(const char *p){
1202
  std::cout << "CLI options: [--response-max-length N] [--save FILE] [--load-kb FILE] [--dictionary-depth D] [--learn f1 f2 ...] [--repeat-penalty P] [--help]\n";
1203
  std::cout << " --response-max-length N Maximum number of tokens in a response.\n";
1204
- std::cout << " --save FILE Save the knowledge-base to a binary file.\n";
1205
  std::cout << " --load-kb FILE Load a previously saved knowledge-base from a binary file.\n";
1206
  std::cout << " --dictionary-depth D Depth of dictionary-definition expansion used during learning.\n";
 
1207
  std::cout << " --learn f1 f2 ... Learn from one or more text files to update the knowledge-base.\n";
1208
  std::cout << " --repeat-penalty P Penalize repeated tokens when constructing response (higher values reduce repetition).\n";
1209
- std::cout << " --help Show Command-Line Interface (CLI) options for using " << p << "\n";
1210
  }
1211
 
1212
  int main(int argc, char **argv){
1213
- size_t response_maxlen = 100;
1214
  std::string savefile;
1215
  std::string load_txt;
1216
  std::string load_kb;
1217
- int dict_depth = 2;
 
1218
  double repeat_penalty = 0.7; // default λ
1219
  std::vector<std::string> learn_files;
1220
 
@@ -1222,18 +1247,13 @@ int main(int argc, char **argv){
1222
  std::string a = argv[i];
1223
  if (a=="--help"){ print_usage(argv[0]); return 0; }
1224
  if (a=="--response-max-length" && i+1<argc){ response_maxlen = std::stoul(argv[++i]); continue; }
1225
- if (a=="--save" && i+1<argc){ savefile = argv[++i]; continue; }
1226
  if (a=="--load-kb" && i+1<argc){ load_kb = argv[++i]; continue; }
1227
  if (a=="--dictionary-depth" && i+1<argc){ dict_depth = std::stoi(argv[++i]); continue; }
 
1228
  if (a=="--repeat-penalty" && i+1<argc){ repeat_penalty = std::stod(argv[++i]); continue; }
1229
  if (a=="--learn"){
1230
- ++i;
1231
- for (; i<argc; ++i){
1232
- if (!argv[i]) break;
1233
- std::string s = argv[i];
1234
- if (!s.empty() && s[0]=='-'){ --i; break; }
1235
- learn_files.push_back(s);
1236
- }
1237
  continue;
1238
  }
1239
  learn_files.push_back(a);
@@ -1253,7 +1273,7 @@ int main(int argc, char **argv){
1253
 
1254
  if (!learn_files.empty()){
1255
  std::cerr << "Learning from file/s (" << learn_files.size() << ") using threads=" << omp_get_max_threads() << "\n";
1256
- learn_files_parallel(kb, learn_files);
1257
  }
1258
 
1259
  std::string line;
@@ -1261,10 +1281,10 @@ int main(int argc, char **argv){
1261
  while (std::cout << "> " , std::getline(std::cin, line)){
1262
  if (line.empty()){ std::cout << "\n"; continue; }
1263
  auto prompt_toks = tokenize_whitespace(line);
1264
- for (size_t i=1;i<prompt_toks.size();++i) kb.add_pair(prompt_toks[i-1], prompt_toks[i]);
1265
- auto resp = construct_response(kb, prompt_toks, response_maxlen, repeat_penalty);
1266
  std::cout << "\n";
1267
- if (!resp.empty()){for (size_t i=1;i<resp.size();++i) kb.add_pair(resp[i-1], resp[i]);}
1268
  if (!savefile.empty()){
1269
  try { std::cerr << "Saving knowledge base: " << savefile << "\n";
1270
  save_kb_binary(kb, savefile); std::cerr << "Saved knowledge base: " << savefile << "\n"; }
 
468
 
469
  using NextSet = std::vector<StrPtr>;
470
 
471
+ struct NgramHash {
472
+ std::size_t operator()(const std::vector<StrPtr>& v) const noexcept {
473
+ std::size_t seed = v.size();
474
+ for(auto& p : v) {
475
+ seed ^= std::hash<StrPtr>()(p) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
476
+ }
477
+ return seed;
478
+ }
479
+ };
480
+
481
  struct KnowledgeBase {
482
  StringInterner interner;
483
+ // Modified to use a vector of StrPtr (N-gram) as the key
484
+ std::unordered_map<std::vector<StrPtr>, NextSet, NgramHash> next;
485
  mutable std::mutex m;
486
 
487
  std::unordered_map<StrPtr, std::vector<StrPtr>, PtrHash, PtrEq> def_index;
488
  mutable std::mutex def_m;
489
  int def_depth = 0;
490
 
491
+ void add_ngram(const std::vector<StrPtr>& ctx, StrPtr v){
492
+ for (auto p : ctx) ensure_def_for_interned(p);
493
+ ensure_def_for_interned(v);
494
+
495
  std::lock_guard<std::mutex> lk(m);
496
+ auto &vec = next[ctx];
 
497
  for (auto p : vec) if (p == v) return;
498
  vec.push_back(v);
499
  }
 
507
  }
508
 
509
  void ensure_def_for_interned(StrPtr wp){
510
+ if (wp == nullptr || def_depth <= 0) return;
 
511
 
512
  {
513
  std::lock_guard<std::mutex> lk(def_m);
 
557
  }
558
  }
559
 
560
+ std::optional<NextSet> lookup_ngram(const std::vector<StrPtr>& ctx) const {
 
 
 
 
 
 
 
 
561
  std::lock_guard<std::mutex> lk(m);
562
+ auto it = next.find(ctx);
 
 
563
  if (it == next.end()) return std::nullopt;
564
  return it->second;
565
  }
566
+ };
567
 
568
+ static void learn_tokens_ngram(KnowledgeBase &kb, const std::vector<std::string>& tokens, int n_gram_size) {
569
+ std::vector<StrPtr> window;
570
+ for (const auto& tok : tokens) {
571
+ StrPtr tp = kb.interner.intern(tok);
572
+ for (size_t j = 0; j < window.size(); ++j) {
573
+ std::vector<StrPtr> ctx(window.begin() + j, window.end());
574
+ kb.add_ngram(ctx, tp);
575
+ }
576
+ window.push_back(tp);
577
+ if (window.size() > static_cast<size_t>(n_gram_size)) window.erase(window.begin());
578
  }
579
+ }
580
 
581
  static std::vector<StrPtr>
582
  intern_tokens(KnowledgeBase &kb, const std::vector<std::string> &tokens)
 
879
  static std::vector<std::string> construct_response(KnowledgeBase &kb,
880
  const std::vector<std::string> &prompt_toks,
881
  size_t response_maxlen,
882
+ double repeat_penalty,
883
+ int n_gram_size)
884
  {
885
  std::vector<std::string> resp;
886
  if (prompt_toks.empty() || response_maxlen == 0) return resp;
887
 
888
  auto prompt_ptrs = intern_tokens(kb, prompt_toks);
889
  std::vector<StrPtr> resp_ptrs;
890
+ std::unordered_map<std::string, int> recent_counts;
891
+
892
+ // The context combines prompt and generated text
893
+ std::vector<StrPtr> context = prompt_ptrs;
894
 
895
  auto would_create_2_cycle = [&](const std::string &cand) -> bool {
896
  if (resp.size() < 3) return false;
 
898
  normalize_dictionary_key(resp.back()) == normalize_dictionary_key(resp[resp.size() - 3]);
899
  };
900
 
 
 
901
  for (size_t step = 0; step < response_maxlen; ++step){
902
  NextSet candidates;
903
  bool found = false;
904
  std::string context_tok;
905
 
906
+ // Dynamic Backoff N-gram lookup
907
+ int current_n = std::min(static_cast<int>(context.size()), n_gram_size);
908
+ while (current_n > 0) {
909
+ std::vector<StrPtr> search_ctx(context.end() - current_n, context.end());
910
+ auto opt = kb.lookup_ngram(search_ctx);
911
+ if (opt && !opt->empty()) {
912
+ candidates = *opt;
913
+ found = true;
914
+ context_tok = *search_ctx.back();
915
+ break;
916
+ }
917
+ current_n--;
918
+ }
919
+
920
+ // Fallback to checking earlier prompt tokens if sequence stalled
921
+ if (!found) {
922
+ for (ssize_t p = static_cast<ssize_t>(prompt_ptrs.size()) - 1; p >= 0; --p){
923
+ std::vector<StrPtr> search_ctx = { prompt_ptrs[(size_t)p] };
924
+ auto opt = kb.lookup_ngram(search_ctx);
925
+ if (opt && !opt->empty()){
926
  candidates = *opt;
927
  found = true;
928
+ context_tok = *prompt_ptrs[(size_t)p];
929
  break;
930
  }
931
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
932
  }
933
 
934
  if (!found || candidates.empty()) break;
 
939
  if (recent_counts[only_key.empty() ? only : only_key] > 0) break;
940
 
941
  resp.push_back(only);
942
+ StrPtr ptr = kb.interner.intern(only);
943
+ resp_ptrs.push_back(ptr);
944
+ context.push_back(ptr);
945
  recent_counts[only_key.empty() ? only : only_key] += 1;
 
946
  std::cout << only << ' ' << std::flush;
947
  continue;
948
  }
 
956
  if (would_create_2_cycle(chosen)) break;
957
 
958
  resp.push_back(chosen);
959
+ StrPtr chosen_ptr = kb.interner.intern(chosen);
960
+ resp_ptrs.push_back(chosen_ptr);
961
+ context.push_back(chosen_ptr);
962
 
963
  std::string chosen_key = normalize_dictionary_key(chosen);
964
  recent_counts[chosen_key.empty() ? chosen : chosen_key] += 1;
965
 
 
966
  std::cout << chosen << ' ' << std::flush;
967
  }
968
 
969
  return resp;
970
  }
971
 
972
+ static void learn_from_file(KnowledgeBase &kb, const std::string &fname, int n_gram_size){
973
  std::ifstream ifs(fname);
974
  if (!ifs) return;
975
+ std::vector<std::string> tokens;
976
  std::string tok;
977
+ while (ifs >> tok) tokens.push_back(tok);
978
+ learn_tokens_ngram(kb, tokens, n_gram_size);
 
 
 
 
979
  }
980
 
981
+ static void learn_files_parallel(KnowledgeBase &kb, const std::vector<std::string> &files, int n_gram_size){
982
  #pragma omp parallel for schedule(dynamic)
983
+ for (ptrdiff_t i=0;i<static_cast<ptrdiff_t>(files.size());++i){
984
+ learn_from_file(kb, files[(size_t)i], n_gram_size);
985
+ }
986
  }
987
 
988
  static constexpr std::uint64_t KB_MAGIC = 0x434850434B535641ULL;
989
+ static constexpr std::uint64_t KB_VERSION = 2ULL;
990
 
991
  static void write_u64(std::ostream &os, std::uint64_t v){
992
  os.write(reinterpret_cast<const char*>(&v), sizeof(v));
 
1010
 
1011
  static std::string read_string(std::istream &is){
1012
  std::uint64_t n = read_u64(is);
1013
+ if (n > (1ULL << 30)) throw std::runtime_error("save file is corrupted: string too large");
1014
 
1015
  std::string s;
1016
  s.resize(static_cast<size_t>(n));
 
1050
 
1051
  write_u64(ofs, static_cast<std::uint64_t>(kb.next.size()));
1052
  for (const auto &pr : kb.next){
1053
+ write_u64(ofs, static_cast<std::uint64_t>(pr.first.size()));
1054
+ for (StrPtr ctx_tok : pr.first){
1055
+ write_u64(ofs, id.at(*ctx_tok));
1056
+ }
1057
  write_u64(ofs, static_cast<std::uint64_t>(pr.second.size()));
1058
  for (StrPtr nxt : pr.second){
1059
  write_u64(ofs, id.at(*nxt));
 
1093
  const std::uint64_t file_def_depth = read_u64(ifs);
1094
 
1095
  const std::uint64_t N = read_u64(ifs);
1096
+ if (N > (1ULL << 26)) throw std::runtime_error("save file is corrupted: pool too large");
1097
 
1098
  std::vector<std::string> strings;
1099
  strings.reserve(static_cast<size_t>(N));
 
1113
 
1114
  // Rebuild next
1115
  const std::uint64_t E = read_u64(ifs);
1116
+ if (E > (1ULL << 26)) throw std::runtime_error("save file is corrupted: graph too large");
1117
 
1118
  {
1119
  std::lock_guard<std::mutex> lk(kb.m);
1120
  kb.next.clear();
 
1121
  kb.next.reserve(static_cast<size_t>(E));
 
1122
  }
1123
 
1124
  for (std::uint64_t i = 0; i < E; ++i){
1125
+ const std::uint64_t ctx_size = read_u64(ifs);
1126
+ if (ctx_size > (1ULL << 16)) throw std::runtime_error("save file is corrupted: n-gram context too large");
1127
+
1128
+ std::vector<StrPtr> ctx;
1129
+ ctx.reserve(static_cast<size_t>(ctx_size));
1130
+ for(std::uint64_t c = 0; c < ctx_size; ++c) {
1131
+ const std::uint64_t c_idx = read_u64(ifs);
1132
+ if (c_idx >= ptrs.size()) throw std::runtime_error("save file is corrupted: bad context key");
1133
+ ctx.push_back(ptrs[(size_t)c_idx]);
1134
+ }
1135
 
1136
+ const std::uint64_t M = read_u64(ifs);
1137
+ if (M > (1ULL << 26)) throw std::runtime_error("save file is corrupted: graph degree too large");
1138
 
 
1139
  NextSet vec;
1140
  vec.reserve(static_cast<size_t>(M));
1141
 
1142
  for (std::uint64_t j = 0; j < M; ++j){
1143
  const std::uint64_t v_idx = read_u64(ifs);
1144
+ if (v_idx >= ptrs.size()) throw std::runtime_error("save file is corrupted: bad graph value");
1145
  vec.push_back(ptrs[(size_t)v_idx]);
1146
  }
1147
 
1148
  {
1149
  std::lock_guard<std::mutex> lk(kb.m);
1150
+ kb.next.emplace(std::move(ctx), std::move(vec));
 
1151
  }
1152
  }
1153
 
1154
  // Rebuild def_index from file
1155
  const std::uint64_t K = read_u64(ifs);
1156
+ if (K > (1ULL << 26)) throw std::runtime_error("save file is corrupted: def_index too large");
1157
 
1158
  {
1159
  std::lock_guard<std::mutex> lk(kb.def_m);
 
1166
  const std::uint64_t key_idx = read_u64(ifs);
1167
  const std::uint64_t M = read_u64(ifs);
1168
 
1169
+ if (key_idx >= ptrs.size()) throw std::runtime_error("save file is corrupted: bad def key");
1170
+ if (M > (1ULL << 26)) throw std::runtime_error("save file is corrupted: def list too large");
1171
 
1172
  std::vector<StrPtr> toks;
1173
  toks.reserve(static_cast<size_t>(M));
1174
 
1175
  for (std::uint64_t j = 0; j < M; ++j){
1176
  const std::uint64_t v_idx = read_u64(ifs);
1177
+ if (v_idx >= ptrs.size()) throw std::runtime_error("save file is corrupted: bad def value");
1178
  toks.push_back(ptrs[(size_t)v_idx]);
1179
  }
1180
 
 
1200
  {
1201
  std::lock_guard<std::mutex> lk(kb.m);
1202
  for (const auto &pr : kb.next){
1203
+ for (StrPtr ctx_element : pr.first) {
1204
+ if (seen.insert(ctx_element).second) {
1205
+ targets.push_back(ctx_element);
1206
+ }
1207
+ }
1208
+
1209
  for (StrPtr v : pr.second){
1210
+ if (seen.insert(v).second) {
1211
+ targets.push_back(v);
1212
+ }
1213
  }
1214
  }
1215
  }
 
1224
  static void print_usage(const char *p){
1225
  std::cout << "CLI options: [--response-max-length N] [--save FILE] [--load-kb FILE] [--dictionary-depth D] [--learn f1 f2 ...] [--repeat-penalty P] [--help]\n";
1226
  std::cout << " --response-max-length N Maximum number of tokens in a response.\n";
1227
+ std::cout << " --save-kb FILE Save the knowledge-base to a binary file.\n";
1228
  std::cout << " --load-kb FILE Load a previously saved knowledge-base from a binary file.\n";
1229
  std::cout << " --dictionary-depth D Depth of dictionary-definition expansion used during learning.\n";
1230
+ std::cout << " --n-gram N Size of the n-gram where N is the size.\n";
1231
  std::cout << " --learn f1 f2 ... Learn from one or more text files to update the knowledge-base.\n";
1232
  std::cout << " --repeat-penalty P Penalize repeated tokens when constructing response (higher values reduce repetition).\n";
1233
+ std::cout << " --help Show all the Command-Line Interface (CLI) options for using " << p << "\n";
1234
  }
1235
 
1236
  int main(int argc, char **argv){
1237
+ size_t response_maxlen = 500;
1238
  std::string savefile;
1239
  std::string load_txt;
1240
  std::string load_kb;
1241
+ int dict_depth = 3;
1242
+ int n_gram_size = 3;
1243
  double repeat_penalty = 0.7; // default λ
1244
  std::vector<std::string> learn_files;
1245
 
 
1247
  std::string a = argv[i];
1248
  if (a=="--help"){ print_usage(argv[0]); return 0; }
1249
  if (a=="--response-max-length" && i+1<argc){ response_maxlen = std::stoul(argv[++i]); continue; }
1250
+ if (a=="--save-kb" && i+1<argc){ savefile = argv[++i]; continue; }
1251
  if (a=="--load-kb" && i+1<argc){ load_kb = argv[++i]; continue; }
1252
  if (a=="--dictionary-depth" && i+1<argc){ dict_depth = std::stoi(argv[++i]); continue; }
1253
+ if (a=="--n-gram" && i+1<argc){ n_gram_size = std::max(1, std::stoi(argv[++i])); continue; }
1254
  if (a=="--repeat-penalty" && i+1<argc){ repeat_penalty = std::stod(argv[++i]); continue; }
1255
  if (a=="--learn"){
1256
+ while(i+1<argc && argv[i+1][0] != '-') learn_files.push_back(argv[++i]);
 
 
 
 
 
 
1257
  continue;
1258
  }
1259
  learn_files.push_back(a);
 
1273
 
1274
  if (!learn_files.empty()){
1275
  std::cerr << "Learning from file/s (" << learn_files.size() << ") using threads=" << omp_get_max_threads() << "\n";
1276
+ learn_files_parallel(kb, learn_files, n_gram_size);
1277
  }
1278
 
1279
  std::string line;
 
1281
  while (std::cout << "> " , std::getline(std::cin, line)){
1282
  if (line.empty()){ std::cout << "\n"; continue; }
1283
  auto prompt_toks = tokenize_whitespace(line);
1284
+ learn_tokens_ngram(kb, prompt_toks, n_gram_size);
1285
+ auto resp = construct_response(kb, prompt_toks, response_maxlen, repeat_penalty, n_gram_size);
1286
  std::cout << "\n";
1287
+ if (!resp.empty()){learn_tokens_ngram(kb, resp, n_gram_size);}
1288
  if (!savefile.empty()){
1289
  try { std::cerr << "Saving knowledge base: " << savefile << "\n";
1290
  save_kb_binary(kb, savefile); std::cerr << "Saved knowledge base: " << savefile << "\n"; }