@iflow-mcp/jeanibarz-knowledge-base-mcp-server 0.1.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.
package/.gitattributes ADDED
@@ -0,0 +1 @@
1
+ * text=auto
package/CHANGELOG.md ADDED
@@ -0,0 +1,23 @@
1
+ # Changelog
2
+
3
+ ## [Unreleased]
4
+
5
+ ### Added
6
+
7
+ - Ollama embedding provider support as a local alternative to HuggingFace API for embeddings.
8
+ - Environment variable configuration for embedding provider selection (`EMBEDDING_PROVIDER`, `OLLAMA_BASE_URL`, `OLLAMA_MODEL`).
9
+ - End-to-end test evidence file: `ollama-embedding-e2e-results.md`.
10
+ - Documentation updates for setup and usage of both embedding providers.
11
+
12
+ ### Changed
13
+
14
+ - Refactored embedding logic to support provider abstraction and selection.
15
+ - Improved error handling and logging for embedding operations.
16
+
17
+ ### Fixed
18
+
19
+ - Addressed reliability issues (timeouts, hanging) with HuggingFace API by providing a local fallback.
20
+
21
+ ---
22
+
23
+ > For details, see the implementation log and test evidence files included in this release.
package/Dockerfile ADDED
@@ -0,0 +1,22 @@
1
+ # Generated by https://smithery.ai. See: https://smithery.ai/docs/config#dockerfile
2
+ # syntax=docker/dockerfile:1
3
+ FROM node:lts-alpine AS builder
4
+ WORKDIR /app
5
+ # Install dependencies
6
+ COPY package.json package-lock.json ./
7
+ RUN npm ci
8
+ # Copy source and build
9
+ COPY . .
10
+ RUN npm run build
11
+
12
+ # Runtime image
13
+ FROM node:lts-alpine
14
+ WORKDIR /app
15
+ # Install production dependencies
16
+ COPY package.json package-lock.json ./
17
+ RUN npm ci --only=production
18
+ # Copy built files and dependencies
19
+ COPY --from=builder /app/build ./build
20
+
21
+ # Default command
22
+ CMD ["node", "build/index.js"]
package/README.md ADDED
@@ -0,0 +1,224 @@
1
+ # Knowledge Base MCP Server
2
+
3
+ [![smithery badge](https://smithery.ai/badge/@jeanibarz/knowledge-base-mcp-server)](https://smithery.ai/server/@jeanibarz/knowledge-base-mcp-server)
4
+ This MCP server provides tools for listing and retrieving content from different knowledge bases.
5
+
6
+ <a href="https://glama.ai/mcp/servers/n0p6v0o0a4">
7
+ <img width="380" height="200" src="https://glama.ai/mcp/servers/n0p6v0o0a4/badge" alt="Knowledge Base Server MCP server" />
8
+ </a>
9
+
10
+ ## Setup Instructions
11
+
12
+ These instructions assume you have Node.js and npm installed on your system.
13
+
14
+ ### Installing via Smithery
15
+
16
+ To install Knowledge Base Server for Claude Desktop automatically via [Smithery](https://smithery.ai/server/@jeanibarz/knowledge-base-mcp-server):
17
+
18
+ ```bash
19
+ npx -y @smithery/cli install @jeanibarz/knowledge-base-mcp-server --client claude
20
+ ```
21
+
22
+ ### Manual Installation
23
+ **Prerequisites**
24
+
25
+ * [Node.js](https://nodejs.org/) (version 16 or higher)
26
+ * [npm](https://www.npmjs.com/) (Node Package Manager)
27
+
28
+ 1. **Clone the repository:**
29
+
30
+ ```bash
31
+ git clone <repository_url>
32
+ cd knowledge-base-mcp-server
33
+ ```
34
+
35
+ 2. **Install dependencies:**
36
+
37
+ ```bash
38
+ npm install
39
+ ```
40
+
41
+ 3. **Configure environment variables:**
42
+
43
+ This server supports three embedding providers: **Ollama** (recommended for reliability), **OpenAI** and **HuggingFace** (fallback option).
44
+
45
+ ### Option 1: Ollama Configuration (Recommended)
46
+
47
+ * Set `EMBEDDING_PROVIDER=ollama` to use local Ollama embeddings
48
+ * Install [Ollama](https://ollama.ai/) and pull an embedding model: `ollama pull dengcao/Qwen3-Embedding-0.6B:Q8_0`
49
+ * Configure the following environment variables:
50
+ ```bash
51
+ EMBEDDING_PROVIDER=ollama
52
+ OLLAMA_BASE_URL=http://localhost:11434 # Default Ollama URL
53
+ OLLAMA_MODEL=dengcao/Qwen3-Embedding-0.6B:Q8_0 # Default embedding model
54
+ KNOWLEDGE_BASES_ROOT_DIR=$HOME/knowledge_bases
55
+ ```
56
+
57
+ ### Option 2: OpenAI Configuration
58
+
59
+ * Set `EMBEDDING_PROVIDER=openai` to use OpenAI API for embeddings
60
+ * Configure the following environment variables:
61
+ ```bash
62
+ EMBEDDING_PROVIDER=openai
63
+ OPENAI_API_KEY=your_api_key_here
64
+ OPENAI_MODEL_NAME=text-embedding-ada-002
65
+ KNOWLEDGE_BASES_ROOT_DIR=$HOME/knowledge_bases
66
+ ```
67
+
68
+ ### Option 3: HuggingFace Configuration (Fallback)
69
+
70
+ * Set `EMBEDDING_PROVIDER=huggingface` or leave unset (default)
71
+ * Obtain a free API key from [HuggingFace](https://huggingface.co/)
72
+ * Configure the following environment variables:
73
+ ```bash
74
+ EMBEDDING_PROVIDER=huggingface # Optional, this is the default
75
+ HUGGINGFACE_API_KEY=your_api_key_here
76
+ HUGGINGFACE_MODEL_NAME=sentence-transformers/all-MiniLM-L6-v2
77
+ KNOWLEDGE_BASES_ROOT_DIR=$HOME/knowledge_bases
78
+ ```
79
+
80
+ ### Additional Configuration
81
+
82
+ * The server supports the `FAISS_INDEX_PATH` environment variable to specify the path to the FAISS index. If not set, it will default to `$HOME/knowledge_bases/.faiss`.
83
+ * Logging can be routed to a file by setting `LOG_FILE=/path/to/logs/knowledge-base.log`. Log verbosity defaults to `info` and can be adjusted with `LOG_LEVEL=debug|info|warn|error`.
84
+ * You can set these environment variables in your `.bashrc` or `.zshrc` file, or directly in the MCP settings.
85
+
86
+ 4. **Build the server:**
87
+
88
+ ```bash
89
+ npm run build
90
+ ```
91
+
92
+ 5. **Add the server to the MCP settings:**
93
+
94
+ * Edit the `cline_mcp_settings.json` file located at `/home/jean/.vscode-server/data/User/globalStorage/saoudrizwan.claude-dev/settings/`.
95
+ * Add the following configuration to the `mcpServers` object:
96
+
97
+ * **Option 1: Ollama Configuration**
98
+
99
+ ```json
100
+ "knowledge-base-mcp-ollama": {
101
+ "command": "node",
102
+ "args": [
103
+ "/path/to/knowledge-base-mcp-server/build/index.js"
104
+ ],
105
+ "disabled": false,
106
+ "autoApprove": [],
107
+ "env": {
108
+ "KNOWLEDGE_BASES_ROOT_DIR": "/path/to/knowledge_bases",
109
+ "EMBEDDING_PROVIDER": "ollama",
110
+ "OLLAMA_BASE_URL": "http://localhost:11434",
111
+ "OLLAMA_MODEL": "dengcao/Qwen3-Embedding-0.6B:Q8_0"
112
+ },
113
+ "description": "Retrieves similar chunks from the knowledge base based on a query using Ollama."
114
+ },
115
+ ```
116
+
117
+ * **Option 2: OpenAI Configuration**
118
+
119
+ ```json
120
+ "knowledge-base-mcp-openai": {
121
+ "command": "node",
122
+ "args": [
123
+ "/path/to/knowledge-base-mcp-server/build/index.js"
124
+ ],
125
+ "disabled": false,
126
+ "autoApprove": [],
127
+ "env": {
128
+ "KNOWLEDGE_BASES_ROOT_DIR": "/path/to/knowledge_bases",
129
+ "EMBEDDING_PROVIDER": "openai",
130
+ "OPENAI_API_KEY": "YOUR_OPENAI_API_KEY",
131
+ "OPENAI_MODEL_NAME": "text-embedding-ada-002"
132
+ },
133
+ "description": "Retrieves similar chunks from the knowledge base based on a query using OpenAI."
134
+ },
135
+ ```
136
+
137
+ * **Option 3: HuggingFace Configuration**
138
+
139
+ ```json
140
+ "knowledge-base-mcp-huggingface": {
141
+ "command": "node",
142
+ "args": [
143
+ "/path/to/knowledge-base-mcp-server/build/index.js"
144
+ ],
145
+ "disabled": false,
146
+ "autoApprove": [],
147
+ "env": {
148
+ "KNOWLEDGE_BASES_ROOT_DIR": "/path/to/knowledge_bases",
149
+ "EMBEDDING_PROVIDER": "huggingface",
150
+ "HUGGINGFACE_API_KEY": "YOUR_HUGGINGFACE_API_KEY",
151
+ "HUGGINGFACE_MODEL_NAME": "sentence-transformers/all-MiniLM-L6-v2"
152
+ },
153
+ "description": "Retrieves similar chunks from the knowledge base based on a query using HuggingFace."
154
+ },
155
+ ```
156
+
157
+ * **Note:** You only need to add one of the above configurations (either Ollama, OpenAI or HuggingFace) to your `cline_mcp_settings.json` file, depending on your preferred embedding provider.
158
+ ```
159
+
160
+ * Replace `/path/to/knowledge-base-mcp-server` with the actual path to the server directory.
161
+ * Replace `/path/to/knowledge_bases` with the actual path to the knowledge bases directory.
162
+
163
+ 6. **Create knowledge base directories:**
164
+
165
+ * Create subdirectories within the `KNOWLEDGE_BASES_ROOT_DIR` for each knowledge base (e.g., `company`, `it_support`, `onboarding`).
166
+ * Place text files (e.g., `.txt`, `.md`) containing the knowledge base content within these subdirectories.
167
+
168
+ * The server recursively reads all text files (e.g., `.txt`, `.md`) within the specified knowledge base subdirectories.
169
+ * The server skips hidden files and directories (those starting with a `.`).
170
+ * For each file, the server calculates the SHA256 hash and stores it in a file with the same name in a hidden `.index` subdirectory. This hash is used to determine if the file has been modified since the last indexing.
171
+ * The file content is splitted into chunks using the `MarkdownTextSplitter` from `langchain/text_splitter`.
172
+ * The content of each chunk is then added to a FAISS index, which is used for similarity search.
173
+ * The FAISS index is automatically initialized when the server starts. It checks for changes in the knowledge base files and updates the index accordingly.
174
+
175
+ ## Usage
176
+
177
+ The server exposes two tools:
178
+
179
+ * `list_knowledge_bases`: Lists the available knowledge bases.
180
+ * `retrieve_knowledge`: Retrieves similar chunks from the knowledge base based on a query. Optionally, if a knowledge base is specified, only that one is searched; otherwise, all available knowledge bases are considered. By default, at most 10 document chunks are returned with a score below a threshold of 2. A different threshold can optionally be provided using the `threshold` parameter.
181
+
182
+ You can use these tools through the MCP interface.
183
+
184
+ The `retrieve_knowledge` tool performs a semantic search using a FAISS index. The index is automatically updated when the server starts or when a file in a knowledge base is modified.
185
+
186
+ The output of the `retrieve_knowledge` tool is a markdown formatted string with the following structure:
187
+
188
+ ````markdown
189
+ ## Semantic Search Results
190
+
191
+ **Result 1:**
192
+
193
+ [Content of the most similar chunk]
194
+
195
+ **Source:**
196
+ ```json
197
+ {
198
+ "source": "[Path to the file containing the chunk]"
199
+ }
200
+ ```
201
+
202
+ ---
203
+
204
+ **Result 2:**
205
+
206
+ [Content of the second most similar chunk]
207
+
208
+ **Source:**
209
+ ```json
210
+ {
211
+ "source": "[Path to the file containing the chunk]"
212
+ }
213
+ ```
214
+
215
+ > **Disclaimer:** The provided results might not all be relevant. Please cross-check the relevance of the information.
216
+ ````
217
+
218
+ Each result includes the content of the most similar chunk, the source file, and a similarity score.
219
+
220
+ ## Troubleshooting & Logging
221
+
222
+ - Set `LOG_FILE` to capture structured logs (JSON-RPC traffic continues to use stdout). This is especially helpful when diagnosing MCP handshake errors because all diagnostic messages are written to stderr and the optional log file.
223
+ - Permission errors when creating or updating the FAISS index are surfaced with explicit messages in both the console and the log file. Verify that the process can write to `FAISS_INDEX_PATH` and the `.index` directories inside each knowledge base.
224
+ - Run `npm test` to execute the Jest suite (serialised with `--runInBand`) that covers logger fallback behaviour and FAISS permission handling.
package/UNLICENSE ADDED
@@ -0,0 +1,24 @@
1
+ This is free and unencumbered software released into the public domain.
2
+
3
+ Anyone is free to copy, modify, publish, use, compile, sell, or
4
+ distribute this software, either in source code form or as a compiled
5
+ binary, for any purpose, commercial or non-commercial, and by any
6
+ means.
7
+
8
+ In jurisdictions that recognize copyright laws, the author or authors
9
+ of this software dedicate any and all copyright interest in the
10
+ software to the public domain. We make this dedication for the benefit
11
+ of the public at large and to the detriment of our heirs and
12
+ successors. We intend this dedication to be an overt act of
13
+ relinquishment in perpetuity of all present and future rights to this
14
+ software under copyright law.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ For more information, please refer to <http://unlicense.org/>
@@ -0,0 +1,341 @@
1
+ // FaissIndexManager.ts
2
+ import * as fsp from 'fs/promises';
3
+ import * as fs from 'fs';
4
+ import * as path from 'path';
5
+ import { HuggingFaceInferenceEmbeddings } from "@langchain/community/embeddings/hf";
6
+ import { OllamaEmbeddings } from "@langchain/ollama";
7
+ import { OpenAIEmbeddings } from "@langchain/openai";
8
+ import { FaissStore } from "@langchain/community/vectorstores/faiss";
9
+ import { Document } from "@langchain/core/documents";
10
+ import { MarkdownTextSplitter } from "langchain/text_splitter";
11
+ import { calculateSHA256, getFilesRecursively } from './utils.js';
12
+ import { KNOWLEDGE_BASES_ROOT_DIR, FAISS_INDEX_PATH, EMBEDDING_PROVIDER, HUGGINGFACE_MODEL_NAME, OLLAMA_BASE_URL, OLLAMA_MODEL, OPENAI_MODEL_NAME, } from './config.js';
13
+ import { logger } from './logger.js';
14
+ const MODEL_NAME_FILE = path.join(FAISS_INDEX_PATH, 'model_name.txt');
15
+ function isPermissionError(error) {
16
+ if (!error || typeof error !== 'object') {
17
+ return false;
18
+ }
19
+ const code = error.code;
20
+ return code === 'EACCES' || code === 'EPERM' || code === 'EROFS';
21
+ }
22
+ function handleFsOperationError(action, targetPath, error) {
23
+ const pathDescription = path.resolve(targetPath);
24
+ const stack = error?.stack;
25
+ if (isPermissionError(error)) {
26
+ const message = `Permission denied while attempting to ${action} ${pathDescription}. Grant write access and retry.`;
27
+ logger.error(message);
28
+ if (stack) {
29
+ logger.error(stack);
30
+ }
31
+ const loggedError = new Error(message, { cause: error instanceof Error ? error : undefined });
32
+ loggedError.__alreadyLogged = true;
33
+ throw loggedError;
34
+ }
35
+ logger.error(`Failed to ${action} ${pathDescription}:`, error);
36
+ if (stack) {
37
+ logger.error(stack);
38
+ }
39
+ if (error instanceof Error) {
40
+ error.__alreadyLogged = true;
41
+ throw error;
42
+ }
43
+ const newError = new Error(`Failed to ${action} ${pathDescription}: ${String(error)}`);
44
+ newError.__alreadyLogged = true;
45
+ throw newError;
46
+ }
47
+ export class FaissIndexManager {
48
+ faissIndex = null;
49
+ embeddings;
50
+ modelName;
51
+ embeddingProvider;
52
+ constructor() {
53
+ this.embeddingProvider = EMBEDDING_PROVIDER;
54
+ if (this.embeddingProvider === 'ollama') {
55
+ logger.info('Initializing FaissIndexManager with Ollama embeddings');
56
+ this.modelName = OLLAMA_MODEL;
57
+ this.embeddings = new OllamaEmbeddings({
58
+ baseUrl: OLLAMA_BASE_URL,
59
+ model: this.modelName,
60
+ });
61
+ }
62
+ else if (this.embeddingProvider === 'openai') {
63
+ logger.info('Initializing FaissIndexManager with OpenAI embeddings');
64
+ const openaiApiKey = process.env.OPENAI_API_KEY;
65
+ if (!openaiApiKey) {
66
+ throw new Error('OPENAI_API_KEY environment variable is required when using OpenAI provider');
67
+ }
68
+ this.modelName = OPENAI_MODEL_NAME;
69
+ this.embeddings = new OpenAIEmbeddings({
70
+ apiKey: openaiApiKey,
71
+ model: this.modelName,
72
+ });
73
+ }
74
+ else {
75
+ logger.info('Initializing FaissIndexManager with HuggingFace embeddings');
76
+ const huggingFaceApiKey = process.env.HUGGINGFACE_API_KEY;
77
+ if (!huggingFaceApiKey) {
78
+ throw new Error('HUGGINGFACE_API_KEY environment variable is required when using HuggingFace provider');
79
+ }
80
+ this.modelName = HUGGINGFACE_MODEL_NAME;
81
+ this.embeddings = new HuggingFaceInferenceEmbeddings({
82
+ apiKey: huggingFaceApiKey,
83
+ model: this.modelName,
84
+ });
85
+ }
86
+ logger.info(`Using embedding provider: ${this.embeddingProvider}, model: ${this.modelName}`);
87
+ }
88
+ async initialize() {
89
+ try {
90
+ if (!fs.existsSync(FAISS_INDEX_PATH)) {
91
+ try {
92
+ await fsp.mkdir(FAISS_INDEX_PATH, { recursive: true });
93
+ }
94
+ catch (error) {
95
+ handleFsOperationError('create FAISS index directory', FAISS_INDEX_PATH, error);
96
+ }
97
+ }
98
+ const indexFilePath = path.join(FAISS_INDEX_PATH, 'faiss.index');
99
+ let storedModelName = null;
100
+ try {
101
+ storedModelName = fs.existsSync(MODEL_NAME_FILE) ? (await fsp.readFile(MODEL_NAME_FILE, 'utf-8')) : null;
102
+ }
103
+ catch (error) {
104
+ logger.warn('Error reading stored model name:', error);
105
+ }
106
+ if (storedModelName && storedModelName !== this.modelName) {
107
+ logger.warn(`Model name has changed from ${storedModelName} to ${this.modelName}. Recreating index.`);
108
+ if (fs.existsSync(indexFilePath)) {
109
+ try {
110
+ await fsp.unlink(indexFilePath);
111
+ logger.info('Existing FAISS index deleted.');
112
+ }
113
+ catch (error) {
114
+ handleFsOperationError('delete stale FAISS index', indexFilePath, error);
115
+ }
116
+ }
117
+ this.faissIndex = null; // Ensure index is recreated
118
+ }
119
+ if (fs.existsSync(indexFilePath)) {
120
+ logger.info('Loading existing FAISS index from:', indexFilePath);
121
+ try {
122
+ this.faissIndex = await FaissStore.load(indexFilePath, this.embeddings);
123
+ }
124
+ catch (error) {
125
+ handleFsOperationError('load FAISS index from', indexFilePath, error);
126
+ }
127
+ logger.info('FAISS index loaded.');
128
+ }
129
+ else {
130
+ logger.info('FAISS index file not found at', indexFilePath, '. It will be created if documents are available.');
131
+ this.faissIndex = null;
132
+ }
133
+ // Save the current model name for future checks
134
+ try {
135
+ await fsp.writeFile(MODEL_NAME_FILE, this.modelName, 'utf-8');
136
+ }
137
+ catch (error) {
138
+ handleFsOperationError('persist embedding model metadata in', MODEL_NAME_FILE, error);
139
+ }
140
+ }
141
+ catch (error) {
142
+ if (!error?.__alreadyLogged) {
143
+ logger.error('Error initializing FAISS index:', error);
144
+ if (error?.stack) {
145
+ logger.error(error.stack);
146
+ }
147
+ }
148
+ throw error;
149
+ }
150
+ }
151
+ /**
152
+ * Updates the FAISS index.
153
+ * If `specificKnowledgeBase` is provided, only files from that knowledge base will be checked and updated.
154
+ * If no update occurs (and the FAISS index remains uninitialized) but there are documents,
155
+ * then the index is built from all available files.
156
+ */
157
+ async updateIndex(specificKnowledgeBase) {
158
+ logger.debug('Updating FAISS index...');
159
+ try {
160
+ let knowledgeBases = [];
161
+ if (specificKnowledgeBase) {
162
+ knowledgeBases.push(specificKnowledgeBase);
163
+ }
164
+ else {
165
+ knowledgeBases = await fsp.readdir(KNOWLEDGE_BASES_ROOT_DIR);
166
+ }
167
+ let anyFileProcessed = false;
168
+ // Process each knowledge base directory.
169
+ for (const knowledgeBaseName of knowledgeBases) {
170
+ if (knowledgeBaseName.startsWith('.')) {
171
+ logger.debug(`Skipping dot folder: ${knowledgeBaseName}`);
172
+ continue;
173
+ }
174
+ const knowledgeBasePath = path.join(KNOWLEDGE_BASES_ROOT_DIR, knowledgeBaseName);
175
+ const filePaths = await getFilesRecursively(knowledgeBasePath);
176
+ for (const filePath of filePaths) {
177
+ anyFileProcessed = true;
178
+ const fileHash = await calculateSHA256(filePath);
179
+ const relativePath = path.relative(knowledgeBasePath, filePath);
180
+ const indexDirPath = path.join(knowledgeBasePath, '.index', path.dirname(relativePath));
181
+ const indexFilePath = path.join(indexDirPath, path.basename(filePath));
182
+ if (!fs.existsSync(indexDirPath)) {
183
+ try {
184
+ await fsp.mkdir(indexDirPath, { recursive: true });
185
+ }
186
+ catch (error) {
187
+ handleFsOperationError('create index metadata directory', indexDirPath, error);
188
+ }
189
+ }
190
+ let storedHash = null;
191
+ try {
192
+ const buffer = await fsp.readFile(indexFilePath);
193
+ storedHash = buffer.toString('utf-8');
194
+ }
195
+ catch (error) {
196
+ // The hash file may not exist yet; that's fine.
197
+ }
198
+ // If the file is new or has changed, process it.
199
+ if (fileHash !== storedHash) {
200
+ logger.info(`File ${filePath} has changed. Updating index...`);
201
+ let content = '';
202
+ try {
203
+ content = await fsp.readFile(filePath, 'utf-8');
204
+ }
205
+ catch (error) {
206
+ logger.error(`Error reading file ${filePath}:`, error);
207
+ continue;
208
+ }
209
+ let documentsToAdd = [];
210
+ if (path.extname(filePath).toLowerCase() === '.md') {
211
+ const splitter = new MarkdownTextSplitter({
212
+ chunkSize: 1000,
213
+ chunkOverlap: 200,
214
+ keepSeparator: false,
215
+ });
216
+ documentsToAdd = await splitter.createDocuments([content], [{ source: filePath }]);
217
+ }
218
+ else {
219
+ documentsToAdd = [
220
+ new Document({
221
+ pageContent: content,
222
+ metadata: { source: filePath },
223
+ }),
224
+ ];
225
+ }
226
+ if (documentsToAdd.length > 0) {
227
+ if (this.faissIndex === null) {
228
+ logger.info('Creating new FAISS index from texts...');
229
+ this.faissIndex = await FaissStore.fromTexts(documentsToAdd.map((doc) => doc.pageContent), documentsToAdd.map((doc) => doc.metadata), this.embeddings);
230
+ }
231
+ else {
232
+ await this.faissIndex.addDocuments(documentsToAdd);
233
+ }
234
+ const indexFileSavePath = path.join(FAISS_INDEX_PATH, 'faiss.index');
235
+ try {
236
+ await this.faissIndex.save(indexFileSavePath);
237
+ logger.info('FAISS index saved successfully to', indexFileSavePath);
238
+ }
239
+ catch (saveError) {
240
+ handleFsOperationError('save FAISS index at', indexFileSavePath, saveError);
241
+ }
242
+ try {
243
+ await fsp.writeFile(indexFilePath, fileHash, { encoding: 'utf-8' });
244
+ }
245
+ catch (error) {
246
+ handleFsOperationError('write file hash metadata to', indexFilePath, error);
247
+ }
248
+ logger.info(`Index updated for ${filePath}.`);
249
+ }
250
+ else {
251
+ logger.debug(`No documents generated from ${filePath}. Skipping index update.`);
252
+ }
253
+ }
254
+ else {
255
+ logger.debug(`File ${filePath} unchanged, skipping.`);
256
+ }
257
+ }
258
+ }
259
+ // If at least one file was processed but no changes triggered index creation,
260
+ // then attempt to build the FAISS index from all available documents.
261
+ if (this.faissIndex === null && anyFileProcessed) {
262
+ logger.info('No updates detected but FAISS index is not initialized. Building index from all available documents...');
263
+ let allDocuments = [];
264
+ for (const knowledgeBaseName of knowledgeBases) {
265
+ if (knowledgeBaseName.startsWith('.'))
266
+ continue;
267
+ const knowledgeBasePath = path.join(KNOWLEDGE_BASES_ROOT_DIR, knowledgeBaseName);
268
+ const filePaths = await getFilesRecursively(knowledgeBasePath);
269
+ for (const filePath of filePaths) {
270
+ let content = '';
271
+ try {
272
+ content = await fsp.readFile(filePath, 'utf-8');
273
+ }
274
+ catch (error) {
275
+ logger.error(`Error reading file ${filePath}:`, error);
276
+ continue;
277
+ }
278
+ let documents;
279
+ if (path.extname(filePath).toLowerCase() === '.md') {
280
+ const splitter = new MarkdownTextSplitter({
281
+ chunkSize: 1000,
282
+ chunkOverlap: 200,
283
+ keepSeparator: false,
284
+ });
285
+ documents = await splitter.createDocuments([content], [{ source: filePath }]);
286
+ }
287
+ else {
288
+ documents = [
289
+ new Document({
290
+ pageContent: content,
291
+ metadata: { source: filePath },
292
+ }),
293
+ ];
294
+ }
295
+ if (documents.length > 0) {
296
+ allDocuments.push(...documents);
297
+ }
298
+ }
299
+ }
300
+ if (allDocuments.length > 0) {
301
+ this.faissIndex = await FaissStore.fromTexts(allDocuments.map((doc) => doc.pageContent), allDocuments.map((doc) => doc.metadata), this.embeddings);
302
+ const indexFileSavePath = path.join(FAISS_INDEX_PATH, 'faiss.index');
303
+ try {
304
+ await this.faissIndex.save(indexFileSavePath);
305
+ logger.info('FAISS index saved successfully to', indexFileSavePath);
306
+ }
307
+ catch (saveError) {
308
+ handleFsOperationError('save FAISS index at', indexFileSavePath, saveError);
309
+ }
310
+ }
311
+ }
312
+ logger.debug('FAISS index update process completed.');
313
+ }
314
+ catch (error) {
315
+ if (!error?.__alreadyLogged) {
316
+ logger.error('Error updating FAISS index:', error);
317
+ if (error?.stack) {
318
+ logger.error(error.stack);
319
+ }
320
+ }
321
+ throw error;
322
+ }
323
+ }
324
+ /**
325
+ * Performs a similarity search and returns the results with their similarity scores.
326
+ */
327
+ async similaritySearch(query, k, threshold = 2) {
328
+ if (!this.faissIndex) {
329
+ throw new Error('FAISS index is not initialized');
330
+ }
331
+ const filter = { score: { $lte: threshold } };
332
+ // Use the vector store's method that returns [DocumentInterface, number] tuples.
333
+ const resultsWithScore = await this.faissIndex.similaritySearchWithScore(query, k, filter);
334
+ // Map the tuple into an object that includes the score.
335
+ return resultsWithScore.map(([doc, score]) => ({
336
+ ...doc,
337
+ score,
338
+ }));
339
+ }
340
+ }
341
+ //# sourceMappingURL=FaissIndexManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FaissIndexManager.js","sourceRoot":"","sources":["../src/FaissIndexManager.ts"],"names":[],"mappings":"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"}