regen-koi-mcp 1.0.4 → 1.0.6
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 +51 -0
- package/dist/graph_client.d.ts +204 -0
- package/dist/graph_client.d.ts.map +1 -0
- package/dist/graph_client.js +360 -0
- package/dist/graph_client.js.map +1 -0
- package/dist/graph_tool.d.ts +68 -0
- package/dist/graph_tool.d.ts.map +1 -0
- package/dist/graph_tool.js +656 -0
- package/dist/graph_tool.js.map +1 -0
- package/dist/hybrid-client.js +1 -1
- package/dist/hybrid-client.js.map +1 -1
- package/dist/index.js +695 -4
- package/dist/index.js.map +1 -1
- package/dist/query_router.d.ts +81 -0
- package/dist/query_router.d.ts.map +1 -0
- package/dist/query_router.js +205 -0
- package/dist/query_router.js.map +1 -0
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +78 -0
- package/dist/tools.js.map +1 -1
- package/dist/unified_search.d.ts +109 -0
- package/dist/unified_search.d.ts.map +1 -0
- package/dist/unified_search.js +352 -0
- package/dist/unified_search.js.map +1 -0
- package/package.json +3 -1
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified Search - Single-Query Hybrid Search with RRF
|
|
3
|
+
*
|
|
4
|
+
* Combines graph-based entity search and vector semantic search
|
|
5
|
+
* in a SINGLE PostgreSQL query using Reciprocal Rank Fusion (RRF).
|
|
6
|
+
*
|
|
7
|
+
* Key Design Principles:
|
|
8
|
+
* 1. NO parallel database calls - Everything in one SQL query
|
|
9
|
+
* 2. Database does the work - RRF calculated in Postgres
|
|
10
|
+
* 3. Unified stack - AGE + pgvector in same database
|
|
11
|
+
*
|
|
12
|
+
* RRF Formula: score = 1.0 / (k + rank) where k=60
|
|
13
|
+
* This gives higher scores to higher-ranked results, with diminishing returns.
|
|
14
|
+
*/
|
|
15
|
+
export interface UnifiedSearchConfig {
|
|
16
|
+
host: string;
|
|
17
|
+
port: number;
|
|
18
|
+
database: string;
|
|
19
|
+
user?: string;
|
|
20
|
+
password?: string;
|
|
21
|
+
graphName?: string;
|
|
22
|
+
embeddingDimension?: 384 | 512 | 768 | 1024 | 1536 | 3072;
|
|
23
|
+
rrfConstant?: number;
|
|
24
|
+
}
|
|
25
|
+
export interface SearchHit {
|
|
26
|
+
id: string;
|
|
27
|
+
title: string;
|
|
28
|
+
content?: string;
|
|
29
|
+
source: 'graph' | 'vector' | 'module' | 'both';
|
|
30
|
+
graph_rank?: number;
|
|
31
|
+
vector_rank?: number;
|
|
32
|
+
module_rank?: number;
|
|
33
|
+
rrf_score: number;
|
|
34
|
+
final_score: number;
|
|
35
|
+
entity_type?: string;
|
|
36
|
+
file_path?: string;
|
|
37
|
+
line_number?: number;
|
|
38
|
+
docstring?: string;
|
|
39
|
+
}
|
|
40
|
+
export interface ModuleHit {
|
|
41
|
+
name: string;
|
|
42
|
+
repo: string;
|
|
43
|
+
path: string;
|
|
44
|
+
summary?: string;
|
|
45
|
+
entity_count: number;
|
|
46
|
+
score: number;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* UnifiedSearch - Hybrid search combining graph and vector results with RRF
|
|
50
|
+
*/
|
|
51
|
+
export declare class UnifiedSearch {
|
|
52
|
+
private pool;
|
|
53
|
+
private graphName;
|
|
54
|
+
private embeddingDim;
|
|
55
|
+
private rrfK;
|
|
56
|
+
constructor(config: UnifiedSearchConfig);
|
|
57
|
+
/**
|
|
58
|
+
* Close the database connection pool
|
|
59
|
+
*/
|
|
60
|
+
close(): Promise<void>;
|
|
61
|
+
/**
|
|
62
|
+
* Execute unified hybrid search - Single SQL query with RRF
|
|
63
|
+
*
|
|
64
|
+
* @param queryText - Natural language query
|
|
65
|
+
* @param queryEmbedding - Pre-computed query embedding vector
|
|
66
|
+
* @param entityNames - Entity names detected by router (optional, improves graph results)
|
|
67
|
+
* @param limit - Maximum results to return (default: 10)
|
|
68
|
+
*/
|
|
69
|
+
search(queryText: string, queryEmbedding: number[], entityNames?: string[], limit?: number): Promise<SearchHit[]>;
|
|
70
|
+
/**
|
|
71
|
+
* Graph-only search (for entity lookup / relationship queries)
|
|
72
|
+
*/
|
|
73
|
+
graphSearch(entityNames: string[], limit?: number): Promise<SearchHit[]>;
|
|
74
|
+
/**
|
|
75
|
+
* Vector-only search (for conceptual / semantic queries)
|
|
76
|
+
*/
|
|
77
|
+
vectorSearch(queryEmbedding: number[], limit?: number): Promise<SearchHit[]>;
|
|
78
|
+
/**
|
|
79
|
+
* Module-level semantic search (RAPTOR)
|
|
80
|
+
* Searches module summaries by vector similarity
|
|
81
|
+
*/
|
|
82
|
+
moduleSearch(queryEmbedding: number[], limit?: number): Promise<ModuleHit[]>;
|
|
83
|
+
/**
|
|
84
|
+
* Combined search including modules (RAPTOR-enhanced hybrid search)
|
|
85
|
+
* For high-level conceptual queries, searches module summaries first
|
|
86
|
+
*/
|
|
87
|
+
searchWithModules(queryText: string, queryEmbedding: number[], entityNames?: string[], options?: {
|
|
88
|
+
includeModules?: boolean;
|
|
89
|
+
moduleLimit?: number;
|
|
90
|
+
limit?: number;
|
|
91
|
+
}): Promise<{
|
|
92
|
+
hits: SearchHit[];
|
|
93
|
+
moduleHits: ModuleHit[];
|
|
94
|
+
}>;
|
|
95
|
+
/**
|
|
96
|
+
* Get search statistics
|
|
97
|
+
*/
|
|
98
|
+
getStats(): Promise<{
|
|
99
|
+
total_memories: number;
|
|
100
|
+
total_embeddings: number;
|
|
101
|
+
embedding_coverage: number;
|
|
102
|
+
graph_entities: number;
|
|
103
|
+
}>;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Create a default UnifiedSearch instance using environment variables
|
|
107
|
+
*/
|
|
108
|
+
export declare function createUnifiedSearch(): UnifiedSearch;
|
|
109
|
+
//# sourceMappingURL=unified_search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unified_search.d.ts","sourceRoot":"","sources":["../src/unified_search.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified Search - Single-Query Hybrid Search with RRF
|
|
3
|
+
*
|
|
4
|
+
* Combines graph-based entity search and vector semantic search
|
|
5
|
+
* in a SINGLE PostgreSQL query using Reciprocal Rank Fusion (RRF).
|
|
6
|
+
*
|
|
7
|
+
* Key Design Principles:
|
|
8
|
+
* 1. NO parallel database calls - Everything in one SQL query
|
|
9
|
+
* 2. Database does the work - RRF calculated in Postgres
|
|
10
|
+
* 3. Unified stack - AGE + pgvector in same database
|
|
11
|
+
*
|
|
12
|
+
* RRF Formula: score = 1.0 / (k + rank) where k=60
|
|
13
|
+
* This gives higher scores to higher-ranked results, with diminishing returns.
|
|
14
|
+
*/
|
|
15
|
+
import pkg from 'pg';
|
|
16
|
+
const { Pool } = pkg;
|
|
17
|
+
/**
|
|
18
|
+
* UnifiedSearch - Hybrid search combining graph and vector results with RRF
|
|
19
|
+
*/
|
|
20
|
+
export class UnifiedSearch {
|
|
21
|
+
pool;
|
|
22
|
+
graphName;
|
|
23
|
+
embeddingDim;
|
|
24
|
+
rrfK;
|
|
25
|
+
constructor(config) {
|
|
26
|
+
this.pool = new Pool({
|
|
27
|
+
host: config.host,
|
|
28
|
+
port: config.port,
|
|
29
|
+
database: config.database,
|
|
30
|
+
user: config.user,
|
|
31
|
+
password: config.password,
|
|
32
|
+
});
|
|
33
|
+
this.graphName = config.graphName || 'regen_graph';
|
|
34
|
+
this.embeddingDim = `dim_${config.embeddingDimension || 1536}`;
|
|
35
|
+
this.rrfK = config.rrfConstant || 60;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Close the database connection pool
|
|
39
|
+
*/
|
|
40
|
+
async close() {
|
|
41
|
+
await this.pool.end();
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Execute unified hybrid search - Single SQL query with RRF
|
|
45
|
+
*
|
|
46
|
+
* @param queryText - Natural language query
|
|
47
|
+
* @param queryEmbedding - Pre-computed query embedding vector
|
|
48
|
+
* @param entityNames - Entity names detected by router (optional, improves graph results)
|
|
49
|
+
* @param limit - Maximum results to return (default: 10)
|
|
50
|
+
*/
|
|
51
|
+
async search(queryText, queryEmbedding, entityNames = [], limit = 10) {
|
|
52
|
+
const client = await this.pool.connect();
|
|
53
|
+
try {
|
|
54
|
+
// Load AGE extension for this session
|
|
55
|
+
await client.query("LOAD 'age';");
|
|
56
|
+
await client.query(`SET search_path = ag_catalog, "$user", public;`);
|
|
57
|
+
// Build the unified hybrid search query
|
|
58
|
+
const query = `
|
|
59
|
+
WITH graph_hits AS (
|
|
60
|
+
-- Graph: Entity-based search using detected entities
|
|
61
|
+
SELECT
|
|
62
|
+
'graph_' || (e->>'name')::text || '_' || id(e)::text as id,
|
|
63
|
+
(e->>'name')::text as title,
|
|
64
|
+
(e->>'docstring')::text as content,
|
|
65
|
+
(e->>'file_path')::text as file_path,
|
|
66
|
+
(e->>'line_number')::int as line_number,
|
|
67
|
+
'graph' as source,
|
|
68
|
+
labels(e)[0]::text as entity_type,
|
|
69
|
+
ROW_NUMBER() OVER (ORDER BY id(e)) as rank
|
|
70
|
+
FROM cypher('${this.graphName}', $$
|
|
71
|
+
MATCH (e)
|
|
72
|
+
WHERE e.name = ANY($1::text[])
|
|
73
|
+
RETURN e
|
|
74
|
+
$$, $2) as (e agtype)
|
|
75
|
+
WHERE e IS NOT NULL
|
|
76
|
+
LIMIT 10
|
|
77
|
+
),
|
|
78
|
+
vector_hits AS (
|
|
79
|
+
-- Vector: Semantic search on document embeddings
|
|
80
|
+
SELECT
|
|
81
|
+
m.id::text as id,
|
|
82
|
+
COALESCE(m.metadata->>'title', m.rid) as title,
|
|
83
|
+
m.content::text as content,
|
|
84
|
+
m.metadata->>'file_path' as file_path,
|
|
85
|
+
NULL::int as line_number,
|
|
86
|
+
'vector' as source,
|
|
87
|
+
NULL::text as entity_type,
|
|
88
|
+
ROW_NUMBER() OVER (ORDER BY e.${this.embeddingDim} <=> $3::vector) as rank
|
|
89
|
+
FROM koi_memories m
|
|
90
|
+
INNER JOIN koi_embeddings e ON e.memory_id = m.id
|
|
91
|
+
WHERE e.${this.embeddingDim} IS NOT NULL
|
|
92
|
+
AND m.superseded_at IS NULL
|
|
93
|
+
ORDER BY e.${this.embeddingDim} <=> $3::vector
|
|
94
|
+
LIMIT 10
|
|
95
|
+
),
|
|
96
|
+
combined AS (
|
|
97
|
+
-- Combine graph and vector results
|
|
98
|
+
SELECT
|
|
99
|
+
id,
|
|
100
|
+
title,
|
|
101
|
+
content,
|
|
102
|
+
file_path,
|
|
103
|
+
line_number,
|
|
104
|
+
source,
|
|
105
|
+
entity_type,
|
|
106
|
+
rank,
|
|
107
|
+
1.0 / ($4::float + rank) as rrf_score
|
|
108
|
+
FROM graph_hits
|
|
109
|
+
UNION ALL
|
|
110
|
+
SELECT
|
|
111
|
+
id,
|
|
112
|
+
title,
|
|
113
|
+
content,
|
|
114
|
+
file_path,
|
|
115
|
+
line_number,
|
|
116
|
+
source,
|
|
117
|
+
entity_type,
|
|
118
|
+
rank,
|
|
119
|
+
1.0 / ($4::float + rank) as rrf_score
|
|
120
|
+
FROM vector_hits
|
|
121
|
+
),
|
|
122
|
+
aggregated AS (
|
|
123
|
+
-- Aggregate scores for items appearing in both sources
|
|
124
|
+
SELECT
|
|
125
|
+
id,
|
|
126
|
+
MAX(title) as title,
|
|
127
|
+
MAX(content) as content,
|
|
128
|
+
MAX(file_path) as file_path,
|
|
129
|
+
MAX(line_number) as line_number,
|
|
130
|
+
MAX(entity_type) as entity_type,
|
|
131
|
+
CASE
|
|
132
|
+
WHEN COUNT(DISTINCT source) > 1 THEN 'both'
|
|
133
|
+
ELSE MAX(source)
|
|
134
|
+
END as source,
|
|
135
|
+
MAX(CASE WHEN source = 'graph' THEN rank END) as graph_rank,
|
|
136
|
+
MAX(CASE WHEN source = 'vector' THEN rank END) as vector_rank,
|
|
137
|
+
SUM(rrf_score) as final_score
|
|
138
|
+
FROM combined
|
|
139
|
+
GROUP BY id
|
|
140
|
+
)
|
|
141
|
+
SELECT
|
|
142
|
+
id,
|
|
143
|
+
title,
|
|
144
|
+
content,
|
|
145
|
+
file_path,
|
|
146
|
+
line_number,
|
|
147
|
+
entity_type,
|
|
148
|
+
source,
|
|
149
|
+
graph_rank,
|
|
150
|
+
vector_rank,
|
|
151
|
+
final_score,
|
|
152
|
+
final_score as rrf_score -- For backwards compatibility
|
|
153
|
+
FROM aggregated
|
|
154
|
+
ORDER BY final_score DESC
|
|
155
|
+
LIMIT $5
|
|
156
|
+
`;
|
|
157
|
+
// Convert embedding to postgres array format
|
|
158
|
+
const embeddingStr = `[${queryEmbedding.join(',')}]`;
|
|
159
|
+
const result = await client.query(query, [
|
|
160
|
+
entityNames.length > 0 ? entityNames : ['__NONE__'], // Fallback if no entities
|
|
161
|
+
JSON.stringify(entityNames.length > 0 ? entityNames : ['__NONE__']),
|
|
162
|
+
embeddingStr,
|
|
163
|
+
this.rrfK,
|
|
164
|
+
limit
|
|
165
|
+
]);
|
|
166
|
+
return result.rows;
|
|
167
|
+
}
|
|
168
|
+
finally {
|
|
169
|
+
client.release();
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Graph-only search (for entity lookup / relationship queries)
|
|
174
|
+
*/
|
|
175
|
+
async graphSearch(entityNames, limit = 10) {
|
|
176
|
+
const client = await this.pool.connect();
|
|
177
|
+
try {
|
|
178
|
+
await client.query("LOAD 'age';");
|
|
179
|
+
await client.query(`SET search_path = ag_catalog, "$user", public;`);
|
|
180
|
+
const query = `
|
|
181
|
+
SELECT
|
|
182
|
+
'graph_' || (e->>'name')::text || '_' || id(e)::text as id,
|
|
183
|
+
(e->>'name')::text as title,
|
|
184
|
+
(e->>'docstring')::text as content,
|
|
185
|
+
(e->>'file_path')::text as file_path,
|
|
186
|
+
(e->>'line_number')::int as line_number,
|
|
187
|
+
labels(e)[0]::text as entity_type,
|
|
188
|
+
'graph' as source,
|
|
189
|
+
ROW_NUMBER() OVER (ORDER BY id(e)) as graph_rank,
|
|
190
|
+
NULL::int as vector_rank,
|
|
191
|
+
1.0 as rrf_score,
|
|
192
|
+
1.0 as final_score
|
|
193
|
+
FROM cypher('${this.graphName}', $$
|
|
194
|
+
MATCH (e)
|
|
195
|
+
WHERE e.name = ANY($1::text[])
|
|
196
|
+
RETURN e
|
|
197
|
+
$$, $2) as (e agtype)
|
|
198
|
+
WHERE e IS NOT NULL
|
|
199
|
+
LIMIT $3
|
|
200
|
+
`;
|
|
201
|
+
const result = await client.query(query, [
|
|
202
|
+
entityNames,
|
|
203
|
+
JSON.stringify(entityNames),
|
|
204
|
+
limit
|
|
205
|
+
]);
|
|
206
|
+
return result.rows;
|
|
207
|
+
}
|
|
208
|
+
finally {
|
|
209
|
+
client.release();
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Vector-only search (for conceptual / semantic queries)
|
|
214
|
+
*/
|
|
215
|
+
async vectorSearch(queryEmbedding, limit = 10) {
|
|
216
|
+
const embeddingStr = `[${queryEmbedding.join(',')}]`;
|
|
217
|
+
const query = `
|
|
218
|
+
SELECT
|
|
219
|
+
m.id::text as id,
|
|
220
|
+
COALESCE(m.metadata->>'title', m.rid) as title,
|
|
221
|
+
m.content::text as content,
|
|
222
|
+
m.metadata->>'file_path' as file_path,
|
|
223
|
+
NULL::int as line_number,
|
|
224
|
+
NULL::text as entity_type,
|
|
225
|
+
'vector' as source,
|
|
226
|
+
NULL::int as graph_rank,
|
|
227
|
+
ROW_NUMBER() OVER (ORDER BY e.${this.embeddingDim} <=> $1::vector) as vector_rank,
|
|
228
|
+
(1.0 - (e.${this.embeddingDim} <=> $1::vector)) as rrf_score,
|
|
229
|
+
(1.0 - (e.${this.embeddingDim} <=> $1::vector)) as final_score
|
|
230
|
+
FROM koi_memories m
|
|
231
|
+
INNER JOIN koi_embeddings e ON e.memory_id = m.id
|
|
232
|
+
WHERE e.${this.embeddingDim} IS NOT NULL
|
|
233
|
+
AND m.superseded_at IS NULL
|
|
234
|
+
ORDER BY e.${this.embeddingDim} <=> $1::vector
|
|
235
|
+
LIMIT $2
|
|
236
|
+
`;
|
|
237
|
+
const result = await this.pool.query(query, [embeddingStr, limit]);
|
|
238
|
+
return result.rows;
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Module-level semantic search (RAPTOR)
|
|
242
|
+
* Searches module summaries by vector similarity
|
|
243
|
+
*/
|
|
244
|
+
async moduleSearch(queryEmbedding, limit = 5) {
|
|
245
|
+
const client = await this.pool.connect();
|
|
246
|
+
try {
|
|
247
|
+
await client.query("LOAD 'age';");
|
|
248
|
+
await client.query(`SET search_path = ag_catalog, "$user", public;`);
|
|
249
|
+
const embeddingStr = `[${queryEmbedding.join(',')}]`;
|
|
250
|
+
// Search module summaries stored in koi_memories with type='module_summary'
|
|
251
|
+
const query = `
|
|
252
|
+
SELECT
|
|
253
|
+
m.rid,
|
|
254
|
+
m.content as summary,
|
|
255
|
+
m.metadata->>'repo' as repo,
|
|
256
|
+
m.metadata->>'path' as path,
|
|
257
|
+
m.metadata->>'title' as title,
|
|
258
|
+
COALESCE((m.metadata->>'entity_count')::int, 0) as entity_count,
|
|
259
|
+
(1.0 - (e.${this.embeddingDim} <=> $1::vector)) as score
|
|
260
|
+
FROM koi_memories m
|
|
261
|
+
INNER JOIN koi_embeddings e ON e.memory_id = m.id
|
|
262
|
+
WHERE e.${this.embeddingDim} IS NOT NULL
|
|
263
|
+
AND m.superseded_at IS NULL
|
|
264
|
+
AND m.metadata->>'type' = 'module_summary'
|
|
265
|
+
ORDER BY e.${this.embeddingDim} <=> $1::vector
|
|
266
|
+
LIMIT $2
|
|
267
|
+
`;
|
|
268
|
+
const result = await client.query(query, [embeddingStr, limit]);
|
|
269
|
+
return result.rows.map(row => ({
|
|
270
|
+
name: row.rid.replace('module:', '').split('/').pop() || row.title,
|
|
271
|
+
repo: row.repo || row.rid.split('/')[0]?.replace('module:', ''),
|
|
272
|
+
path: row.path || '',
|
|
273
|
+
summary: row.summary,
|
|
274
|
+
entity_count: row.entity_count,
|
|
275
|
+
score: parseFloat(row.score) || 0
|
|
276
|
+
}));
|
|
277
|
+
}
|
|
278
|
+
finally {
|
|
279
|
+
client.release();
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Combined search including modules (RAPTOR-enhanced hybrid search)
|
|
284
|
+
* For high-level conceptual queries, searches module summaries first
|
|
285
|
+
*/
|
|
286
|
+
async searchWithModules(queryText, queryEmbedding, entityNames = [], options = {}) {
|
|
287
|
+
const { includeModules = true, moduleLimit = 3, limit = 10 } = options;
|
|
288
|
+
// Run standard search
|
|
289
|
+
const hits = await this.search(queryText, queryEmbedding, entityNames, limit);
|
|
290
|
+
// Also search modules if enabled
|
|
291
|
+
let moduleHits = [];
|
|
292
|
+
if (includeModules) {
|
|
293
|
+
try {
|
|
294
|
+
moduleHits = await this.moduleSearch(queryEmbedding, moduleLimit);
|
|
295
|
+
}
|
|
296
|
+
catch (error) {
|
|
297
|
+
// Module search is optional - if it fails, just return empty
|
|
298
|
+
console.error('[UnifiedSearch] Module search failed:', error);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return { hits, moduleHits };
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Get search statistics
|
|
305
|
+
*/
|
|
306
|
+
async getStats() {
|
|
307
|
+
const client = await this.pool.connect();
|
|
308
|
+
try {
|
|
309
|
+
await client.query("LOAD 'age';");
|
|
310
|
+
await client.query(`SET search_path = ag_catalog, "$user", public;`);
|
|
311
|
+
const queries = await Promise.all([
|
|
312
|
+
this.pool.query('SELECT COUNT(*) as count FROM koi_memories WHERE superseded_at IS NULL'),
|
|
313
|
+
this.pool.query(`SELECT COUNT(*) as count FROM koi_embeddings WHERE ${this.embeddingDim} IS NOT NULL`),
|
|
314
|
+
client.query(`
|
|
315
|
+
SELECT COUNT(*) as count
|
|
316
|
+
FROM cypher('${this.graphName}', $$
|
|
317
|
+
MATCH (e)
|
|
318
|
+
RETURN count(e)
|
|
319
|
+
$$) as (count agtype)
|
|
320
|
+
`)
|
|
321
|
+
]);
|
|
322
|
+
const total_memories = parseInt(queries[0].rows[0].count);
|
|
323
|
+
const total_embeddings = parseInt(queries[1].rows[0].count);
|
|
324
|
+
const graph_entities = parseInt(queries[2].rows[0].count);
|
|
325
|
+
return {
|
|
326
|
+
total_memories,
|
|
327
|
+
total_embeddings,
|
|
328
|
+
embedding_coverage: total_memories > 0 ? total_embeddings / total_memories : 0,
|
|
329
|
+
graph_entities
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
finally {
|
|
333
|
+
client.release();
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Create a default UnifiedSearch instance using environment variables
|
|
339
|
+
*/
|
|
340
|
+
export function createUnifiedSearch() {
|
|
341
|
+
return new UnifiedSearch({
|
|
342
|
+
host: process.env.GRAPH_DB_HOST || 'localhost',
|
|
343
|
+
port: parseInt(process.env.GRAPH_DB_PORT || '5432'),
|
|
344
|
+
database: process.env.GRAPH_DB_NAME || 'eliza',
|
|
345
|
+
user: process.env.GRAPH_DB_USER,
|
|
346
|
+
password: process.env.GRAPH_DB_PASSWORD,
|
|
347
|
+
graphName: process.env.GRAPH_NAME || 'regen_graph',
|
|
348
|
+
embeddingDimension: parseInt(process.env.EMBEDDING_DIM || '1536'),
|
|
349
|
+
rrfConstant: parseInt(process.env.RRF_K || '60'),
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
//# sourceMappingURL=unified_search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unified_search.js","sourceRoot":"","sources":["../src/unified_search.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "regen-koi-mcp",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "MCP server for accessing Regen Network's KOI (Knowledge Organization Infrastructure) system",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -47,8 +47,10 @@
|
|
|
47
47
|
"homepage": "https://github.com/gaiaaiagent/regen-koi-mcp#readme",
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"@modelcontextprotocol/sdk": "^1.20.0",
|
|
50
|
+
"@types/pg": "^8.15.6",
|
|
50
51
|
"axios": "^1.7.7",
|
|
51
52
|
"dotenv": "^16.4.5",
|
|
53
|
+
"pg": "^8.16.3",
|
|
52
54
|
"regen-koi-mcp": "^1.0.0"
|
|
53
55
|
},
|
|
54
56
|
"devDependencies": {
|