@mhalder/qdrant-mcp-server 1.1.1 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,127 @@
1
+ /**
2
+ * BM25 Sparse Vector Generator
3
+ *
4
+ * This module provides a simple BM25-like sparse vector generation for keyword search.
5
+ * For production use, consider using a proper BM25 implementation or Qdrant's built-in
6
+ * sparse vector generation via FastEmbed.
7
+ */
8
+
9
+ import type { SparseVector } from "../qdrant/client.js";
10
+
11
+ interface TokenFrequency {
12
+ [token: string]: number;
13
+ }
14
+
15
+ export class BM25SparseVectorGenerator {
16
+ private vocabulary: Map<string, number>;
17
+ private idfScores: Map<string, number>;
18
+ private documentCount: number;
19
+ private k1: number;
20
+ private b: number;
21
+
22
+ constructor(k1: number = 1.2, b: number = 0.75) {
23
+ this.vocabulary = new Map();
24
+ this.idfScores = new Map();
25
+ this.documentCount = 0;
26
+ this.k1 = k1;
27
+ this.b = b;
28
+ }
29
+
30
+ /**
31
+ * Tokenize text into words (simple whitespace tokenization + lowercase)
32
+ */
33
+ private tokenize(text: string): string[] {
34
+ return text
35
+ .toLowerCase()
36
+ .replace(/[^\w\s]/g, " ")
37
+ .split(/\s+/)
38
+ .filter((token) => token.length > 0);
39
+ }
40
+
41
+ /**
42
+ * Calculate term frequency for a document
43
+ */
44
+ private getTermFrequency(tokens: string[]): TokenFrequency {
45
+ const tf: TokenFrequency = {};
46
+ for (const token of tokens) {
47
+ tf[token] = (tf[token] || 0) + 1;
48
+ }
49
+ return tf;
50
+ }
51
+
52
+ /**
53
+ * Build vocabulary from training documents (optional pre-training step)
54
+ * In a simple implementation, we can skip this and use on-the-fly vocabulary
55
+ */
56
+ train(documents: string[]): void {
57
+ this.documentCount = documents.length;
58
+ const documentFrequency = new Map<string, number>();
59
+
60
+ // Calculate document frequency for each term
61
+ for (const doc of documents) {
62
+ const tokens = this.tokenize(doc);
63
+ const uniqueTokens = new Set(tokens);
64
+
65
+ for (const token of uniqueTokens) {
66
+ if (!this.vocabulary.has(token)) {
67
+ this.vocabulary.set(token, this.vocabulary.size);
68
+ }
69
+ documentFrequency.set(token, (documentFrequency.get(token) || 0) + 1);
70
+ }
71
+ }
72
+
73
+ // Calculate IDF scores
74
+ for (const [token, df] of documentFrequency.entries()) {
75
+ const idf = Math.log((this.documentCount - df + 0.5) / (df + 0.5) + 1.0);
76
+ this.idfScores.set(token, idf);
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Generate sparse vector for a query or document
82
+ * Returns indices and values for non-zero dimensions
83
+ */
84
+ generate(text: string, avgDocLength: number = 50): SparseVector {
85
+ const tokens = this.tokenize(text);
86
+ const tf = this.getTermFrequency(tokens);
87
+ const docLength = tokens.length;
88
+
89
+ const indices: number[] = [];
90
+ const values: number[] = [];
91
+
92
+ // Calculate BM25 score for each term
93
+ for (const [token, freq] of Object.entries(tf)) {
94
+ // Ensure token is in vocabulary
95
+ if (!this.vocabulary.has(token)) {
96
+ // For unseen tokens, add them to vocabulary dynamically
97
+ this.vocabulary.set(token, this.vocabulary.size);
98
+ }
99
+
100
+ const index = this.vocabulary.get(token)!;
101
+
102
+ // Use a default IDF if not trained
103
+ const idf = this.idfScores.get(token) || 1.0;
104
+
105
+ // BM25 formula
106
+ const numerator = freq * (this.k1 + 1);
107
+ const denominator = freq + this.k1 * (1 - this.b + this.b * (docLength / avgDocLength));
108
+ const score = idf * (numerator / denominator);
109
+
110
+ if (score > 0) {
111
+ indices.push(index);
112
+ values.push(score);
113
+ }
114
+ }
115
+
116
+ return { indices, values };
117
+ }
118
+
119
+ /**
120
+ * Simple static method for generating sparse vectors without training
121
+ * Useful for quick implementation
122
+ */
123
+ static generateSimple(text: string): SparseVector {
124
+ const generator = new BM25SparseVectorGenerator();
125
+ return generator.generate(text);
126
+ }
127
+ }