@cachly-dev/mcp-server 0.10.129 → 0.10.131

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 (223) hide show
  1. package/README.md +2 -2
  2. package/dist/index.js +1 -7
  3. package/dist/packages/telegram-notify/client.js +2 -75
  4. package/dist/src/ambient-cli.js +7 -253
  5. package/dist/src/ambient-deps.js +1 -55
  6. package/dist/src/ambient-hooks.js +5 -278
  7. package/dist/src/ambient-ledger.js +6 -95
  8. package/dist/src/ambient-memory.js +1 -67
  9. package/dist/src/ambient-recall.js +1 -221
  10. package/dist/src/aussetzer.js +1 -225
  11. package/dist/src/auth.js +9 -140
  12. package/dist/src/autor-abzeichen.js +1 -56
  13. package/dist/src/autor.js +1 -136
  14. package/dist/src/bauabdruck.js +1 -93
  15. package/dist/src/bedeutung.js +2 -323
  16. package/dist/src/bench/cachly-bench.js +4 -249
  17. package/dist/src/bench/external-corpus.js +4 -135
  18. package/dist/src/bench/fixtures.js +1 -225
  19. package/dist/src/brain-export.js +16 -119
  20. package/dist/src/brain-watch-hook.js +8 -86
  21. package/dist/src/cachly-url.js +1 -21
  22. package/dist/src/ckg.js +1 -108
  23. package/dist/src/cls-hook.js +8 -94
  24. package/dist/src/confidence.js +1 -50
  25. package/dist/src/credentials.js +1 -85
  26. package/dist/src/deutsch.js +1 -223
  27. package/dist/src/doctor.js +2 -248
  28. package/dist/src/editor.js +1 -63
  29. package/dist/src/eingaenge-nachruesten.js +1 -290
  30. package/dist/src/eingaenge.js +2 -260
  31. package/dist/src/embeddings.js +13 -223
  32. package/dist/src/feldreparatur.js +3 -113
  33. package/dist/src/first-contact.js +4 -108
  34. package/dist/src/first-recall-proof.js +4 -41
  35. package/dist/src/funnel-milestones.js +2 -48
  36. package/dist/src/geliefert.js +1 -114
  37. package/dist/src/handlers/advanced.js +38 -537
  38. package/dist/src/handlers/brain.js +69 -4397
  39. package/dist/src/handlers/cache.js +59 -638
  40. package/dist/src/handlers/context.js +17 -182
  41. package/dist/src/handlers/fedbrain.js +40 -1436
  42. package/dist/src/handlers/instances.js +16 -201
  43. package/dist/src/handlers/roadmap.js +13 -175
  44. package/dist/src/handlers/share.js +19 -530
  45. package/dist/src/handlers/syndicate.js +38 -904
  46. package/dist/src/handlers/team.js +89 -1865
  47. package/dist/src/handlers/viz.js +3 -148
  48. package/dist/src/index.js +321 -4281
  49. package/dist/src/interferenz.js +1 -141
  50. package/dist/src/kostprobe.js +7 -125
  51. package/dist/src/lesson-preview.js +2 -133
  52. package/dist/src/namespace.js +1 -128
  53. package/dist/src/notifier.js +1 -6
  54. package/dist/src/rangfolge-stellschrauben.js +1 -0
  55. package/dist/src/rangfolge.js +1 -249
  56. package/dist/src/raumkorrektur.js +1 -197
  57. package/dist/src/recall-tiefe.js +1 -42
  58. package/dist/src/rerank.js +1 -154
  59. package/dist/src/resonanz.js +1 -140
  60. package/dist/src/search.js +1 -2104
  61. package/dist/src/seltenheitsbestand.js +1 -92
  62. package/dist/src/speicherlage.js +1 -84
  63. package/dist/src/spuren.js +1 -150
  64. package/dist/src/starter-corpus.js +1 -165
  65. package/dist/src/telemetry-types.js +0 -13
  66. package/dist/src/tools.js +10 -2465
  67. package/dist/src/toolspecs.js +1 -121
  68. package/dist/src/upgrade-nudge.js +1 -29
  69. package/dist/src/utils.js +1 -89
  70. package/dist/src/versuch.js +1 -264
  71. package/dist/src/vorspann.js +1 -146
  72. package/dist/src/wartung/vektoren-verdichten.js +1 -200
  73. package/dist/src/werkzeug-nutzung.js +1 -72
  74. package/dist/src/wertbeitrag.js +1 -203
  75. package/dist/src/wirkungsspur.js +1 -0
  76. package/glama.json +1 -1
  77. package/package.json +8 -7
  78. package/server.json +3 -3
  79. package/dist/index.d.ts +0 -1
  80. package/dist/index.js.map +0 -1
  81. package/dist/packages/telegram-notify/client.d.ts +0 -19
  82. package/dist/packages/telegram-notify/client.js.map +0 -1
  83. package/dist/src/ambient-cli.d.ts +0 -101
  84. package/dist/src/ambient-cli.js.map +0 -1
  85. package/dist/src/ambient-deps.d.ts +0 -26
  86. package/dist/src/ambient-deps.js.map +0 -1
  87. package/dist/src/ambient-hooks.d.ts +0 -137
  88. package/dist/src/ambient-hooks.js.map +0 -1
  89. package/dist/src/ambient-ledger.d.ts +0 -19
  90. package/dist/src/ambient-ledger.js.map +0 -1
  91. package/dist/src/ambient-memory.d.ts +0 -12
  92. package/dist/src/ambient-memory.js.map +0 -1
  93. package/dist/src/ambient-recall.d.ts +0 -127
  94. package/dist/src/ambient-recall.js.map +0 -1
  95. package/dist/src/aussetzer.d.ts +0 -156
  96. package/dist/src/aussetzer.js.map +0 -1
  97. package/dist/src/auth.d.ts +0 -43
  98. package/dist/src/auth.js.map +0 -1
  99. package/dist/src/autor-abzeichen.d.ts +0 -41
  100. package/dist/src/autor-abzeichen.js.map +0 -1
  101. package/dist/src/autor.d.ts +0 -101
  102. package/dist/src/autor.js.map +0 -1
  103. package/dist/src/bauabdruck-daten.generiert.json +0 -5
  104. package/dist/src/bauabdruck.d.ts +0 -38
  105. package/dist/src/bauabdruck.js.map +0 -1
  106. package/dist/src/bedeutung.d.ts +0 -168
  107. package/dist/src/bedeutung.js.map +0 -1
  108. package/dist/src/bench/cachly-bench.d.ts +0 -61
  109. package/dist/src/bench/cachly-bench.js.map +0 -1
  110. package/dist/src/bench/external-corpus.d.ts +0 -44
  111. package/dist/src/bench/external-corpus.js.map +0 -1
  112. package/dist/src/bench/fixtures.d.ts +0 -37
  113. package/dist/src/bench/fixtures.js.map +0 -1
  114. package/dist/src/brain-export.d.ts +0 -64
  115. package/dist/src/brain-export.js.map +0 -1
  116. package/dist/src/brain-watch-hook.d.ts +0 -21
  117. package/dist/src/brain-watch-hook.js.map +0 -1
  118. package/dist/src/cachly-url.d.ts +0 -68
  119. package/dist/src/cachly-url.js.map +0 -1
  120. package/dist/src/ckg.d.ts +0 -54
  121. package/dist/src/ckg.js.map +0 -1
  122. package/dist/src/cls-hook.d.ts +0 -15
  123. package/dist/src/cls-hook.js.map +0 -1
  124. package/dist/src/confidence.d.ts +0 -19
  125. package/dist/src/confidence.js.map +0 -1
  126. package/dist/src/credentials.d.ts +0 -37
  127. package/dist/src/credentials.js.map +0 -1
  128. package/dist/src/deutsch.d.ts +0 -98
  129. package/dist/src/deutsch.js.map +0 -1
  130. package/dist/src/doctor.d.ts +0 -47
  131. package/dist/src/doctor.js.map +0 -1
  132. package/dist/src/editor.d.ts +0 -9
  133. package/dist/src/editor.js.map +0 -1
  134. package/dist/src/eingaenge-nachruesten.d.ts +0 -41
  135. package/dist/src/eingaenge-nachruesten.js.map +0 -1
  136. package/dist/src/eingaenge.d.ts +0 -159
  137. package/dist/src/eingaenge.js.map +0 -1
  138. package/dist/src/embeddings.d.ts +0 -48
  139. package/dist/src/embeddings.js.map +0 -1
  140. package/dist/src/feldreparatur.d.ts +0 -58
  141. package/dist/src/feldreparatur.js.map +0 -1
  142. package/dist/src/first-contact.d.ts +0 -56
  143. package/dist/src/first-contact.js.map +0 -1
  144. package/dist/src/first-recall-proof.d.ts +0 -19
  145. package/dist/src/first-recall-proof.js.map +0 -1
  146. package/dist/src/funnel-milestones.d.ts +0 -5
  147. package/dist/src/funnel-milestones.js.map +0 -1
  148. package/dist/src/geliefert.d.ts +0 -63
  149. package/dist/src/geliefert.js.map +0 -1
  150. package/dist/src/handlers/advanced.d.ts +0 -6
  151. package/dist/src/handlers/advanced.js.map +0 -1
  152. package/dist/src/handlers/brain.d.ts +0 -41
  153. package/dist/src/handlers/brain.js.map +0 -1
  154. package/dist/src/handlers/cache.d.ts +0 -6
  155. package/dist/src/handlers/cache.js.map +0 -1
  156. package/dist/src/handlers/context.d.ts +0 -6
  157. package/dist/src/handlers/context.js.map +0 -1
  158. package/dist/src/handlers/fedbrain.d.ts +0 -18
  159. package/dist/src/handlers/fedbrain.js.map +0 -1
  160. package/dist/src/handlers/instances.d.ts +0 -33
  161. package/dist/src/handlers/instances.js.map +0 -1
  162. package/dist/src/handlers/roadmap.d.ts +0 -6
  163. package/dist/src/handlers/roadmap.js.map +0 -1
  164. package/dist/src/handlers/share.d.ts +0 -8
  165. package/dist/src/handlers/share.js.map +0 -1
  166. package/dist/src/handlers/syndicate.d.ts +0 -6
  167. package/dist/src/handlers/syndicate.js.map +0 -1
  168. package/dist/src/handlers/team.d.ts +0 -45
  169. package/dist/src/handlers/team.js.map +0 -1
  170. package/dist/src/handlers/viz.d.ts +0 -6
  171. package/dist/src/handlers/viz.js.map +0 -1
  172. package/dist/src/index.d.ts +0 -93
  173. package/dist/src/index.js.map +0 -1
  174. package/dist/src/interferenz.d.ts +0 -111
  175. package/dist/src/interferenz.js.map +0 -1
  176. package/dist/src/kostprobe.d.ts +0 -25
  177. package/dist/src/kostprobe.js.map +0 -1
  178. package/dist/src/lesson-preview.d.ts +0 -81
  179. package/dist/src/lesson-preview.js.map +0 -1
  180. package/dist/src/namespace.d.ts +0 -20
  181. package/dist/src/namespace.js.map +0 -1
  182. package/dist/src/notifier.d.ts +0 -5
  183. package/dist/src/notifier.js.map +0 -1
  184. package/dist/src/rangfolge.d.ts +0 -205
  185. package/dist/src/rangfolge.js.map +0 -1
  186. package/dist/src/raumkorrektur.d.ts +0 -100
  187. package/dist/src/raumkorrektur.js.map +0 -1
  188. package/dist/src/recall-tiefe.d.ts +0 -48
  189. package/dist/src/recall-tiefe.js.map +0 -1
  190. package/dist/src/rerank.d.ts +0 -48
  191. package/dist/src/rerank.js.map +0 -1
  192. package/dist/src/resonanz.d.ts +0 -85
  193. package/dist/src/resonanz.js.map +0 -1
  194. package/dist/src/search.d.ts +0 -153
  195. package/dist/src/search.js.map +0 -1
  196. package/dist/src/seltenheitsbestand.d.ts +0 -53
  197. package/dist/src/seltenheitsbestand.js.map +0 -1
  198. package/dist/src/speicherlage.d.ts +0 -51
  199. package/dist/src/speicherlage.js.map +0 -1
  200. package/dist/src/spuren.d.ts +0 -97
  201. package/dist/src/spuren.js.map +0 -1
  202. package/dist/src/starter-corpus.d.ts +0 -28
  203. package/dist/src/starter-corpus.js.map +0 -1
  204. package/dist/src/telemetry-types.d.ts +0 -87
  205. package/dist/src/telemetry-types.js.map +0 -1
  206. package/dist/src/tools.d.ts +0 -2933
  207. package/dist/src/tools.js.map +0 -1
  208. package/dist/src/toolspecs.d.ts +0 -43
  209. package/dist/src/toolspecs.js.map +0 -1
  210. package/dist/src/upgrade-nudge.d.ts +0 -15
  211. package/dist/src/upgrade-nudge.js.map +0 -1
  212. package/dist/src/utils.d.ts +0 -34
  213. package/dist/src/utils.js.map +0 -1
  214. package/dist/src/versuch.d.ts +0 -187
  215. package/dist/src/versuch.js.map +0 -1
  216. package/dist/src/vorspann.d.ts +0 -73
  217. package/dist/src/vorspann.js.map +0 -1
  218. package/dist/src/wartung/vektoren-verdichten.d.ts +0 -50
  219. package/dist/src/wartung/vektoren-verdichten.js.map +0 -1
  220. package/dist/src/werkzeug-nutzung.d.ts +0 -62
  221. package/dist/src/werkzeug-nutzung.js.map +0 -1
  222. package/dist/src/wertbeitrag.d.ts +0 -148
  223. package/dist/src/wertbeitrag.js.map +0 -1
package/dist/src/autor.js CHANGED
@@ -1,136 +1 @@
1
- import { execSync } from 'node:child_process';
2
- export const echterGitLeser = (cwd) => (schluessel) => {
3
- try {
4
- return execSync(`git config ${schluessel}`, {
5
- cwd,
6
- encoding: 'utf-8',
7
- stdio: 'pipe',
8
- timeout: 1500,
9
- }).trim();
10
- }
11
- catch {
12
- return '';
13
- }
14
- };
15
- /**
16
- * Einen Namen auf die Form bringen, in der Autoren gespeichert werden.
17
- *
18
- * Klein geschrieben und ohne Leerzeichen: "Heinrich Neb" wird zu "heinrich".
19
- * Das ist keine Kosmetik — brain_who_knows, team_recall und die
20
- * Wiederverwendungs-Zaehlung vergleichen Autoren als Zeichenketten. "Heinrich"
21
- * und "heinrich" waeren zwei Menschen.
22
- */
23
- export function normalisiere(roh) {
24
- const t = (roh ?? '').trim();
25
- if (!t)
26
- return '';
27
- // Nur der Rufname: Nachnamen aendern sich (Heirat), Rufnamen selten. Und
28
- // ein Nachname im Autorenfeld ist eine Personendatei mehr, die niemand
29
- // gebeten hat anzulegen.
30
- const erster = t.split(/[\s.]+/)[0] ?? '';
31
- return erster
32
- .toLowerCase()
33
- .replace(/[^a-z0-9_-]/g, '')
34
- .slice(0, 32);
35
- }
36
- /**
37
- * Ausdrueckliche Wortmeldung "ohne meinen Namen".
38
- *
39
- * Ohne diesen Weg gaebe es keinen mehr: seit der Name aus git hergeleitet
40
- * wird, bekaeme JEDE Lektion einen Autor, auch die, bei der der Mensch das
41
- * nicht will. Eine private Notiz ueber einen Kunden, ein Fund, den man nicht
42
- * an sich haengen will — das muss weiterhin gehen, und zwar sichtbar statt
43
- * durch Weglassen.
44
- *
45
- * Der Test, der beim Umbau rot wurde, hat genau diese Luecke gezeigt: er
46
- * speicherte eine Lektion ohne Autor und erwartete, dass skill_gaps sie
47
- * anmeckert. Das ging vorher versehentlich, jetzt geht es absichtlich.
48
- */
49
- const ANONYM = new Set(['-', 'anonym', 'anonymous', 'none', 'kein', 'off']);
50
- export function ermittleAutor(input) {
51
- const rohUeb = (input.uebergeben ?? '').trim().toLowerCase();
52
- if (ANONYM.has(rohUeb))
53
- return { autor: '', quelle: 'anonym' };
54
- const rohEnv = (input.umgebung ?? '').trim().toLowerCase();
55
- if (ANONYM.has(rohEnv))
56
- return { autor: '', quelle: 'anonym' };
57
- const ueb = normalisiere(input.uebergeben ?? '');
58
- if (ueb)
59
- return { autor: ueb, quelle: 'uebergeben' };
60
- const env = normalisiere(input.umgebung ?? '');
61
- if (env)
62
- return { autor: env, quelle: 'umgebung' };
63
- const name = normalisiere(input.git('user.name'));
64
- if (name)
65
- return { autor: name, quelle: 'git-name' };
66
- const mail = input.git('user.email');
67
- const vorDemAt = normalisiere((mail ?? '').split('@')[0] ?? '');
68
- if (vorDemAt)
69
- return { autor: vorDemAt, quelle: 'git-mail' };
70
- return { autor: '', quelle: 'unbekannt' };
71
- }
72
- /**
73
- * Der Mitautor — standardmaessig AUS.
74
- *
75
- * Heinrich: "Wir koennen im gleichen Zuge ja auch zum Beispiel claude als
76
- * Co-Author nehmen, wenn der User das moechte? Einstellungssache,
77
- * standardmaessig aber aus."
78
- *
79
- * Aus zwei Gruenden aus: erstens gehoert die Entscheidung dem Menschen, und
80
- * zweitens wuerde ein automatisch gesetzter Mitautor die Zahl, um die es hier
81
- * geht — wie viele Lektionen einen MENSCHEN als Autor haben — sofort wieder
82
- * unbrauchbar machen.
83
- *
84
- * Eingeschaltet wird er mit
85
- * brain_set_pref(key="coauthor", value="claude")
86
- * und wieder aus mit value="" oder "off".
87
- */
88
- export const COAUTHOR_PREF_KEY = 'coauthor';
89
- /** Aus dem gespeicherten Wert den Mitautor lesen — leer heisst aus. */
90
- export function coautorAus(prefWert) {
91
- const t = (prefWert ?? '').trim().toLowerCase();
92
- if (!t || t === 'off' || t === 'false' || t === '0' || t === 'none')
93
- return '';
94
- return normalisiere(t);
95
- }
96
- /**
97
- * Wie der Autor gespeichert wird, wenn ein Mitautor eingeschaltet ist.
98
- *
99
- * "heinrich+claude" — ein Feld, kein zweites. Ein zweites Feld haette jede
100
- * Abfrage im ganzen Haus anfassen muessen (who_knows, team_recall,
101
- * reuse_pairs, das Briefing), und die Zahl "wie viele Lektionen haben einen
102
- * Autor" waere dabei zweideutig geworden.
103
- *
104
- * Der Mensch steht VORN. Wer die Zeichenkette an "+" trennt, bekommt zuerst
105
- * den, der die Entscheidung getroffen hat.
106
- */
107
- export function mitCoautor(autor, coautor) {
108
- if (!autor)
109
- return '';
110
- if (!coautor || coautor === autor)
111
- return autor;
112
- return `${autor}+${coautor}`;
113
- }
114
- /** Nur der Mensch, ohne Mitautor — fuer Zaehlungen und Vergleiche. */
115
- export function nurMensch(autorFeld) {
116
- return (autorFeld ?? '').split('+')[0] ?? '';
117
- }
118
- /**
119
- * Ein Satz an den Aufrufer, wenn kein Autor ermittelt werden konnte.
120
- *
121
- * Wieder ein HINWEIS und keine Ablehnung: eine abgelehnte Lektion ist immer
122
- * schlechter als eine ohne Autor. Aber schweigen waere falsch — genau das
123
- * Schweigen hat die 195 erzeugt.
124
- */
125
- export function autorHinweis(h) {
126
- if (h.autor)
127
- return null;
128
- // Wer ausdruecklich anonym speichert, bekommt keine Belehrung.
129
- if (h.quelle === 'anonym')
130
- return null;
131
- return (`👤 **Kein Autor ermittelt** — weder \`author\`, noch \`CACHLY_AUTHOR\`, noch \`git config user.name\`. ` +
132
- `Ohne Autor zählt diese Lektion nicht als Team-Wissen: \`brain_who_knows\` findet niemanden und ` +
133
- `Wiederverwendung über Personengrenzen bleibt ungezählt. Ein \`git config user.name "Vorname"\` ` +
134
- `im Projekt genügt für alle künftigen Lektionen.`);
135
- }
136
- //# sourceMappingURL=autor.js.map
1
+ import{execSync as c}from"node:child_process";const f=e=>n=>{try{return c(`git config ${n}`,{cwd:e,encoding:"utf-8",stdio:"pipe",timeout:1500}).trim()}catch{return""}};function t(e){const n=(e??"").trim();return n?(n.split(/[\s.]+/)[0]??"").toLowerCase().replace(/[^a-z0-9_-]/g,"").slice(0,32):""}const a=new Set(["-","anonym","anonymous","none","kein","off"]);function g(e){const n=(e.uebergeben??"").trim().toLowerCase();if(a.has(n))return{autor:"",quelle:"anonym"};const r=(e.umgebung??"").trim().toLowerCase();if(a.has(r))return{autor:"",quelle:"anonym"};const o=t(e.uebergeben??"");if(o)return{autor:o,quelle:"uebergeben"};const u=t(e.umgebung??"");if(u)return{autor:u,quelle:"umgebung"};const i=t(e.git("user.name"));if(i)return{autor:i,quelle:"git-name"};const l=e.git("user.email"),s=t((l??"").split("@")[0]??"");return s?{autor:s,quelle:"git-mail"}:{autor:"",quelle:"unbekannt"}}const b="coauthor";function h(e){const n=(e??"").trim().toLowerCase();return!n||n==="off"||n==="false"||n==="0"||n==="none"?"":t(n)}function p(e,n){return e?!n||n===e?e:`${e}+${n}`:""}function w(e){return(e??"").split("+")[0]??""}function d(e){return e.autor||e.quelle==="anonym"?null:'\u{1F464} **Kein Autor ermittelt** \u2014 weder `author`, noch `CACHLY_AUTHOR`, noch `git config user.name`. Ohne Autor z\xE4hlt diese Lektion nicht als Team-Wissen: `brain_who_knows` findet niemanden und Wiederverwendung \xFCber Personengrenzen bleibt ungez\xE4hlt. Ein `git config user.name "Vorname"` im Projekt gen\xFCgt f\xFCr alle k\xFCnftigen Lektionen.'}export{b as COAUTHOR_PREF_KEY,d as autorHinweis,h as coautorAus,f as echterGitLeser,g as ermittleAutor,p as mitCoautor,t as normalisiere,w as nurMensch};
@@ -1,93 +1 @@
1
- // Bauabdruck: zeigt, welcher Stand des Servers gerade laeuft.
2
- //
3
- // Zwei Bauten koennen dieselbe Versionsnummer tragen und trotzdem
4
- // verschiedenen Code ausfuehren. Gemessen am 20.08.2026: package.json sagte
5
- // beim installierten UND beim aktuellen Server 0.10.124 — der installierte
6
- // dist/ hatte den Code aus instances.ts (get_connection_string) trotzdem
7
- // nicht. Von aussen war das nicht zu unterscheiden. Der Bauabdruck macht den
8
- // Commit sichtbar, nicht nur die Versionsnummer.
9
- //
10
- // Die echten Werte kommen aus scripts/bauabdruck.mjs, das beim Bauen
11
- // (`npm run build`) eine JSON-Datei daneben schreibt. Diese Datei ist
12
- // generiert und steht darum in .gitignore. DIESE Datei (bauabdruck.ts) ist
13
- // eingecheckt und bleibt es — sie ist die Rueckfall-Fassung, die einspringt,
14
- // wenn noch nie gebaut wurde. Genau das prueft CI im Schritt "Type check":
15
- // `tsc --noEmit` laeuft dort OHNE vorherigen `npm run build`.
16
- import { existsSync, readFileSync } from 'node:fs';
17
- import { dirname, join } from 'node:path';
18
- import { fileURLToPath } from 'node:url';
19
- /** Rueckfall, solange kein Bauabdruck generiert wurde (kein Absturz, keine Erfindung). */
20
- export const RUECKFALL_BAUABDRUCK = {
21
- version: 'unbekannt',
22
- commit: 'unbekannt',
23
- gebautAm: 'unbekannt',
24
- };
25
- function istBauabdruck(wert) {
26
- if (typeof wert !== 'object' || wert === null)
27
- return false;
28
- const w = wert;
29
- return typeof w.version === 'string' && typeof w.commit === 'string' && typeof w.gebautAm === 'string';
30
- }
31
- /**
32
- * Laedt den generierten Bauabdruck aus dem eigenen Verzeichnis.
33
- *
34
- * Faellt auf RUECKFALL_BAUABDRUCK zurueck, wenn die Datei fehlt, kein gueltiges
35
- * JSON enthaelt oder die falsche Form hat — ein fehlender Bauabdruck ist kein
36
- * Grund, den Server gar nicht erst zu starten.
37
- */
38
- export function ladeBauabdruck(verzeichnis = dirname(fileURLToPath(import.meta.url))) {
39
- const pfad = join(verzeichnis, 'bauabdruck-daten.generiert.json');
40
- if (!existsSync(pfad))
41
- return RUECKFALL_BAUABDRUCK;
42
- try {
43
- const wert = JSON.parse(readFileSync(pfad, 'utf-8'));
44
- return istBauabdruck(wert) ? wert : RUECKFALL_BAUABDRUCK;
45
- }
46
- catch {
47
- return RUECKFALL_BAUABDRUCK;
48
- }
49
- }
50
- /** Der Bauabdruck dieses laufenden Prozesses — einmal beim Start geladen. */
51
- export const bauabdruck = ladeBauabdruck();
52
- /** Ab wie vielen Tagen der Bauabdruck in brain_doctor als alt gilt (🟡). */
53
- export const ALTER_SCHWELLE_TAGE = 14;
54
- /**
55
- * Der Waechter: ist der Bauabdruck aelter als die Schwelle?
56
- *
57
- * Eigene Funktion, damit sie einzeln aufrufbar und pruefbar ist — aus
58
- * demselben Grund wie beurteileDeckung in eingaenge.ts: ein Wächter, den man
59
- * nicht einzeln aufrufen kann, ist ein Wächter, dessen Nein niemand je sieht.
60
- * "unbekannt" gilt NICHT als alt — ein Server, der nie gebaut wurde, ist ein
61
- * anderer Zustand als einer, der seit Wochen laeuft.
62
- */
63
- export function istAlt(gebautAm, jetzt = new Date()) {
64
- const datum = new Date(gebautAm);
65
- if (Number.isNaN(datum.getTime()))
66
- return false;
67
- const tage = (jetzt.getTime() - datum.getTime()) / 86_400_000;
68
- return tage > ALTER_SCHWELLE_TAGE;
69
- }
70
- /**
71
- * Baut die Zeile fuer brain_doctor — Form und Ton wie die Zeile
72
- * "Semantic coverage" dort: Emoji, Fettdruck-Label, Zahlen statt Adjektive.
73
- *
74
- * Ein Server, der seit Wochen laeuft, ist kein Fehler — aber man muss es
75
- * sehen koennen. Darum 🟡 statt ❌ fuer den Altersfall.
76
- */
77
- export function formatiereBauabdruck(a, jetzt = new Date()) {
78
- if (a.gebautAm === 'unbekannt') {
79
- return `❔ **Bauabdruck: unbekannt** — noch kein \`npm run build\` gelaufen, Version/Commit nicht feststellbar`;
80
- }
81
- const datum = new Date(a.gebautAm);
82
- const gueltigesDatum = !Number.isNaN(datum.getTime());
83
- const alt = gueltigesDatum && istAlt(a.gebautAm, jetzt);
84
- const tage = gueltigesDatum ? Math.floor((jetzt.getTime() - datum.getTime()) / 86_400_000) : null;
85
- const alterText = tage === null ? '' : ` (vor ${tage} Tag${tage === 1 ? '' : 'en'})`;
86
- const emoji = alt ? '🟡' : '✅';
87
- const hinweis = alt
88
- ? ` — laeuft laenger als ${ALTER_SCHWELLE_TAGE} Tage. Kein Fehler, aber sichtbar.`
89
- : '';
90
- const datumsText = gueltigesDatum ? a.gebautAm.slice(0, 10) : a.gebautAm;
91
- return `${emoji} **Bauabdruck:** v${a.version} · commit \`${a.commit}\` · gebaut am ${datumsText}${alterText}${hinweis}`;
92
- }
93
- //# sourceMappingURL=bauabdruck.js.map
1
+ import{existsSync as g,readFileSync as l}from"node:fs";import{dirname as f,join as p}from"node:path";import{fileURLToPath as d}from"node:url";const o={version:"unbekannt",commit:"unbekannt",gebautAm:"unbekannt"};function k(t){if(typeof t!="object"||t===null)return!1;const e=t;return typeof e.version=="string"&&typeof e.commit=="string"&&typeof e.gebautAm=="string"}function A(t=f(d(import.meta.url))){const e=p(t,"bauabdruck-daten.generiert.json");if(!g(e))return o;try{const n=JSON.parse(l(e,"utf-8"));return k(n)?n:o}catch{return o}}const y=A(),i=14;function T(t,e=new Date){const n=new Date(t);return Number.isNaN(n.getTime())?!1:(e.getTime()-n.getTime())/864e5>i}function B(t,e=new Date){if(t.gebautAm==="unbekannt")return"\u2754 **Bauabdruck: unbekannt** \u2014 noch kein `npm run build` gelaufen, Version/Commit nicht feststellbar";const n=new Date(t.gebautAm),r=!Number.isNaN(n.getTime()),u=r&&T(t.gebautAm,e),a=r?Math.floor((e.getTime()-n.getTime())/864e5):null,m=a===null?"":` (vor ${a} Tag${a===1?"":"en"})`,s=u?"\u{1F7E1}":"\u2705",c=u?` \u2014 laeuft laenger als ${i} Tage. Kein Fehler, aber sichtbar.`:"",b=r?t.gebautAm.slice(0,10):t.gebautAm;return`${s} **Bauabdruck:** v${t.version} \xB7 commit \`${t.commit}\` \xB7 gebaut am ${b}${m}${c}`}export{i as ALTER_SCHWELLE_TAGE,o as RUECKFALL_BAUABDRUCK,y as bauabdruck,B as formatiereBauabdruck,T as istAlt,A as ladeBauabdruck};
@@ -1,323 +1,2 @@
1
- /**
2
- * Bedeutungsabgleich: die Frage findet die Lektion auch ohne gemeinsame Wörter.
3
- *
4
- * ── Warum es das gibt ────────────────────────────────────────────────────────
5
- *
6
- * Gemessen am 19.08.2026 an 499 echten Lektionen mit 100 Fragen in Alltags-
7
- * sprache:
8
- *
9
- * Verfahren Platz 1 Top 3 MRR
10
- * Wörter (bisher) 21 % 33 % 29,3 %
11
- * Bedeutung (bge-m3) 40 % 60 % 51,4 %
12
- *
13
- * Der Wortabgleich war ausgereizt: nach drei Reparaturen brachten weitere
14
- * Eingriffe je eine Frage. Der Grund liegt in der Aufgabe, nicht in der
15
- * Umsetzung. Eine Frage beschreibt ein SYMPTOM, die Lektion eine URSACHE.
16
- * "Der Deploy hängt beim Bauen" und "No space left on device" teilen kein Wort
17
- * und meinen dasselbe. Kein Wörterbuch schließt diese Lücke.
18
- *
19
- * ── Und es ist SCHNELLER, nicht langsamer ───────────────────────────────────
20
- *
21
- * Das war die Überraschung. Am selben Tag gemessen:
22
- *
23
- * Wortabgleich (heute, 499 Lektionen) 788 ms (Mitte)
24
- * Einbettung der Frage (Netzaufruf) 282 ms
25
- * Vergleich gegen 494 Vektoren 0 ms (14 ms im schlechtesten Fall)
26
- *
27
- * Der Wortabgleich baut seinen Index bei JEDER Frage neu: alle Datensätze
28
- * holen, zerlegen, Worthäufigkeiten und Bigramme rechnen. Das wächst linear
29
- * mit dem Bestand — gemessen 1,6 ms je Lektion und Frage:
30
- *
31
- * Lektionen 50 100 250 499 998 1996
32
- * Wartezeit 75ms 156ms 408ms 792ms 1601ms 3185ms
33
- *
34
- * Bei 2000 Lektionen sind das 3,2 Sekunden. Die automatische Einblendung hat
35
- * ein 3-Sekunden-Budget — das erklärt, warum sie nichts liefert
36
- * (cachly:ambient-recall-blendet-nichts-ein).
37
- *
38
- * Der Bedeutungsabgleich kostet dagegen konstant einen Netzaufruf. Er wird
39
- * nicht langsamer, wenn der Bestand wächst.
40
- *
41
- * ── Was das Modul NICHT tut ─────────────────────────────────────────────────
42
- *
43
- * Es ersetzt den Wortabgleich nicht. Zwei Gründe:
44
- *
45
- * 1. Ohne Netz gibt es keine Einbettung. Der Wortabgleich läuft immer.
46
- * 2. Gemischt sind die ersten zehn Treffer besser als bei beiden einzeln
47
- * (71 % gegen 68 und 47). Wörter finden das wörtlich Genannte, Bedeutung
48
- * das sinngemäß Gemeinte.
49
- */
50
- /** Wo ein Lektionsvektor liegt. Ein Schlüssel je Lektion, neben der Lektion. */
51
- export const VEKTOR_PRAEFIX = 'cachly:lesson:vec:';
52
- /**
53
- * Wo der Vektor des THEMENNAMENS liegt.
54
- *
55
- * Warum getrennt vom Volltextvektor: der Name ist kurz und frageähnlich, der
56
- * Volltext ist 1376 Zeichen lang. Eine 60-Zeichen-Frage gegen 60 Zeichen zu
57
- * halten ist symmetrisch, gegen 1376 nicht — der Sortierer bewertet beide
58
- * deshalb als eigene Merkmale (`naeheText` 1,0 und `naeheThema` 0,6).
59
- *
60
- * Gemessen am 19.08.2026: der Name allein findet nur 26 %, im Verbund trägt er
61
- * verlässlich. Bis zum 20.08. gab es diese Vektoren nur im Messstand
62
- * (korpus-gross.sicht-c.json), im Produkt nicht — der Sortierer aus
63
- * rangfolge.ts war deshalb auch gar nicht verdrahtet.
64
- */
65
- export const NAME_VEKTOR_PRAEFIX = 'cachly:lesson:vecname:';
66
- /** Der Text, aus dem der Namensvektor gebildet wird: Trennzeichen zu Wörtern. */
67
- export function textFuerNamensVektor(topic) {
68
- return topic.replace(/[:_-]+/g, ' ').replace(/\s+/g, ' ').trim();
69
- }
70
- /**
71
- * Vektoren werden als base64 gespeichert, nicht als JSON-Zahlenliste.
72
- *
73
- * 1024 Zahlen als JSON sind rund 20 KB je Lektion, als Float32 sind es 4 KB.
74
- * Bei 500 Lektionen ist das der Unterschied zwischen 10 MB und 2 MB, die bei
75
- * jedem kalten Start über die Leitung gehen.
76
- *
77
- * ── Seit 20.08.2026: int8 statt float32 ────────────────────────────────────
78
- *
79
- * Gemessen an diesem Tag: von 23,6 MB im Speicher waren 11,2 MB (47 %)
80
- * Vektoren — 6,0 MB Lektionsvektoren und 5,2 MB Eingänge. Der Freitarif hat
81
- * 25 MB. Der Bestand stand bei 94 % und die nächste Lektion wäre an einem
82
- * Schreibfehler gescheitert.
83
- *
84
- * Eine Zahl braucht hier keine 4 Bytes. Die Vektoren sind normiert, alle Werte
85
- * liegen dicht beieinander, und für den Kosinus zählt die RICHTUNG. Also wird
86
- * je Vektor eine Skala mitgeschrieben und jede Zahl als ein Byte abgelegt.
87
- * Aus 4096 Bytes werden 1033 — Faktor 3,96.
88
- *
89
- * Warum weiter base64 und nicht roh: rohe Bytes müssten überall als Buffer
90
- * gelesen werden. Eine einzige Stelle, die weiter Text liest, macht Vektoren
91
- * still kaputt statt laut. Der base64-Aufschlag von einem Drittel ist billiger
92
- * als diese Fehlerklasse.
93
- *
94
- * ── Aufbau des neuen Formats ───────────────────────────────────────────────
95
- *
96
- * Byte 0 Kennung 0x01
97
- * Byte 1–2 uint16 LE — wie viele Zahlen
98
- * Byte 3–6 float32 LE — die Skala
99
- * ab Byte 7 je Zahl ein int8
100
- * danach Füllbytes, bis die Länge durch 4 geteilt den Rest 1 lässt
101
- *
102
- * Die Füllbytes sind kein Schönheitsfehler, sie sind die Unterscheidung: das
103
- * alte Format ist IMMER durch 4 teilbar (float32), das neue lässt immer den
104
- * Rest 1. Die Kennung allein würde nicht reichen — ein float32 darf zufällig
105
- * mit 0x01 anfangen. Zwei unabhängige Merkmale, nicht eines.
106
- *
107
- * `entpacke` liest beide Formate. Alte Vektoren bleiben gültig und werden
108
- * ersetzt, wenn ihre Lektion das nächste Mal geschrieben wird.
109
- */
110
- const KENNUNG_INT8 = 0x01;
111
- const KOPF_BYTES = 7; // Kennung(1) + Anzahl(2) + Skala(4)
112
- export function packe(vektor) {
113
- let groesstes = 0;
114
- for (const x of vektor) {
115
- const a = Math.abs(x);
116
- if (a > groesstes)
117
- groesstes = a;
118
- }
119
- // Skala 1 bei einem Nullvektor: dann ist jede Zahl 0 und die Skala egal.
120
- const skala = groesstes > 0 ? groesstes / 127 : 1;
121
- let laenge = KOPF_BYTES + vektor.length;
122
- while (laenge % 4 !== 1)
123
- laenge++;
124
- const b = Buffer.alloc(laenge); // alloc, nicht allocUnsafe: die Füllbytes sollen 0 sein
125
- b.writeUInt8(KENNUNG_INT8, 0);
126
- b.writeUInt16LE(vektor.length, 1);
127
- b.writeFloatLE(skala, 3);
128
- for (let i = 0; i < vektor.length; i++) {
129
- const q = Math.round(vektor[i] / skala);
130
- b.writeInt8(q > 127 ? 127 : q < -127 ? -127 : q, KOPF_BYTES + i);
131
- }
132
- return b.toString('base64');
133
- }
134
- export function entpacke(s) {
135
- try {
136
- const b = Buffer.from(s, 'base64');
137
- if (b.byteLength === 0)
138
- return null;
139
- // Neues Format: Kennung UND Restklasse 1 müssen beide stimmen.
140
- if (b[0] === KENNUNG_INT8 && b.byteLength % 4 === 1 && b.byteLength > KOPF_BYTES) {
141
- const anzahl = b.readUInt16LE(1);
142
- if (anzahl === 0 || KOPF_BYTES + anzahl > b.byteLength)
143
- return null;
144
- const skala = b.readFloatLE(3);
145
- if (!Number.isFinite(skala))
146
- return null;
147
- const aus = new Array(anzahl);
148
- for (let i = 0; i < anzahl; i++)
149
- aus[i] = b.readInt8(KOPF_BYTES + i) * skala;
150
- return aus;
151
- }
152
- // Altes Format: reine float32-Folge.
153
- if (b.byteLength % 4 !== 0)
154
- return null;
155
- const f = new Float32Array(b.buffer, b.byteOffset, b.byteLength / 4);
156
- return Array.from(f);
157
- }
158
- catch {
159
- return null;
160
- }
161
- }
162
- /**
163
- * Kosinus-Ähnlichkeit zweier Vektoren.
164
- *
165
- * Die Länge wird bei JEDEM Aufruf neu gerechnet statt einmal vorher. Das ist
166
- * absichtlich: gemessen liegt der gesamte Vergleich gegen 494 Vektoren bei
167
- * 0 ms. Eine Vorberechnung wäre eine zweite Wahrheit über denselben Vektor —
168
- * und der Gewinn wäre nicht messbar.
169
- */
170
- export function kosinus(a, b) {
171
- if (a.length !== b.length)
172
- return -1;
173
- let p = 0;
174
- let qa = 0;
175
- let qb = 0;
176
- for (let i = 0; i < a.length; i++) {
177
- p += a[i] * b[i];
178
- qa += a[i] * a[i];
179
- qb += b[i] * b[i];
180
- }
181
- return qa && qb ? p / Math.sqrt(qa * qb) : 0;
182
- }
183
- /**
184
- * Der Text, aus dem der Vektor einer Lektion gebildet wird.
185
- *
186
- * Bewusst NICHT das rohe JSON: Feldnamen, Zeitstempel und Prüfspuren tragen
187
- * keine Bedeutung, verbrauchen aber vom Fenster des Einbettungsmodells. Der
188
- * Dienst kürzt bei rund 2000 Zeichen — gemessen liefern 2000, 4000 und 6000
189
- * Zeichen denselben Vektor. Was hinten steht, existiert für ihn nicht.
190
- */
191
- export function textFuerVektor(lektion) {
192
- const s = (v) => (typeof v === 'string' ? v : '');
193
- return [s(lektion.topic), s(lektion.what_worked), s(lektion.what_failed)]
194
- .filter(Boolean).join('\n').slice(0, 2000);
195
- }
196
- /**
197
- * Ein Vektorbestand, der im Arbeitsspeicher bleibt.
198
- *
199
- * Warum: 494 Vektoren sind rund 2 MB. Die bei jeder Frage aus dem Speicher zu
200
- * holen wäre langsamer als der Wortabgleich, den wir gerade ersetzen — der
201
- * Gewinn wäre weg, bevor er anfängt.
202
- *
203
- * Der MCP-Server läuft lange, also wird einmal geladen und danach nur
204
- * nachgetragen. Die Prüfung "hat sich etwas geändert" läuft über die ANZAHL
205
- * der Schlüssel, nicht über ihren Inhalt: eine geänderte Lektion bekommt beim
206
- * Lernen sofort ihren neuen Vektor geschrieben, und dieser Bestand holt ihn
207
- * beim nächsten Nachladen.
208
- */
209
- export class Vektorbestand {
210
- frischeMs;
211
- praefix;
212
- vektoren = new Map();
213
- geladen = 0;
214
- /**
215
- * @param frischeMs wie lange ein geladener Bestand als frisch gilt
216
- * @param praefix welche Sicht — Volltext (Standard) oder Themenname
217
- *
218
- * Der Präfix ist ein Parameter und keine zweite Klasse: die Mechanik ist
219
- * identisch (scannen, in Blöcken holen, entpacken, Gelöschtes vergessen).
220
- * Zwei Klassen dafür wären zwei Orte, an denen dieselbe Änderung nachgezogen
221
- * werden müsste.
222
- */
223
- constructor(frischeMs = 60_000, praefix = VEKTOR_PRAEFIX) {
224
- this.frischeMs = frischeMs;
225
- this.praefix = praefix;
226
- }
227
- /** Die Nähe einer Frage zu EINER Lektion. -2, wenn sie keinen Vektor hat. */
228
- naehe(frage, topic) {
229
- const v = this.vektoren.get(`${this.praefix}${topic}`);
230
- return v ? kosinus(frage, v) : -2;
231
- }
232
- /**
233
- * Der Vektor selbst — für die Rückkopplung, die aus den besten Treffern
234
- * einen angereicherten Fragevektor baut (`reichereAn`). `null`, wenn die
235
- * Lektion keinen hat; der Aufrufer filtert das weg.
236
- */
237
- rohvektor(topic) {
238
- return this.vektoren.get(`${this.praefix}${topic}`) ?? null;
239
- }
240
- get groesse() { return this.vektoren.size; }
241
- /** Lädt fehlende Vektoren nach. Tut nichts, wenn kürzlich geladen wurde. */
242
- async aktualisiere(redis, jetzt = Date.now()) {
243
- if (this.vektoren.size > 0 && jetzt - this.geladen < this.frischeMs)
244
- return;
245
- const schluessel = [];
246
- let cursor = '0';
247
- do {
248
- const [next, gefunden] = await redis.scan(cursor, 'MATCH', `${this.praefix}*`, 'COUNT', 500);
249
- cursor = next;
250
- schluessel.push(...gefunden);
251
- } while (cursor !== '0');
252
- const fehlend = schluessel.filter((k) => !this.vektoren.has(k));
253
- for (let i = 0; i < fehlend.length; i += 100) {
254
- const block = fehlend.slice(i, i + 100);
255
- const werte = await redis.mget(...block);
256
- for (const [j, roh] of werte.entries()) {
257
- if (!roh)
258
- continue;
259
- const v = entpacke(roh);
260
- if (v)
261
- this.vektoren.set(block[j], v);
262
- }
263
- }
264
- // Gelöschte Lektionen wieder loswerden.
265
- if (schluessel.length < this.vektoren.size) {
266
- const da = new Set(schluessel);
267
- for (const k of [...this.vektoren.keys()])
268
- if (!da.has(k))
269
- this.vektoren.delete(k);
270
- }
271
- this.geladen = jetzt;
272
- }
273
- /**
274
- * Die ähnlichsten Lektionen zu einem Fragevektor.
275
- *
276
- * Zurück kommen die THEMEN, nicht die Schlüssel — der Aufrufer arbeitet mit
277
- * Themen und soll das Schlüsselformat nicht kennen müssen.
278
- */
279
- aehnlichste(frage, anzahl) {
280
- const aus = [];
281
- for (const [k, v] of this.vektoren) {
282
- aus.push({ topic: k.slice(this.praefix.length), naehe: kosinus(frage, v) });
283
- }
284
- aus.sort((a, b) => b.naehe - a.naehe);
285
- return aus.slice(0, anzahl);
286
- }
287
- }
288
- /**
289
- * Mischt zwei Rangfolgen über die PLATZIERUNG, nicht über die Punktzahl.
290
- *
291
- * Punktzahlen aus BM25 und aus einem Kosinus sind nicht vergleichbar: die eine
292
- * ist nach oben offen, die andere liegt zwischen -1 und 1. Wer sie addiert oder
293
- * skaliert, wählt unbeabsichtigt einen Gewinner und merkt es nie.
294
- *
295
- * Reciprocal Rank Fusion braucht keine gemeinsame Einheit.
296
- *
297
- * ── Wie viel Wortabgleich? ──────────────────────────────────────────────────
298
- *
299
- * Gemessen am 19.08.2026 an 499 Lektionen mit 100 Fragen:
300
- *
301
- * Wortgewicht Platz 1 Top 3 Top 10 MRR
302
- * 0,0 (nur Sinn) 40 % 60 % 68 % 51,4 %
303
- * 0,1 41 % 63 % 71 % 53,5 %
304
- * 0,2 42 % 57 % 70 % 52,0 %
305
- * 0,3 39 % 55 % 71 % 49,0 %
306
- * 0,4 38 % 51 % 69 % 47,7 %
307
- * 1,0 (nur Wörter) 21 % 33 % 47 % 29,3 %
308
- *
309
- * 0,1 ist auf drei von vier Maßen vorn und überall besser als reine Bedeutung.
310
- * Wenig Wortabgleich hilft, viel schadet: die Wörter fangen den Fall, in dem
311
- * jemand eine Fehlermeldung wörtlich einfügt, und stören sonst.
312
- *
313
- * Zu 0,2 ist der Abstand bei Platz 1 EINE Frage. Das ist kein Beleg, sondern
314
- * eine Richtung — wer hier dreht, misst nach.
315
- */
316
- export const WORT_GEWICHT = 0.1;
317
- export function mischeRangfolgen(woerter, bedeutung, wortGewicht = WORT_GEWICHT, k = 60) {
318
- const punkte = new Map();
319
- woerter.forEach((t, i) => punkte.set(t, (punkte.get(t) ?? 0) + wortGewicht / (k + i + 1)));
320
- bedeutung.forEach((t, i) => punkte.set(t, (punkte.get(t) ?? 0) + (1 - wortGewicht) / (k + i + 1)));
321
- return [...punkte.entries()].sort((a, b) => b[1] - a[1]).map(([t]) => t);
322
- }
323
- //# sourceMappingURL=bedeutung.js.map
1
+ const x="cachly:lesson:vec:",y="cachly:lesson:vecname:";function b(o){return o.replace(/[:_-]+/g," ").replace(/\s+/g," ").trim()}const f=1,c=7;function d(o){let e=0;for(const t of o){const n=Math.abs(t);n>e&&(e=n)}const i=e>0?e/127:1;let r=c+o.length;for(;r%4!==1;)r++;const s=Buffer.alloc(r);s.writeUInt8(f,0),s.writeUInt16LE(o.length,1),s.writeFloatLE(i,3);for(let t=0;t<o.length;t++){const n=Math.round(o[t]/i);s.writeInt8(n>127?127:n<-127?-127:n,c+t)}return s.toString("base64")}function k(o){try{const e=Buffer.from(o,"base64");if(e.byteLength===0)return null;if(e[0]===f&&e.byteLength%4===1&&e.byteLength>c){const r=e.readUInt16LE(1);if(r===0||c+r>e.byteLength)return null;const s=e.readFloatLE(3);if(!Number.isFinite(s))return null;const t=new Array(r);for(let n=0;n<r;n++)t[n]=e.readInt8(c+n)*s;return t}if(e.byteLength%4!==0)return null;const i=new Float32Array(e.buffer,e.byteOffset,e.byteLength/4);return Array.from(i)}catch{return null}}function u(o,e){if(o.length!==e.length)return-1;let i=0,r=0,s=0;for(let t=0;t<o.length;t++)i+=o[t]*e[t],r+=o[t]*o[t],s+=e[t]*e[t];return r&&s?i/Math.sqrt(r*s):0}function E(o){const e=i=>typeof i=="string"?i:"";return[e(o.topic),e(o.what_worked),e(o.what_failed)].filter(Boolean).join(`
2
+ `).slice(0,2e3)}class v{frischeMs;praefix;vektoren=new Map;geladen=0;constructor(e=6e4,i=x){this.frischeMs=e,this.praefix=i}naehe(e,i){const r=this.vektoren.get(`${this.praefix}${i}`);return r?u(e,r):-2}rohvektor(e){return this.vektoren.get(`${this.praefix}${e}`)??null}get groesse(){return this.vektoren.size}async aktualisiere(e,i=Date.now()){if(this.vektoren.size>0&&i-this.geladen<this.frischeMs)return;const r=[];let s="0";do{const[n,a]=await e.scan(s,"MATCH",`${this.praefix}*`,"COUNT",500);s=n,r.push(...a)}while(s!=="0");const t=r.filter(n=>!this.vektoren.has(n));for(let n=0;n<t.length;n+=100){const a=t.slice(n,n+100),g=await e.mget(...a);for(const[p,l]of g.entries()){if(!l)continue;const h=k(l);h&&this.vektoren.set(a[p],h)}}if(r.length<this.vektoren.size){const n=new Set(r);for(const a of[...this.vektoren.keys()])n.has(a)||this.vektoren.delete(a)}this.geladen=i}aehnlichste(e,i){const r=[];for(const[s,t]of this.vektoren)r.push({topic:s.slice(this.praefix.length),naehe:u(e,t)});return r.sort((s,t)=>t.naehe-s.naehe),r.slice(0,i)}}const w=.1;function L(o,e,i=w,r=60){const s=new Map;return o.forEach((t,n)=>s.set(t,(s.get(t)??0)+i/(r+n+1))),e.forEach((t,n)=>s.set(t,(s.get(t)??0)+(1-i)/(r+n+1))),[...s.entries()].sort((t,n)=>n[1]-t[1]).map(([t])=>t)}export{y as NAME_VEKTOR_PRAEFIX,x as VEKTOR_PRAEFIX,v as Vektorbestand,w as WORT_GEWICHT,k as entpacke,u as kosinus,L as mischeRangfolgen,d as packe,b as textFuerNamensVektor,E as textFuerVektor};