@peleke.s/langchain-qortex 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/LICENSE +21 -0
- package/README.md +148 -0
- package/dist/index.cjs +324 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +234 -0
- package/dist/index.d.ts +234 -0
- package/dist/index.js +320 -0
- package/dist/index.js.map +1 -0
- package/package.json +72 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
import { VectorStore } from '@langchain/core/vectorstores';
|
|
2
|
+
import { Document } from '@langchain/core/documents';
|
|
3
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
4
|
+
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
5
|
+
import { Embeddings } from '@langchain/core/embeddings';
|
|
6
|
+
|
|
7
|
+
// src/vectorstore.ts
|
|
8
|
+
var QortexMcpClient = class {
|
|
9
|
+
client = null;
|
|
10
|
+
transport = null;
|
|
11
|
+
config;
|
|
12
|
+
_connected = false;
|
|
13
|
+
constructor(config = {}) {
|
|
14
|
+
this.config = config;
|
|
15
|
+
if (config.mcpClient) {
|
|
16
|
+
this.client = config.mcpClient;
|
|
17
|
+
this._connected = true;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
get connected() {
|
|
21
|
+
return this._connected;
|
|
22
|
+
}
|
|
23
|
+
async connect() {
|
|
24
|
+
if (this._connected) return;
|
|
25
|
+
const command = this.config.serverCommand ?? "uvx";
|
|
26
|
+
const args = this.config.serverArgs ?? ["qortex", "mcp-serve"];
|
|
27
|
+
const env = {};
|
|
28
|
+
for (const [k, v] of Object.entries(process.env)) {
|
|
29
|
+
if (v !== void 0) env[k] = v;
|
|
30
|
+
}
|
|
31
|
+
if (this.config.serverEnv) {
|
|
32
|
+
Object.assign(env, this.config.serverEnv);
|
|
33
|
+
}
|
|
34
|
+
this.transport = new StdioClientTransport({
|
|
35
|
+
command,
|
|
36
|
+
args,
|
|
37
|
+
env
|
|
38
|
+
});
|
|
39
|
+
this.client = new Client(
|
|
40
|
+
{ name: "langchain-qortex", version: "0.1.0" },
|
|
41
|
+
{ capabilities: {} }
|
|
42
|
+
);
|
|
43
|
+
await this.client.connect(this.transport);
|
|
44
|
+
this._connected = true;
|
|
45
|
+
}
|
|
46
|
+
async disconnect() {
|
|
47
|
+
if (this.transport) {
|
|
48
|
+
await this.transport.close();
|
|
49
|
+
this.transport = null;
|
|
50
|
+
}
|
|
51
|
+
this.client = null;
|
|
52
|
+
this._connected = false;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Call a qortex MCP tool and return the parsed result.
|
|
56
|
+
*
|
|
57
|
+
* @param name - Tool name (e.g. "qortex_vector_query")
|
|
58
|
+
* @param args - Tool arguments as a plain object
|
|
59
|
+
* @returns Parsed JSON result from the tool
|
|
60
|
+
*/
|
|
61
|
+
async callTool(name, args) {
|
|
62
|
+
if (!this._connected) {
|
|
63
|
+
await this.connect();
|
|
64
|
+
}
|
|
65
|
+
const result = await this.client.callTool({
|
|
66
|
+
name,
|
|
67
|
+
arguments: args
|
|
68
|
+
});
|
|
69
|
+
const content = result.content;
|
|
70
|
+
if (!content || content.length === 0) {
|
|
71
|
+
return {};
|
|
72
|
+
}
|
|
73
|
+
const textBlock = content.find((c) => c.type === "text");
|
|
74
|
+
if (!textBlock?.text) {
|
|
75
|
+
return {};
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
return JSON.parse(textBlock.text);
|
|
79
|
+
} catch {
|
|
80
|
+
if (result.isError) {
|
|
81
|
+
throw new Error(`MCP tool error: ${textBlock.text}`);
|
|
82
|
+
}
|
|
83
|
+
return { raw: textBlock.text };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// src/vectorstore.ts
|
|
89
|
+
var QortexVectorStore = class _QortexVectorStore extends VectorStore {
|
|
90
|
+
mcp;
|
|
91
|
+
indexName;
|
|
92
|
+
domain;
|
|
93
|
+
feedbackSource;
|
|
94
|
+
_lastQueryId = null;
|
|
95
|
+
constructor(embeddings, config = {}) {
|
|
96
|
+
super(embeddings, config);
|
|
97
|
+
this.mcp = new QortexMcpClient(config);
|
|
98
|
+
this.indexName = config.indexName ?? "default";
|
|
99
|
+
this.domain = config.domain ?? "default";
|
|
100
|
+
this.feedbackSource = config.feedbackSource ?? "langchain";
|
|
101
|
+
}
|
|
102
|
+
_vectorstoreType() {
|
|
103
|
+
return "qortex";
|
|
104
|
+
}
|
|
105
|
+
/** Ensure the MCP connection is established. */
|
|
106
|
+
async connect() {
|
|
107
|
+
await this.mcp.connect();
|
|
108
|
+
}
|
|
109
|
+
/** Disconnect from the MCP server. */
|
|
110
|
+
async disconnect() {
|
|
111
|
+
await this.mcp.disconnect();
|
|
112
|
+
}
|
|
113
|
+
/** The query_id from the most recent text-level search. */
|
|
114
|
+
get lastQueryId() {
|
|
115
|
+
return this._lastQueryId;
|
|
116
|
+
}
|
|
117
|
+
// ---------------------------------------------------------------------------
|
|
118
|
+
// Index management (MCP passthrough)
|
|
119
|
+
// ---------------------------------------------------------------------------
|
|
120
|
+
/** Create a vector index. Must be called before addVectors/addDocuments. */
|
|
121
|
+
async createIndex(params) {
|
|
122
|
+
const result = await this.mcp.callTool("qortex_vector_create_index", {
|
|
123
|
+
index_name: params.indexName ?? this.indexName,
|
|
124
|
+
dimension: params.dimension,
|
|
125
|
+
metric: params.metric ?? "cosine"
|
|
126
|
+
});
|
|
127
|
+
if (result.error) {
|
|
128
|
+
throw new Error(result.error);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/** Delete a vector index. */
|
|
132
|
+
async deleteIndex(params) {
|
|
133
|
+
const result = await this.mcp.callTool("qortex_vector_delete_index", {
|
|
134
|
+
index_name: params?.indexName ?? this.indexName
|
|
135
|
+
});
|
|
136
|
+
if (result.error) {
|
|
137
|
+
throw new Error(result.error);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/** List all vector indexes. */
|
|
141
|
+
async listIndexes() {
|
|
142
|
+
const result = await this.mcp.callTool(
|
|
143
|
+
"qortex_vector_list_indexes",
|
|
144
|
+
{}
|
|
145
|
+
);
|
|
146
|
+
return result.indexes;
|
|
147
|
+
}
|
|
148
|
+
// ---------------------------------------------------------------------------
|
|
149
|
+
// Abstract method implementations (required by VectorStore)
|
|
150
|
+
// ---------------------------------------------------------------------------
|
|
151
|
+
async addVectors(vectors, documents, options) {
|
|
152
|
+
const metadata = documents.map((doc) => ({
|
|
153
|
+
text: doc.pageContent,
|
|
154
|
+
...doc.metadata
|
|
155
|
+
}));
|
|
156
|
+
const ids = options?.ids ?? documents.map((doc) => doc.id).filter(Boolean);
|
|
157
|
+
const result = await this.mcp.callTool("qortex_vector_upsert", {
|
|
158
|
+
index_name: this.indexName,
|
|
159
|
+
vectors,
|
|
160
|
+
metadata,
|
|
161
|
+
ids: ids.length > 0 ? ids : void 0
|
|
162
|
+
});
|
|
163
|
+
if (result.error) {
|
|
164
|
+
throw new Error(result.error);
|
|
165
|
+
}
|
|
166
|
+
return result.ids;
|
|
167
|
+
}
|
|
168
|
+
async addDocuments(documents, options) {
|
|
169
|
+
const texts = documents.map((doc) => doc.pageContent);
|
|
170
|
+
const vectors = await this.embeddings.embedDocuments(texts);
|
|
171
|
+
return this.addVectors(vectors, documents, options);
|
|
172
|
+
}
|
|
173
|
+
async similaritySearchVectorWithScore(query, k, filter) {
|
|
174
|
+
const result = await this.mcp.callTool("qortex_vector_query", {
|
|
175
|
+
index_name: this.indexName,
|
|
176
|
+
query_vector: query,
|
|
177
|
+
top_k: k,
|
|
178
|
+
filter: filter ?? void 0,
|
|
179
|
+
include_vector: false
|
|
180
|
+
});
|
|
181
|
+
if (result.error) {
|
|
182
|
+
throw new Error(result.error);
|
|
183
|
+
}
|
|
184
|
+
return (result.results ?? []).map((item) => {
|
|
185
|
+
const { text, ...meta } = item.metadata ?? {};
|
|
186
|
+
const doc = new Document({
|
|
187
|
+
pageContent: text ?? "",
|
|
188
|
+
metadata: { ...meta, score: item.score },
|
|
189
|
+
id: item.id
|
|
190
|
+
});
|
|
191
|
+
return [doc, item.score];
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
// ---------------------------------------------------------------------------
|
|
195
|
+
// Overridden methods: graph-enhanced text-level search
|
|
196
|
+
// ---------------------------------------------------------------------------
|
|
197
|
+
async similaritySearch(query, k = 4, filter) {
|
|
198
|
+
const docsAndScores = await this.similaritySearchWithScore(query, k, filter);
|
|
199
|
+
return docsAndScores.map(([doc]) => doc);
|
|
200
|
+
}
|
|
201
|
+
async similaritySearchWithScore(query, k = 4, filter) {
|
|
202
|
+
const domains = filter?.domains ? filter.domains : [this.domain];
|
|
203
|
+
const minConfidence = typeof filter?.min_confidence === "number" ? filter.min_confidence : 0;
|
|
204
|
+
const result = await this.mcp.callTool("qortex_query", {
|
|
205
|
+
context: query,
|
|
206
|
+
domains,
|
|
207
|
+
top_k: k,
|
|
208
|
+
min_confidence: minConfidence,
|
|
209
|
+
mode: "auto"
|
|
210
|
+
});
|
|
211
|
+
this._lastQueryId = result.query_id ?? null;
|
|
212
|
+
return (result.items ?? []).map((item) => {
|
|
213
|
+
const meta = {
|
|
214
|
+
score: item.score,
|
|
215
|
+
domain: item.domain,
|
|
216
|
+
node_id: item.node_id,
|
|
217
|
+
...item.metadata
|
|
218
|
+
};
|
|
219
|
+
if (result.rules?.length) {
|
|
220
|
+
const linkedRules = result.rules.filter((r) => r.source_concepts?.includes(item.node_id)).map((r) => ({
|
|
221
|
+
id: r.id,
|
|
222
|
+
text: r.text,
|
|
223
|
+
relevance: r.relevance
|
|
224
|
+
}));
|
|
225
|
+
if (linkedRules.length > 0) {
|
|
226
|
+
meta.rules = linkedRules;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
const doc = new Document({
|
|
230
|
+
pageContent: item.content,
|
|
231
|
+
metadata: meta,
|
|
232
|
+
id: item.id
|
|
233
|
+
});
|
|
234
|
+
return [doc, item.score];
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
// ---------------------------------------------------------------------------
|
|
238
|
+
// Qortex extras: graph exploration + rules + feedback
|
|
239
|
+
// ---------------------------------------------------------------------------
|
|
240
|
+
/**
|
|
241
|
+
* Explore a concept's graph neighborhood.
|
|
242
|
+
* Use node_id from search results metadata to navigate the graph.
|
|
243
|
+
*/
|
|
244
|
+
async explore(nodeId, depth = 1) {
|
|
245
|
+
const result = await this.mcp.callTool("qortex_explore", {
|
|
246
|
+
node_id: nodeId,
|
|
247
|
+
depth
|
|
248
|
+
});
|
|
249
|
+
if (result.node === null) {
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
return result;
|
|
253
|
+
}
|
|
254
|
+
/** Get projected rules from the knowledge graph. */
|
|
255
|
+
async getRules(options = {}) {
|
|
256
|
+
const result = await this.mcp.callTool("qortex_rules", {
|
|
257
|
+
domains: options.domains ?? void 0,
|
|
258
|
+
concept_ids: options.conceptIds ?? void 0,
|
|
259
|
+
categories: options.categories ?? void 0,
|
|
260
|
+
include_derived: options.includeDerived ?? true,
|
|
261
|
+
min_confidence: options.minConfidence ?? 0
|
|
262
|
+
});
|
|
263
|
+
return result;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Report feedback for retrieved items to improve future retrieval.
|
|
267
|
+
* Accepted items get higher PPR teleportation probability; rejected lower.
|
|
268
|
+
*/
|
|
269
|
+
async feedback(outcomes) {
|
|
270
|
+
if (!this._lastQueryId) {
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
const result = await this.mcp.callTool("qortex_feedback", {
|
|
274
|
+
query_id: this._lastQueryId,
|
|
275
|
+
outcomes,
|
|
276
|
+
source: this.feedbackSource
|
|
277
|
+
});
|
|
278
|
+
return result;
|
|
279
|
+
}
|
|
280
|
+
// ---------------------------------------------------------------------------
|
|
281
|
+
// Static factory methods
|
|
282
|
+
// ---------------------------------------------------------------------------
|
|
283
|
+
static async fromTexts(texts, metadatas, embeddings, config = {}) {
|
|
284
|
+
const store = new _QortexVectorStore(embeddings, config);
|
|
285
|
+
await store.connect();
|
|
286
|
+
const metaArray = Array.isArray(metadatas) ? metadatas : texts.map(() => metadatas);
|
|
287
|
+
const docs = texts.map(
|
|
288
|
+
(text, i) => new Document({
|
|
289
|
+
pageContent: text,
|
|
290
|
+
metadata: metaArray[i]
|
|
291
|
+
})
|
|
292
|
+
);
|
|
293
|
+
await store.addDocuments(docs);
|
|
294
|
+
return store;
|
|
295
|
+
}
|
|
296
|
+
static async fromDocuments(docs, embeddings, config = {}) {
|
|
297
|
+
const store = new _QortexVectorStore(embeddings, config);
|
|
298
|
+
await store.connect();
|
|
299
|
+
await store.addDocuments(docs);
|
|
300
|
+
return store;
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
var QortexEmbeddings = class extends Embeddings {
|
|
304
|
+
model;
|
|
305
|
+
constructor(params) {
|
|
306
|
+
super(params);
|
|
307
|
+
this.model = params.model;
|
|
308
|
+
}
|
|
309
|
+
async embedDocuments(texts) {
|
|
310
|
+
return await this.model.embed(texts);
|
|
311
|
+
}
|
|
312
|
+
async embedQuery(text) {
|
|
313
|
+
const result = await this.model.embed([text]);
|
|
314
|
+
return result[0];
|
|
315
|
+
}
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
export { QortexEmbeddings, QortexMcpClient, QortexVectorStore };
|
|
319
|
+
//# sourceMappingURL=index.js.map
|
|
320
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/client.ts","../src/vectorstore.ts","../src/embeddings.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * MCP client wrapper for communicating with the qortex MCP server.\n *\n * Handles connection lifecycle and tool invocation. The qortex server\n * is spawned as a subprocess (stdio transport) or connected to an\n * existing server.\n */\n\nimport { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\nimport { StdioClientTransport } from \"@modelcontextprotocol/sdk/client/stdio.js\";\n\nexport interface QortexMcpClientConfig {\n /** Command to spawn the qortex MCP server (default: \"uvx\") */\n serverCommand?: string;\n /** Arguments for the server command (default: [\"qortex\", \"mcp-serve\"]) */\n serverArgs?: string[];\n /** Environment variables for the server process */\n serverEnv?: Record<string, string>;\n /** Pre-configured MCP client (skip spawning) */\n mcpClient?: Client;\n}\n\nexport class QortexMcpClient {\n private client: Client | null = null;\n private transport: StdioClientTransport | null = null;\n private config: QortexMcpClientConfig;\n private _connected = false;\n\n constructor(config: QortexMcpClientConfig = {}) {\n this.config = config;\n if (config.mcpClient) {\n this.client = config.mcpClient;\n this._connected = true;\n }\n }\n\n get connected(): boolean {\n return this._connected;\n }\n\n async connect(): Promise<void> {\n if (this._connected) return;\n\n const command = this.config.serverCommand ?? \"uvx\";\n const args = this.config.serverArgs ?? [\"qortex\", \"mcp-serve\"];\n const env: Record<string, string> = {};\n for (const [k, v] of Object.entries(process.env)) {\n if (v !== undefined) env[k] = v;\n }\n if (this.config.serverEnv) {\n Object.assign(env, this.config.serverEnv);\n }\n\n this.transport = new StdioClientTransport({\n command,\n args,\n env,\n });\n\n this.client = new Client(\n { name: \"langchain-qortex\", version: \"0.1.0\" },\n { capabilities: {} },\n );\n\n await this.client.connect(this.transport);\n this._connected = true;\n }\n\n async disconnect(): Promise<void> {\n if (this.transport) {\n await this.transport.close();\n this.transport = null;\n }\n this.client = null;\n this._connected = false;\n }\n\n /**\n * Call a qortex MCP tool and return the parsed result.\n *\n * @param name - Tool name (e.g. \"qortex_vector_query\")\n * @param args - Tool arguments as a plain object\n * @returns Parsed JSON result from the tool\n */\n async callTool(\n name: string,\n args: Record<string, unknown>,\n ): Promise<unknown> {\n if (!this._connected) {\n await this.connect();\n }\n\n const result = await this.client!.callTool({\n name,\n arguments: args,\n });\n\n // MCP tool results come as content blocks\n const content = result.content as Array<{ type: string; text?: string }>;\n if (!content || content.length === 0) {\n return {};\n }\n\n const textBlock = content.find((c) => c.type === \"text\");\n if (!textBlock?.text) {\n return {};\n }\n\n // Handle MCP error responses that aren't valid JSON\n try {\n return JSON.parse(textBlock.text);\n } catch {\n if (result.isError) {\n throw new Error(`MCP tool error: ${textBlock.text}`);\n }\n return { raw: textBlock.text };\n }\n }\n}\n","/**\n * QortexVectorStore: LangChain.js VectorStore backed by qortex knowledge graph.\n *\n * Drop-in replacement for MemoryVectorStore, Chroma, Pinecone, or any\n * LangChain VectorStore. Implements the full VectorStore interface and\n * adds graph structure, rules, and feedback-driven learning.\n *\n * Text-level search (similaritySearch, similaritySearchWithScore) uses\n * qortex's full pipeline: embedding + graph PPR + rules. Vector-level\n * search (similaritySearchVectorWithScore) provides raw LangChain\n * compatibility via qortex_vector_query.\n *\n * Usage:\n * import { QortexVectorStore } from \"@peleke.s/langchain-qortex\";\n *\n * const store = await QortexVectorStore.fromTexts(\n * texts, metadatas, embeddings, { indexName: \"docs\" }\n * );\n * const docs = await store.similaritySearch(\"authentication\", 5);\n * const retriever = store.asRetriever({ k: 10 });\n */\n\nimport { VectorStore } from \"@langchain/core/vectorstores\";\nimport { Document } from \"@langchain/core/documents\";\nimport type { EmbeddingsInterface } from \"@langchain/core/embeddings\";\nimport type { DocumentInterface } from \"@langchain/core/documents\";\nimport { QortexMcpClient, type QortexMcpClientConfig } from \"./client.js\";\nimport type {\n ExploreResult,\n RulesResult,\n FeedbackOutcome,\n FeedbackResult,\n QortexQueryResult,\n QortexRule,\n} from \"./types.js\";\n\nexport interface QortexVectorStoreConfig extends QortexMcpClientConfig {\n /** Index name for vector operations (default: \"default\"). */\n indexName?: string;\n /** Default domain for text-level queries (default: \"default\"). */\n domain?: string;\n /** Source identifier for feedback events (default: \"langchain\"). */\n feedbackSource?: string;\n}\n\nexport class QortexVectorStore extends VectorStore {\n declare FilterType: Record<string, unknown>;\n\n private mcp: QortexMcpClient;\n private indexName: string;\n private domain: string;\n private feedbackSource: string;\n private _lastQueryId: string | null = null;\n\n constructor(\n embeddings: EmbeddingsInterface,\n config: QortexVectorStoreConfig = {},\n ) {\n super(embeddings, config);\n this.mcp = new QortexMcpClient(config);\n this.indexName = config.indexName ?? \"default\";\n this.domain = config.domain ?? \"default\";\n this.feedbackSource = config.feedbackSource ?? \"langchain\";\n }\n\n _vectorstoreType(): string {\n return \"qortex\";\n }\n\n /** Ensure the MCP connection is established. */\n async connect(): Promise<void> {\n await this.mcp.connect();\n }\n\n /** Disconnect from the MCP server. */\n async disconnect(): Promise<void> {\n await this.mcp.disconnect();\n }\n\n /** The query_id from the most recent text-level search. */\n get lastQueryId(): string | null {\n return this._lastQueryId;\n }\n\n // ---------------------------------------------------------------------------\n // Index management (MCP passthrough)\n // ---------------------------------------------------------------------------\n\n /** Create a vector index. Must be called before addVectors/addDocuments. */\n async createIndex(params: {\n indexName?: string;\n dimension: number;\n metric?: \"cosine\" | \"euclidean\" | \"dotproduct\";\n }): Promise<void> {\n const result = (await this.mcp.callTool(\"qortex_vector_create_index\", {\n index_name: params.indexName ?? this.indexName,\n dimension: params.dimension,\n metric: params.metric ?? \"cosine\",\n })) as Record<string, unknown>;\n\n if (result.error) {\n throw new Error(result.error as string);\n }\n }\n\n /** Delete a vector index. */\n async deleteIndex(params?: { indexName?: string }): Promise<void> {\n const result = (await this.mcp.callTool(\"qortex_vector_delete_index\", {\n index_name: params?.indexName ?? this.indexName,\n })) as Record<string, unknown>;\n\n if (result.error) {\n throw new Error(result.error as string);\n }\n }\n\n /** List all vector indexes. */\n async listIndexes(): Promise<string[]> {\n const result = (await this.mcp.callTool(\n \"qortex_vector_list_indexes\",\n {},\n )) as { indexes: string[] };\n return result.indexes;\n }\n\n // ---------------------------------------------------------------------------\n // Abstract method implementations (required by VectorStore)\n // ---------------------------------------------------------------------------\n\n async addVectors(\n vectors: number[][],\n documents: DocumentInterface[],\n options?: { ids?: string[] },\n ): Promise<string[] | void> {\n const metadata = documents.map((doc) => ({\n text: doc.pageContent,\n ...doc.metadata,\n }));\n const ids = options?.ids ?? documents.map((doc) => doc.id).filter(Boolean) as string[];\n\n const result = (await this.mcp.callTool(\"qortex_vector_upsert\", {\n index_name: this.indexName,\n vectors,\n metadata,\n ids: ids.length > 0 ? ids : undefined,\n })) as { ids?: string[]; error?: string };\n\n if (result.error) {\n throw new Error(result.error);\n }\n\n return result.ids;\n }\n\n async addDocuments(\n documents: DocumentInterface[],\n options?: { ids?: string[] },\n ): Promise<string[] | void> {\n const texts = documents.map((doc) => doc.pageContent);\n const vectors = await this.embeddings.embedDocuments(texts);\n return this.addVectors(vectors, documents, options);\n }\n\n async similaritySearchVectorWithScore(\n query: number[],\n k: number,\n filter?: this[\"FilterType\"],\n ): Promise<[DocumentInterface, number][]> {\n const result = (await this.mcp.callTool(\"qortex_vector_query\", {\n index_name: this.indexName,\n query_vector: query,\n top_k: k,\n filter: filter ?? undefined,\n include_vector: false,\n })) as {\n results?: Array<{\n id: string;\n score: number;\n metadata?: Record<string, unknown>;\n }>;\n error?: string;\n };\n\n if (result.error) {\n throw new Error(result.error);\n }\n\n return (result.results ?? []).map((item) => {\n const { text, ...meta } = (item.metadata ?? {}) as Record<string, unknown>;\n const doc = new Document({\n pageContent: (text as string) ?? \"\",\n metadata: { ...meta, score: item.score },\n id: item.id,\n });\n return [doc, item.score] as [DocumentInterface, number];\n });\n }\n\n // ---------------------------------------------------------------------------\n // Overridden methods: graph-enhanced text-level search\n // ---------------------------------------------------------------------------\n\n async similaritySearch(\n query: string,\n k: number = 4,\n filter?: this[\"FilterType\"],\n ): Promise<DocumentInterface[]> {\n const docsAndScores = await this.similaritySearchWithScore(query, k, filter);\n return docsAndScores.map(([doc]) => doc);\n }\n\n async similaritySearchWithScore(\n query: string,\n k: number = 4,\n filter?: this[\"FilterType\"],\n ): Promise<[DocumentInterface, number][]> {\n const domains = filter?.domains\n ? (filter.domains as string[])\n : [this.domain];\n const minConfidence =\n typeof filter?.min_confidence === \"number\" ? filter.min_confidence : 0.0;\n\n const result = (await this.mcp.callTool(\"qortex_query\", {\n context: query,\n domains,\n top_k: k,\n min_confidence: minConfidence,\n mode: \"auto\",\n })) as QortexQueryResult;\n\n this._lastQueryId = result.query_id ?? null;\n\n return (result.items ?? []).map((item) => {\n const meta: Record<string, unknown> = {\n score: item.score,\n domain: item.domain,\n node_id: item.node_id,\n ...item.metadata,\n };\n\n if (result.rules?.length) {\n const linkedRules = result.rules\n .filter((r: QortexRule) => r.source_concepts?.includes(item.node_id))\n .map((r: QortexRule) => ({\n id: r.id,\n text: r.text,\n relevance: r.relevance,\n }));\n if (linkedRules.length > 0) {\n meta.rules = linkedRules;\n }\n }\n\n const doc = new Document({\n pageContent: item.content,\n metadata: meta,\n id: item.id,\n });\n return [doc, item.score] as [DocumentInterface, number];\n });\n }\n\n // ---------------------------------------------------------------------------\n // Qortex extras: graph exploration + rules + feedback\n // ---------------------------------------------------------------------------\n\n /**\n * Explore a concept's graph neighborhood.\n * Use node_id from search results metadata to navigate the graph.\n */\n async explore(\n nodeId: string,\n depth: number = 1,\n ): Promise<ExploreResult | null> {\n const result = (await this.mcp.callTool(\"qortex_explore\", {\n node_id: nodeId,\n depth,\n })) as ExploreResult & { node: unknown };\n\n if (result.node === null) {\n return null;\n }\n\n return result;\n }\n\n /** Get projected rules from the knowledge graph. */\n async getRules(\n options: {\n domains?: string[];\n conceptIds?: string[];\n categories?: string[];\n includeDerived?: boolean;\n minConfidence?: number;\n } = {},\n ): Promise<RulesResult> {\n const result = (await this.mcp.callTool(\"qortex_rules\", {\n domains: options.domains ?? undefined,\n concept_ids: options.conceptIds ?? undefined,\n categories: options.categories ?? undefined,\n include_derived: options.includeDerived ?? true,\n min_confidence: options.minConfidence ?? 0.0,\n })) as RulesResult;\n\n return result;\n }\n\n /**\n * Report feedback for retrieved items to improve future retrieval.\n * Accepted items get higher PPR teleportation probability; rejected lower.\n */\n async feedback(\n outcomes: Record<string, FeedbackOutcome>,\n ): Promise<FeedbackResult | null> {\n if (!this._lastQueryId) {\n return null;\n }\n\n const result = (await this.mcp.callTool(\"qortex_feedback\", {\n query_id: this._lastQueryId,\n outcomes,\n source: this.feedbackSource,\n })) as FeedbackResult;\n\n return result;\n }\n\n // ---------------------------------------------------------------------------\n // Static factory methods\n // ---------------------------------------------------------------------------\n\n static async fromTexts(\n texts: string[],\n metadatas: object[] | object,\n embeddings: EmbeddingsInterface,\n config: QortexVectorStoreConfig = {},\n ): Promise<QortexVectorStore> {\n const store = new QortexVectorStore(embeddings, config);\n await store.connect();\n\n const metaArray = Array.isArray(metadatas)\n ? metadatas\n : texts.map(() => metadatas);\n\n const docs = texts.map(\n (text, i) =>\n new Document({\n pageContent: text,\n metadata: metaArray[i] as Record<string, unknown>,\n }),\n );\n\n await store.addDocuments(docs);\n return store;\n }\n\n static async fromDocuments(\n docs: DocumentInterface[],\n embeddings: EmbeddingsInterface,\n config: QortexVectorStoreConfig = {},\n ): Promise<QortexVectorStore> {\n const store = new QortexVectorStore(embeddings, config);\n await store.connect();\n await store.addDocuments(docs);\n return store;\n }\n}\n","/**\n * QortexEmbeddings: wrap a qortex-style embedding model in LangChain's\n * Embeddings interface.\n *\n * Any object with `.embed(texts) -> number[][]` works. This is a thin\n * utility -- most users will bring their own LangChain embeddings\n * (OpenAI, HuggingFace, etc.) and pass them to QortexVectorStore directly.\n */\n\nimport { Embeddings, type EmbeddingsParams } from \"@langchain/core/embeddings\";\n\nexport interface QortexEmbeddingsParams extends EmbeddingsParams {\n /** Any object with `.embed(texts: string[]) -> Promise<number[][]> | number[][]`. */\n model: { embed(texts: string[]): number[][] | Promise<number[][]> };\n}\n\nexport class QortexEmbeddings extends Embeddings {\n private model: { embed(texts: string[]): number[][] | Promise<number[][]> };\n\n constructor(params: QortexEmbeddingsParams) {\n super(params);\n this.model = params.model;\n }\n\n async embedDocuments(texts: string[]): Promise<number[][]> {\n return await this.model.embed(texts);\n }\n\n async embedQuery(text: string): Promise<number[]> {\n const result = await this.model.embed([text]);\n return result[0];\n }\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@peleke.s/langchain-qortex",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "LangChain.js VectorStore backed by qortex knowledge graph — graph-enhanced retrieval via MCP",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"require": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"default": "./dist/index.cjs"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"./package.json": "./package.json"
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsup",
|
|
23
|
+
"build:watch": "tsup --watch",
|
|
24
|
+
"test": "vitest run",
|
|
25
|
+
"test:watch": "vitest",
|
|
26
|
+
"test:e2e": "vitest run tests/e2e.test.ts",
|
|
27
|
+
"test:dogfood": "vitest run tests/dogfood.test.ts",
|
|
28
|
+
"dogfood": "tsx scripts/dogfood.ts",
|
|
29
|
+
"lint": "tsc --noEmit",
|
|
30
|
+
"prepublishOnly": "npm run build"
|
|
31
|
+
},
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@modelcontextprotocol/sdk": "^1.17.5"
|
|
35
|
+
},
|
|
36
|
+
"peerDependencies": {
|
|
37
|
+
"@langchain/core": ">=0.3.0"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@langchain/core": "^0.3.80",
|
|
41
|
+
"@types/node": "^22.0.0",
|
|
42
|
+
"tsup": "^8.5.0",
|
|
43
|
+
"tsx": "^4.21.0",
|
|
44
|
+
"typescript": "^5.8.0",
|
|
45
|
+
"vitest": "^3.0.0"
|
|
46
|
+
},
|
|
47
|
+
"files": [
|
|
48
|
+
"dist",
|
|
49
|
+
"CHANGELOG.md"
|
|
50
|
+
],
|
|
51
|
+
"homepage": "https://github.com/Peleke/langchain-qortex-js",
|
|
52
|
+
"repository": {
|
|
53
|
+
"type": "git",
|
|
54
|
+
"url": "git+https://github.com/Peleke/langchain-qortex-js.git"
|
|
55
|
+
},
|
|
56
|
+
"bugs": {
|
|
57
|
+
"url": "https://github.com/Peleke/langchain-qortex-js/issues"
|
|
58
|
+
},
|
|
59
|
+
"keywords": [
|
|
60
|
+
"langchain",
|
|
61
|
+
"qortex",
|
|
62
|
+
"vectorstore",
|
|
63
|
+
"knowledge-graph",
|
|
64
|
+
"graphrag",
|
|
65
|
+
"mcp",
|
|
66
|
+
"retrieval",
|
|
67
|
+
"embedding"
|
|
68
|
+
],
|
|
69
|
+
"engines": {
|
|
70
|
+
"node": ">=20.0.0"
|
|
71
|
+
}
|
|
72
|
+
}
|