knowledge-rag 4.8.4 → 4.8.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +716 -1907
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,1907 +1,716 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
[
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
###
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
|
204
|
-
|
|
205
|
-
|
|
|
206
|
-
|
|
|
207
|
-
|
|
|
208
|
-
|
|
|
209
|
-
|
|
|
210
|
-
|
|
|
211
|
-
|
|
|
212
|
-
|
|
|
213
|
-
|
|
|
214
|
-
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
```
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
**
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
---
|
|
718
|
-
|
|
719
|
-
## Usage
|
|
720
|
-
|
|
721
|
-
### Adding Documents
|
|
722
|
-
|
|
723
|
-
Place your documents in the `documents/` directory, organized by category:
|
|
724
|
-
|
|
725
|
-
```
|
|
726
|
-
documents/
|
|
727
|
-
├── security/ # Pentest, exploit, vulnerability docs
|
|
728
|
-
├── development/ # Code, APIs, frameworks
|
|
729
|
-
├── ctf/ # CTF writeups and methodology
|
|
730
|
-
├── logscale/ # LogScale/LQL documentation
|
|
731
|
-
└── general/ # Everything else
|
|
732
|
-
```
|
|
733
|
-
|
|
734
|
-
Or add documents programmatically via MCP tools:
|
|
735
|
-
|
|
736
|
-
```python
|
|
737
|
-
# Add from content
|
|
738
|
-
add_document(
|
|
739
|
-
content="# My Document\n\nContent here...",
|
|
740
|
-
filepath="security/my-technique.md",
|
|
741
|
-
category="security"
|
|
742
|
-
)
|
|
743
|
-
|
|
744
|
-
# Add from URL
|
|
745
|
-
add_from_url(
|
|
746
|
-
url="https://example.com/article",
|
|
747
|
-
category="security",
|
|
748
|
-
title="Custom Title"
|
|
749
|
-
)
|
|
750
|
-
```
|
|
751
|
-
|
|
752
|
-
### Searching
|
|
753
|
-
|
|
754
|
-
Claude uses the RAG system automatically when configured. You can also control search behavior:
|
|
755
|
-
|
|
756
|
-
```python
|
|
757
|
-
# Pure keyword search — instant, no embedding needed
|
|
758
|
-
search_knowledge("gtfobins suid", hybrid_alpha=0.0)
|
|
759
|
-
|
|
760
|
-
# Keyword-heavy (default) — fast, slight semantic boost
|
|
761
|
-
search_knowledge("mimikatz", hybrid_alpha=0.3)
|
|
762
|
-
|
|
763
|
-
# Balanced hybrid — both engines equally weighted
|
|
764
|
-
search_knowledge("SQL injection techniques", hybrid_alpha=0.5)
|
|
765
|
-
|
|
766
|
-
# Semantic-heavy — better for conceptual queries
|
|
767
|
-
search_knowledge("how to escalate privileges", hybrid_alpha=0.7)
|
|
768
|
-
|
|
769
|
-
# Pure semantic — embedding similarity only
|
|
770
|
-
search_knowledge("lateral movement strategies", hybrid_alpha=1.0)
|
|
771
|
-
```
|
|
772
|
-
|
|
773
|
-
### Indexing
|
|
774
|
-
|
|
775
|
-
Documents are automatically indexed on first startup. All reindex operations run **in background** — they return immediately and you poll progress via `get_reindex_status()`:
|
|
776
|
-
|
|
777
|
-
```python
|
|
778
|
-
# Incremental: only re-index changed files (fast)
|
|
779
|
-
reindex_documents()
|
|
780
|
-
|
|
781
|
-
# Smart reindex: detect changes + rebuild BM25
|
|
782
|
-
reindex_documents(force=True)
|
|
783
|
-
|
|
784
|
-
# Nuclear rebuild: delete everything, re-embed all (use after model change)
|
|
785
|
-
reindex_documents(full_rebuild=True)
|
|
786
|
-
|
|
787
|
-
# Poll progress (lightweight, no full stats computation)
|
|
788
|
-
get_reindex_status()
|
|
789
|
-
# → {"reindex": {"active": true, "percent": 56, "progress": "2090/3734", ...}}
|
|
790
|
-
```
|
|
791
|
-
|
|
792
|
-
### Evaluating Retrieval Quality
|
|
793
|
-
|
|
794
|
-
```python
|
|
795
|
-
evaluate_retrieval(test_cases='[
|
|
796
|
-
{"query": "sql injection", "expected_filepath": "security/sqli-guide.md"},
|
|
797
|
-
{"query": "privilege escalation", "expected_filepath": "security/privesc.md"}
|
|
798
|
-
]')
|
|
799
|
-
# Returns: MRR@5, Recall@5, per-query results
|
|
800
|
-
```
|
|
801
|
-
|
|
802
|
-
---
|
|
803
|
-
|
|
804
|
-
## API Reference
|
|
805
|
-
|
|
806
|
-
### Search & Query
|
|
807
|
-
|
|
808
|
-
#### `search_knowledge`
|
|
809
|
-
|
|
810
|
-
Hybrid search combining semantic search + BM25 keyword search with cross-encoder reranking.
|
|
811
|
-
|
|
812
|
-
| Parameter | Type | Default | Description |
|
|
813
|
-
|-----------|------|---------|-------------|
|
|
814
|
-
| `query` | string | required | Search query text (1-3 keywords recommended) |
|
|
815
|
-
| `max_results` | int | 5 | Maximum results to return (1-20) |
|
|
816
|
-
| `category` | string | null | Filter by category |
|
|
817
|
-
| `hybrid_alpha` | float | 0.3 | Balance: 0.0 = keyword only, 1.0 = semantic only |
|
|
818
|
-
| `min_score` | float | 0.0 | Minimum relevance score (0.0-1.0) to include a result. Use 0.2-0.4 to cut noise |
|
|
819
|
-
| `snippet_mode` | bool | true | Truncate content to ~500 chars at natural break points. Adds `content_length` field |
|
|
820
|
-
|
|
821
|
-
**Returns:**
|
|
822
|
-
|
|
823
|
-
```json
|
|
824
|
-
{
|
|
825
|
-
"status": "success",
|
|
826
|
-
"query": "mimikatz credential dump",
|
|
827
|
-
"hybrid_alpha": 0.5,
|
|
828
|
-
"result_count": 3,
|
|
829
|
-
"filtered_by_score": 2,
|
|
830
|
-
"cache_hit_rate": "0.0%",
|
|
831
|
-
"results": [
|
|
832
|
-
{
|
|
833
|
-
"content": "Mimikatz can extract credentials from memory...",
|
|
834
|
-
"source": "documents/security/credential-attacks.md",
|
|
835
|
-
"filename": "credential-attacks.md",
|
|
836
|
-
"category": "security",
|
|
837
|
-
"score": 0.9823,
|
|
838
|
-
"raw_rrf_score": 0.016393,
|
|
839
|
-
"reranker_score": 0.987654,
|
|
840
|
-
"semantic_rank": 2,
|
|
841
|
-
"bm25_rank": 1,
|
|
842
|
-
"search_method": "hybrid",
|
|
843
|
-
"keywords": ["mimikatz", "credential", "lsass"],
|
|
844
|
-
"routed_by": "redteam"
|
|
845
|
-
}
|
|
846
|
-
]
|
|
847
|
-
}
|
|
848
|
-
```
|
|
849
|
-
|
|
850
|
-
**Search Method Values:**
|
|
851
|
-
- `hybrid`: Found by both semantic and BM25 search (highest confidence)
|
|
852
|
-
- `semantic`: Found only by semantic search
|
|
853
|
-
- `keyword`: Found only by BM25 keyword search
|
|
854
|
-
|
|
855
|
-
---
|
|
856
|
-
|
|
857
|
-
#### `get_document`
|
|
858
|
-
|
|
859
|
-
Retrieve the full content of a specific document.
|
|
860
|
-
|
|
861
|
-
| Parameter | Type | Description |
|
|
862
|
-
|-----------|------|-------------|
|
|
863
|
-
| `filepath` | string | Path to the document file |
|
|
864
|
-
|
|
865
|
-
**Returns:** JSON with document content, metadata, keywords, and chunk count.
|
|
866
|
-
|
|
867
|
-
---
|
|
868
|
-
|
|
869
|
-
#### `reindex_documents`
|
|
870
|
-
|
|
871
|
-
Index or reindex all documents in the knowledge base. **Runs in background** — returns immediately. Poll progress via `get_reindex_status()`.
|
|
872
|
-
|
|
873
|
-
| Parameter | Type | Default | Description |
|
|
874
|
-
|-----------|------|---------|-------------|
|
|
875
|
-
| `force` | bool | false | Smart reindex: detects changes, rebuilds BM25. Fast. |
|
|
876
|
-
| `full_rebuild` | bool | false | Nuclear rebuild: deletes everything, re-embeds all documents. Use after model change. |
|
|
877
|
-
|
|
878
|
-
**Returns:** `{"status": "started", "operation": "..."}` immediately. If already running, returns `{"status": "already_running", "progress": "1200/3734"}`.
|
|
879
|
-
|
|
880
|
-
---
|
|
881
|
-
|
|
882
|
-
#### `get_reindex_status`
|
|
883
|
-
|
|
884
|
-
Get the current status of a background reindex operation. Lightweight — does not compute full index statistics.
|
|
885
|
-
|
|
886
|
-
**Returns (active):**
|
|
887
|
-
```json
|
|
888
|
-
{
|
|
889
|
-
"status": "success",
|
|
890
|
-
"reindex": {
|
|
891
|
-
"active": true,
|
|
892
|
-
"operation": "nuclear_rebuild",
|
|
893
|
-
"progress": "1200/3734",
|
|
894
|
-
"percent": 32,
|
|
895
|
-
"indexed": 1200,
|
|
896
|
-
"skipped": 0,
|
|
897
|
-
"errors": 0,
|
|
898
|
-
"started_at": "2026-06-17T18:29:49"
|
|
899
|
-
}
|
|
900
|
-
}
|
|
901
|
-
```
|
|
902
|
-
|
|
903
|
-
**Returns (idle):** `{"status": "success", "reindex": {"active": false}}`
|
|
904
|
-
|
|
905
|
-
---
|
|
906
|
-
|
|
907
|
-
#### `list_categories`
|
|
908
|
-
|
|
909
|
-
List all document categories with their document counts.
|
|
910
|
-
|
|
911
|
-
**Returns:**
|
|
912
|
-
|
|
913
|
-
```json
|
|
914
|
-
{
|
|
915
|
-
"status": "success",
|
|
916
|
-
"categories": {
|
|
917
|
-
"security": 52,
|
|
918
|
-
"development": 8,
|
|
919
|
-
"ctf": 12,
|
|
920
|
-
"general": 3
|
|
921
|
-
},
|
|
922
|
-
"total_documents": 75
|
|
923
|
-
}
|
|
924
|
-
```
|
|
925
|
-
|
|
926
|
-
---
|
|
927
|
-
|
|
928
|
-
#### `list_documents`
|
|
929
|
-
|
|
930
|
-
List all indexed documents, optionally filtered by category.
|
|
931
|
-
|
|
932
|
-
| Parameter | Type | Description |
|
|
933
|
-
|-----------|------|-------------|
|
|
934
|
-
| `category` | string | Optional category filter |
|
|
935
|
-
|
|
936
|
-
**Returns:** JSON array of documents with id, source, category, format, chunks, and keywords.
|
|
937
|
-
|
|
938
|
-
---
|
|
939
|
-
|
|
940
|
-
#### `get_index_stats`
|
|
941
|
-
|
|
942
|
-
Get statistics about the knowledge base index.
|
|
943
|
-
|
|
944
|
-
**Returns:**
|
|
945
|
-
|
|
946
|
-
```json
|
|
947
|
-
{
|
|
948
|
-
"status": "success",
|
|
949
|
-
"stats": {
|
|
950
|
-
"total_documents": 75,
|
|
951
|
-
"total_chunks": 9256,
|
|
952
|
-
"categories": {"security": 52, "development": 8},
|
|
953
|
-
"supported_formats": [".md", ".txt", ".pdf", ".py", ".json", ".docx", ".xlsx", ".pptx", ".csv", ".ipynb"],
|
|
954
|
-
"embedding_model": "BAAI/bge-small-en-v1.5",
|
|
955
|
-
"embedding_dim": 384,
|
|
956
|
-
"reranker_model": "Xenova/ms-marco-MiniLM-L-6-v2",
|
|
957
|
-
"chunk_size": 1000,
|
|
958
|
-
"chunk_overlap": 200,
|
|
959
|
-
"query_cache": {
|
|
960
|
-
"size": 12,
|
|
961
|
-
"max_size": 100,
|
|
962
|
-
"ttl_seconds": 300,
|
|
963
|
-
"hits": 45,
|
|
964
|
-
"misses": 23,
|
|
965
|
-
"hit_rate": "66.2%"
|
|
966
|
-
}
|
|
967
|
-
}
|
|
968
|
-
}
|
|
969
|
-
```
|
|
970
|
-
|
|
971
|
-
---
|
|
972
|
-
|
|
973
|
-
### Document Management
|
|
974
|
-
|
|
975
|
-
#### `add_document`
|
|
976
|
-
|
|
977
|
-
Add a new document to the knowledge base from raw content. Saves the file to the documents directory and indexes it immediately.
|
|
978
|
-
|
|
979
|
-
| Parameter | Type | Default | Description |
|
|
980
|
-
|-----------|------|---------|-------------|
|
|
981
|
-
| `content` | string | required | Full text content of the document |
|
|
982
|
-
| `filepath` | string | required | Relative path within documents dir (e.g., `security/new-technique.md`) |
|
|
983
|
-
| `category` | string | "general" | Document category |
|
|
984
|
-
|
|
985
|
-
---
|
|
986
|
-
|
|
987
|
-
#### `update_document`
|
|
988
|
-
|
|
989
|
-
Update an existing document. Removes old chunks from the index and re-indexes with new content.
|
|
990
|
-
|
|
991
|
-
| Parameter | Type | Description |
|
|
992
|
-
|-----------|------|-------------|
|
|
993
|
-
| `filepath` | string | Full path to the document file |
|
|
994
|
-
| `content` | string | New content for the document |
|
|
995
|
-
|
|
996
|
-
---
|
|
997
|
-
|
|
998
|
-
#### `remove_document`
|
|
999
|
-
|
|
1000
|
-
Remove a document from the knowledge base index. Optionally deletes the file from disk.
|
|
1001
|
-
|
|
1002
|
-
| Parameter | Type | Default | Description |
|
|
1003
|
-
|-----------|------|---------|-------------|
|
|
1004
|
-
| `filepath` | string | required | Path to the document file |
|
|
1005
|
-
| `delete_file` | bool | false | If true, also delete the file from disk |
|
|
1006
|
-
|
|
1007
|
-
---
|
|
1008
|
-
|
|
1009
|
-
#### `add_from_url`
|
|
1010
|
-
|
|
1011
|
-
Fetch content from a URL, strip HTML (scripts, styles, nav, footer, header), convert to markdown, and add to the knowledge base.
|
|
1012
|
-
|
|
1013
|
-
| Parameter | Type | Default | Description |
|
|
1014
|
-
|-----------|------|---------|-------------|
|
|
1015
|
-
| `url` | string | required | URL to fetch content from |
|
|
1016
|
-
| `category` | string | "general" | Document category |
|
|
1017
|
-
| `title` | string | null | Custom title (auto-detected from `<title>` tag if not provided) |
|
|
1018
|
-
|
|
1019
|
-
---
|
|
1020
|
-
|
|
1021
|
-
#### `search_similar`
|
|
1022
|
-
|
|
1023
|
-
Find documents similar to a given document using embedding similarity.
|
|
1024
|
-
|
|
1025
|
-
| Parameter | Type | Default | Description |
|
|
1026
|
-
|-----------|------|---------|-------------|
|
|
1027
|
-
| `filepath` | string | required | Path to the reference document |
|
|
1028
|
-
| `max_results` | int | 5 | Number of similar documents to return (1-20) |
|
|
1029
|
-
|
|
1030
|
-
---
|
|
1031
|
-
|
|
1032
|
-
#### `evaluate_retrieval`
|
|
1033
|
-
|
|
1034
|
-
Evaluate retrieval quality with test queries. Useful for tuning `hybrid_alpha`, testing query expansion effectiveness, or validating after reindexing.
|
|
1035
|
-
|
|
1036
|
-
| Parameter | Type | Description |
|
|
1037
|
-
|-----------|------|-------------|
|
|
1038
|
-
| `test_cases` | string (JSON) | Array of test cases: `[{"query": "...", "expected_filepath": "..."}, ...]` |
|
|
1039
|
-
|
|
1040
|
-
**Metrics:**
|
|
1041
|
-
- **MRR@5** (Mean Reciprocal Rank): Average of 1/rank for expected documents. 1.0 = always first result.
|
|
1042
|
-
- **Recall@5**: Fraction of expected documents found in top 5 results. 1.0 = all found.
|
|
1043
|
-
|
|
1044
|
-
---
|
|
1045
|
-
|
|
1046
|
-
## Configuration
|
|
1047
|
-
|
|
1048
|
-
Knowledge RAG is fully configurable via a `config.yaml` file in the project root. If no `config.yaml` exists, sensible defaults are used — the system works out of the box with zero configuration.
|
|
1049
|
-
|
|
1050
|
-
### Quick Start
|
|
1051
|
-
|
|
1052
|
-
```bash
|
|
1053
|
-
# Option 1: Use a preset
|
|
1054
|
-
cp presets/cybersecurity.yaml config.yaml # Offensive/defensive security, CTFs
|
|
1055
|
-
cp presets/developer.yaml config.yaml # Software engineering, APIs, DevOps
|
|
1056
|
-
cp presets/research.yaml config.yaml # Academic research, papers, studies
|
|
1057
|
-
cp presets/general.yaml config.yaml # Blank slate, pure semantic search
|
|
1058
|
-
|
|
1059
|
-
# Option 2: Start from the documented template
|
|
1060
|
-
cp config.example.yaml config.yaml
|
|
1061
|
-
# Edit config.yaml to your needs
|
|
1062
|
-
```
|
|
1063
|
-
|
|
1064
|
-
Restart Claude Code after changing `config.yaml`.
|
|
1065
|
-
|
|
1066
|
-
### config.yaml Structure
|
|
1067
|
-
|
|
1068
|
-
```yaml
|
|
1069
|
-
# Paths — where your documents live
|
|
1070
|
-
paths:
|
|
1071
|
-
documents_dir: "./documents" # Scanned recursively
|
|
1072
|
-
data_dir: "./data" # Index storage
|
|
1073
|
-
models_cache_dir: "./models_cache" # Persistent embedding model cache
|
|
1074
|
-
|
|
1075
|
-
# Documents — what gets indexed and how
|
|
1076
|
-
documents:
|
|
1077
|
-
supported_formats: # File types to index
|
|
1078
|
-
- .md
|
|
1079
|
-
- .txt
|
|
1080
|
-
- .pdf
|
|
1081
|
-
- .docx
|
|
1082
|
-
- .ipynb
|
|
1083
|
-
# - .py # Uncomment to index code
|
|
1084
|
-
exclude_patterns: # Glob patterns to skip
|
|
1085
|
-
- "node_modules"
|
|
1086
|
-
- ".venv"
|
|
1087
|
-
- "__pycache__"
|
|
1088
|
-
chunking:
|
|
1089
|
-
chunk_size: 1000 # Max chars per chunk
|
|
1090
|
-
chunk_overlap: 200 # Shared chars between chunks
|
|
1091
|
-
|
|
1092
|
-
# Models — AI models for search (all run locally, no API keys)
|
|
1093
|
-
models:
|
|
1094
|
-
embedding:
|
|
1095
|
-
model: "BAAI/bge-small-en-v1.5" # ONNX, ~33MB, auto-downloaded
|
|
1096
|
-
dimensions: 384
|
|
1097
|
-
gpu: false # Set true + pip install knowledge-rag[gpu]
|
|
1098
|
-
reranker:
|
|
1099
|
-
enabled: true # Falls back to RRF if model is unavailable
|
|
1100
|
-
model: "Xenova/ms-marco-MiniLM-L-6-v2"
|
|
1101
|
-
top_k_multiplier: 3 # Candidates fetched before reranking
|
|
1102
|
-
|
|
1103
|
-
# Search — result limits and collection name
|
|
1104
|
-
search:
|
|
1105
|
-
default_results: 5
|
|
1106
|
-
max_results: 20
|
|
1107
|
-
collection_name: "knowledge_base" # Change for separate knowledge bases
|
|
1108
|
-
|
|
1109
|
-
# Categories — auto-tag documents by folder path
|
|
1110
|
-
# Set to {} to disable categorization entirely
|
|
1111
|
-
category_mappings:
|
|
1112
|
-
"security/redteam": "redteam"
|
|
1113
|
-
"security/blueteam": "blueteam"
|
|
1114
|
-
"notes": "notes"
|
|
1115
|
-
|
|
1116
|
-
# Keyword routing — prioritize categories based on query keywords
|
|
1117
|
-
# Set to {} for pure semantic search with no routing bias
|
|
1118
|
-
keyword_routes:
|
|
1119
|
-
redteam:
|
|
1120
|
-
- pentest
|
|
1121
|
-
- exploit
|
|
1122
|
-
- privilege escalation
|
|
1123
|
-
|
|
1124
|
-
# Query expansion — expand abbreviations for better BM25 recall
|
|
1125
|
-
# Set to {} for no expansion (search terms used as-is)
|
|
1126
|
-
query_expansions:
|
|
1127
|
-
sqli:
|
|
1128
|
-
- sql injection
|
|
1129
|
-
- sqli
|
|
1130
|
-
privesc:
|
|
1131
|
-
- privilege escalation
|
|
1132
|
-
- privesc
|
|
1133
|
-
|
|
1134
|
-
# Server — enterprise features (new in v4.0.0)
|
|
1135
|
-
server:
|
|
1136
|
-
transport: "stdio" # "stdio" | "sse" | "streamable-http"
|
|
1137
|
-
host: "127.0.0.1" # Bind address (SSE/HTTP only)
|
|
1138
|
-
port: 8179 # Bind port (SSE/HTTP only)
|
|
1139
|
-
auth:
|
|
1140
|
-
bearer_token: "" # Set a secret to enable auth (SSE/HTTP only)
|
|
1141
|
-
rate_limit:
|
|
1142
|
-
enabled: false
|
|
1143
|
-
requests_per_minute: 60
|
|
1144
|
-
burst: 10
|
|
1145
|
-
metrics:
|
|
1146
|
-
enabled: false
|
|
1147
|
-
port: 9179 # Separate port for Prometheus scraping
|
|
1148
|
-
```
|
|
1149
|
-
|
|
1150
|
-
> See `config.example.yaml` for the fully documented template with explanations for every field.
|
|
1151
|
-
|
|
1152
|
-
### Presets
|
|
1153
|
-
|
|
1154
|
-
Pre-built configurations for common use cases:
|
|
1155
|
-
|
|
1156
|
-
| Preset | File | Categories | Keywords | Expansions | Best For |
|
|
1157
|
-
|--------|------|-----------|----------|-----------|----------|
|
|
1158
|
-
| **Cybersecurity** | `presets/cybersecurity.yaml` | 8 | 200+ | 69 | Red/Blue Team, CTFs, threat hunting, exploit dev |
|
|
1159
|
-
| **Developer** | `presets/developer.yaml` | 9 | 150+ | 50+ | Full-stack dev, APIs, DevOps, cloud, databases |
|
|
1160
|
-
| **Research** | `presets/research.yaml` | 9 | 100+ | 40+ | Academic papers, thesis, lab notebooks, datasets |
|
|
1161
|
-
| **General** | `presets/general.yaml` | 0 | 0 | 0 | Blank slate — pure semantic search, no domain logic |
|
|
1162
|
-
|
|
1163
|
-
**Creating your own preset**: Copy `config.example.yaml`, fill in your categories/keywords/expansions, save to `presets/your-domain.yaml`.
|
|
1164
|
-
|
|
1165
|
-
### Configuration Reference
|
|
1166
|
-
|
|
1167
|
-
#### Server
|
|
1168
|
-
|
|
1169
|
-
| Field | Default | Description |
|
|
1170
|
-
|-------|---------|-------------|
|
|
1171
|
-
| `server.transport` | `"stdio"` | Transport protocol: `"stdio"`, `"sse"`, or `"streamable-http"` |
|
|
1172
|
-
| `server.host` | `"127.0.0.1"` | Bind address for SSE/HTTP mode |
|
|
1173
|
-
| `server.port` | `8179` | Bind port for SSE/HTTP mode |
|
|
1174
|
-
| `server.auth.bearer_token` | `""` (disabled) | Bearer token for SSE/HTTP auth. Empty = no auth |
|
|
1175
|
-
| `server.rate_limit.enabled` | `false` | Enable per-client rate limiting |
|
|
1176
|
-
| `server.rate_limit.requests_per_minute` | `60` | Max requests per minute |
|
|
1177
|
-
| `server.rate_limit.burst` | `10` | Burst allowance above steady rate |
|
|
1178
|
-
| `server.metrics.enabled` | `false` | Enable Prometheus `/metrics` endpoint |
|
|
1179
|
-
| `server.metrics.port` | `9179` | Port for metrics scraping |
|
|
1180
|
-
|
|
1181
|
-
In stdio mode (default), server settings are ignored. SSE/HTTP mode auto-enables the single-instance lock.
|
|
1182
|
-
|
|
1183
|
-
#### Paths
|
|
1184
|
-
|
|
1185
|
-
| Field | Default | Description |
|
|
1186
|
-
|-------|---------|-------------|
|
|
1187
|
-
| `paths.documents_dir` | `./documents` | Root folder scanned recursively for documents |
|
|
1188
|
-
| `paths.data_dir` | `./data` | Internal storage for ChromaDB and index metadata |
|
|
1189
|
-
| `paths.models_cache_dir` | `./models_cache` | Persistent cache for embedding models (~250MB). Survives reboots |
|
|
1190
|
-
|
|
1191
|
-
Relative paths resolve from the project root. Absolute paths work too.
|
|
1192
|
-
|
|
1193
|
-
#### Documents
|
|
1194
|
-
|
|
1195
|
-
| Field | Default | Description |
|
|
1196
|
-
|-------|---------|-------------|
|
|
1197
|
-
| `documents.supported_formats` | .md .txt .pdf .py .json .docx .xlsx .pptx .csv .ipynb | File extensions to index |
|
|
1198
|
-
| `documents.exclude_patterns` | `[]` (empty) | Glob patterns for files/dirs to skip during indexing |
|
|
1199
|
-
| `documents.chunking.chunk_size` | 1000 | Max characters per chunk |
|
|
1200
|
-
| `documents.chunking.chunk_overlap` | 200 | Characters shared between consecutive chunks |
|
|
1201
|
-
|
|
1202
|
-
**Chunking guidelines**: Short notes → 500/100. General use → 1000/200. Long technical docs → 1500/300.
|
|
1203
|
-
|
|
1204
|
-
For `.md` files, chunking splits at `##` and `###` header boundaries first. Sections larger than `chunk_size` are sub-chunked with overlap. Non-markdown files use fixed-size chunking.
|
|
1205
|
-
|
|
1206
|
-
#### Models
|
|
1207
|
-
|
|
1208
|
-
| Field | Default | Description |
|
|
1209
|
-
|-------|---------|-------------|
|
|
1210
|
-
| `models.embedding.model` | `BAAI/bge-small-en-v1.5` | Embedding model (ONNX, runs locally) |
|
|
1211
|
-
| `models.embedding.dimensions` | 384 | Vector dimensions (must match model) |
|
|
1212
|
-
| `models.embedding.gpu` | false | Enable CUDA GPU acceleration. See [GPU Acceleration](#gpu-acceleration) for full setup |
|
|
1213
|
-
| `models.reranker.enabled` | true | Enable cross-encoder reranking |
|
|
1214
|
-
| `models.reranker.model` | `Xenova/ms-marco-MiniLM-L-6-v2` | Reranker model |
|
|
1215
|
-
| `models.reranker.top_k_multiplier` | 3 | Fetch N*multiplier candidates for reranking |
|
|
1216
|
-
|
|
1217
|
-
If the reranker model is not available locally and the machine cannot download it, search now falls back to the RRF order from hybrid semantic+BM25 retrieval. This keeps `search_knowledge` available offline, but result ordering may be less precise for ambiguous queries until the reranker model is cached.
|
|
1218
|
-
|
|
1219
|
-
**Embedding model options** (fastest → most accurate):
|
|
1220
|
-
- `BAAI/bge-small-en-v1.5` — 384D, ~33MB (default)
|
|
1221
|
-
- `BAAI/bge-base-en-v1.5` — 768D, ~130MB
|
|
1222
|
-
- `BAAI/bge-large-en-v1.5` — 1024D, ~335MB
|
|
1223
|
-
- `intfloat/multilingual-e5-small` — 384D, 100+ languages
|
|
1224
|
-
|
|
1225
|
-
> **Warning**: Changing the embedding model after indexing requires `reindex_documents(full_rebuild=True)`.
|
|
1226
|
-
|
|
1227
|
-
#### Search
|
|
1228
|
-
|
|
1229
|
-
| Field | Default | Description |
|
|
1230
|
-
|-------|---------|-------------|
|
|
1231
|
-
| `search.default_results` | 5 | Results returned when no limit specified |
|
|
1232
|
-
| `search.max_results` | 20 | Hard cap even if client requests more |
|
|
1233
|
-
| `search.collection_name` | `knowledge_base` | ChromaDB collection — change for separate KBs |
|
|
1234
|
-
|
|
1235
|
-
#### Categories
|
|
1236
|
-
|
|
1237
|
-
Map folder paths to category names. Documents in matching folders get auto-tagged, enabling filtered searches.
|
|
1238
|
-
|
|
1239
|
-
```yaml
|
|
1240
|
-
category_mappings:
|
|
1241
|
-
"security/redteam": "redteam"
|
|
1242
|
-
"security": "security"
|
|
1243
|
-
```
|
|
1244
|
-
|
|
1245
|
-
Set `category_mappings: {}` to disable — documents are still searchable, just without category filters.
|
|
1246
|
-
|
|
1247
|
-
#### Keyword Routing
|
|
1248
|
-
|
|
1249
|
-
Route queries to categories based on keywords. When a query contains listed keywords, results from that category are prioritized (not filtered — other categories still appear, ranked lower).
|
|
1250
|
-
|
|
1251
|
-
```yaml
|
|
1252
|
-
keyword_routes:
|
|
1253
|
-
redteam:
|
|
1254
|
-
- pentest
|
|
1255
|
-
- exploit
|
|
1256
|
-
- sqli
|
|
1257
|
-
```
|
|
1258
|
-
|
|
1259
|
-
Single-word keywords use regex word boundaries (`\b`) — "api" won't match "RAPID". Multi-word keywords use substring matching.
|
|
1260
|
-
|
|
1261
|
-
Set `keyword_routes: {}` for pure semantic search.
|
|
1262
|
-
|
|
1263
|
-
#### Query Expansion
|
|
1264
|
-
|
|
1265
|
-
Expand search terms with synonyms before BM25 search. Supports single tokens, bigrams, and full query matches.
|
|
1266
|
-
|
|
1267
|
-
```yaml
|
|
1268
|
-
query_expansions:
|
|
1269
|
-
sqli:
|
|
1270
|
-
- sql injection
|
|
1271
|
-
- sqli
|
|
1272
|
-
k8s:
|
|
1273
|
-
- kubernetes
|
|
1274
|
-
- k8s
|
|
1275
|
-
```
|
|
1276
|
-
|
|
1277
|
-
Set `query_expansions: {}` for no expansion.
|
|
1278
|
-
|
|
1279
|
-
`query_expansions` is directional: only the key on the left triggers the terms on the right. If you need mutual expansion without duplicating entries, use `query_expansion_groups`.
|
|
1280
|
-
|
|
1281
|
-
```yaml
|
|
1282
|
-
query_expansion_groups:
|
|
1283
|
-
- ["triple barrier", "tb", "trip_barr"]
|
|
1284
|
-
- ["profit factor", "pf"]
|
|
1285
|
-
```
|
|
1286
|
-
|
|
1287
|
-
Each group is interpreted symmetrically, so every term expands to the rest of the group. The final internal expansion table is built by merging both sources:
|
|
1288
|
-
|
|
1289
|
-
1. `query_expansions` entries are loaded as-is.
|
|
1290
|
-
2. `query_expansion_groups` adds reciprocal links for every term in each group.
|
|
1291
|
-
3. Overlaps are merged by union with duplicate terms removed.
|
|
1292
|
-
|
|
1293
|
-
This keeps backward compatibility while allowing concise synonym groups.
|
|
1294
|
-
|
|
1295
|
-
### Hybrid Search Tuning
|
|
1296
|
-
|
|
1297
|
-
| hybrid_alpha | Behavior | Best For |
|
|
1298
|
-
|--------------|----------|----------|
|
|
1299
|
-
| 0.0 | Pure BM25 keyword | Exact terms, CVEs, tool names |
|
|
1300
|
-
| 0.3 | Keyword-heavy **(default)** | Technical queries with specific terms |
|
|
1301
|
-
| 0.5 | Balanced | General queries |
|
|
1302
|
-
| 0.7 | Semantic-heavy | Conceptual queries, related topics |
|
|
1303
|
-
| 1.0 | Pure semantic | "How to..." questions, abstract concepts |
|
|
1304
|
-
|
|
1305
|
-
### Search Method (v4.8.2+)
|
|
1306
|
-
|
|
1307
|
-
Two paths ship with v4.8.2 — the default `hybrid` path (BM25 + semantic + RRF
|
|
1308
|
-
+ optional rerank) that has been the sole path since v4.0, and an opt-in
|
|
1309
|
-
`fts5` lexical fast-path optimised for exact identifier queries (CVEs, MITRE
|
|
1310
|
-
ATT&CK codes, CWEs, file hashes, bug-bounty IDs). Enable via
|
|
1311
|
-
`search.lexical_fast_path.enabled: true` in `config.yaml`.
|
|
1312
|
-
|
|
1313
|
-
| Method | Path | Latency (typical) | Best For |
|
|
1314
|
-
|-------------------------|-------------------|-----------------------|----------------------------------------------|
|
|
1315
|
-
| `auto` **(default)** | Router decides | Router adds ~0.1ms | Mixed workloads — safe default |
|
|
1316
|
-
| `hybrid` | BM25 + semantic | 50-150ms (with rerank)| Prose queries, "how does X work", exploration|
|
|
1317
|
-
| `fts5` | SQLite FTS5 only | <10ms cold / <2ms hot | CVE / MITRE / CWE / hash lookups |
|
|
1318
|
-
|
|
1319
|
-
The `search_knowledge` MCP tool exposes `search_method: Literal["auto",
|
|
1320
|
-
"hybrid", "fts5"] = "auto"` (ADR-006, additive on the LEI 1 contract). The
|
|
1321
|
-
default `enabled: false` preserves v4.8.1 behaviour byte-for-byte; the flip
|
|
1322
|
-
to `enabled: true` by default is reserved for v4.9.0 pending the CI
|
|
1323
|
-
perf-gate adjudication documented in ADR-004 and ADR-009. Full user guide
|
|
1324
|
-
in [`docs/features/fts5_fast_path.md`](docs/features/fts5_fast_path.md).
|
|
1325
|
-
|
|
1326
|
-
---
|
|
1327
|
-
|
|
1328
|
-
## Project Structure
|
|
1329
|
-
|
|
1330
|
-
```
|
|
1331
|
-
knowledge-rag/
|
|
1332
|
-
├── mcp_server/
|
|
1333
|
-
│ ├── __init__.py # Stdout protection + version
|
|
1334
|
-
│ ├── config.py # YAML config loader + defaults
|
|
1335
|
-
│ ├── ingestion.py # 20 parsers, chunking, metadata extraction
|
|
1336
|
-
│ └── server.py # MCP server, ChromaDB, BM25, reranker, 12 tools
|
|
1337
|
-
├── config.example.yaml # Documented config template (copy to config.yaml)
|
|
1338
|
-
├── config.yaml # Your active configuration (git-ignored)
|
|
1339
|
-
├── presets/ # Ready-to-use domain configurations
|
|
1340
|
-
│ ├── cybersecurity.yaml
|
|
1341
|
-
│ ├── developer.yaml
|
|
1342
|
-
│ ├── research.yaml
|
|
1343
|
-
│ └── general.yaml
|
|
1344
|
-
├── documents/ # Your documents (scanned recursively)
|
|
1345
|
-
├── data/
|
|
1346
|
-
│ ├── chroma_db/ # ChromaDB vector database
|
|
1347
|
-
│ └── index_metadata.json # Incremental indexing state
|
|
1348
|
-
├── models_cache/ # Persistent embedding model cache
|
|
1349
|
-
├── tests/ # Test suite (82 tests)
|
|
1350
|
-
├── install.sh # Linux/macOS installer
|
|
1351
|
-
├── install.ps1 # Windows installer
|
|
1352
|
-
├── venv/ # Python virtual environment
|
|
1353
|
-
├── requirements.txt
|
|
1354
|
-
├── pyproject.toml
|
|
1355
|
-
├── LICENSE
|
|
1356
|
-
└── README.md
|
|
1357
|
-
```
|
|
1358
|
-
|
|
1359
|
-
---
|
|
1360
|
-
|
|
1361
|
-
## Troubleshooting
|
|
1362
|
-
|
|
1363
|
-
### Python version mismatch
|
|
1364
|
-
|
|
1365
|
-
Requires Python 3.11 or newer.
|
|
1366
|
-
|
|
1367
|
-
```bash
|
|
1368
|
-
python --version # Must be 3.11+
|
|
1369
|
-
```
|
|
1370
|
-
|
|
1371
|
-
### FastEmbed model download fails
|
|
1372
|
-
|
|
1373
|
-
On first run, FastEmbed downloads models to `models_cache/`. If the download fails:
|
|
1374
|
-
|
|
1375
|
-
```bash
|
|
1376
|
-
# Clear cache and retry
|
|
1377
|
-
# Windows:
|
|
1378
|
-
rmdir /s /q models_cache
|
|
1379
|
-
|
|
1380
|
-
# Linux/macOS:
|
|
1381
|
-
rm -rf models_cache
|
|
1382
|
-
|
|
1383
|
-
# Then restart the MCP server
|
|
1384
|
-
```
|
|
1385
|
-
|
|
1386
|
-
### Reranker model download fails
|
|
1387
|
-
|
|
1388
|
-
The reranker is lazy-loaded on the first query. If the model is not cached and the machine is offline, search continues without reranking and uses the RRF order from hybrid retrieval. To keep reranking enabled offline, run one query while online or pre-populate `models_cache/` on the target machine.
|
|
1389
|
-
|
|
1390
|
-
You can still disable reranking explicitly in `config.yaml`:
|
|
1391
|
-
|
|
1392
|
-
```yaml
|
|
1393
|
-
models:
|
|
1394
|
-
reranker:
|
|
1395
|
-
enabled: false
|
|
1396
|
-
```
|
|
1397
|
-
|
|
1398
|
-
Disabling reranking reduces memory use and avoids first-query model loading. The tradeoff is lower ranking precision, especially when several chunks match the same terms but only one is the best answer.
|
|
1399
|
-
|
|
1400
|
-
### ChromaDB index crashes on startup
|
|
1401
|
-
|
|
1402
|
-
Native ChromaDB failures can terminate Python before normal exception handling runs. Startup now probes ChromaDB in a child process before initializing the MCP server. If the probe crashes, the active `chroma_db/` and `index_metadata.json` are moved to `data/backups/auto-repair-*`, and the next startup can rebuild a clean index.
|
|
1403
|
-
|
|
1404
|
-
The same guarded behavior is available through either console script:
|
|
1405
|
-
|
|
1406
|
-
```bash
|
|
1407
|
-
knowledge-rag
|
|
1408
|
-
knowledge-rag-guarded
|
|
1409
|
-
```
|
|
1410
|
-
|
|
1411
|
-
### Index is empty
|
|
1412
|
-
|
|
1413
|
-
```bash
|
|
1414
|
-
# Check documents directory has files
|
|
1415
|
-
ls documents/
|
|
1416
|
-
|
|
1417
|
-
# Force reindex via Claude Code:
|
|
1418
|
-
# reindex_documents(force=True)
|
|
1419
|
-
|
|
1420
|
-
# Or nuclear rebuild if model changed:
|
|
1421
|
-
# reindex_documents(full_rebuild=True)
|
|
1422
|
-
```
|
|
1423
|
-
|
|
1424
|
-
### MCP server not loading
|
|
1425
|
-
|
|
1426
|
-
1. Check `~/.claude.json` exists and has valid JSON in the `mcpServers` section
|
|
1427
|
-
2. Verify paths use double backslashes (`\\`) on Windows
|
|
1428
|
-
3. Restart Claude Code completely
|
|
1429
|
-
4. Run `claude mcp list` to check connection status
|
|
1430
|
-
|
|
1431
|
-
### "Failed to connect" error
|
|
1432
|
-
|
|
1433
|
-
The MCP server uses stdout for JSON-RPC communication. If a library prints to stdout during init, the stream gets corrupted. v3.4.3+ includes stdout protection that prevents this. If you're on an older version, upgrade:
|
|
1434
|
-
|
|
1435
|
-
```bash
|
|
1436
|
-
pip install --upgrade knowledge-rag
|
|
1437
|
-
```
|
|
1438
|
-
|
|
1439
|
-
### Slow first query
|
|
1440
|
-
|
|
1441
|
-
The cross-encoder reranker model is lazy-loaded on the first query. This adds a one-time ~2-3 second delay for model download and loading. Subsequent queries are fast. If the model cannot be loaded, search falls back to RRF ordering and does not retry loading the reranker until the server restarts.
|
|
1442
|
-
|
|
1443
|
-
### Memory usage
|
|
1444
|
-
|
|
1445
|
-
With ~200 documents, expect ~300-500MB RAM. The embedding model (~200MB ONNX runtime resident, lazy-loaded on first query since v3.8.0) and reranker (~25MB, lazy-loaded) are loaded into memory only when actually used. For very large knowledge bases (1000+ documents), consider enabling GPU acceleration and using exclude patterns to limit index scope.
|
|
1446
|
-
|
|
1447
|
-
### Multiple MCP clients spawn duplicate servers
|
|
1448
|
-
|
|
1449
|
-
MCP stdio is one process per client by protocol — multiple Claude Code windows, Claude Desktop + IDE, etc. each spawn their own `knowledge-rag` process. Since v3.8.0 idle processes are cheap (no embedding model loaded until first query). If you've measured and want a hard cap of one server per data directory, opt in:
|
|
1450
|
-
|
|
1451
|
-
```bash
|
|
1452
|
-
export KNOWLEDGE_RAG_SINGLE_INSTANCE=1
|
|
1453
|
-
```
|
|
1454
|
-
|
|
1455
|
-
A second instance exits immediately with code 75. Default is OFF (multi-client friendly). Full guide: [docs/single-instance.md](docs/single-instance.md). Sample MCP config: [examples/mcp-config-single-instance.json](examples/mcp-config-single-instance.json).
|
|
1456
|
-
|
|
1457
|
-
### SSE server won't start
|
|
1458
|
-
|
|
1459
|
-
```bash
|
|
1460
|
-
# Check if port 8179 is already in use
|
|
1461
|
-
# Windows:
|
|
1462
|
-
netstat -aon | findstr :8179
|
|
1463
|
-
# Linux/macOS:
|
|
1464
|
-
lsof -i :8179
|
|
1465
|
-
```
|
|
1466
|
-
|
|
1467
|
-
If `uvicorn` is not found, install the server extras: `pip install knowledge-rag[server]`
|
|
1468
|
-
|
|
1469
|
-
### Can't connect to SSE server
|
|
1470
|
-
|
|
1471
|
-
Verify the server is running and the URL is correct:
|
|
1472
|
-
|
|
1473
|
-
```bash
|
|
1474
|
-
curl http://127.0.0.1:8179/sse
|
|
1475
|
-
```
|
|
1476
|
-
|
|
1477
|
-
Common issues:
|
|
1478
|
-
- Wrong URL: must end with `/sse` (not just the port)
|
|
1479
|
-
- Firewall blocking the port
|
|
1480
|
-
- Server started with a different host/port than configured in the MCP client
|
|
1481
|
-
|
|
1482
|
-
---
|
|
1483
|
-
|
|
1484
|
-
## Changelog
|
|
1485
|
-
|
|
1486
|
-
### Unreleased
|
|
1487
|
-
|
|
1488
|
-
### v4.8.4 (2026-08-13) — Patch: security + durability + defensive fixes
|
|
1489
|
-
|
|
1490
|
-
**Recommended upgrade for anyone running HTTP/SSE transport with bearer auth on a non-loopback host, or relying on `reindex_documents(force=True)`.** Four independent fixes accumulated since v4.8.3: one previously blocked remote-with-auth deployments (#174), one silently corrupted durable index metadata after a failed rebuild (#173), and two defensive fixes hardened `.ipynb` ingestion (#175) and the nightly chaos suite (#164).
|
|
1491
|
-
|
|
1492
|
-
**Fixed:**
|
|
1493
|
-
|
|
1494
|
-
- **fix(security)** — create the HTTP MCP app with the configured `server_host`. On the bearer-auth path the app factory was called without a host, so it defaulted to `127.0.0.1` and MCP 2.0.0's localhost-only DNS-rebinding allowlist rejected a configured non-loopback host with `421 Invalid Host header` before auth ran — even though uvicorn was bound to that host. Remote/LAN deployments with auth are now reachable (#174).
|
|
1495
|
-
- **fix(reindex)** — restore durable metadata on a failed zero-downtime rebuild. The staging populate path persisted the staging map to `index_metadata.json` before validation ran; when validation failed, rollback restored only the in-memory `_indexed_docs`, leaving the file describing staging while memory held production. After a restart `_load_metadata` trusted the stale file, corrupting skip/update and embedding-dimension decisions. Rollback now re-persists the restored production metadata (#173).
|
|
1496
|
-
- **fix(ingestion)** — `_parse_ipynb` handled invalid JSON gracefully but then assumed the parsed notebook was a well-formed dict. A valid-JSON notebook with a null `metadata` or `cells` field, a non-dict cell, a bare list, or a dict cell whose `source` is not `str | list[str]` (a number, a dict, or a list with non-string items) crashed with `AttributeError`/`TypeError`. Guarded the notebook, `metadata`, `cells`, cell, and cell `source` lookups so a parseable-but-malformed notebook is handled the same as any other odd input (#175, thanks @eeshsaxena).
|
|
1497
|
-
- **fix(ci)** — the nightly chaos job (`pytest -m chaos`) aborted collection with `ModuleNotFoundError: No module named 'hypothesis'` because `tests/test_query_router.py` imported hypothesis at module top-level while the chaos job installs only `pytest psutil numpy`. Guarded the import with `pytest.importorskip("hypothesis")` so the module is skipped cleanly instead of failing the whole nightly run (#164).
|
|
1498
|
-
|
|
1499
|
-
**Docs:**
|
|
1500
|
-
|
|
1501
|
-
- **docs(architecture)** — sync the 4 Mermaid diagrams in `## Architecture` (System Overview, Query Processing Flow, Document Ingestion Flow, hybrid_alpha Parameter Effect) with the v4.8.2 FTS5 fast-path (ADR-002/003/006), the v4.8.3 `_write_collection` staging dispatcher (#161), and the current 8-category layout. The diagrams previously described only the pre-v4.8 hybrid pipeline and 4-category storage; they now also show FTS5 dispatch, fallback semantics, FTS5 CRUD sync (ADR-008), and the zero-downtime staging write path. `hybrid_alpha` now clarifies that the fast-path bypasses RRF entirely so the weighting only matters on the hybrid path.
|
|
1502
|
-
|
|
1503
|
-
**No breaking changes.** Backwards compat verified by `check_api_surface.py` (zero diff vs baseline). All 13 MCP tool parameter names frozen (verified by `tests/test_backwards_compat.py`).
|
|
1504
|
-
|
|
1505
|
-
### v4.8.3 (2026-08-10) — Critical hotfix: nuclear-rebuild + smart-reindex hardening
|
|
1506
|
-
|
|
1507
|
-
**Recommended upgrade for any corpus with >1000 chunks or any user relying on `reindex_documents(force=True)` after a config change.** The v4.8.0 nuclear rebuild + v4.8.2 FTS5 fast-path have several latent state-management bugs that only trigger under a real-world combination (large corpus + rebuild + concurrent queries + FTS5 opt-in). Enterprise users with hacktricks-scale corpora (~50k+ chunks) are affected.
|
|
1508
|
-
|
|
1509
|
-
Fixes 3 GitHub issues opened by @grishkovei against v4.8.1:
|
|
1510
|
-
|
|
1511
|
-
- **Fixes #161** — `_rebuild_via_swap()` no longer rebinds `self.collection` to the empty staging collection during populate. Writes now route through the new `_write_collection` property (returns `self._staging_target` when set, else production). Queries keep reading `self.collection` unchanged. Zero-downtime is now real, not just a docs promise. `nuclear_rebuild(swap=True)` on 50k-chunk corpora previously served empty results for the entire hours-long populate window; queries now hit production until the atomic swap completes.
|
|
1512
|
-
- **Fixes #162** — Smart-reindex checkpoint now serializes only doc-ids the current run actually committed (via new `tracking["committed_this_run"]` set), not the full `_indexed_docs.keys()` snapshot. A document that changed on disk before the reindex reached it is no longer silently skipped on resume — its mtime/size delta is properly detected.
|
|
1513
|
-
- **Fixes #163** — `reindex_documents(force=True)` now propagates through to `index_all(force=True)`. The v4.8 migration path (`docs/migration-v4.8.0.md`) documented `force=True` as the way to re-embed after prefix/model changes; the hardcoded `force=False` in `reindex_all()` silently skipped every unchanged file. Prefix-only changes no longer leave mixed old/new embedding conventions.
|
|
1514
|
-
|
|
1515
|
-
Additional fixes discovered while triaging production issues on a 5889-doc / 75016-chunk corpus:
|
|
1516
|
-
|
|
1517
|
-
- **fix(indexing)** — `_index_lock` moved from class-level to instance-level (`__init__`). The class-level `threading.Lock` was silently shared across all `KnowledgeOrchestrator` instances, so a staging orchestrator from a previous `nuclear_rebuild` swap could be garbage-collected while holding the lock, leaving every subsequent `reindex_documents` returning `reindex_already_running` despite `active: false`. Requires daemon restart to recover pre-fix.
|
|
1518
|
-
- **fix(chromadb)** — `_ensure_bm25_initialized` and `_iter_chroma_chunks_for_fts5` now batch-read Chroma via `offset+limit=500` instead of `collection.get(limit=count)`. Chromadb 1.x rebuilds an `IN (?, ?, ...)` clause per returned row, so a single `limit=48184` call blew past SQLite's default `max_variable_number=999` with `Internal error: too many SQL variables`. Any user with >999 chunks who ran nuclear rebuild or triggered the FTS5 lazy migration got a silently failed rebuild.
|
|
1519
|
-
- **fix(fts5)** — `Fts5LexicalIndex.is_ready()` now re-reads the marker file when the cached `_ready` is False. Previously an orchestrator instantiated during nuclear-swap while the FTS5 migration was still running cached `_ready=False` forever and never re-checked the marker, permanently silencing the fast-path.
|
|
1520
|
-
- **fix(fts5)** — `_maybe_start_fts5_migration` cross-checks FTS5 row count vs Chroma count on startup. A stale `complete` marker paired with a near-empty FTS5 index (post-swap orphan cleanup, manual truncate, disk corruption) previously left the fast-path silent forever. The new `_fts5_marker_matches_reality` helper triggers rebuild when FTS5 holds less than 10% of Chroma's row count.
|
|
1521
|
-
- **fix(fts5)** — `_format_fts5_results` skips orphan hits (FTS5 has the `chunk_id` but Chroma doesn't — nuclear-rebuild residue or manual-delete race). Previously emitted empty result items with `source=""` that broke reranking and polluted result lists.
|
|
1522
|
-
- **fix(indexing, config)** — Added `Fts5LexicalIndex.count()` for the new sanity check.
|
|
1523
|
-
|
|
1524
|
-
All 6 core fixes were confirmed via production reproduction on a 75016-chunk corpus + 2052 newly-added catalog documents; the `nuclear_rebuild(full_rebuild=True)` path now completes cleanly and populates FTS5 with 75016 rows on first attempt.
|
|
1525
|
-
|
|
1526
|
-
Migration path: `pip install -U knowledge-rag==4.8.3` (or `npx -y knowledge-rag@4.8.3` / `docker pull ghcr.io/lyonzin/knowledge-rag:4.8.3`) then restart the MCP daemon. No config changes required. If your FTS5 index is corrupted from a prior partial rebuild (marker says `complete` but queries return empty), delete `data/fts5_migration.state` and restart — the daemon will detect the drift and rebuild automatically.
|
|
1527
|
-
|
|
1528
|
-
### v4.8.2 (2026-08-10) — FTS5 Lexical Fast-Path Opt-In Release (default OFF)
|
|
1529
|
-
|
|
1530
|
-
v4.8.2 bundles Fases 1–5 of the FTS5 Lexical Fast-Path plan
|
|
1531
|
-
(`.compozy/tasks/fts5-lexical-fast-path/`) as an **opt-in** release —
|
|
1532
|
-
`search.lexical_fast_path.enabled` remains `false` by default, so users
|
|
1533
|
-
who do not touch `config.yaml` keep v4.8.1 behaviour byte-for-byte
|
|
1534
|
-
(LEI 1 preserved). The v4.9.0 default flip is deferred to the CI
|
|
1535
|
-
perf-gate adjudication documented in ADR-009 (bench harness ships in
|
|
1536
|
-
this release; the flip PR follows once G1 ≥15× and G2 ≤2% both pass on
|
|
1537
|
-
CI hardware).
|
|
1538
|
-
|
|
1539
|
-
- **DOCS (search)**: new `docs/features/fts5_fast_path.md` user guide
|
|
1540
|
-
covering overview / quick-start / 5-key config reference / how it
|
|
1541
|
-
works / 5 troubleshooting scenarios / when-to-use guidance. README
|
|
1542
|
-
Configuration section cross-links it.
|
|
1543
|
-
- **NEW (bench)**: `bench/test_bench_fts5_lexical.py` ships the
|
|
1544
|
-
BENCH-001 (cold, p95 ≤ 10ms target) + BENCH-002 (hot, p95 ≤ 2ms
|
|
1545
|
-
target) harness against a self-contained 3865-row FTS5 corpus. The
|
|
1546
|
-
gate G2 comparison against `bench/test_bench_search.py` is executed
|
|
1547
|
-
by CI per `.compozy/tasks/fts5-lexical-fast-path/bench_v4_9_0_gate.md`;
|
|
1548
|
-
the two-round A/B was not run locally because the release-authoring
|
|
1549
|
-
workstation cannot fairly baseline against CI hardware (ADR-004
|
|
1550
|
-
§Risks).
|
|
1551
|
-
- **DOCS (adr)**: new ADR-009 documenting why the ADR-004 gate is
|
|
1552
|
-
deferred to the CI perf-gate step and what conditions unlock the
|
|
1553
|
-
v4.9.0 flip PR.
|
|
1554
|
-
- **CHANGE (config)**: `config.example.yaml` uncomments the
|
|
1555
|
-
`search.lexical_fast_path.*` block so new users see the full
|
|
1556
|
-
configuration surface (still `enabled: false`). Existing installs
|
|
1557
|
-
are unaffected.
|
|
1558
|
-
- **VERSION**: 4.8.1 → **4.8.2** (PATCH — opt-in infrastructure + docs,
|
|
1559
|
-
zero behaviour change on the default install).
|
|
1560
|
-
- **feat (search)**: FTS5 lazy background migration for existing corpus + CRUD sync (ADR-008). `Fts5LexicalIndex` gains `add_document`/`update_document`/`remove_document` (SQL INSERT/DELETE under `_fts5_lock`, incremental — diverges from BM25's full-rebuild pattern because FTS5 mutations are O(1) native SQL) and `start_migration_background(chunk_iter_factory, docs_total, resume_from=0, on_progress=...)` (daemon thread that persists checkpoint every 100 rows into the marker file). On daemon startup, `_maybe_start_fts5_migration` reads the marker: `complete` = fast-path live, `in_progress` = resume from `docs_indexed`, `failed`/missing = start fresh migration; queries during migration fall back to hybrid + emit `fast_path_fallback_total{reason="disabled"}`. `KnowledgeOrchestrator.{add,update,remove}_document_*` gain best-effort FTS5 sync hooks after the BM25 rebuild — errors are caught, logged, and counted (`fast_path_errors_total{error_class="Fts5CrudSyncError"}`) but never propagate to the CRUD tool (upstream write must keep succeeding). `nuclear_rebuild` and `_rebuild_bm25_post_swap` drop the FTS5 db + WAL/SHM/marker and dispatch a fresh migration from the swapped-in Chroma corpus. Two Prometheus gauges expose migration progress (`knowledge_rag_fast_path_migration_docs_indexed`, `_docs_total`). Standalone rebuild helper `scripts/build_fts5_index.py --data-dir <path> [--force] [--foreground] [--verbose]` for operator-driven repair. Operational runbook in `docs/runbooks/fts5_migration.md` covering enable / wait / verify / manual rebuild / large-corpus caveat. New coverage: `tests/test_fts5_migration.py` (17 tests — TestMigrationLifecycle UT-043..048, TestCRUDSync UT-049..052, TestMigrationIntegration IT-021..023, TestCRUDSyncIntegration IT-CRUD-001..004) + `tests/test_e2e_fts5.py::test_e2e006_forced_fts5_with_migration_pending_raises` (E2E-006). Fase 4 of the FTS5 Lexical Fast-Path plan.
|
|
1561
|
-
- **NEW (search)**: Optional cross-encoder rerank toggle on the FTS5 fast-path via `search.lexical_fast_path.rerank_enabled` (default `false` — see ADR-003 for rationale). When `true`, `KnowledgeOrchestrator._run_fts5_search` layers `CrossEncoderReranker.rerank` on top of the FTS5 results before adjacent-chunk expansion; result items keep `search_method="fts5"` (rerank is a rescorer, not a path change) and `reranker_score` is populated. When `false`, the `knowledge_rag_fast_path_rerank_skipped_total` counter increments and the fast-path stays under the <10ms lexical latency budget. Empirical basis: Flash-Rerank MiniLM-L-6-v2 CPU ≈72ms/100 docs vs the <10ms target — keeping rerank OFF by default is the correct trade-off; opt in only when recall on lexical queries matters more than latency. New coverage: `tests/test_search.py::TestRerankToggle` (UT-062, UT-063, IT-024, IT-025). Fase 3.5 of the FTS5 Lexical Fast-Path plan.
|
|
1562
|
-
- **NEW (search)**: FTS5 lexical fast-path dispatch wired inside `KnowledgeOrchestrator.query()` behind the opt-in `search.lexical_fast_path.enabled` toggle (default `false` — v4.8.1 behavior byte-for-byte for users who don't touch config). When enabled, the query router (Fase 2) classifies each query; lexical queries dispatch to the FTS5 index (Fase 1) via a new `_maybe_dispatch_fts5` helper, semantic queries flow through the existing hybrid pipeline. Per ADR-003 the cross-encoder reranker is skipped on the fast-path (real opt-in toggle deferred to Task 04). Per ADR-006 the `search_knowledge` MCP tool gains one optional param `search_method: Literal["auto","hybrid","fts5"] = "auto"` — additive-safe on the LEI 1 backward-compat contract. Explicit `"fts5"` when the feature is disabled OR the index is not ready raises `Fts5NotReadyError` and the MCP wrapper surfaces it as a JSON error envelope with a `suggestion` field pointing at `search_method='auto'`. `QueryCache._make_key` gains a 5th `search_method` param so paths cannot share cache entries. `MetricsCollector` gains five FTS5 counters plus a bucketed latency histogram (`knowledge_rag_fast_path_hits_total{path}`, `..._fallback_total{reason}`, `..._latency_seconds`, `..._errors_total{error_class}`, `..._rerank_skipped_total`) exposed through `/metrics`. New coverage: `tests/test_search_method_override.py` (5 IT), `tests/test_metrics.py` (2 UT), `tests/test_e2e_fts5.py` (7 E2E), and extensions to `tests/test_search.py` (16 IT/UT — dispatch, config toggle, cache key, metrics scrape), `tests/test_backwards_compat.py` (3 UT — MCP tool signature + guardrail), `tests/test_fts5_index.py` (3 UT — fallback dispatch, busy_timeout, NotReady message). Fase 3 of the FTS5 Lexical Fast-Path plan.
|
|
1563
|
-
- **NEW (search)**: Query router lexical/semantic classifier (`mcp_server/query_router.py`) ships isolated and unused. Static regex classifier per ADR-002 — patterns are pre-compiled at construction (fail-fast on `re.error`) and matched in YAML-declared order (first-match-wins, OQ-2). API surface: `QueryRouter(patterns: Sequence[str])` plus `classify(query: str) -> Literal["lexical", "semantic"]`; empty query classifies as `"semantic"` (safe default). Zero coupling to `mcp_server/fts5_index` — the router is storage-agnostic. Task 03 will wire the dispatch inside `KnowledgeOrchestrator.query()` behind `config.fts5_enabled`; this PR is deliberately unwired so runtime behaviour is preserved. New coverage in `tests/test_query_router.py` (19 tests covering UT-001..UT-013 including a `hypothesis` property test). Fase 2 of the FTS5 Lexical Fast-Path plan (`.compozy/tasks/fts5-lexical-fast-path/`).
|
|
1564
|
-
- **NEW (search)**: FTS5 lexical index module (`mcp_server/fts5_index.py`) ships isolated and unused behind the opt-in `search.lexical_fast_path.enabled` config field (default `false`). Encapsulates the dedicated `data/fts5_index.db` SQLite FTS5 store with the ADR-005 tokenizer (`unicode61 remove_diacritics 2 tokenchars '-_.'`) so composite identifiers like `MDR-AD002`, `CVE-2021-4034`, `T1078.001`, `pass-the-hash`, `context.py` survive tokenization intact. Public surface: `Fts5LexicalIndex` (search + connection lifecycle + WAL/busy_timeout=5000ms/synchronous=NORMAL PRAGMAs per ADR-001), `Fts5MigrationState` (atomic JSON marker via `tempfile` + `fsync` + `os.replace`), and three `RuntimeError`-derived exceptions (`Fts5NotReadyError`, `Fts5CorruptError`, `Fts5MigrationError`) per ADR-004/Q4. `Config` gains four `fts5_*` fields (`fts5_enabled`, `fts5_min_hits`, `fts5_rerank_enabled`, `fts5_patterns`) backed by a new `_get_nested()` helper that mirrors the `models.reranker.*` pattern. `config.example.yaml` documents the new `search.lexical_fast_path.*` block (commented out). All FTS5 queries use prepared statements and every user-supplied token is quoted so FTS5 metacharacters (`MATCH`, `NEAR`, `OR`, `AND`, `NOT`, `*`, `:`, `(`, `)`, `"`) cannot leak through. Task 03 will wire the dispatch inside `KnowledgeOrchestrator.query()`; this PR is deliberately unwired so runtime behaviour of v4.8.1 is preserved byte-for-byte. New coverage in `tests/test_fts5_index.py` (25 tests covering UT-020..028, UT-054..058, UT-061, IT-MIG-001) and `tests/test_config_fts5.py` (9 tests covering UT-030..038). Fase 1 of the FTS5 Lexical Fast-Path plan (`.compozy/tasks/fts5-lexical-fast-path/`).
|
|
1565
|
-
|
|
1566
|
-
### v4.8.0 (2026-08-06) — Multilingual Foundation + Zero-Downtime Reindex
|
|
1567
|
-
|
|
1568
|
-
v4.8.0 é a release de fundação para migração pra embeddings multilingual (`intfloat/multilingual-e5-large`) sem forçar downtime nem breaking change. Sete fases entregaram: security wire (7 vetores fechados que existiam desde v4.5.1), embedding profiles com prefix opt-in, GPU auto-detect com dependency chain documentada, batch/parallel indexing configurável + fix da assimetria BM25/semantic candidate pool, progress granular + resume checkpoint, dual-collection swap zero-downtime, e refactor pra manter complexidade Radon C+ na superfície tocada.
|
|
1569
|
-
|
|
1570
|
-
Zero breaking change: usuários que atualizarem SEM tocar em `config.yaml` continuam com `bge-small-en-v1.5` byte-for-byte. Opt-in para multilingual: `profile: "multilingual"` + `reindex_documents(force=True)` — durante o reindex, RAG continua servindo da collection antiga (zero downtime via staging swap). Detalhes de upgrade em `docs/migration-v4.8.0.md`.
|
|
1571
|
-
|
|
1572
|
-
- **REFACTOR (server + config)**: extracted 40+ helpers from 12 functions touched by v4.8.0 F1–F5 to enforce Core hard limits (function ≤30 lines). Biggest wins — `KnowledgeOrchestrator._index_all_impl` split from 246 → 23 lines (14 focused helpers covering setup / per-doc processing / progress metrics / persistence / teardown), `Config.__post_init__` split from 185 → 12 lines (13 domain validators), `reindex_documents` MCP tool from 100 → 30 lines (mode resolution + resume hydration + response formatting extracted to module helpers). Also refactored `_index_document` (91→19), `_rebuild_via_swap` (66→29), `_validate_staging` (56→24), `_cleanup_stale_staging_collections` (48→26), `get_reindex_status` (49→12), `_print_gpu_banner` (46→20), `start_reindex_background` (46→26), `_swap_collections_atomic` (43→25), `_route_load` (41→9). New sentinel `_SKIP_DOC` distinguishes "caller must skip" from "index as fresh" in the per-doc worker. Radon complexity average dropped from D (23.4) to C (17.2); zero D+ functions remain in F1–F5 touched surface. File-level extract (`server.py` = 4009 lines, `config.py` = 990 lines) deferred to v4.9.0 because `test_backwards_compat.py` validates the MCP contract by AST and moving classes across modules multiplies regression surface. Zero behavior change — full test sweep (`test_config` + `test_embedding_profile` + `test_gpu_auto` + `test_batch_parallel` + `test_reindex_resume` + `test_swap_zero_downtime` + `test_backwards_compat` + `test_bm25_*` + `test_search` + `test_dedup` + `test_ingestion*` + `test_lazy_embeddings` + `test_reranker_fallback` + `tests/security/` + all misc) = 557 passed / 3 skipped / 9 xfailed / 0 failed. Full audit report in `docs/refactor-audit-v4.8.0-fase-6.md`. v4.8.0 Fase 6.
|
|
1573
|
-
- **PERF (reindex)**: `nuclear_rebuild(swap=True)` is now the default, replacing the destructive-first rebuild that took the RAG offline for the entire duration (4min–40h depending on corpus + hardware). New workflow — cleanup stale stagings (24h TTL) → snapshot orchestrator state (`self.collection` / `self.bm25_index` / `self._indexed_docs` / `self._source_to_docid`) → create `{collection_name}__staging_{unix_ts}` (timestamped to avoid collisions with concurrent or previously-crashed rebuilds) → populate via temporary orchestrator rebind (`self.collection` points at staging, `self.bm25_index` is a throwaway `BM25Index()` so production BM25 keeps serving queries throughout) → validate three gates (`count >= baseline * 0.9` with 10% loss threshold for parser-skipped docs / 4 of 5 canonical queries `readme/function/import/return/class` return hits / `query()` does not raise, catching embedding dim mismatches) → atomic two-step swap via `Collection.modify(name=)` (prod → `__old_{ts}` frees the name, staging → prod assumes it, delete `__old_{ts}` non-fatally) with rollback-rename on mid-swap failure → post-swap BM25 rebuild from swapped-in ChromaDB contents + query cache invalidation. Any failure in populate/validate/swap triggers rollback via the snapshot and staging cleanup — production sees zero net change. Startup cleanup handles crash-orphaned stagings automatically (also runs before each rebuild). Storage doubles temporarily during rebuild (both collections on disk), back to 1x after swap. `nuclear_rebuild(swap=False)` preserves the legacy destructive behavior byte-for-byte in `_rebuild_destructive` for backwards compat / forced-cleanup edge cases. New `tests/test_swap_zero_downtime.py` (15 tests, ~1.7s, all mocked — no real ChromaDB or FastEmbed touched) covers naming format + populate BM25 isolation + all three validation gates + fresh-install zero-baseline path + post-swap `self.collection` reconnect + BM25 rebuild only after swap success + 100 parallel queries against a captured production handle during populate (zero errors, non-empty results) + stale/recent staging cleanup + non-staging collection preservation + backwards-compat dispatch. Docs in `docs/reindex-operations.md` section "Zero-downtime Rebuild (v4.8.0 Fase 5+)". v4.8.0 Fase 5.
|
|
1574
|
-
- **NEW (reindex)**: `reindex_documents(resume=True)` recovers from `data/reindex_checkpoint.json` after an interrupted smart reindex. Checkpoint is written every 500 docs or 30s (whichever first), and stores `indexed_doc_ids` + `chunks_processed` + `config_signature` (SHA256 of `embedding_model | embedding_dim | chunk_size | chunk_overlap`). A config mismatch between checkpoint write and resume load invalidates the checkpoint with a WARN — this prevents a mixed collection (partial old-model vectors + partial new-model vectors) which would silently degrade retrieval. The combination `resume=True + full_rebuild=True` is rejected with a structured error because nuclear rebuild wipes the collection first — resuming into a partial state would leave no coherent index. The `resume=True` flag also implicitly forces `mode='smart_reindex'` even when `force=False` was passed, so an interrupted smart run cannot silently fall back to `incremental` (which would ignore the checkpoint entirely). Existing 2-arg `reindex_documents(force, full_rebuild)` signature preserved — `resume` is an opt-in tail kwarg. Missing / corrupt / future-version / non-dict checkpoints all degrade gracefully to "start fresh" instead of raising. Written to `data/reindex_checkpoint.json` via atomic `.tmp` + `os.replace()` — Windows-safe on NTFS in-directory. Cleared automatically on successful reindex completion. Metadata (`index_metadata.json`) is flushed alongside every checkpoint write so `_indexed_docs` on disk stays in sync with the checkpoint's `indexed_doc_ids` list. New `tests/test_reindex_resume.py` (22 tests, all `object.__new__` isolation — no ChromaDB, no FastEmbed download) covers write cadence + atomic write + schema + load validation (6 failure modes) + resume kwarg happy path + rejection combos + status field flow. v4.8.0 Fase 4.
|
|
1575
|
-
- **NEW (reindex)**: `get_reindex_status()` now returns 6 new fields during active reindex — `chunks_processed` (chunks committed to ChromaDB so far), `chunks_total` (rolling estimate, 0 during warmup then running average from completed docs × `total_files`), `throughput_cps` (chunks/sec, sliding window bounded by the smaller of 100 samples or 30 seconds — deque `maxlen=100` combined with age-based pruning each iteration keeps the number stable during transient stalls without diluting current speed with ancient samples), `eta_seconds` (estimated seconds to completion, derived from throughput + remaining chunks, guarded against div-by-zero), `checkpoint_saved_at` (ISO timestamp of last checkpoint write, `null` until first checkpoint at 500 docs / 30s), `resumed` (bool — `true` when the current run was recovered from a checkpoint via `resume=True`, `false` otherwise). Enables real-time progress dashboards and the "should I keep waiting or restart" call. Idle path (`active: false`) unchanged — still returns just `last_result` or `last_error`. v4.8.0 Fase 4.
|
|
1576
|
-
- **DOCS (reindex)**: `docs/reindex-operations.md` NEW — documents the sync-vs-async gotcha for `reindex_documents(full_rebuild=True)` (daemon thread dies with process → RAG offline until manual restart), recommends `orchestrator.nuclear_rebuild()` sync for CLI scripts and `reindex_documents(force=True) + resume=True` for long unstable smart rebuilds. Also covers resume semantics (only valid with `full_rebuild=False`, implicitly forces `smart_reindex` mode, graceful degrade to fresh start on missing/corrupt/version/signature mismatch), checkpoint cadence (500 docs OR 30s, whichever first, plus rationale for the OR condition), `config_signature` invalidation semantic, full lifecycle (write on cadence, clear on success, clear on failed resume load), progress fields table with warmup behavior, example recovery flow showing crash → `ls checkpoint` → `resume=True` → poll status with `resumed:true` marker, and escalation rules of thumb for the three most likely operator questions. v4.8.0 Fase 4.
|
|
1577
|
-
- **PERF (indexing)**: `documents.batch_size` (default 500) and `documents.parallel_workers` (default 1, opt-in) are now configurable in `config.yaml`. The previously hardcoded `_CHROMA_BATCH_SIZE = 500` in `server.py:_index_document` continues as fallback constant for tests that instantiate the orchestrator without a full `Config` (via `object.__new__`, as `tests/test_search.py` does). When `parallel_workers > 1` AND the document produces more than one batch, the per-batch `collection.add(...)` calls run inside a `ThreadPoolExecutor(max_workers=parallel_workers)`. Single-batch documents skip the pool entirely — no orchestration overhead to extract from a single call. Gain comes from SQLite writes overlapping with the NEXT batch's ONNX inference (embedding itself is serialized by ONNX session lock — never parallel inference). Windows users should monitor stability at `workers > 4` (extra `[WARN]` on startup). `batch_size` clamped to `[1, 5000]`, `parallel_workers` clamped to `[1, 16]` — out-of-range values are clamped with a `[WARN]`. New `bench/test_bench_reindex.py` (3 benchmarks) uses a `_SleepingCollection` stub with 5ms per-`add()` sleep so the parallel path measurably overlaps without depending on a real ChromaDB; local numbers: reindex_batch_2000 5.66ms, reindex_default 5.68ms, reindex_parallel_4 7.30ms (200-chunk doc, 4 concurrent batches, sequential would be ~20ms). New `tests/test_batch_parallel.py` (20 tests, ~1.6s) covers config parsing, clamping, Windows-only warn on `workers > 4`, sequential vs parallel dispatch, single-batch skip, `getattr` fallback to `_CHROMA_BATCH_SIZE`, exception propagation via `.result()`, and empty-doc short-circuit. Defaults preserve v4.7.1 behavior byte-for-byte. v4.8.0 Fase 3.
|
|
1578
|
-
- **FIX (search)**: `search.max_results` default raised from `20` to `100`. This eliminates the asymmetric candidate pool in hybrid retrieval where `_do_semantic` clamped candidates to `min(max_results * 3, config.max_results) = min(15, 20) = 20` while BM25 pulled up to `max_results * 20 = 400` — semantic silently starved without any log signal, degrading hybrid retrieval quality on any query where the semantic branch could have contributed better candidates. Callers passing `max_results` explicitly in the MCP tool call (`search_knowledge`, `search_similar`) are unaffected — only the config-loaded default changed. New `TestDoSemanticCandidateMath` class in `tests/test_search.py` (2 tests) pins the fix against future regressions: `test_semantic_asks_chromadb_for_3x_max_results` proves the 3× semantic candidate pool, `test_semantic_pool_is_bounded_by_config_max_results` proves the cap still works when `max_results * 3` exceeds it. v4.8.0 Fase 3.
|
|
1579
|
-
- **CHANGE (gpu)**: `models.embedding.gpu` is now tri-state (`"auto"` | `"true"` | `"false"`) with `"auto"` as the new default. In auto mode, `FastEmbedEmbeddings.verify_gpu_readiness()` runs four startup checks (`CUDAExecutionProvider` present in `onnxruntime.get_available_providers()` / required NVIDIA DLLs reachable via `PATH` — `cudart64_12.dll`, `cudnn64_9.dll`, `cublasLt64_12.dll` on Windows and their `.so.12` / `.so.9` counterparts on Linux / `nvidia-smi --query-gpu=name,memory.total` exits 0 / a minimal single-op ONNX `InferenceSession` initializes on the CUDA provider) and uses CUDA when all pass, CPU otherwise. Legacy bool `gpu: true`/`gpu: false` continues to work — normalized to the string form internally. The startup banner now prints on every path (including CPU-only and fallback), reports the mode (`auto-cuda` / `auto-cpu-fallback` / `forced-cpu` / `forced-cuda` / `forced-cuda-fallback`), and surfaces the probe `fallback_reason` on CPU paths with a hint pointing at the full pip install chain. Full dependency chain (onnxruntime-gpu CUDA 12 variant via `--extra-index-url` + `nvidia-cudnn-cu12` + `nvidia-cublas-cu12` + siblings) and troubleshooting for the six most common failures documented in `docs/gpu-setup.md`. Zero behavior change for users who had `gpu: false` explicitly. New coverage in `tests/test_gpu_auto.py` (13 tests, all mocked — no real GPU required to run). v4.8.0 Fase 2. (#149)
|
|
1580
|
-
- **NEW (embedding)**: `models.embedding.profile` opt-in preset (`compact` | `quality` | `multilingual` | `custom`) plus `query_prefix` / `passage_prefix` fields. The `multilingual` profile ships `intfloat/multilingual-e5-large` (1024D, 100+ languages) with the required `"query: "` / `"passage: "` prefixes wired into `FastEmbedEmbeddings.__call__`, `.embed_query`, and `.embed_documents`. `compact` preserves the current `bge-small-en-v1.5` default byte-for-byte. Changing profile or prefix requires `reindex_documents(force=True)` because existing chunks were embedded without the new prefix. (#148)
|
|
1581
|
-
- **FIX (bm25)**: BM25 tokenizer is now Unicode-aware. Before this fix, the `[a-z0-9]` character class in `BM25Index._tokenize()` and `_metadata_path_score()` (server.py:652, 659, 720) dropped every code point outside Basic Latin — Cyrillic, Greek, CJK, Arabic corpora silently degraded to semantic-only search (empty `bm25_rank`, `search_method: semantic`, no warning). Portuguese/Spanish/French text with diacritics was **fragmented** on the accent: `"informação técnica"` tokenised as `['informa', 'o', 't', 'cnica']` and BM25 scored garbage fragments instead of the actual words. The regex now uses `[^\W_]+(?:-[^\W_]+)*` which keeps hyphenated composites (`CVE-2024-1234`, `rule-a002`, `pass-the-hash`) byte-identical to prior behaviour while also matching letters from any script. All 28 tests in `test_bm25_tokenizer_fragment.py` remain green. New coverage in `tests/test_bm25_unicode_tokenizer.py` (13 tests) exercises Cyrillic / Greek / Arabic / CJK / PT-BR / mixed-script / composite-with-diacritics / underscore-separation. Closes #145. (#147)
|
|
1582
|
-
- **DOCS (security)**: rewrite `SECURITY.md` reporting section — remove the `lyonzin@users.noreply.github.com` fallback (GitHub `noreply.github.com` addresses are outbound-only and never receive mail, so that channel was decorative) and direct all reports to the GitHub Security Advisory form at `/security/advisories/new`. Private vulnerability reporting is now enabled on the repo, so the form is available to any GitHub user without collaborator status. Closes #146. (#147)
|
|
1583
|
-
- **FIX (security)**: wire `validate_path_within` on all filepath-taking orchestrator tools — `get_document`, `add_document_from_content`, `update_document_content`, `remove_document_by_path`, `search_similar`. Escape attempts return a structured `{"error": "Filepath rejected: ..."}` and touch nothing on disk. Closes 5 latent path traversal / arbitrary-file-{read,write,delete} vectors that the `mcp_server/security.py` primitives had covered since v4.5.1 but which never had callers in the mutation path. (#147)
|
|
1584
|
-
- **FIX (security)**: wire `sanitize_external_content` on `add_from_url`. The fetched body now flows through a new `external_source` kwarg on `add_document_from_content` (default `None` — operator ingests stay byte-identical) which fences it in a provenance envelope and defuses known prompt-injection sentinels (`<|im_start|>`, `[INST]`, etc.) before the parser or any downstream LLM sees it. Closes OWASP LLM01:2025 on the URL ingest path. (#147)
|
|
1585
|
-
- **FIX (security)**: install `BearerAuthMiddleware` on HTTP transports (`sse`, `streamable-http`) when `config.auth_bearer_token` is set. Wraps the FastMCP ASGI app and serves via uvicorn; unset token keeps prior `mcp.run()` open-port behaviour with a `[WARN]` on stderr; `stdio` transport is never wrapped. Closes the auth-bypass gap where `auth_bearer_token` had existed as a config field since v4.0.0 but was never compared. (#147)
|
|
1586
|
-
- **NEW (tests)**: `tests/security/test_add_from_url_sanitized.py` and `tests/security/test_bearer_middleware_attached.py` — integration tests that prove the wire (not just the library primitives). The `@pytest.mark.xfail("integration pending v4.6.0")` markers on 9 tests across `test_path_traversal.py`, `test_bearer_auth.py`, `test_prompt_injection.py` are removed — they now pass. `tests/security/` totals: 138 passed, 7 skipped, 6 xfailed. (#147)
|
|
1587
|
-
- **DOCS (perf)**: `docs/perf-baseline-v4.7.1.md` + `perf-baseline-v4.7.1.json` — reference microbenchmarks + `nuclear_rebuild()` timing (100-doc synthetic corpus) captured at the tip of v4.7.1. Non-runtime baseline for the planned Phase 3 batch-parallel embedding work in v4.8.0.
|
|
1588
|
-
- **NEW (release automation)**: `release.yml` now has a `yank-superseded` job that reads a `YANKS:` marker from the release body and automatically yanks the listed version from PyPI + deprecates it on NPM after the new version publishes. Format: `YANKS: v4.7.0` or `YANKS: v4.7.0 — some reason`. Requires the `PYPI_API_TOKEN` repo secret (classic scoped token — Trusted Publisher OIDC does not currently support yank). Job runs after `publish-pypi`/`publish-npm`/`publish-docker`, is idempotent, and degrades gracefully when secrets are missing (emits `::warning::` instead of failing). If the marker is absent, the job is a no-op.
|
|
1589
|
-
|
|
1590
|
-
### v4.7.1 (2026-08-01) — Documentation Hygiene
|
|
1591
|
-
|
|
1592
|
-
- **CHORE (docs, tests)**: sanitize identifiers in test fixtures and CHANGELOG examples. v4.7.0 referenced code names from a real deployment corpus in `tests/test_bm25_tokenizer_fragment.py` and the v4.7.0 CHANGELOG entry; those are replaced with synthetic placeholders (`RULE-A00X`, `PROJECT-Custom001-xxxx`) and well-known public identifiers (`CVE-*`, `MS17-*`, `ADR-*`, `T1078.002`) that carry no organization-specific taxonomy. Zero behavior change — all 28 fragment tests pass unchanged, tokenizer logic identical.
|
|
1593
|
-
- **NOTE**: v4.7.0 is **yanked from PyPI** and **deprecated on NPM** because its published README contained the same corporate example strings. The Docker tag `v4.7.0` remains (image tags are immutable) but `latest` now points to v4.7.1. Please upgrade — v4.7.0 and v4.7.1 are functionally equivalent.
|
|
1594
|
-
- **VERSION**: 4.7.0 → **4.7.1** (PATCH — documentation hygiene, zero code / API / config change).
|
|
1595
|
-
|
|
1596
|
-
### v4.7.0 (2026-07-31) — BM25 Fragment Query Support ⚠️ **YANKED — use v4.7.1**
|
|
1597
|
-
|
|
1598
|
-
- **FIX (retrieval)**: BM25 tokenizer now emits **sub-tokens** for hyphenated alphanumeric-code composites, so fragment queries no longer silently return `NO_RESULTS`. Before v4.7.0, `RULE-A002` was indexed as a single token `rule-a002`; queries like `A002`, `RULE`, `B005` returned zero even when the composite lived in the corpus. This is not a bug specific to any naming scheme — it hit every hyphenated code taxonomy in typical infosec / doc corpora (`RULE-*`, `CVE-*`, `ADR-*`, `MS17-*`, `PROJECT-Custom001-xxxx`). The tokenizer now emits both the composite token AND its sub-parts of length ≥ 2 (`rule-a002` → `["rule-a002", "rule", "a002"]`). IDF preserves ranking — composite matches still rank above fragment matches because the composite is rarer. **Heuristic (perf-safety)**: sub-token expansion is triggered ONLY when the composite contains at least one digit — so `pass-the-hash`, `state-of-the-art`, and other natural-language hyphenated phrases stay as single tokens (expanding them would flood the inverted index with stop-word-like sub-parts and hurt concurrent-query throughput). (#140)
|
|
1599
|
-
- **NEW (tests)**: `tests/test_bm25_tokenizer_fragment.py` — 28 tests across 4 classes covering unit tokenizer behavior, end-to-end fragment matching, multi-doc disambiguation, and the 5 query patterns explicitly reported (`RULE-A019`, `PROJECT-Custom001-xxxx`, `RULE`, `A019`, `B005`). Includes retrocompat sentinel + IDF ranking invariant. Baseline: 314 → 342.
|
|
1600
|
-
- **UPGRADE NOTE**: run `reindex_documents(force=True)` once after upgrade to rebuild the BM25 inverted index with the new sub-token emission. ChromaDB vectors are untouched. Expect a **~30–50% increase in BM25 index memory** (measured +32.5% on synthetic corpus) — negligible for typical corpora, worth reviewing for deployments with >100K docs.
|
|
1601
|
-
- **KNOWN LIMITATION**: alphanumeric-glued tokens without separators (single-token identifiers with no hyphens, hash prefixes) still require exact match — no sub-token expansion possible without character n-grams (5-10× index overhead). Tracked as follow-up.
|
|
1602
|
-
- **VERSION**: 4.6.0 → **4.7.0** (MINOR — tokenization behavior change + reindex requirement, though no API/config break).
|
|
1603
|
-
|
|
1604
|
-
### v4.6.0 (2026-07-30) — MCP spec 2026-07-28 via Anthropic Tier 1 SDK (mcp 2.x)
|
|
1605
|
-
|
|
1606
|
-
- **NEW (protocol)**: adopt the [MCP spec `2026-07-28`](https://blog.modelcontextprotocol.io/posts/2026-07-28/) — stateless request/response core, per-request `_meta.io.modelcontextprotocol/protocolVersion` + `clientCapabilities`, `serverInfo` on responses, retirement of the `initialize`/`initialized` handshake and the `Mcp-Session-Id` header. The MCP client Claude Code (and every Tier 1 client) negotiates the spec version per request, so this release stays backwards-compatible for clients speaking earlier spec revisions.
|
|
1607
|
-
- **NEW (deps)**: bump `mcp` from `>=1.6.0,<2.0.0` to `>=2.0.0,<3.0.0`. Anthropic published `mcp 2.0.0` on 2026-07-28 as the Tier 1 Python SDK — it speaks the new spec natively and is the direct upstream reference. Delivers the follow-up promised in v4.5.0-post — no longer trapped on `mcp.server.fastmcp` which upstream `mcp 2.0.0` renamed to `mcp.server.MCPServer`.
|
|
1608
|
-
- **CHANGE (imports)**: `mcp_server/server.py` now imports `MCPServer` from `mcp.server` instead of `FastMCP` from `mcp.server.fastmcp`. `@mcp.tool()` / `@mcp.resource()` / `@mcp.prompt()` decorator ergonomics are unchanged — only the class name and import path move. All 13 MCP tool signatures, all tool names, and every public function under `mcp_server.*` are **frozen and unchanged** — `check_api_surface.py --check` passes clean, `test_backwards_compat.py` stays green.
|
|
1609
|
-
- **CHANGE (constructor)**: `MCPServer(...)` (like FastMCP 3.x) no longer accepts `host` / `port` on the constructor; those parameters now belong to the transport and are passed to `mcp.run(transport=..., host=..., port=...)` on the non-stdio branches. stdio users see zero behaviour change.
|
|
1610
|
-
- **CHANGE (server identity)**: `MCPServer(name="knowledge-rag", version="4.6.0")` — server now advertises its version explicitly in `serverInfo`, matching the `2026-07-28` spec.
|
|
1611
|
-
- **NON-GOAL — why not `fastmcp` (jlowin)**: an initial attempt via the third-party standalone `fastmcp` package added ~20 transitive dependencies (from its `[client,server]` extras) and pushed `test_bench_orchestrator_idle_rss` +21.9% and `test_bench_query_cache_5000_entries` +17.1% — a real regression, not jitter. Cutting the extras to `fastmcp-slim[mcp]` broke `@mcp.tool()` at runtime (`ImportError: FastMCP server support is not installed`). Anthropic's official `mcp 2.x` is the correct entry point.
|
|
1612
|
-
- **VERSION**: 4.5.1 → **4.6.0** (MINOR — new spec adoption via Tier 1 SDK; every public API + every MCP tool signature preserved).
|
|
1613
|
-
|
|
1614
|
-
### v4.5.1 (2026-07-28) — Fase 1 Security Hardening (library, standalone)
|
|
1615
|
-
|
|
1616
|
-
- **NEW (security)**: Fase 1 Security Hardening — a standalone `mcp_server.security` library ships with the tools needed to close three attack classes against untrusted document ingestion. **Nothing in the existing call graph changes** in v4.5.1: `security.py` is imported by no other module in this release, so byte-identical behaviour is preserved for every user. Integration with `add_document` / `add_from_url` / `parse_file` / MCP HTTP transports lands in v4.6.0 (14 tests in `tests/security/` are marked `xfail` accordingly — they turn green once the callers are wired up). Users motivated to adopt hardening today can `from mcp_server.security import ...` explicitly. Contract per-defense:
|
|
1617
|
-
- **CWE-22 / CWE-59** path traversal + symlink escape: `validate_path_within(base, candidate)` + `is_path_within(base, candidate)` + `PathEscapeError`. Rejects `..`, absolute-outside-base, and symlinks whose target leaves the corpus root.
|
|
1618
|
-
- **OWASP LLM01:2025** prompt injection: `neutralize_injection_sentinels` + `sanitize_external_content` + `wrap_external_content` + `detect_external_marker`. Wraps content fetched from URLs / dropped files with tagged sentinels the downstream LLM can distinguish from operator-authored context.
|
|
1619
|
-
- **CWE-287** unauthenticated HTTP transport: `BearerAuthMiddleware` + `bearer_token_matches` (constant-time comparison) + `extract_bearer_token`. Attach to a Starlette / FastAPI app; stdio transport bypasses auth by construction (no network surface).
|
|
1620
|
-
- **NEW**: `docs/adr/0001-fase1-security-hardening.md` — ADR capturing threat model + v4.6.0 integration roadmap.
|
|
1621
|
-
- **NEW**: `.github/SECURITY.md` — GitHub vulnerability reporting policy pointer + `.github/openssf-best-practices.md` (evidence pack for OpenSSF Best Practices badge registration).
|
|
1622
|
-
- **NEW**: `tests/security/` (71 tests total; 57 library unit tests green in v4.5.1, 14 integration tests `xfail` pending v4.6.0 wire-up, `strict=False` so they auto-turn-green after integration) + `tests/test_security_docs.py` (9 assertions pinning `SECURITY.md`, README badge block, dependabot weekly pip schedule).
|
|
1623
|
-
- **DOCS**: README carries the OpenSSF Best Practices badge (placeholder project ID until registration completes).
|
|
1624
|
-
- **DEPS**: `dependabot.yml` pip schedule tightened `monthly` → `weekly` (OpenSSF-recommended floor for language ecosystems).
|
|
1625
|
-
- **VERSION**: 4.5.0 → **4.5.1** (PATCH — additive library only, zero behaviour change on the default install).
|
|
1626
|
-
|
|
1627
|
-
### v4.5.0-post (rolling fixes since v4.5.0 release)
|
|
1628
|
-
|
|
1629
|
-
- **FIX (deps)**: pin `mcp<2.0.0` in both `requirements.txt` and `pyproject.toml` after the upstream `mcp` package published 2.0.0, which drops/relocates `mcp.server.fastmcp`. Every `mcp_server.server` import chain — and therefore every test module — was failing collection with `ModuleNotFoundError: No module named 'mcp.server.fastmcp'` on fresh CI runners across all 9 OS×Python cells. Pin restores green until we migrate to the new 2.x FastMCP entry point in a follow-up. Root cause: `requirements.txt` had `mcp>=1.0.0` unbounded, so `pip install -r requirements.txt` in CI ignored the pyproject constraint. (#122)
|
|
1630
|
-
- **FIX**: `_ensure_bm25_index` no longer traps the guard flag `_bm25_initialized` in a stuck-True state when the server boots against an empty ChromaDB collection. Prior behavior set the flag unconditionally at the end of the guarded block, so a fresh-install bootup or a metadata-mapping loss left BM25 permanently uninitialized: subsequent `add_document()` calls populate `bm25_index.corpus` but do not rebuild the inverted index, and `_ensure_bm25_index` — the only path that does — short-circuited on the stale flag. All keyword-only searches returned zero results; hybrid searches returned only semantic hits with `bm25_rank: null`. The flag is now set only after a build that actually produced an index; empty collections and exceptions leave it False so the next call retries. (#114)
|
|
1631
|
-
- **TEST**: New `TestEmptyBootupDoesNotTrapGuardFlag` (5 tests) pins the contract — empty-collection boot leaves flag False, populated collection marks True, empty-then-populated recovery works, empty `get()` result leaves False, and `build_index()` exceptions leave False for retry. Baseline: 271 → 276.
|
|
1632
|
-
|
|
1633
|
-
### v4.5.0 (2026-07-06) — Hybrid Search Ranking & Routing Fix
|
|
1634
|
-
|
|
1635
|
-
- **FIX**: `search_knowledge` now searches the entire index when the user omits `category_filter`. The internal `_route_by_keywords()` heuristic previously acted as a hard where-filter on both the semantic and BM25 branches, so queries whose terms happened to map to a sparsely-populated category could return two documents while thousands of relevant chunks in other categories were silently dropped. The router is now **informational only**: the `routed_by` field is still populated in every result for telemetry (public API unchanged), but the candidate set is never restricted. Users who want a hard filter still get it by passing `category_filter=...` explicitly — which continues to filter BM25 consistently with #109. (#112)
|
|
1636
|
-
- **NEW**: Hybrid search adds a small bounded ranking boost when query terms match indexed `source` or `filename` metadata, helping navigational queries surface the canonical file for a topic instead of adjacent files that only cross-reference it. The boost is capped at ~20% of typical RRF magnitudes, so it breaks near-ties without overriding semantic or BM25 signal. Public API unchanged. (#110, thanks @Hohlas)
|
|
1637
|
-
- **TEST**: New `TestKeywordRoutingBehavior` (3 tests) pins the routing fix — BM25 and semantic branches must both see the full corpus when no explicit filter is passed, and explicit `category_filter` still overrides the router. New `TestPathAwareRanking` covers the path-metadata boost. Baseline: 266 → 271.
|
|
1638
|
-
- **UPGRADE NOTE**: warm `query_cache` entries from before v4.5.0 should be invalidated by restarting the server so cached responses no longer reflect the pre-fix restrictive behavior.
|
|
1639
|
-
|
|
1640
|
-
### v4.4.0 (2026-07-06) — Cross-Platform Installer & Hybrid Search Category Filter
|
|
1641
|
-
|
|
1642
|
-
- **NEW**: Cross-platform, multi-LLM-client installer (`install.py`) driving both `install.sh` (Linux/macOS) and `install.ps1` (Windows) as thin wrappers. One codebase, one behavior across every OS.
|
|
1643
|
-
- **NEW**: Auto-detects and registers `knowledge-rag` in 8 LLM clients — Claude Code, Claude Desktop, Cursor, Windsurf, VS Code (Copilot Chat), Cline, Gemini CLI, Zed — writing to each tool's canonical config path with the correct JSON schema per client (VS Code uses `servers`, Zed uses `context_servers`, everyone else uses `mcpServers`).
|
|
1644
|
-
- **NEW**: `--for <clients>` / `--exclude <clients>` opt-in/opt-out selection, `--dry-run` preview, `--list-clients` registry inspection, `--pypi-version <ver>` pinning, `--skip-init` / `--skip-model` for fast reruns. See `python install.py --help`.
|
|
1645
|
-
- **FIX**: `install.ps1` no longer writes MCP config to `~/.claude/mcp.json` (a stale secondary path); it now targets `~/.claude.json` — the file Claude Code actually reads — via idempotent JSON merge that preserves every existing MCP server entry with an automatic `.knowledge-rag.bak` backup.
|
|
1646
|
-
- **FIX**: `install.ps1` gains PyPI mode (`pip install knowledge-rag`) and runs `mcp_server.server init` on install — feature parity with `install.sh`.
|
|
1647
|
-
- **FIX**: MCP server spec no longer uses the fragile `cmd /c cd /d ... && python ...` wrapper on Windows; it emits the standard `command` + `cwd` shape supported natively by every modern client.
|
|
1648
|
-
- **FIX**: `install.sh` guards against `sh install.sh` (bash-only features now emit a clear error instead of a cryptic syntax failure).
|
|
1649
|
-
- **FIX**: Both scripts now correctly advertise **13 MCP tools** (was outdated at 12; `get_reindex_status` shipped in v4.3.0).
|
|
1650
|
-
- **FIX**: Windows-side `install.ps1` prefers `winget install Python.Python.3.12 --scope user` (no admin), falls back to python.org 3.12.7 (was pinned to 3.12.0).
|
|
1651
|
-
- **FIX**: Hybrid search now applies the active category filter to BM25 results before RRF fusion, preventing keyword-only BM25 hits from other categories from leaking into filtered searches. Both leak paths are closed: BM25 candidates are metadata-filtered before RRF (with `top_k` widened to `max_results * 20` to compensate for post-filter drop), and the fallback fetch during fusion re-checks `category` before adding a chunk to `combined_scores`. Note: the `category_filter` guard now also applies to the keyword-routed category — `_route_by_keywords()`-inferred routing filters BM25 too, consistent with the semantic branch. Users with warm `query_cache` entries should restart the server to invalidate stale results. (#109, thanks @Hohlas)
|
|
1652
|
-
- **TEST**: New `tests/test_installer_no_data_loss.py` (22 tests) locks in the installer's zero-data-loss contract across all three JSON schemas (`mcpServers` / `servers` / `context_servers`): top-level keys preserved, sibling MCP servers byte-identical, `.knowledge-rag.bak` backup written before every mutation, idempotent second run, `--dry-run` writes nothing, atomic `os.replace` write. Baseline: 231 → 266.
|
|
1653
|
-
- **TEST**: New `TestHybridCategoryFilter::test_bm25_results_respect_category_filter` in `tests/test_search.py` — deterministic regression covering BM25-only hits with mixed categories. Baseline: 266 → 267.
|
|
1654
|
-
|
|
1655
|
-
### v4.3.1 (2026-06-22) — Hybrid Search Fixes
|
|
1656
|
-
|
|
1657
|
-
- **FIX**: Accept `"general"` as a valid category in `search_knowledge`. The parser hardcodes `"general"` as the fallback in `_detect_category` (`ingestion.py`), but the validator only built `valid_categories` from `config.keyword_routes` + `config.category_mappings.values()` — so users who customized `config.yaml` and dropped the default `"general": "general"` mapping hit `Invalid category` even though the index contained `general` documents. Validator now always tolerates `"general"`. (#98, thanks @Hohlas)
|
|
1658
|
-
- **FIX**: Skip BM25-only search results when Chroma can no longer resolve the chunk ID. Stale BM25 indices (typically right after `remove_document` or in the window between async reindex and BM25 rebuild) returned hits whose `collection.get()` came back empty; the previous fallback inserted entries with `document=""` / `metadata={}` into the reranker, polluting results with empty matches. The pipeline now `continue`s past those, dropping the stale hit cleanly. (#98, thanks @Hohlas)
|
|
1659
|
-
- **TEST**: Added `tests/test_pr98_regression.py` (4 tests) pinning both contracts so future refactors cannot silently revert either fix. Test count baseline: 227 → 231. (#99)
|
|
1660
|
-
- **CI**: Bumped `[tool.mypy] python_version` from 3.11 to 3.12 to accept PEP 695 `type` statements in the numpy stub (`numpy/__init__.pyi`) which were breaking the Pillar 7 strict gate. Only affects static analysis; `requires-python = ">=3.11"` unchanged. (#100)
|
|
1661
|
-
|
|
1662
|
-
### v4.3.0 (2026-06-17) — Async Reindex, GPU CUDA 12, 13th MCP Tool
|
|
1663
|
-
|
|
1664
|
-
- **NEW**: `get_reindex_status` MCP tool — lightweight reindex progress polling without computing full index stats. Returns active/idle status, percent, processed/total, errors, and last result.
|
|
1665
|
-
- **NEW**: `reindex_documents` now runs in background via daemon thread — returns immediately with `{"status": "started"}`. Eliminates MCP timeout on large document sets (5K+ files). Concurrent calls return `already_running` with current progress.
|
|
1666
|
-
- **NEW**: GPU acceleration with full CUDA 12 support — `onnxruntime-gpu` + 7 NVIDIA pip packages (`cublas`, `cudnn`, `cuda-runtime`, `cufft`, `cusparse`, `cusolver`, `curand`, `nvjitlink`). Server auto-detects GPU on startup with 4-step verification (providers, DLLs, nvidia-smi, session creation). Falls back to CPU gracefully.
|
|
1667
|
-
- **NEW**: `_setup_cuda_dll_paths()` adds NVIDIA pip package DLL directories to `PATH` automatically on Windows — onnxruntime finds CUDA 12 DLLs without a full CUDA Toolkit install.
|
|
1668
|
-
- **DEPS**: `[gpu]` extra expanded from 3 to 8 packages (added `cufft`, `cusparse`, `cusolver`, `curand`, `nvjitlink`).
|
|
1669
|
-
- **FIX**: GPU status reporting now uses actual ONNX session creation test instead of just checking `get_available_providers()` — prevents false "GPU ACTIVE" when CUDA DLLs are missing.
|
|
1670
|
-
- **DOCS**: GPU Acceleration section rewritten with complete requirements table, setup steps, verification instructions, and fallback behavior.
|
|
1671
|
-
- **DOCS**: Tool reference updated — `reindex_documents` async behavior documented, `get_reindex_status` reference added.
|
|
1672
|
-
- **TEST**: Backwards-compat baseline updated for 13 MCP tools.
|
|
1673
|
-
|
|
1674
|
-
### v4.2.0 (2026-06-17) — Search Performance & Output Quality
|
|
1675
|
-
|
|
1676
|
-
- **PERF**: Custom inverted-index BM25 replaces `rank-bm25` full-corpus scan — 128× faster keyword search on 50K+ chunk corpora. Only documents containing query terms are scored via posting lists.
|
|
1677
|
-
- **PERF**: `numpy.argpartition` for O(n) top-k selection instead of O(n log n) sort.
|
|
1678
|
-
- **PERF**: Batched adjacent chunk fetch — single ChromaDB `collection.get()` call replaces N round-trips per result.
|
|
1679
|
-
- **PERF**: O(1) reverse lookup via `_source_to_docid` dict eliminates linear scans of `_indexed_docs` in `search_similar`, `update_document`, `remove_document`, and `_expand_with_adjacent_chunks`.
|
|
1680
|
-
- **NEW**: `snippet_mode` parameter on `search_knowledge` (default: `true`) — truncates content to ~500 chars at natural break points with `content_length` field. Reduces token consumption by ~72%.
|
|
1681
|
-
- **NEW**: `min_score` parameter on `search_knowledge` (default: `0.0`) — filters results below a normalized relevance threshold. Response includes `filtered_by_score` count.
|
|
1682
|
-
- **NEW**: `filtered_by_score` field in search response JSON for transparency.
|
|
1683
|
-
- **DEPS**: `numpy` added as direct dependency (was transitive via fastembed); `rank-bm25` import removed from server.py.
|
|
1684
|
-
- **TEST**: 6 new tests for `min_score` filtering and `snippet_mode` truncation.
|
|
1685
|
-
- **TEST**: Updated backwards-compat baseline to include new `search_knowledge` parameters.
|
|
1686
|
-
|
|
1687
|
-
### v4.1.2 (2026-06-17)
|
|
1688
|
-
|
|
1689
|
-
- **FIX**: `_save_metadata` dict snapshot prevents concurrent modification crash during file watcher events.
|
|
1690
|
-
- **STYLE**: ruff format applied to server.py.
|
|
1691
|
-
|
|
1692
|
-
### v4.1.1 (2026-06-17)
|
|
1693
|
-
|
|
1694
|
-
- **FIX**: All `_indexed_docs` iterations now use `list()` snapshot, preventing `dictionary changed size during iteration` crash when FileWatcher modifies the index concurrently with MCP tool calls (affects `search_knowledge`, `search_similar`, `update_document`, `remove_document`, `evaluate_retrieval`, `list_categories`, `list_documents`)
|
|
1695
|
-
|
|
1696
|
-
### v4.1.0 (2026-06-17)
|
|
1697
|
-
|
|
1698
|
-
- **Added:** `query_expansion_groups` config for symmetric synonym expansion (#92)
|
|
1699
|
-
- **Improved:** `expand_query()` now returns deterministic expansion order (set → ordered list with dedup)
|
|
1700
|
-
|
|
1701
|
-
### v4.0.1 (2026-06-16)
|
|
1702
|
-
|
|
1703
|
-
- **FIX**: Orphan cleanup now runs before indexing loop, preventing chunk loss when files are moved (#90).
|
|
1704
|
-
- **FIX**: Chunk deduplication is now per-document instead of global, preventing cross-document chunk deletion (#91).
|
|
1705
|
-
- **FIX**: Added `on_moved` handler to `DocumentWatcher` for proper file move detection.
|
|
1706
|
-
- **FIX**: Startup preflight probes ChromaDB in a child process and moves crashing persistent indexes to `data/backups/auto-repair-*` before MCP initialization.
|
|
1707
|
-
- **FIX**: Reranker load failures now fall back to RRF ordering instead of failing `search_knowledge` on offline machines.
|
|
1708
|
-
- **FIX**: Virtualenv project-root detection now handles Python symlinks that resolve to the system interpreter.
|
|
1709
|
-
- **NEW**: `knowledge-rag-guarded` console script kept as an explicit guarded startup alias.
|
|
1710
|
-
|
|
1711
|
-
### v4.0.0 (2026-06-09) — Enterprise Concurrent Access
|
|
1712
|
-
|
|
1713
|
-
- **NEW**: SSE and streamable-http transport modes — 1 server serves N clients (`server.transport: "sse"` in config.yaml or `--transport sse` CLI).
|
|
1714
|
-
- **NEW**: Thread-safe shared state for concurrent queries — QueryCache locking, BM25 build lock, orchestrator double-checked locking.
|
|
1715
|
-
- **NEW**: ChromaDB WAL mode enabled automatically in SSE/HTTP mode for concurrent read performance.
|
|
1716
|
-
- **NEW**: Optional rate limiting — sliding-window counter, configurable RPM and burst, disabled by default.
|
|
1717
|
-
- **NEW**: Optional Prometheus metrics endpoint — tool call counts, latency histograms, separate port, disabled by default.
|
|
1718
|
-
- **NEW**: All 13 MCP tools instrumented with `@rate_limited` and `@instrument` decorators (zero-cost when disabled).
|
|
1719
|
-
- **NEW**: `--transport` CLI override for Docker/systemd deployments.
|
|
1720
|
-
- **NEW**: `pip install knowledge-rag[server]` optional dependency for SSE/HTTP (uvicorn).
|
|
1721
|
-
- **CHANGED**: SSE/HTTP mode auto-enables single-instance lock (port collision prevention).
|
|
1722
|
-
- **CHANGED**: `mcp` dependency bumped to `>=1.6.0` (SSE/streamable-http support).
|
|
1723
|
-
- **MIGRATION**: Default transport remains `stdio` — existing users need zero changes. See config.example.yaml for SSE setup.
|
|
1724
|
-
|
|
1725
|
-
### v3.9.1 (2026-06-08)
|
|
1726
|
-
|
|
1727
|
-
- **FIX**: Expand `~` in `config.yaml` path values (`documents_dir`, `data_dir`, `models_cache_dir`) via `expanduser()` on all platforms (#86).
|
|
1728
|
-
- **FIX**: Warn when `documents_dir` resolves to a non-existent path instead of silently indexing zero files.
|
|
1729
|
-
- **FIX**: File watcher now uses accumulate-mode debounce — bulk file copies no longer starve the reindex trigger.
|
|
1730
|
-
- **FIX**: Concurrent `index_all()` calls are serialized via `_index_lock` to prevent ChromaDB SQLite corruption.
|
|
1731
|
-
- **FIX**: `collection.add()` is batched (500 chunks/call) to cap memory usage during large reindex operations.
|
|
1732
|
-
- **NEW**: `KNOWLEDGE_RAG_WATCHER_DISABLED=1` env var to disable the file watcher for troubleshooting.
|
|
1733
|
-
- **NEW**: Progress logging every 10% for reindex operations with >100 documents.
|
|
1734
|
-
|
|
1735
|
-
### v3.9.0 (2026-05-10) — Quality Gate
|
|
1736
|
-
|
|
1737
|
-
**Major governance + CI hardening release. No runtime behavior change in `mcp_server/`. Public API surface unchanged from v3.8.1.**
|
|
1738
|
-
|
|
1739
|
-
- **NEW** Quality Gate workflow (`.github/workflows/quality-gate.yml`) enforcing the 7 pillars on every PR: Security, Stability, Memory Leak, Versatility, Scalability, Versioning, Quality. 35+ status checks total.
|
|
1740
|
-
- **NEW** Nightly resilience workflow (`.github/workflows/nightly.yml`): chaos suite (failure injection), 1h soak test (50K-iteration loop), determinism check (full suite × 3), mutation testing (mutmut). Auto-opens GitHub issue on any nightly failure.
|
|
1741
|
-
- **NEW** Performance benchmark suite under `bench/` (12 microbenchmarks, pytest-benchmark) with 10% regression gate on every PR.
|
|
1742
|
-
- **NEW** Public performance dashboard via GitHub Pages (`.github/workflows/bench-pages.yml`) — chart of latency/throughput per commit. Dormant until repo Pages is enabled.
|
|
1743
|
-
- **NEW** Property-based fuzzing of all parsers via Hypothesis (`tests/test_ingestion_property.py`) — 200 random examples per CI run.
|
|
1744
|
-
- **NEW** Memory baseline regression tests (`tests/test_memory_baseline.py`, cross-platform via psutil) — RSS bounded under 1000 queries; nightly soak amplifies to 50K iterations.
|
|
1745
|
-
- **NEW** Property/locale/format/preset matrices (`tests/test_presets.py`, `tests/test_locale.py`, `tests/test_format_smoke.py`).
|
|
1746
|
-
- **NEW** Backwards-compatibility regression tests (`tests/test_backwards_compat.py`) — legacy YAML configs from v3.6.0 / v3.7.0 still parse; all 13 MCP tool parameter names frozen.
|
|
1747
|
-
- **NEW** AST-based public API surface diff (`scripts/check_api_surface.py`) — any breaking change blocks merge, baseline at `.github/api-surface-baseline.json`.
|
|
1748
|
-
- **NEW** CHANGELOG enforcement (`scripts/check_changelog.py`) — user-facing PRs must add a bullet under `## Unreleased`; bypass via `skip-changelog` label.
|
|
1749
|
-
- **NEW** Test count anti-regression (`scripts/check_test_count.py`) — guards against silent test deletion.
|
|
1750
|
-
- **NEW** Conventional commits required on every PR title (commitlint via `amannn/action-semantic-pull-request`).
|
|
1751
|
-
- **NEW** mypy `--strict` rolling out per-module (currently `instance_lock.py` + `preflight.py` + `scripts/`); interrogate docstring coverage ≥ 80%; radon, vulture, PR-size guard report-only.
|
|
1752
|
-
- **NEW** CI matrix expanded to 9 cells: Linux + Windows + **macOS** × 3.11 + 3.12 + **3.13** (all required at v3.9.0; macOS / 3.13 promoted from experimental after two clean cycles).
|
|
1753
|
-
- **NEW** Governance docs: `CONTRIBUTING.md`, `CODE_OF_CONDUCT.md`, `SECURITY.md`, `.github/PULL_REQUEST_TEMPLATE.md`, 3 issue templates, expanded `CODEOWNERS`.
|
|
1754
|
-
- **NEW** Pre-commit hooks: ruff, gitleaks, version-sync, conventional commits.
|
|
1755
|
-
- **CHORE** `.github/codecov.yml` enforcing coverage trend gate (-0.5pp blocks; new code ≥ 70%).
|
|
1756
|
-
|
|
1757
|
-
### v3.8.1 (2026-05-10) — hotfix
|
|
1758
|
-
|
|
1759
|
-
- **FIX (critical)**: `FastEmbedEmbeddings.__call__` no longer returns vectors of zeros when the ONNX model fails to load or `embed()` raises. The previous behavior silently corrupted the index — ChromaDB stored zero embeddings, `count()` reported normal numbers, smart-reindex skipped the bad chunks, and queries returned garbage scores with no error visible. Now raises `EmbeddingModelLoadError` / `EmbeddingError`. (#36)
|
|
1760
|
-
- **FIX**: Sticky `_load_failed` flag — after a load failure, subsequent calls re-raise immediately instead of looping through HuggingFace download attempts (was the "frozen query" UX in v3.8.0).
|
|
1761
|
-
- **NEW**: Sanity checks in `__call__` — embed count and dim mismatches raise `EmbeddingError` instead of silently returning malformed vectors.
|
|
1762
|
-
- **TEST**: 7 new regression cases in `tests/test_lazy_embeddings.py`, including `test_does_not_return_zero_vectors_silently` as a guard for the whole class of bug.
|
|
1763
|
-
- **NOTE**: This is a pre-existing bug in master, not introduced by v3.8.0. v3.8.0 lazy-load expanded the impact (failures moved to query time). All v3.8.0 users should upgrade.
|
|
1764
|
-
|
|
1765
|
-
### v3.8.0 (2026-05-10)
|
|
1766
|
-
|
|
1767
|
-
- **NEW**: Lazy-load FastEmbed embedding model (~200MB ONNX runtime). Loads on first query instead of startup — idle `knowledge-rag` processes are now cheap, which matters when MCP stdio clients spawn parallel server processes (multiple Claude Code windows, Claude Desktop + IDE, etc.). Public API unchanged. (#32)
|
|
1768
|
-
- **NEW**: Opt-in single-instance guard via `KNOWLEDGE_RAG_SINGLE_INSTANCE=1` env var. **OFF by default** — multi-client MCP usage continues to work unchanged. When enabled, a second server process for the same `data_dir` exits with code 75 (`EX_TEMPFAIL`). Includes stale-PID recovery and SIGINT/SIGTERM handlers. See [docs/single-instance.md](docs/single-instance.md). (#33, original concept by @Hohlas in #31)
|
|
1769
|
-
- **NEW**: `examples/mcp-config-single-instance.json` — sample MCP client config for the opt-in guard.
|
|
1770
|
-
- **DOCS**: New `docs/single-instance.md` — when to use, when NOT to use, troubleshooting, full activation reference.
|
|
1771
|
-
- **DOCS**: README troubleshooting section for "Multiple MCP clients spawn duplicate servers" + memory-usage note for lazy embeddings.
|
|
1772
|
-
- **CHORE**: Sync version across `pyproject.toml`, `mcp_server/__init__.py`, and `npm/package.json` (was drifting since v3.5.x).
|
|
1773
|
-
- **CHORE**: pytest `tmp_path_retention_count=1` to avoid Windows atexit cleanup race in CI.
|
|
1774
|
-
- **ROADMAP**: Tracked v4.0 shared-service architecture (one daemon, many thin MCP clients) as the long-term fix for multi-process resource duplication. (#34)
|
|
1775
|
-
|
|
1776
|
-
### v3.6.2 (2026-04-23)
|
|
1777
|
-
|
|
1778
|
-
- **INFRA**: NPM provenance attestation (SLSA supply chain security), full README on npm page
|
|
1779
|
-
- **DOCS**: Reorganize Installation section — add NPX and Docker install methods, update What's New to v3.6.0
|
|
1780
|
-
|
|
1781
|
-
### v3.6.0 (2026-04-23)
|
|
1782
|
-
|
|
1783
|
-
- **NEW**: Multi-language code parsing — C (`.c`), C++ (`.cpp`/`.h`), JavaScript (`.js`/`.jsx`), TypeScript (`.ts`/`.tsx`) with per-language function/class/import extraction
|
|
1784
|
-
- **NEW**: XML parser (`.xml`) — root element and namespace metadata extraction
|
|
1785
|
-
- **NEW**: All 8 new formats default enabled — no config change needed
|
|
1786
|
-
- **NEW**: NPM wrapper (`npx knowledge-rag`) + Docker image (`ghcr.io/lyonzin/knowledge-rag`)
|
|
1787
|
-
- **NEW**: Automated release pipeline — PyPI (Trusted Publishing), NPM, Docker GHCR
|
|
1788
|
-
- **IMPROVED**: Code parser reports correct `language` metadata per file type (was hardcoded to `"python"` for all code files)
|
|
1789
|
-
|
|
1790
|
-
### v3.5.2 (2026-04-16)
|
|
1791
|
-
|
|
1792
|
-
- **NEW**: Auto-discovery of CUDA 12 DLLs from pip-installed NVIDIA packages — no manual PATH configuration needed
|
|
1793
|
-
- **NEW**: Graceful GPU→CPU fallback with `[WARN]` log when CUDA init fails (missing drivers, wrong version, etc.)
|
|
1794
|
-
- **FIX**: Explicit `CPUExecutionProvider` when `gpu: false` — eliminates noisy CUDA probe errors in logs
|
|
1795
|
-
- **FIX**: BASE_DIR resolution now correctly prefers directories with `config.yaml` over those with only `config.example.yaml` (fixes editable installs)
|
|
1796
|
-
|
|
1797
|
-
### v3.5.1 (2026-04-16)
|
|
1798
|
-
|
|
1799
|
-
- **FIX**: Removed Python upper bound constraint (`<3.13` → `>=3.11`). Python 3.13 and 3.14 now supported — onnxruntime ships wheels for both.
|
|
1800
|
-
|
|
1801
|
-
### v3.5.0 (2026-04-16)
|
|
1802
|
-
|
|
1803
|
-
- **NEW**: Optional GPU acceleration for ONNX embeddings — `pip install knowledge-rag[gpu]` + `models.embedding.gpu: true` in config. 5-10x faster indexing on NVIDIA GPUs with automatic CPU fallback.
|
|
1804
|
-
- **DOCS**: Supported formats table added to README (20 formats)
|
|
1805
|
-
|
|
1806
|
-
### v3.4.3 (2026-04-16)
|
|
1807
|
-
|
|
1808
|
-
- **FIX**: Correct stdout protection via save/restore pattern — `__init__.py` saves original stdout and redirects to stderr during init, `server.py main()` restores it before `mcp.run()`. v3.4.2's global redirect broke MCP JSON-RPC response channel.
|
|
1809
|
-
|
|
1810
|
-
### v3.4.1 (2026-04-16)
|
|
1811
|
-
|
|
1812
|
-
- **FIX**: `pip install knowledge-rag` now auto-detects project directory from venv location
|
|
1813
|
-
- **NEW**: `install.sh` — Linux/macOS installer with pip and from-source modes
|
|
1814
|
-
- **IMPROVED**: BASE_DIR resolution chain: env var → source dir → venv parent → CWD → fallback
|
|
1815
|
-
|
|
1816
|
-
### v3.4.0 (2026-04-16)
|
|
1817
|
-
|
|
1818
|
-
- **NEW**: `models_cache_dir` — persistent embedding model cache, prevents re-download after reboots
|
|
1819
|
-
- **NEW**: `exclude_patterns` — glob-based file/directory exclusion during indexing
|
|
1820
|
-
- **NEW**: Jupyter Notebook (.ipynb) parser — extracts markdown and code cell sources only
|
|
1821
|
-
- **NEW**: MCP stdout protection — redirects stdout to stderr before server start
|
|
1822
|
-
- **NEW**: File watcher resilience — graceful fallback when Linux inotify limits are reached
|
|
1823
|
-
- **NEW**: MetaTrader (.mq4, .mqh) support — opt-in code parsing
|
|
1824
|
-
- **NEW**: 23 new tests (exclude patterns, ipynb parser, stdout protection)
|
|
1825
|
-
- Community credit: [@Hohlas](https://github.com/Hohlas) ([PR #18](https://github.com/lyonzin/knowledge-rag/pull/18))
|
|
1826
|
-
|
|
1827
|
-
### v3.3.x
|
|
1828
|
-
|
|
1829
|
-
- **v3.3.2**: Full type validation on YAML config, bounds checking, version sync
|
|
1830
|
-
- **v3.3.1**: YAML null value crash fix, presets bundled in pip wheel, `knowledge-rag init` CLI
|
|
1831
|
-
- **v3.3.0**: YAML configuration system, 4 domain presets, generic use support
|
|
1832
|
-
|
|
1833
|
-
### v3.2.x
|
|
1834
|
-
|
|
1835
|
-
- **v3.2.4**: Symlink support with circular loop protection
|
|
1836
|
-
- **v3.2.3**: BASE_DIR smart detection for pip installs
|
|
1837
|
-
- **v3.2.2**: Plug-and-play pip install, `KNOWLEDGE_RAG_DIR` env var
|
|
1838
|
-
- **v3.2.1**: Auto-recovery from corrupted ChromaDB
|
|
1839
|
-
- **v3.2.0**: Parallel BM25 + Semantic search, adjacent chunk retrieval
|
|
1840
|
-
|
|
1841
|
-
### v3.1.x
|
|
1842
|
-
|
|
1843
|
-
- **v3.1.1**: Code block protection in markdown chunker, AAR category, 14 CVE aliases
|
|
1844
|
-
- **v3.1.0**: DOCX/XLSX/PPTX/CSV support, file watcher, MMR diversification, PyPI publish
|
|
1845
|
-
|
|
1846
|
-
### v3.0.0 (2026-03-19)
|
|
1847
|
-
|
|
1848
|
-
- Replaced Ollama with FastEmbed (ONNX in-process)
|
|
1849
|
-
- Cross-encoder reranking, markdown-aware chunking, query expansion
|
|
1850
|
-
- 6 new MCP tools (12 total), auto-migration from v2.x
|
|
1851
|
-
|
|
1852
|
-
<details>
|
|
1853
|
-
<summary>v2.x and earlier</summary>
|
|
1854
|
-
|
|
1855
|
-
- **v2.2.0**: `hybrid_alpha=0` skips Ollama, default changed from 0.5 to 0.3
|
|
1856
|
-
- **v2.1.0**: Mermaid architecture diagrams
|
|
1857
|
-
- **v2.0.0**: Hybrid search, RRF fusion, `hybrid_alpha` parameter
|
|
1858
|
-
- **v1.1.0**: Incremental indexing, query cache, chunk deduplication
|
|
1859
|
-
- **v1.0.1**: Auto-cleanup orphan folders, removed hardcoded paths
|
|
1860
|
-
- **v1.0.0**: Initial release
|
|
1861
|
-
</details>
|
|
1862
|
-
|
|
1863
|
-
---
|
|
1864
|
-
|
|
1865
|
-
## Contributing
|
|
1866
|
-
|
|
1867
|
-
1. Fork the repository
|
|
1868
|
-
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
|
|
1869
|
-
3. Commit your changes
|
|
1870
|
-
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
1871
|
-
5. Open a Pull Request
|
|
1872
|
-
|
|
1873
|
-
---
|
|
1874
|
-
|
|
1875
|
-
## License
|
|
1876
|
-
|
|
1877
|
-
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
1878
|
-
|
|
1879
|
-
---
|
|
1880
|
-
|
|
1881
|
-
## Acknowledgments
|
|
1882
|
-
|
|
1883
|
-
- [ChromaDB](https://www.trychroma.com/) — Vector database
|
|
1884
|
-
- [FastEmbed](https://qdrant.github.io/fastembed/) — ONNX Runtime embeddings
|
|
1885
|
-
- [FastMCP](https://github.com/anthropics/mcp) — Model Context Protocol framework
|
|
1886
|
-
- [PyMuPDF](https://pymupdf.readthedocs.io/) — PDF parsing
|
|
1887
|
-
- [rank-bm25](https://github.com/dorianbrown/rank_bm25) — BM25 Okapi implementation
|
|
1888
|
-
- [Watchdog](https://github.com/gorakhargosh/watchdog) — File system monitoring
|
|
1889
|
-
- [python-docx](https://python-docx.readthedocs.io/) / [openpyxl](https://openpyxl.readthedocs.io/) / [python-pptx](https://python-pptx.readthedocs.io/) — Office document parsing
|
|
1890
|
-
- [PyYAML](https://pyyaml.org/) — YAML configuration parsing
|
|
1891
|
-
- [Beautiful Soup](https://www.crummy.com/software/BeautifulSoup/) — HTML parsing for URL ingestion
|
|
1892
|
-
|
|
1893
|
-
---
|
|
1894
|
-
|
|
1895
|
-
## Author
|
|
1896
|
-
|
|
1897
|
-
**Lyon.**
|
|
1898
|
-
|
|
1899
|
-
Security Researcher | Developer
|
|
1900
|
-
|
|
1901
|
-
---
|
|
1902
|
-
|
|
1903
|
-
<div align="center">
|
|
1904
|
-
|
|
1905
|
-
**[Back to Top](#knowledge-rag)**
|
|
1906
|
-
|
|
1907
|
-
</div>
|
|
1
|
+
<!--
|
|
2
|
+
knowledge-rag — Local RAG server for Claude Code, Cursor, Windsurf and other MCP clients.
|
|
3
|
+
SEO keywords: local rag, mcp server, self-hosted rag, hybrid search, cross-encoder reranking, claude code rag, cursor rag, python rag, chromadb rag, fastembed rag, air-gapped rag, gdpr rag, retrieval augmented generation, model context protocol server, zero cloud rag.
|
|
4
|
+
|
|
5
|
+
Structured data (schema.org SoftwareApplication) — indexed by search engines, invisible on GitHub render:
|
|
6
|
+
{
|
|
7
|
+
"@context": "https://schema.org",
|
|
8
|
+
"@type": "SoftwareApplication",
|
|
9
|
+
"name": "knowledge-rag",
|
|
10
|
+
"applicationCategory": "DeveloperApplication",
|
|
11
|
+
"operatingSystem": "Windows, Linux, macOS",
|
|
12
|
+
"description": "Local-first RAG server for AI agents. Hybrid search, MCP-native, zero cloud, enterprise-grade plumbing (bearer auth, Prometheus metrics, rate limiting, health probes, structured JSON logging, zero-downtime reindex).",
|
|
13
|
+
"offers": { "@type": "Offer", "price": "0", "priceCurrency": "USD" },
|
|
14
|
+
"license": "https://opensource.org/licenses/MIT",
|
|
15
|
+
"downloadUrl": "https://pypi.org/project/knowledge-rag/",
|
|
16
|
+
"codeRepository": "https://github.com/lyonzin/knowledge-rag",
|
|
17
|
+
"programmingLanguage": "Python",
|
|
18
|
+
"runtimePlatform": "Python 3.11+"
|
|
19
|
+
}
|
|
20
|
+
-->
|
|
21
|
+
|
|
22
|
+
# knowledge-rag
|
|
23
|
+
|
|
24
|
+
<div align="center">
|
|
25
|
+
|
|
26
|
+
[](https://pypi.org/project/knowledge-rag/)
|
|
27
|
+
[](https://www.npmjs.com/package/knowledge-rag)
|
|
28
|
+
[](https://pepy.tech/projects/knowledge-rag)
|
|
29
|
+

|
|
30
|
+

|
|
31
|
+

|
|
32
|
+

|
|
33
|
+
[](https://github.com/lyonzin/knowledge-rag/actions/workflows/ci.yml)
|
|
34
|
+
[](https://github.com/lyonzin/knowledge-rag/actions/workflows/security.yml)
|
|
35
|
+
[](https://github.com/lyonzin/knowledge-rag/actions/workflows/quality-gate.yml)
|
|
36
|
+
[](https://glama.ai/mcp/servers/lyonzin/knowledge-rag)
|
|
37
|
+
[](https://bestpractices.coreinfrastructure.org/projects/13864)
|
|
38
|
+
|
|
39
|
+
### The MCP-first local RAG server for Claude Code, Cursor, and every AI agent.
|
|
40
|
+
|
|
41
|
+
**Hybrid search · Cross-encoder reranking · 20 file formats · 100% local · Zero cloud · Enterprise-grade plumbing built-in.**
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install knowledge-rag → restart Claude Code → search_knowledge("your query")
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
[Quick Start](#quick-start-3-minutes-from-zero-to-your-first-query) ·
|
|
48
|
+
[Why knowledge-rag](#why-knowledge-rag) ·
|
|
49
|
+
[Compare](#how-knowledge-rag-compares-to-other-rag-frameworks) ·
|
|
50
|
+
[Enterprise Features](#enterprise-features-built-in-zero-configuration) ·
|
|
51
|
+
[Docs](#documentation)
|
|
52
|
+
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## ⭐ Star History
|
|
58
|
+
|
|
59
|
+
<div align="center">
|
|
60
|
+
|
|
61
|
+
<a href="https://www.star-history.com/?repos=lyonzin%2Fknowledge-rag&type=date&legend=top-left">
|
|
62
|
+
<img alt="knowledge-rag star history chart — GitHub star growth over time" src="./docs/star-history.svg" width="720" />
|
|
63
|
+
</a>
|
|
64
|
+
|
|
65
|
+
<sub>Chart updated daily by <a href="./.github/workflows/update-star-history.yml">GitHub Action</a></sub>
|
|
66
|
+
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## 🎯 Why knowledge-rag
|
|
72
|
+
|
|
73
|
+
Most RAG frameworks fall into one of three traps: (1) they require you to ship your data to a cloud API, (2) they hand you 300 building blocks and 0 opinionated defaults, or (3) they bundle RAG as a 5% feature of a much bigger platform you didn't ask for.
|
|
74
|
+
|
|
75
|
+
**knowledge-rag does one thing well:** it is the MCP-native local RAG server that Claude Code, Cursor, Windsurf, VS Code, Cline, Gemini CLI and Zed can search out of the box — with enterprise plumbing (bearer auth, Prometheus metrics, rate limiting, health probes, structured JSON logging, zero-downtime reindex) that no other RAG-focused OSS ships built-in.
|
|
76
|
+
|
|
77
|
+
<table>
|
|
78
|
+
<tr>
|
|
79
|
+
<td width="50%" valign="top">
|
|
80
|
+
|
|
81
|
+
### 🔒 100% local, 0% cloud
|
|
82
|
+
Your files never leave the machine. No vendor lock-in, no data-residency headache, no forced cloud dependency. **LGPD / GDPR / HIPAA compliant by architecture** — because there is nothing to comply about when nothing leaves.
|
|
83
|
+
|
|
84
|
+
</td>
|
|
85
|
+
<td width="50%" valign="top">
|
|
86
|
+
|
|
87
|
+
### 🚀 Zero-friction setup
|
|
88
|
+
`pip install knowledge-rag` → restart your MCP client → done. **No Docker mandatory. No Ollama required. No separate embedding server.** Everything runs in-process via FastEmbed ONNX. **Works offline** after the first model download.
|
|
89
|
+
|
|
90
|
+
</td>
|
|
91
|
+
</tr>
|
|
92
|
+
<tr>
|
|
93
|
+
<td width="50%" valign="top">
|
|
94
|
+
|
|
95
|
+
### 🛡️ Production-grade OSS
|
|
96
|
+
7-pillar quality gate on every PR (**35+ automated checks**), 9-cell OS×Python CI matrix (Linux + Windows + macOS × 3.11/3.12/3.13), nightly chaos + 50K-iteration soak + mutation testing. **700+ tests. 0 known regressions.**
|
|
97
|
+
|
|
98
|
+
</td>
|
|
99
|
+
<td width="50%" valign="top">
|
|
100
|
+
|
|
101
|
+
### 💰 Zero ongoing cost
|
|
102
|
+
No token bills. No SaaS tier. No paid features hidden behind a wall. **MIT license, forever.** Runs on the laptop you already have — GPU optional, CPU works fine with FastEmbed ONNX.
|
|
103
|
+
|
|
104
|
+
</td>
|
|
105
|
+
</tr>
|
|
106
|
+
</table>
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## 📊 How knowledge-rag compares to other RAG frameworks
|
|
111
|
+
|
|
112
|
+
We audited **16 popular RAG frameworks and platforms** (LlamaIndex, LangChain, ChromaDB, Weaviate, Qdrant, RAGFlow, LightRAG, DSPy, GraphRAG, Haystack, RAG-Anything, kotaemon, txtai, llmware, Dify, open-webui, FastGPT) so you can pick honestly.
|
|
113
|
+
|
|
114
|
+
**Legend:** ✅ built-in · 🟡 plugin / paid tier / partial · ❌ not available · ⚠️ license or default concern
|
|
115
|
+
|
|
116
|
+
| Dimension | 🎯 **knowledge-rag** | LlamaIndex | LangChain | Haystack | RAGFlow | txtai | open-webui | Dify | Qdrant |
|
|
117
|
+
|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
|
118
|
+
| **100% local, zero cloud** | ✅ | 🟡 | ✅ | 🟡 | 🟡 | ✅ | ✅ | 🟡 | 🟡 |
|
|
119
|
+
| **MCP native (Claude/Cursor)** | ✅ 13 tools | 🟡 pkg | 🟡 adapter | 🟡 wrapper | 🟡 add-on | ✅ | ✅ consumer | ✅ | ❌ |
|
|
120
|
+
| **Hybrid BM25 + semantic** | ✅ 128× faster | 🟡 | 🟡 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
121
|
+
| **Cross-encoder rerank** | ✅ builtin | ❌ | 🟡 | ✅ | ✅ fused | ❌ | ✅ | 🟡 | 🟡 |
|
|
122
|
+
| **Bearer auth builtin** | ✅ | ❌ | ❌ | ❌ core | ❌ | 🟡 | ✅ RBAC | ✅ OAuth2 | ✅ |
|
|
123
|
+
| **Prometheus `/metrics`** | ✅ | ❌ | ❌ | ❌ core | ❌ | ❌ | ✅ OTel | ❌ | ✅ |
|
|
124
|
+
| **Rate limiting** | ✅ sliding-window | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ | ✅ |
|
|
125
|
+
| **Health probes** (`/health`) | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | 🟡 | 🟡 | ✅ |
|
|
126
|
+
| **Structured JSON logging** | ✅ opt-in | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ OTel | 🟡 | ✅ |
|
|
127
|
+
| **Zero-downtime reindex** | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
|
|
128
|
+
| **Async background reindex** | ✅ + polling | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | 🟡 |
|
|
129
|
+
| **GPU CUDA optional** | ✅ 12 auto | ❌ | 🟡 | ✅ | ✅ | ✅ | ✅ | 🟡 | 🟡 |
|
|
130
|
+
| **File formats builtin** | ✅ **20** | 0 (LlamaParse=$) | 50+ plugins | ✅ **36+** | 8+ | ? | ? | ~10 | ❌ |
|
|
131
|
+
| **Setup < 5 min POC** | ✅ pip 1-liner | ✅ | ✅ | ✅ | ❌ 16GB RAM | ✅ | ✅ docker | ✅ docker | ✅ |
|
|
132
|
+
| **Nightly chaos + soak + mutation** | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
133
|
+
| **License** | ✅ MIT | MIT | MIT | Apache-2.0 | Apache-2.0 | Apache-2.0 | ⚠️ preserving | ⚠️ restrictive | Apache-2.0 |
|
|
134
|
+
|
|
135
|
+
> **The 5 dimensions where knowledge-rag is unique:** health probes + JSON logging + Prometheus + rate limit + bearer auth **simultaneously built-in on an OSS RAG-focused MCP server**. Zero-downtime reindex + async background reindex + nightly chaos/soak/mutation are documented on nobody else's README.
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## 🚀 Quick Start (3 minutes, from zero to your first query)
|
|
140
|
+
|
|
141
|
+
Pick your integration path — knowledge-rag ships the same server through every channel.
|
|
142
|
+
|
|
143
|
+
### Path 1 — Claude Code, Cursor, Windsurf, Cline, VS Code, Gemini CLI, Zed (MCP)
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
pip install knowledge-rag
|
|
147
|
+
knowledge-rag init # scaffolds config.yaml + documents/
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Drop your PDFs, markdown, code files into `documents/`. Restart your MCP client. Ask it:
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
search_knowledge("your query")
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
That's it. First query loads the ONNX embedding model (~200MB, one-off download). Subsequent queries are cached and hit sub-second latency.
|
|
157
|
+
|
|
158
|
+
### Path 2 — HTTP / SSE server (multi-user, air-gapped, load-balanced)
|
|
159
|
+
|
|
160
|
+
```yaml
|
|
161
|
+
# config.yaml
|
|
162
|
+
server:
|
|
163
|
+
transport: "sse" # or "streamable-http"
|
|
164
|
+
host: "0.0.0.0"
|
|
165
|
+
port: 8179
|
|
166
|
+
auth:
|
|
167
|
+
bearer_token: "your-secret-token"
|
|
168
|
+
rate_limit:
|
|
169
|
+
enabled: true
|
|
170
|
+
requests_per_minute: 60
|
|
171
|
+
metrics:
|
|
172
|
+
enabled: true
|
|
173
|
+
port: 9179
|
|
174
|
+
logging:
|
|
175
|
+
format: "json" # ELK / Loki / Datadog / CloudWatch ready
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
knowledge-rag --transport sse
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
- Health probe: `curl http://your-host:8179/health` → 200 + JSON payload
|
|
183
|
+
- Prometheus scrape: `http://your-host:9179/metrics`
|
|
184
|
+
- MCP dispatcher: authenticated via `Authorization: Bearer your-secret-token`
|
|
185
|
+
|
|
186
|
+
### Path 3 — Docker (models pre-downloaded, air-gapped ready)
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
docker pull ghcr.io/lyonzin/knowledge-rag:latest
|
|
190
|
+
docker run -v $(pwd)/documents:/app/documents -p 8179:8179 ghcr.io/lyonzin/knowledge-rag:latest
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
**Full installation guide with all 5 methods, 8 MCP client configurations, and GPU setup:** [docs/INSTALLATION.md →](docs/INSTALLATION.md)
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## 🤖 Ready-to-use skills for AI agents
|
|
198
|
+
|
|
199
|
+
Installing knowledge-rag gives your agent 13 MCP tools. It does not tell the agent **when** to use them. That is what the [`skills/`](skills/) folder solves — drop-in behavioural skills for Claude Code, Cursor, Windsurf, Cline, Zed, VS Code Copilot that turn "AI with access to RAG" into "AI that actually uses RAG first".
|
|
200
|
+
|
|
201
|
+
**10 skills, MIT licensed, organized by kind:**
|
|
202
|
+
|
|
203
|
+
| # | Skill | What it does |
|
|
204
|
+
|---|---|---|
|
|
205
|
+
| 1 | [`rag-check-first`](skills/foundation/rag-check-first/SKILL.md) | Search the corpus **before** answering any technical claim |
|
|
206
|
+
| 2 | [`rag-cite-sources`](skills/foundation/rag-cite-sources/SKILL.md) | Every claim ships with `path:line` citations |
|
|
207
|
+
| 3 | [`rag-onboard-context`](skills/foundation/rag-onboard-context/SKILL.md) | First interaction of a session probes what is indexed |
|
|
208
|
+
| 4 | [`rag-deep-dive`](skills/workflow/rag-deep-dive/SKILL.md) | 3-step drill: `search` → `fetch` → `find similar` |
|
|
209
|
+
| 5 | [`rag-web-fallback`](skills/workflow/rag-web-fallback/SKILL.md) | Only hit the web when local RAG comes back empty |
|
|
210
|
+
| 6 | [`rag-troubleshoot`](skills/workflow/rag-troubleshoot/SKILL.md) | Bug / error → RAG first for prior fixes |
|
|
211
|
+
| 7 | [`rag-code-review`](skills/workflow/rag-code-review/SKILL.md) | Review consults ADRs / patterns before commenting |
|
|
212
|
+
| 8 | [`rag-index-decisions`](skills/maintenance/rag-index-decisions/SKILL.md) | After a decision, index it back — close the feedback loop |
|
|
213
|
+
| 9 | [`rag-security-first`](skills/domain/rag-security-first/SKILL.md) | Security tasks: MITRE / CVE / runbook first |
|
|
214
|
+
| 10 | [`rag-evaluate-quality`](skills/maintenance/rag-evaluate-quality/SKILL.md) | Weekly checkup — MRR@5 · Recall@5 · Precision@5 |
|
|
215
|
+
|
|
216
|
+
**Install — pick the shortest path for your machine:**
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
# Option 1 — Via skills.sh (needs Node — one command, zero clone)
|
|
220
|
+
npx skills add lyonzin/knowledge-rag
|
|
221
|
+
|
|
222
|
+
# Option 2 — Via our install.sh (no Node needed; works on Linux/macOS/WSL/Git Bash)
|
|
223
|
+
curl -fsSL https://raw.githubusercontent.com/lyonzin/knowledge-rag/master/skills/install.sh | bash
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Both restart-Claude-Code and you are done. Option 2 supports `--project`, `--only rag-check-first,rag-cite-sources`, `--dry-run`, `--help`.
|
|
227
|
+
|
|
228
|
+
For Cursor, Windsurf, Cline and full manual instructions → [skills/README.md](skills/README.md) · Full catalog with skill chains → [skills/CATALOG.md](skills/CATALOG.md)
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## 🛠️ The 13 MCP tools your agent gets
|
|
233
|
+
|
|
234
|
+
Once installed, your AI agent gets these 13 tools automatically:
|
|
235
|
+
|
|
236
|
+
| Tool | Purpose |
|
|
237
|
+
|---|---|
|
|
238
|
+
| `search_knowledge` | Hybrid semantic + BM25 with cross-encoder rerank |
|
|
239
|
+
| `get_document` | Retrieve full content of one document |
|
|
240
|
+
| `search_similar` | Find documents similar to a reference |
|
|
241
|
+
| `evaluate_retrieval` | Measure MRR@5 · Recall@5 · Precision@5 |
|
|
242
|
+
| `add_document` | Index a new document via MCP |
|
|
243
|
+
| `update_document` | Re-index a changed document |
|
|
244
|
+
| `remove_document` | Drop a document + all its chunks |
|
|
245
|
+
| `add_from_url` | Fetch, sanitize, and index a URL |
|
|
246
|
+
| `list_documents` | Enumerate indexed documents |
|
|
247
|
+
| `list_categories` | Auto-tagged by folder path |
|
|
248
|
+
| `get_index_stats` | Corpus size, cache hit rate, embedding dim |
|
|
249
|
+
| `reindex_documents` | Smart incremental OR nuclear rebuild |
|
|
250
|
+
| `get_reindex_status` | Live progress polling (async reindex) |
|
|
251
|
+
|
|
252
|
+
**Full API reference with parameter details, return schemas, examples:** [docs/API.md →](docs/API.md)
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## 🏢 Enterprise Features (built-in, zero configuration)
|
|
257
|
+
|
|
258
|
+
Every RAG framework claims "production-ready." Here is what knowledge-rag ships **in the OSS core, verified by regression tests, that competitors either paywall, plugin-ify, or simply don't have.**
|
|
259
|
+
|
|
260
|
+
<table>
|
|
261
|
+
<tr>
|
|
262
|
+
<td valign="top" width="50%">
|
|
263
|
+
|
|
264
|
+
### Security
|
|
265
|
+
|
|
266
|
+
- **Bearer token auth** on SSE / HTTP transports — constant-time comparison (`hmac.compare_digest`), RFC 6750 challenge, 401 fenced with `WWW-Authenticate` header
|
|
267
|
+
- **Path traversal + symlink escape defenses** — `validate_path_within` guarding 6 CRUD tools (CWE-22, CWE-59)
|
|
268
|
+
- **Prompt injection 3-layer defense** — sentinel neutralization + provenance fence + `external_source` flag (OWASP LLM01:2025)
|
|
269
|
+
- **OpenSSF Best Practices badge** verified · **CodeQL** weekly scan · **Bandit + Semgrep + Gitleaks + pip-audit** on every PR
|
|
270
|
+
- **PyPI Trusted Publishing** via OIDC (zero long-lived tokens in CI)
|
|
271
|
+
|
|
272
|
+
</td>
|
|
273
|
+
<td valign="top" width="50%">
|
|
274
|
+
|
|
275
|
+
### Observability
|
|
276
|
+
|
|
277
|
+
- **Prometheus `/metrics` endpoint** — custom histogram buckets tuned for RAG (p95 ≤ 10ms fast-path targets), 7 canonical metrics via `@instrument` decorator on all 13 tools
|
|
278
|
+
- **Rate limiting** — thread-safe sliding-window counter, per-client RPM + burst, zero overhead when disabled
|
|
279
|
+
- **Health probes** — `GET /health` and `/healthz` returning `{status, version, uptime_seconds, cache}` in front of the auth middleware (probes always succeed)
|
|
280
|
+
- **Structured JSON logging** — opt-in via `server.logging.format: "json"`, one JSON object per record ready for ELK / Loki / Datadog / CloudWatch
|
|
281
|
+
- **Public benchmark dashboard** on GitHub Pages
|
|
282
|
+
|
|
283
|
+
</td>
|
|
284
|
+
</tr>
|
|
285
|
+
<tr>
|
|
286
|
+
<td valign="top" width="50%">
|
|
287
|
+
|
|
288
|
+
### Scale & performance
|
|
289
|
+
|
|
290
|
+
- **SSE / streamable-http transport** — 1 server serves N MCP clients, ChromaDB WAL mode enabled automatically, shared embedding model + query cache
|
|
291
|
+
- **BM25 inverted-index** — **128× faster** than linear scan (custom implementation, replaces `rank-bm25`)
|
|
292
|
+
- **FTS5 SQLite fast-path** (opt-in, ADR-002/003/006/008) — <10ms cold, <2ms hot on lexical queries
|
|
293
|
+
- **Cross-encoder reranking** — Xenova/ms-marco-MiniLM-L-6-v2, +1.88pp Recall@10 (p<0.001)
|
|
294
|
+
- **GPU CUDA 12** with auto DLL discovery + graceful CPU fallback
|
|
295
|
+
- **Query cache** — LRU + 5-min TTL, cuts p95 latency ~40%
|
|
296
|
+
- **Zero-downtime reindex** — staging populate + validation + atomic swap + durable metadata rollback
|
|
297
|
+
- **Async background reindex** with `get_reindex_status()` polling
|
|
298
|
+
|
|
299
|
+
</td>
|
|
300
|
+
<td valign="top" width="50%">
|
|
301
|
+
|
|
302
|
+
### Reliability
|
|
303
|
+
|
|
304
|
+
- **Nightly chaos injection** — HuggingFace Hub offline · ONNX zero-byte replay · watchdog crash recovery (3 scenarios in `tests/chaos/`)
|
|
305
|
+
- **50 000-iteration soak test** — proves no memory leak after 1h of continuous queries (`KNOWLEDGE_RAG_SOAK_ITERATIONS=50000`)
|
|
306
|
+
- **Mutation testing** (mutmut) on `instance_lock` + `preflight` — catches tests that are too weak
|
|
307
|
+
- **Determinism check** — full test suite × 3, catches flakes
|
|
308
|
+
- **Backwards-compat frozen** — 13 MCP tool parameter names guarded by `tests/test_backwards_compat.py` + legacy YAML fixtures (v3.6.0 / v3.7.0) still parse
|
|
309
|
+
- **API surface AST diff** — `check_api_surface.py` blocks any breaking change at PR time
|
|
310
|
+
- **9-cell CI matrix** — Linux + Windows + macOS × 3.11 + 3.12 + 3.13
|
|
311
|
+
|
|
312
|
+
</td>
|
|
313
|
+
</tr>
|
|
314
|
+
</table>
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
## 💼 Use Cases (real corpora, real teams)
|
|
319
|
+
|
|
320
|
+
### Security Teams — Red / Blue / CTF
|
|
321
|
+
|
|
322
|
+
**Preset:** [`cybersecurity.yaml`](presets/cybersecurity.yaml) · 8 categories · 200+ routing keywords · 69 query expansions
|
|
323
|
+
|
|
324
|
+
Ingest MITRE ATT&CK, threat reports, exploit writeups, incident reports. Search from Claude Code with `search_knowledge("privilege escalation windows")` and get instant recall across your entire corpus. Air-gapped — nothing leaves the laptop.
|
|
325
|
+
|
|
326
|
+
### Development Teams — Design Docs, Runbooks, Code
|
|
327
|
+
|
|
328
|
+
**Preset:** [`developer.yaml`](presets/developer.yaml) · 9 categories · 150+ routing keywords · 50+ expansions
|
|
329
|
+
|
|
330
|
+
Replace Confluence hunting. Ingest architecture docs, ADRs, runbooks, code, API specs. Devs ask their AI agent "how do we authenticate the payment service" and get the exact ADR + implementation file citation.
|
|
331
|
+
|
|
332
|
+
### Research Labs — Papers, Notebooks, Datasets
|
|
333
|
+
|
|
334
|
+
**Preset:** [`research.yaml`](presets/research.yaml) · 9 categories · 100+ routing keywords · 40+ expansions
|
|
335
|
+
|
|
336
|
+
Index arXiv papers, lab notebooks, dataset documentation. Semantic search finds papers by intent, not just keywords — cross-encoder reranking surfaces the actually-relevant one instead of five that share a term.
|
|
337
|
+
|
|
338
|
+
### Enterprise Knowledge Base — Air-gapped, Auditable
|
|
339
|
+
|
|
340
|
+
**Preset:** [`general.yaml`](presets/general.yaml) · blank slate, pure semantic search
|
|
341
|
+
|
|
342
|
+
Deploy via SSE on a single VM. 40+ users authenticated via bearer token, rate-limited, Prometheus-monitored, `/health` probes wired to your load balancer, JSON logs shipped to Datadog. No cloud calls. Meets LGPD, GDPR, HIPAA data-locality requirements by design.
|
|
343
|
+
|
|
344
|
+
**Verified at scale:** production reproduction on a **5 889-doc / 75 016-chunk corpus** with concurrent queries during a nuclear rebuild — zero downtime, zero errors (see [CHANGELOG v4.8.3](CHANGELOG.md#v483-2026-08-10--critical-hotfix-nuclear-rebuild--smart-reindex-hardening)).
|
|
345
|
+
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
## 🏗️ Architecture at a glance
|
|
349
|
+
|
|
350
|
+
End-to-end view of how MCP clients, the retrieval pipeline, storage, and enterprise plumbing connect. Every arrow is a real code path — nothing pictured here is aspirational.
|
|
351
|
+
|
|
352
|
+
```mermaid
|
|
353
|
+
flowchart TB
|
|
354
|
+
subgraph CLIENTS["MCP Clients (any of these)"]
|
|
355
|
+
C1[Claude Code]
|
|
356
|
+
C2[Claude Desktop]
|
|
357
|
+
C3[Cursor]
|
|
358
|
+
C4[Windsurf]
|
|
359
|
+
C5[VS Code · Cline · Gemini CLI · Zed]
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
subgraph TRANSPORT["Transport Layer"]
|
|
363
|
+
T1[stdio<br/>1 process per client]
|
|
364
|
+
T2[SSE / streamable-http<br/>1 server serves N clients]
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
subgraph MIDDLEWARE["ASGI Middleware Chain (HTTP mode)"]
|
|
368
|
+
M1[HealthMiddleware<br/>/health · /healthz]
|
|
369
|
+
M2[BearerAuthMiddleware<br/>constant-time compare]
|
|
370
|
+
M3[Rate Limiter<br/>sliding window]
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
subgraph MCP["13 MCP Tools (frozen contract)"]
|
|
374
|
+
MT1[search_knowledge]
|
|
375
|
+
MT2[get_document · search_similar]
|
|
376
|
+
MT3[add_document · add_from_url · update · remove]
|
|
377
|
+
MT4[reindex_documents · get_reindex_status]
|
|
378
|
+
MT5[list_documents · list_categories · get_index_stats · evaluate_retrieval]
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
subgraph SEARCH["Retrieval Pipeline"]
|
|
382
|
+
R[Query Router<br/>lexical vs semantic]
|
|
383
|
+
F[FTS5 Fast-Path<br/>opt-in · lt 10ms]
|
|
384
|
+
BM[BM25 Inverted Index<br/>128x faster than baseline]
|
|
385
|
+
SE[Semantic Search<br/>FastEmbed ONNX lazy-loaded]
|
|
386
|
+
RRF[Reciprocal Rank Fusion]
|
|
387
|
+
CE[Cross-Encoder Rerank<br/>MiniLM-L-6-v2]
|
|
388
|
+
QC[Query Cache<br/>LRU + 5-min TTL]
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
subgraph STORAGE["Storage (100% local)"]
|
|
392
|
+
CH[ChromaDB<br/>vectors + metadata<br/>WAL mode]
|
|
393
|
+
FT[SQLite FTS5<br/>lexical index<br/>WAL + busy-timeout]
|
|
394
|
+
MD[index_metadata.json<br/>durable state]
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
subgraph INGEST["Document Ingestion"]
|
|
398
|
+
FS[documents/ folder]
|
|
399
|
+
WD[Watchdog<br/>10s debounce]
|
|
400
|
+
PA[20 Parsers<br/>MD · PDF · DOCX · code · IPYNB]
|
|
401
|
+
CK[Chunker<br/>markdown-aware · code-aware]
|
|
402
|
+
EM[FastEmbed ONNX<br/>384D bge-small-en-v1.5]
|
|
403
|
+
DD[SHA256 Dedup]
|
|
404
|
+
SW[Zero-downtime Staging Swap<br/>rollback on validation fail]
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
subgraph OBS["Enterprise Observability (opt-in)"]
|
|
408
|
+
PM[Prometheus /metrics<br/>7 canonical + histograms]
|
|
409
|
+
LG[Structured JSON logs<br/>ELK · Loki · Datadog · CloudWatch]
|
|
410
|
+
HC[Health payload<br/>version · uptime · cache stats]
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
subgraph CFG["Configuration"]
|
|
414
|
+
YM[config.yaml<br/>+ 5 domain presets]
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
C1 & C2 & C3 & C4 & C5 -->|MCP protocol| T1
|
|
418
|
+
C1 & C2 & C3 & C4 & C5 -.->|remote deploy| T2
|
|
419
|
+
T1 --> MCP
|
|
420
|
+
T2 --> M1 --> M2 --> M3 --> MCP
|
|
421
|
+
|
|
422
|
+
MT1 --> QC
|
|
423
|
+
QC -->|cache miss| R
|
|
424
|
+
R -->|lexical| F
|
|
425
|
+
R -->|semantic| SE
|
|
426
|
+
R -->|hybrid| BM
|
|
427
|
+
F --> CH
|
|
428
|
+
F --> FT
|
|
429
|
+
BM --> CH
|
|
430
|
+
SE --> CH
|
|
431
|
+
BM --> RRF
|
|
432
|
+
SE --> RRF
|
|
433
|
+
RRF --> CE
|
|
434
|
+
CE --> QC
|
|
435
|
+
|
|
436
|
+
MT2 --> CH
|
|
437
|
+
MT3 --> INGEST
|
|
438
|
+
MT4 --> SW
|
|
439
|
+
MT5 --> CH
|
|
440
|
+
|
|
441
|
+
FS --> WD --> PA
|
|
442
|
+
PA --> CK --> EM --> DD --> CH
|
|
443
|
+
SW -.->|atomic swap| CH
|
|
444
|
+
SW -.-> FT
|
|
445
|
+
CH -.-> MD
|
|
446
|
+
|
|
447
|
+
MCP -.->|instrumented| PM
|
|
448
|
+
MCP -.->|logs| LG
|
|
449
|
+
M1 --> HC
|
|
450
|
+
|
|
451
|
+
YM -.-> SEARCH
|
|
452
|
+
YM -.-> STORAGE
|
|
453
|
+
YM -.-> OBS
|
|
454
|
+
YM -.-> MIDDLEWARE
|
|
455
|
+
|
|
456
|
+
classDef client fill:#3776AB,stroke:#1e5a8a,color:#fff
|
|
457
|
+
classDef transport fill:#00A67E,stroke:#006e54,color:#fff
|
|
458
|
+
classDef middleware fill:#6b46c1,stroke:#4c1d95,color:#fff
|
|
459
|
+
classDef storage fill:#4b5563,stroke:#1f2937,color:#fff
|
|
460
|
+
classDef obs fill:#dc2626,stroke:#7f1d1d,color:#fff
|
|
461
|
+
classDef ingest fill:#f59e0b,stroke:#78350f,color:#fff
|
|
462
|
+
|
|
463
|
+
class C1,C2,C3,C4,C5 client
|
|
464
|
+
class T1,T2 transport
|
|
465
|
+
class M1,M2,M3 middleware
|
|
466
|
+
class CH,FT,MD storage
|
|
467
|
+
class PM,LG,HC obs
|
|
468
|
+
class FS,WD,PA,CK,EM,DD,SW ingest
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
**Reading the diagram (top → bottom):**
|
|
472
|
+
|
|
473
|
+
1. **Any MCP client** — Claude Code, Cursor, Windsurf, and 5 others — connects via the transport of your choice (stdio for personal use, SSE/streamable-http for teams).
|
|
474
|
+
2. **HTTP mode chains 3 ASGI middlewares in order**: health probes first (always answered), then bearer auth (fenced with `WWW-Authenticate`), then rate limiter (sliding window).
|
|
475
|
+
3. **All 13 MCP tools** are decorated with `@rate_limited` + `@instrument` — Prometheus counts every call, rate limiter enforces RPM+burst, both zero-cost when disabled.
|
|
476
|
+
4. **`search_knowledge` checks the query cache first**; cache miss routes through the Query Router (regex classifier) to either the FTS5 fast-path (lexical) or the hybrid pipeline (BM25 + semantic + RRF + cross-encoder rerank).
|
|
477
|
+
5. **Storage is 100% local**: ChromaDB (WAL mode) for vectors + metadata, SQLite FTS5 (WAL + busy-timeout) for lexical fast-path, `index_metadata.json` for durable state.
|
|
478
|
+
6. **Document ingestion runs continuously**: watchdog observes `documents/`, 20 parsers handle each format, chunker respects language boundaries, FastEmbed ONNX generates embeddings, SHA256 deduplicates, and a staging swap performs zero-downtime rebuilds with rollback-on-failure.
|
|
479
|
+
7. **Enterprise observability** (opt-in) — Prometheus `/metrics`, structured JSON logs, `/health` payload — attaches to the same instrumentation points, no code changes required.
|
|
480
|
+
8. **`config.yaml` (with 5 domain presets)** controls every subsystem — no environment variable spaghetti, no hardcoded paths.
|
|
481
|
+
|
|
482
|
+
**Complete architecture — 4 detailed Mermaid diagrams (System Overview · Query Flow · Document Ingestion · hybrid_alpha effect):** [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)
|
|
483
|
+
|
|
484
|
+
---
|
|
485
|
+
|
|
486
|
+
## 📄 20 File Formats — parsed natively, no plugins needed
|
|
487
|
+
|
|
488
|
+
Every parser is **chunk-aware** — Markdown splits at `##` headers, code splits at function/class boundaries, notebooks skip base64 outputs, PDFs use PyMuPDF, spreadsheets extract sheet-by-sheet. **18 formats are enabled by default**; the 2 MetaTrader formats are opt-in (add to `documents.supported_formats` in `config.yaml`).
|
|
489
|
+
|
|
490
|
+
| # | Format | Extension | Parser | Default | Notes |
|
|
491
|
+
|---|--------|-----------|--------|:-------:|-------|
|
|
492
|
+
| 1 | Markdown | `.md` | Section-aware (splits at `##`) | Yes | Headers preserved as chunk boundaries |
|
|
493
|
+
| 2 | Plain Text | `.txt` | Fixed-size chunking | Yes | 1000 chars + 200 overlap |
|
|
494
|
+
| 3 | PDF | `.pdf` | PyMuPDF extraction | Yes | Text-based PDFs only (no OCR) |
|
|
495
|
+
| 4 | Word | `.docx` | python-docx | Yes | Headings preserved as markdown |
|
|
496
|
+
| 5 | Excel | `.xlsx` | openpyxl | Yes | Sheet-by-sheet extraction |
|
|
497
|
+
| 6 | PowerPoint | `.pptx` | python-pptx | Yes | Slide-by-slide extraction |
|
|
498
|
+
| 7 | Jupyter Notebook | `.ipynb` | Cell-aware parser | Yes | Markdown + code cells only; skips outputs/base64 |
|
|
499
|
+
| 8 | JSON | `.json` | Structure-aware | Yes | Flattened key-value extraction |
|
|
500
|
+
| 9 | CSV | `.csv` | Row-based parser | Yes | Headers + rows as text |
|
|
501
|
+
| 10 | XML | `.xml` | XML parser | Yes | Root element + namespace metadata |
|
|
502
|
+
| 11 | Python | `.py` | Code-aware parser | Yes | Functions/classes as chunks |
|
|
503
|
+
| 12 | C Source | `.c` | Code-aware parser | Yes | Functions / structs / includes extracted |
|
|
504
|
+
| 13 | C/C++ Header | `.h` | Code-aware parser | Yes | Function declarations + structs extracted |
|
|
505
|
+
| 14 | C++ Source | `.cpp` | Code-aware parser | Yes | Classes / structs / includes extracted |
|
|
506
|
+
| 15 | JavaScript | `.js` | Code-aware parser | Yes | Functions / classes / imports (ESM + CJS) |
|
|
507
|
+
| 16 | React JSX | `.jsx` | Code-aware parser | Yes | Same as JS parser |
|
|
508
|
+
| 17 | TypeScript | `.ts` | Code-aware parser | Yes | Functions / classes / interfaces / enums / imports |
|
|
509
|
+
| 18 | React TSX | `.tsx` | Code-aware parser | Yes | Same as TS parser |
|
|
510
|
+
| 19 | MQL4 Source | `.mq4` | Code parser | **No** | MetaTrader — opt-in via `documents.supported_formats` |
|
|
511
|
+
| 20 | MQL4 Header | `.mqh` | Code parser | **No** | MetaTrader — opt-in via `documents.supported_formats` |
|
|
512
|
+
|
|
513
|
+
> **Enable an opt-in format** — add the extension to `documents.supported_formats` in your `config.yaml`:
|
|
514
|
+
> ```yaml
|
|
515
|
+
> documents:
|
|
516
|
+
> supported_formats: [".md", ".pdf", ".mq4", ".mqh"]
|
|
517
|
+
> ```
|
|
518
|
+
|
|
519
|
+
**Full parser reference with per-format notes:** [docs/CONFIGURATION.md](docs/CONFIGURATION.md)
|
|
520
|
+
|
|
521
|
+
---
|
|
522
|
+
|
|
523
|
+
## 🔌 Choose your MCP integration
|
|
524
|
+
|
|
525
|
+
<table>
|
|
526
|
+
<tr>
|
|
527
|
+
<td align="center" width="14%">
|
|
528
|
+
|
|
529
|
+
**Claude Code**<br/>
|
|
530
|
+
`~/.claude.json`
|
|
531
|
+
|
|
532
|
+
</td>
|
|
533
|
+
<td align="center" width="14%">
|
|
534
|
+
|
|
535
|
+
**Claude Desktop**<br/>
|
|
536
|
+
`claude_desktop_config.json`
|
|
537
|
+
|
|
538
|
+
</td>
|
|
539
|
+
<td align="center" width="14%">
|
|
540
|
+
|
|
541
|
+
**Cursor**<br/>
|
|
542
|
+
`~/.cursor/mcp.json`
|
|
543
|
+
|
|
544
|
+
</td>
|
|
545
|
+
<td align="center" width="14%">
|
|
546
|
+
|
|
547
|
+
**Windsurf**<br/>
|
|
548
|
+
`~/.codeium/windsurf/mcp_config.json`
|
|
549
|
+
|
|
550
|
+
</td>
|
|
551
|
+
<td align="center" width="14%">
|
|
552
|
+
|
|
553
|
+
**VS Code**<br/>
|
|
554
|
+
Copilot Chat `mcp.json`
|
|
555
|
+
|
|
556
|
+
</td>
|
|
557
|
+
<td align="center" width="14%">
|
|
558
|
+
|
|
559
|
+
**Cline · Gemini CLI · Zed**<br/>
|
|
560
|
+
Native MCP
|
|
561
|
+
|
|
562
|
+
</td>
|
|
563
|
+
</tr>
|
|
564
|
+
</table>
|
|
565
|
+
|
|
566
|
+
**Complete client configuration guide with JSON schemas per client:** [docs/INSTALLATION.md#use-with-other-mcp-clients →](docs/INSTALLATION.md#use-with-other-mcp-clients)
|
|
567
|
+
|
|
568
|
+
---
|
|
569
|
+
|
|
570
|
+
## ⚙️ Configuration in 30 seconds
|
|
571
|
+
|
|
572
|
+
```yaml
|
|
573
|
+
# config.yaml — everything is optional; defaults just work
|
|
574
|
+
|
|
575
|
+
paths:
|
|
576
|
+
documents_dir: "./documents"
|
|
577
|
+
data_dir: "./data"
|
|
578
|
+
|
|
579
|
+
models:
|
|
580
|
+
embedding:
|
|
581
|
+
profile: "compact" # "compact" | "quality" | "multilingual" | "custom"
|
|
582
|
+
gpu: "auto" # "auto" | "true" | "false"
|
|
583
|
+
reranker:
|
|
584
|
+
enabled: true # cross-encoder rerank
|
|
585
|
+
|
|
586
|
+
search:
|
|
587
|
+
default_results: 5
|
|
588
|
+
max_results: 100
|
|
589
|
+
|
|
590
|
+
server: # optional — SSE / HTTP mode
|
|
591
|
+
transport: "stdio" # or "sse" / "streamable-http"
|
|
592
|
+
auth:
|
|
593
|
+
bearer_token: "" # set a secret to enable auth
|
|
594
|
+
rate_limit:
|
|
595
|
+
enabled: false
|
|
596
|
+
metrics:
|
|
597
|
+
enabled: false
|
|
598
|
+
logging:
|
|
599
|
+
format: "text" # or "json"
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
**Pre-built presets:** [`cybersecurity.yaml`](presets/cybersecurity.yaml) · [`developer.yaml`](presets/developer.yaml) · [`research.yaml`](presets/research.yaml) · [`general.yaml`](presets/general.yaml) · [`multilingual.yaml`](presets/multilingual.yaml)
|
|
603
|
+
|
|
604
|
+
**Complete configuration reference — every field, every default, tuning guide:** [docs/CONFIGURATION.md →](docs/CONFIGURATION.md)
|
|
605
|
+
|
|
606
|
+
---
|
|
607
|
+
|
|
608
|
+
## 🔒 Security & Compliance
|
|
609
|
+
|
|
610
|
+
knowledge-rag is designed for teams that cannot let their documents leave the perimeter.
|
|
611
|
+
|
|
612
|
+
| Requirement | How knowledge-rag delivers |
|
|
613
|
+
|---|---|
|
|
614
|
+
| **Data locality (LGPD / GDPR / HIPAA)** | 100% on-premise, zero egress network calls after initial model download |
|
|
615
|
+
| **Air-gapped deployment** | ONNX models pre-cached; set `HF_HUB_OFFLINE=1` to enforce zero-network |
|
|
616
|
+
| **CVE monitoring** | Dependabot (weekly) + pip-audit + Socket + CodeQL |
|
|
617
|
+
| **Supply chain security** | PyPI Trusted Publishing via OIDC (no long-lived tokens) |
|
|
618
|
+
| **Vulnerability disclosure** | Private security advisory via [SECURITY.md](SECURITY.md) |
|
|
619
|
+
| **Signed release attestations** | GitHub release attestations on every published version |
|
|
620
|
+
| **Reproducible builds** | Locked `requirements.txt` with pinned versions |
|
|
621
|
+
| **Authenticated access** | Bearer token middleware on SSE / HTTP transports (constant-time compare, RFC 6750) |
|
|
622
|
+
| **Rate limiting** | Sliding-window per-client RPM + burst (opt-in, zero-cost when disabled) |
|
|
623
|
+
| **Audit-ready logging** | Opt-in structured JSON logs → ship to your SIEM |
|
|
624
|
+
| **Path traversal defenses** | CWE-22 / CWE-59 guards on 6 CRUD tools |
|
|
625
|
+
| **Prompt injection defense** | 3-layer sanitization on `add_from_url` (OWASP LLM01:2025) |
|
|
626
|
+
|
|
627
|
+
**OpenSSF Best Practices** badge: passing · project ID [#13864](https://bestpractices.coreinfrastructure.org/projects/13864)
|
|
628
|
+
|
|
629
|
+
---
|
|
630
|
+
|
|
631
|
+
## 📈 Numbers that matter
|
|
632
|
+
|
|
633
|
+
- **26 000+** total downloads on PyPI · **250+** GitHub stars · **70+** enterprise teams (private + community)
|
|
634
|
+
- **700+ tests** collected · **1.33:1** test-to-code ratio · **codecov trend gate** ±0.5pp
|
|
635
|
+
- **35+ status checks** on every PR (9-cell OS×Python matrix · 7 quality pillars)
|
|
636
|
+
- **20 file formats** parsed natively · **13 MCP tools** frozen · **5 domain presets** (cyber · dev · research · multilingual · general)
|
|
637
|
+
- **BM25 128× faster** than baseline · **cross-encoder +1.88pp** Recall@10 (p<0.001) · **cache −40%** p95 latency
|
|
638
|
+
- **1 800+ files / 39 K chunks indexed in < 3 min** on a modern laptop (typical developer corpus)
|
|
639
|
+
- **Verified in production** on 5 889-doc / 75 016-chunk corpora
|
|
640
|
+
|
|
641
|
+
**Public benchmark dashboard:** https://lyonzin.github.io/knowledge-rag/
|
|
642
|
+
|
|
643
|
+
---
|
|
644
|
+
|
|
645
|
+
## 📚 Documentation
|
|
646
|
+
|
|
647
|
+
| Doc | What's inside |
|
|
648
|
+
|---|---|
|
|
649
|
+
| [**Installation guide**](docs/INSTALLATION.md) | 5 install methods · 8 MCP client integrations · GPU setup |
|
|
650
|
+
| [**API reference**](docs/API.md) | Complete reference for all 13 MCP tools |
|
|
651
|
+
| [**Configuration reference**](docs/CONFIGURATION.md) | Every `config.yaml` field · presets · tuning |
|
|
652
|
+
| [**Architecture**](docs/ARCHITECTURE.md) | 4 Mermaid diagrams: System Overview · Query Flow · Ingestion · hybrid_alpha |
|
|
653
|
+
| [**Troubleshooting**](docs/TROUBLESHOOTING.md) | 11 common issues + solutions |
|
|
654
|
+
| [**FTS5 fast-path guide**](docs/features/fts5_fast_path.md) | Opt-in lexical fast-path — when and how |
|
|
655
|
+
| [**Reindex operations**](docs/reindex-operations.md) | Zero-downtime rebuild · resume · checkpoint |
|
|
656
|
+
| [**GPU setup**](docs/gpu-setup.md) | CUDA 12 installation + troubleshooting |
|
|
657
|
+
| [**Migration to v4.8.0**](docs/migration-v4.8.0.md) | Embedding profile · multilingual · zero-downtime |
|
|
658
|
+
| [**Security policy**](SECURITY.md) | Threat model · disclosure channel |
|
|
659
|
+
| [**Contributing**](CONTRIBUTING.md) | Development · testing · PR process |
|
|
660
|
+
| [**Changelog**](CHANGELOG.md) | All release notes since v1.0.0 |
|
|
661
|
+
|
|
662
|
+
---
|
|
663
|
+
|
|
664
|
+
## 🤝 Community & Support
|
|
665
|
+
|
|
666
|
+
- **Report a bug** → [Open an issue](https://github.com/lyonzin/knowledge-rag/issues/new/choose)
|
|
667
|
+
- **Ask a question** → [GitHub Discussions](https://github.com/lyonzin/knowledge-rag/discussions)
|
|
668
|
+
- **Report a vulnerability** → [Security advisory](https://github.com/lyonzin/knowledge-rag/security/advisories/new) (private)
|
|
669
|
+
- **Contribute** → [CONTRIBUTING.md](CONTRIBUTING.md)
|
|
670
|
+
|
|
671
|
+
**Response SLA (best-effort, community project):**
|
|
672
|
+
- Security reports: within 48 h
|
|
673
|
+
- Bug reports with reproduction: within 5 business days
|
|
674
|
+
- Feature requests: triaged on next release cycle
|
|
675
|
+
|
|
676
|
+
---
|
|
677
|
+
|
|
678
|
+
## 🗺️ Recent releases
|
|
679
|
+
|
|
680
|
+
- **v4.8.5** (2026-08-13) — Enterprise observability: `/health` endpoint + opt-in JSON structured logging
|
|
681
|
+
- **v4.8.4** (2026-08-13) — Patch: security + durability + defensive fixes
|
|
682
|
+
- **v4.8.3** (2026-08-10) — Critical hotfix: nuclear-rebuild + smart-reindex hardening on 50k+ chunk corpora
|
|
683
|
+
- **v4.8.2** (2026-08-10) — FTS5 lexical fast-path opt-in release
|
|
684
|
+
- **v4.8.0** (2026-08-06) — Multilingual foundation + zero-downtime reindex
|
|
685
|
+
|
|
686
|
+
**Full history:** [CHANGELOG.md →](CHANGELOG.md)
|
|
687
|
+
|
|
688
|
+
---
|
|
689
|
+
|
|
690
|
+
## 📜 License
|
|
691
|
+
|
|
692
|
+
**MIT License** — [LICENSE](LICENSE). Forever. No cloud upsell, no dual-licensing, no restrictive clauses. Fork it, sell derivatives, embed it in commercial products — the license does not care.
|
|
693
|
+
|
|
694
|
+
---
|
|
695
|
+
|
|
696
|
+
## 🙏 Acknowledgments
|
|
697
|
+
|
|
698
|
+
Built on the shoulders of amazing open-source projects:
|
|
699
|
+
|
|
700
|
+
- [**Anthropic MCP**](https://modelcontextprotocol.io/) — Model Context Protocol spec + Python SDK
|
|
701
|
+
- [**ChromaDB**](https://www.trychroma.com/) — vector database that just works
|
|
702
|
+
- [**FastEmbed**](https://github.com/qdrant/fastembed) — ONNX embeddings, no PyTorch bloat
|
|
703
|
+
- [**HuggingFace**](https://huggingface.co/) — model hosting + `Xenova/ms-marco-MiniLM-L-6-v2` cross-encoder
|
|
704
|
+
- [**BAAI**](https://huggingface.co/BAAI) — the `bge-small-en-v1.5` embedding model
|
|
705
|
+
|
|
706
|
+
**Community contributors:** [@Hohlas](https://github.com/Hohlas) · [@eeshsaxena](https://github.com/eeshsaxena) · Sergey Khokhlov · and everyone who filed issues or PRs.
|
|
707
|
+
|
|
708
|
+
---
|
|
709
|
+
|
|
710
|
+
<div align="center">
|
|
711
|
+
|
|
712
|
+
**Built by [Ailton Rocha (Lyon.)](https://github.com/lyonzin)** · Star ⭐ if this saves you time · [Report an issue](https://github.com/lyonzin/knowledge-rag/issues/new/choose) · [Contribute](CONTRIBUTING.md)
|
|
713
|
+
|
|
714
|
+
*knowledge-rag — the MCP-first local RAG server for Claude Code, Cursor, Windsurf, and every AI agent.*
|
|
715
|
+
|
|
716
|
+
</div>
|