praisonai 1.2.0 → 1.2.2
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/dist/cache/index.d.ts +78 -0
- package/dist/cache/index.js +235 -0
- package/dist/db/postgres.d.ts +84 -0
- package/dist/db/postgres.js +266 -0
- package/dist/db/redis.d.ts +109 -0
- package/dist/db/redis.js +307 -0
- package/dist/db/sqlite.d.ts +66 -0
- package/dist/db/sqlite.js +339 -0
- package/dist/events/index.d.ts +84 -0
- package/dist/events/index.js +153 -0
- package/dist/index.d.ts +12 -1
- package/dist/index.js +88 -2
- package/dist/integrations/index.d.ts +7 -0
- package/dist/integrations/index.js +26 -0
- package/dist/integrations/observability/base.d.ts +123 -0
- package/dist/integrations/observability/base.js +183 -0
- package/dist/integrations/observability/index.d.ts +8 -0
- package/dist/integrations/observability/index.js +29 -0
- package/dist/integrations/observability/langfuse.d.ts +32 -0
- package/dist/integrations/observability/langfuse.js +174 -0
- package/dist/integrations/vector/base.d.ts +110 -0
- package/dist/integrations/vector/base.js +158 -0
- package/dist/integrations/vector/chroma.d.ts +25 -0
- package/dist/integrations/vector/chroma.js +143 -0
- package/dist/integrations/vector/index.d.ts +14 -0
- package/dist/integrations/vector/index.js +37 -0
- package/dist/integrations/vector/pinecone.d.ts +28 -0
- package/dist/integrations/vector/pinecone.js +172 -0
- package/dist/integrations/vector/qdrant.d.ts +25 -0
- package/dist/integrations/vector/qdrant.js +146 -0
- package/dist/integrations/vector/weaviate.d.ts +30 -0
- package/dist/integrations/vector/weaviate.js +206 -0
- package/dist/integrations/voice/base.d.ts +76 -0
- package/dist/integrations/voice/base.js +168 -0
- package/dist/integrations/voice/index.d.ts +6 -0
- package/dist/integrations/voice/index.js +26 -0
- package/dist/knowledge/graph-rag.d.ts +125 -0
- package/dist/knowledge/graph-rag.js +289 -0
- package/dist/knowledge/index.d.ts +2 -0
- package/dist/knowledge/index.js +18 -0
- package/dist/knowledge/reranker.d.ts +86 -0
- package/dist/knowledge/reranker.js +196 -0
- package/dist/tools/arxivTools.d.ts +19 -6
- package/dist/tools/arxivTools.js +13 -7
- package/dist/tools/base.d.ts +97 -0
- package/dist/tools/base.js +147 -0
- package/dist/tools/index.d.ts +1 -11
- package/dist/tools/index.js +7 -11
- package/dist/tools/mcpSse.d.ts +5 -3
- package/dist/tools/mcpSse.js +6 -4
- package/dist/workflows/yaml-parser.d.ts +48 -0
- package/dist/workflows/yaml-parser.js +304 -0
- package/package.json +1 -1
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Base Voice - Abstract class for voice/TTS integrations
|
|
4
|
+
* Matches mastra's MastraVoice pattern
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.ElevenLabsVoiceProvider = exports.OpenAIVoiceProvider = exports.BaseVoiceProvider = void 0;
|
|
8
|
+
exports.createOpenAIVoice = createOpenAIVoice;
|
|
9
|
+
exports.createElevenLabsVoice = createElevenLabsVoice;
|
|
10
|
+
/**
|
|
11
|
+
* Abstract base class for voice providers
|
|
12
|
+
*/
|
|
13
|
+
class BaseVoiceProvider {
|
|
14
|
+
constructor(config) {
|
|
15
|
+
this.name = config.name || 'VoiceProvider';
|
|
16
|
+
this.apiKey = config.apiKey;
|
|
17
|
+
this.defaultVoice = config.voice;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
exports.BaseVoiceProvider = BaseVoiceProvider;
|
|
21
|
+
/**
|
|
22
|
+
* OpenAI Voice Provider (TTS and Whisper)
|
|
23
|
+
*/
|
|
24
|
+
class OpenAIVoiceProvider extends BaseVoiceProvider {
|
|
25
|
+
constructor(config = {}) {
|
|
26
|
+
super({ ...config, name: 'OpenAIVoice' });
|
|
27
|
+
this.apiKey = config.apiKey || process.env.OPENAI_API_KEY;
|
|
28
|
+
this.defaultVoice = config.voice || 'alloy';
|
|
29
|
+
this.baseUrl = 'https://api.openai.com/v1';
|
|
30
|
+
}
|
|
31
|
+
async speak(text, options) {
|
|
32
|
+
if (!this.apiKey) {
|
|
33
|
+
throw new Error('OpenAI API key required for TTS');
|
|
34
|
+
}
|
|
35
|
+
const response = await fetch(`${this.baseUrl}/audio/speech`, {
|
|
36
|
+
method: 'POST',
|
|
37
|
+
headers: {
|
|
38
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
39
|
+
'Content-Type': 'application/json'
|
|
40
|
+
},
|
|
41
|
+
body: JSON.stringify({
|
|
42
|
+
model: 'tts-1',
|
|
43
|
+
input: text,
|
|
44
|
+
voice: options?.voice || this.defaultVoice,
|
|
45
|
+
speed: options?.speed || 1.0,
|
|
46
|
+
response_format: options?.format || 'mp3'
|
|
47
|
+
})
|
|
48
|
+
});
|
|
49
|
+
if (!response.ok) {
|
|
50
|
+
const error = await response.text();
|
|
51
|
+
throw new Error(`OpenAI TTS error: ${response.status} - ${error}`);
|
|
52
|
+
}
|
|
53
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
54
|
+
return Buffer.from(arrayBuffer);
|
|
55
|
+
}
|
|
56
|
+
async listen(audio, options) {
|
|
57
|
+
if (!this.apiKey) {
|
|
58
|
+
throw new Error('OpenAI API key required for Whisper');
|
|
59
|
+
}
|
|
60
|
+
const formData = new FormData();
|
|
61
|
+
// Convert Buffer to Blob
|
|
62
|
+
const audioBlob = audio instanceof Buffer
|
|
63
|
+
? new Blob([new Uint8Array(audio)], { type: 'audio/mp3' })
|
|
64
|
+
: audio;
|
|
65
|
+
formData.append('file', audioBlob, 'audio.mp3');
|
|
66
|
+
formData.append('model', options?.model || 'whisper-1');
|
|
67
|
+
if (options?.language) {
|
|
68
|
+
formData.append('language', options.language);
|
|
69
|
+
}
|
|
70
|
+
const response = await fetch(`${this.baseUrl}/audio/transcriptions`, {
|
|
71
|
+
method: 'POST',
|
|
72
|
+
headers: {
|
|
73
|
+
'Authorization': `Bearer ${this.apiKey}`
|
|
74
|
+
},
|
|
75
|
+
body: formData
|
|
76
|
+
});
|
|
77
|
+
if (!response.ok) {
|
|
78
|
+
const error = await response.text();
|
|
79
|
+
throw new Error(`OpenAI Whisper error: ${response.status} - ${error}`);
|
|
80
|
+
}
|
|
81
|
+
const result = await response.json();
|
|
82
|
+
return result.text;
|
|
83
|
+
}
|
|
84
|
+
async getSpeakers() {
|
|
85
|
+
return [
|
|
86
|
+
{ id: 'alloy', name: 'Alloy', gender: 'neutral' },
|
|
87
|
+
{ id: 'echo', name: 'Echo', gender: 'male' },
|
|
88
|
+
{ id: 'fable', name: 'Fable', gender: 'neutral' },
|
|
89
|
+
{ id: 'onyx', name: 'Onyx', gender: 'male' },
|
|
90
|
+
{ id: 'nova', name: 'Nova', gender: 'female' },
|
|
91
|
+
{ id: 'shimmer', name: 'Shimmer', gender: 'female' }
|
|
92
|
+
];
|
|
93
|
+
}
|
|
94
|
+
async isAvailable() {
|
|
95
|
+
return !!this.apiKey;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
exports.OpenAIVoiceProvider = OpenAIVoiceProvider;
|
|
99
|
+
/**
|
|
100
|
+
* ElevenLabs Voice Provider
|
|
101
|
+
*/
|
|
102
|
+
class ElevenLabsVoiceProvider extends BaseVoiceProvider {
|
|
103
|
+
constructor(config = {}) {
|
|
104
|
+
super({ ...config, name: 'ElevenLabsVoice' });
|
|
105
|
+
this.apiKey = config.apiKey || process.env.ELEVENLABS_API_KEY;
|
|
106
|
+
this.baseUrl = 'https://api.elevenlabs.io/v1';
|
|
107
|
+
}
|
|
108
|
+
async speak(text, options) {
|
|
109
|
+
if (!this.apiKey) {
|
|
110
|
+
throw new Error('ElevenLabs API key required');
|
|
111
|
+
}
|
|
112
|
+
const voiceId = options?.voice || this.defaultVoice || '21m00Tcm4TlvDq8ikWAM'; // Rachel
|
|
113
|
+
const response = await fetch(`${this.baseUrl}/text-to-speech/${voiceId}`, {
|
|
114
|
+
method: 'POST',
|
|
115
|
+
headers: {
|
|
116
|
+
'xi-api-key': this.apiKey,
|
|
117
|
+
'Content-Type': 'application/json'
|
|
118
|
+
},
|
|
119
|
+
body: JSON.stringify({
|
|
120
|
+
text,
|
|
121
|
+
model_id: 'eleven_monolingual_v1',
|
|
122
|
+
voice_settings: {
|
|
123
|
+
stability: 0.5,
|
|
124
|
+
similarity_boost: 0.5
|
|
125
|
+
}
|
|
126
|
+
})
|
|
127
|
+
});
|
|
128
|
+
if (!response.ok) {
|
|
129
|
+
const error = await response.text();
|
|
130
|
+
throw new Error(`ElevenLabs error: ${response.status} - ${error}`);
|
|
131
|
+
}
|
|
132
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
133
|
+
return Buffer.from(arrayBuffer);
|
|
134
|
+
}
|
|
135
|
+
async listen(_audio, _options) {
|
|
136
|
+
throw new Error('ElevenLabs does not support speech-to-text');
|
|
137
|
+
}
|
|
138
|
+
async getSpeakers() {
|
|
139
|
+
if (!this.apiKey) {
|
|
140
|
+
return [];
|
|
141
|
+
}
|
|
142
|
+
const response = await fetch(`${this.baseUrl}/voices`, {
|
|
143
|
+
headers: {
|
|
144
|
+
'xi-api-key': this.apiKey
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
if (!response.ok) {
|
|
148
|
+
return [];
|
|
149
|
+
}
|
|
150
|
+
const data = await response.json();
|
|
151
|
+
return (data.voices || []).map((v) => ({
|
|
152
|
+
id: v.voice_id,
|
|
153
|
+
name: v.name,
|
|
154
|
+
gender: v.labels?.gender
|
|
155
|
+
}));
|
|
156
|
+
}
|
|
157
|
+
async isAvailable() {
|
|
158
|
+
return !!this.apiKey;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
exports.ElevenLabsVoiceProvider = ElevenLabsVoiceProvider;
|
|
162
|
+
// Factory functions
|
|
163
|
+
function createOpenAIVoice(config) {
|
|
164
|
+
return new OpenAIVoiceProvider(config);
|
|
165
|
+
}
|
|
166
|
+
function createElevenLabsVoice(config) {
|
|
167
|
+
return new ElevenLabsVoiceProvider(config);
|
|
168
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Voice Integrations
|
|
4
|
+
* Provides adapters for TTS and STT services
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
18
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
19
|
+
};
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
exports.createElevenLabsVoice = exports.createOpenAIVoice = void 0;
|
|
22
|
+
__exportStar(require("./base"), exports);
|
|
23
|
+
// Re-export factory functions
|
|
24
|
+
var base_1 = require("./base");
|
|
25
|
+
Object.defineProperty(exports, "createOpenAIVoice", { enumerable: true, get: function () { return base_1.createOpenAIVoice; } });
|
|
26
|
+
Object.defineProperty(exports, "createElevenLabsVoice", { enumerable: true, get: function () { return base_1.createElevenLabsVoice; } });
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Graph RAG - Graph-based Retrieval Augmented Generation
|
|
3
|
+
* Inspired by mastra's graph-rag module
|
|
4
|
+
*/
|
|
5
|
+
export interface GraphNode {
|
|
6
|
+
id: string;
|
|
7
|
+
type: string;
|
|
8
|
+
content: string;
|
|
9
|
+
metadata?: Record<string, any>;
|
|
10
|
+
embedding?: number[];
|
|
11
|
+
}
|
|
12
|
+
export interface GraphEdge {
|
|
13
|
+
id: string;
|
|
14
|
+
source: string;
|
|
15
|
+
target: string;
|
|
16
|
+
type: string;
|
|
17
|
+
weight?: number;
|
|
18
|
+
metadata?: Record<string, any>;
|
|
19
|
+
}
|
|
20
|
+
export interface GraphQueryResult {
|
|
21
|
+
nodes: GraphNode[];
|
|
22
|
+
edges: GraphEdge[];
|
|
23
|
+
paths: GraphNode[][];
|
|
24
|
+
score: number;
|
|
25
|
+
}
|
|
26
|
+
export interface GraphRAGConfig {
|
|
27
|
+
maxDepth?: number;
|
|
28
|
+
maxNodes?: number;
|
|
29
|
+
minScore?: number;
|
|
30
|
+
includeEdges?: boolean;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* In-memory Graph Store for Graph RAG
|
|
34
|
+
*/
|
|
35
|
+
export declare class GraphStore {
|
|
36
|
+
private nodes;
|
|
37
|
+
private edges;
|
|
38
|
+
private adjacencyList;
|
|
39
|
+
private reverseAdjacencyList;
|
|
40
|
+
/**
|
|
41
|
+
* Add a node to the graph
|
|
42
|
+
*/
|
|
43
|
+
addNode(node: GraphNode): void;
|
|
44
|
+
/**
|
|
45
|
+
* Add an edge to the graph
|
|
46
|
+
*/
|
|
47
|
+
addEdge(edge: GraphEdge): void;
|
|
48
|
+
/**
|
|
49
|
+
* Get a node by ID
|
|
50
|
+
*/
|
|
51
|
+
getNode(id: string): GraphNode | undefined;
|
|
52
|
+
/**
|
|
53
|
+
* Get all nodes
|
|
54
|
+
*/
|
|
55
|
+
getAllNodes(): GraphNode[];
|
|
56
|
+
/**
|
|
57
|
+
* Get neighbors of a node
|
|
58
|
+
*/
|
|
59
|
+
getNeighbors(nodeId: string, direction?: 'outgoing' | 'incoming' | 'both'): GraphNode[];
|
|
60
|
+
/**
|
|
61
|
+
* Get edges between two nodes
|
|
62
|
+
*/
|
|
63
|
+
getEdgesBetween(sourceId: string, targetId: string): GraphEdge[];
|
|
64
|
+
/**
|
|
65
|
+
* Find paths between two nodes using BFS
|
|
66
|
+
*/
|
|
67
|
+
findPaths(startId: string, endId: string, maxDepth?: number): GraphNode[][];
|
|
68
|
+
/**
|
|
69
|
+
* Get subgraph around a node
|
|
70
|
+
*/
|
|
71
|
+
getSubgraph(nodeId: string, depth?: number): {
|
|
72
|
+
nodes: GraphNode[];
|
|
73
|
+
edges: GraphEdge[];
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* Clear the graph
|
|
77
|
+
*/
|
|
78
|
+
clear(): void;
|
|
79
|
+
/**
|
|
80
|
+
* Get graph statistics
|
|
81
|
+
*/
|
|
82
|
+
getStats(): {
|
|
83
|
+
nodeCount: number;
|
|
84
|
+
edgeCount: number;
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Graph RAG - Combines graph traversal with vector similarity
|
|
89
|
+
*/
|
|
90
|
+
export declare class GraphRAG {
|
|
91
|
+
private graphStore;
|
|
92
|
+
private embedFn?;
|
|
93
|
+
constructor(config?: {
|
|
94
|
+
embedFn?: (text: string) => Promise<number[]>;
|
|
95
|
+
});
|
|
96
|
+
/**
|
|
97
|
+
* Add a document as a node
|
|
98
|
+
*/
|
|
99
|
+
addDocument(doc: {
|
|
100
|
+
id: string;
|
|
101
|
+
content: string;
|
|
102
|
+
type?: string;
|
|
103
|
+
metadata?: Record<string, any>;
|
|
104
|
+
}): Promise<void>;
|
|
105
|
+
/**
|
|
106
|
+
* Add a relationship between documents
|
|
107
|
+
*/
|
|
108
|
+
addRelationship(sourceId: string, targetId: string, type: string, weight?: number): void;
|
|
109
|
+
/**
|
|
110
|
+
* Query the graph with a natural language query
|
|
111
|
+
*/
|
|
112
|
+
query(query: string, config?: GraphRAGConfig): Promise<GraphQueryResult>;
|
|
113
|
+
/**
|
|
114
|
+
* Get context string from query results
|
|
115
|
+
*/
|
|
116
|
+
getContext(result: GraphQueryResult): string;
|
|
117
|
+
/**
|
|
118
|
+
* Get the underlying graph store
|
|
119
|
+
*/
|
|
120
|
+
getGraphStore(): GraphStore;
|
|
121
|
+
private cosineSimilarity;
|
|
122
|
+
}
|
|
123
|
+
export declare function createGraphRAG(config?: {
|
|
124
|
+
embedFn?: (text: string) => Promise<number[]>;
|
|
125
|
+
}): GraphRAG;
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Graph RAG - Graph-based Retrieval Augmented Generation
|
|
4
|
+
* Inspired by mastra's graph-rag module
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.GraphRAG = exports.GraphStore = void 0;
|
|
8
|
+
exports.createGraphRAG = createGraphRAG;
|
|
9
|
+
/**
|
|
10
|
+
* In-memory Graph Store for Graph RAG
|
|
11
|
+
*/
|
|
12
|
+
class GraphStore {
|
|
13
|
+
constructor() {
|
|
14
|
+
this.nodes = new Map();
|
|
15
|
+
this.edges = new Map();
|
|
16
|
+
this.adjacencyList = new Map();
|
|
17
|
+
this.reverseAdjacencyList = new Map();
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Add a node to the graph
|
|
21
|
+
*/
|
|
22
|
+
addNode(node) {
|
|
23
|
+
this.nodes.set(node.id, node);
|
|
24
|
+
if (!this.adjacencyList.has(node.id)) {
|
|
25
|
+
this.adjacencyList.set(node.id, new Set());
|
|
26
|
+
}
|
|
27
|
+
if (!this.reverseAdjacencyList.has(node.id)) {
|
|
28
|
+
this.reverseAdjacencyList.set(node.id, new Set());
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Add an edge to the graph
|
|
33
|
+
*/
|
|
34
|
+
addEdge(edge) {
|
|
35
|
+
this.edges.set(edge.id, edge);
|
|
36
|
+
// Update adjacency lists
|
|
37
|
+
if (!this.adjacencyList.has(edge.source)) {
|
|
38
|
+
this.adjacencyList.set(edge.source, new Set());
|
|
39
|
+
}
|
|
40
|
+
this.adjacencyList.get(edge.source).add(edge.target);
|
|
41
|
+
if (!this.reverseAdjacencyList.has(edge.target)) {
|
|
42
|
+
this.reverseAdjacencyList.set(edge.target, new Set());
|
|
43
|
+
}
|
|
44
|
+
this.reverseAdjacencyList.get(edge.target).add(edge.source);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Get a node by ID
|
|
48
|
+
*/
|
|
49
|
+
getNode(id) {
|
|
50
|
+
return this.nodes.get(id);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Get all nodes
|
|
54
|
+
*/
|
|
55
|
+
getAllNodes() {
|
|
56
|
+
return Array.from(this.nodes.values());
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Get neighbors of a node
|
|
60
|
+
*/
|
|
61
|
+
getNeighbors(nodeId, direction = 'both') {
|
|
62
|
+
const neighborIds = new Set();
|
|
63
|
+
if (direction === 'outgoing' || direction === 'both') {
|
|
64
|
+
const outgoing = this.adjacencyList.get(nodeId);
|
|
65
|
+
if (outgoing) {
|
|
66
|
+
outgoing.forEach(id => neighborIds.add(id));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (direction === 'incoming' || direction === 'both') {
|
|
70
|
+
const incoming = this.reverseAdjacencyList.get(nodeId);
|
|
71
|
+
if (incoming) {
|
|
72
|
+
incoming.forEach(id => neighborIds.add(id));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return Array.from(neighborIds)
|
|
76
|
+
.map(id => this.nodes.get(id))
|
|
77
|
+
.filter((n) => n !== undefined);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Get edges between two nodes
|
|
81
|
+
*/
|
|
82
|
+
getEdgesBetween(sourceId, targetId) {
|
|
83
|
+
return Array.from(this.edges.values()).filter(e => (e.source === sourceId && e.target === targetId) ||
|
|
84
|
+
(e.source === targetId && e.target === sourceId));
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Find paths between two nodes using BFS
|
|
88
|
+
*/
|
|
89
|
+
findPaths(startId, endId, maxDepth = 3) {
|
|
90
|
+
const paths = [];
|
|
91
|
+
const queue = [
|
|
92
|
+
{ path: [startId], visited: new Set([startId]) }
|
|
93
|
+
];
|
|
94
|
+
while (queue.length > 0) {
|
|
95
|
+
const { path, visited } = queue.shift();
|
|
96
|
+
const currentId = path[path.length - 1];
|
|
97
|
+
if (currentId === endId) {
|
|
98
|
+
paths.push(path.map(id => this.nodes.get(id)).filter(n => n));
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
if (path.length >= maxDepth) {
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
const neighbors = this.adjacencyList.get(currentId) || new Set();
|
|
105
|
+
for (const neighborId of neighbors) {
|
|
106
|
+
if (!visited.has(neighborId)) {
|
|
107
|
+
const newVisited = new Set(visited);
|
|
108
|
+
newVisited.add(neighborId);
|
|
109
|
+
queue.push({ path: [...path, neighborId], visited: newVisited });
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return paths;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Get subgraph around a node
|
|
117
|
+
*/
|
|
118
|
+
getSubgraph(nodeId, depth = 2) {
|
|
119
|
+
const visitedNodes = new Set();
|
|
120
|
+
const resultEdges = [];
|
|
121
|
+
const queue = [{ id: nodeId, currentDepth: 0 }];
|
|
122
|
+
while (queue.length > 0) {
|
|
123
|
+
const { id, currentDepth } = queue.shift();
|
|
124
|
+
if (visitedNodes.has(id) || currentDepth > depth) {
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
visitedNodes.add(id);
|
|
128
|
+
if (currentDepth < depth) {
|
|
129
|
+
const neighbors = this.getNeighbors(id);
|
|
130
|
+
for (const neighbor of neighbors) {
|
|
131
|
+
if (!visitedNodes.has(neighbor.id)) {
|
|
132
|
+
queue.push({ id: neighbor.id, currentDepth: currentDepth + 1 });
|
|
133
|
+
// Add edges
|
|
134
|
+
const edges = this.getEdgesBetween(id, neighbor.id);
|
|
135
|
+
resultEdges.push(...edges);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const resultNodes = Array.from(visitedNodes)
|
|
141
|
+
.map(id => this.nodes.get(id))
|
|
142
|
+
.filter((n) => n !== undefined);
|
|
143
|
+
return { nodes: resultNodes, edges: resultEdges };
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Clear the graph
|
|
147
|
+
*/
|
|
148
|
+
clear() {
|
|
149
|
+
this.nodes.clear();
|
|
150
|
+
this.edges.clear();
|
|
151
|
+
this.adjacencyList.clear();
|
|
152
|
+
this.reverseAdjacencyList.clear();
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Get graph statistics
|
|
156
|
+
*/
|
|
157
|
+
getStats() {
|
|
158
|
+
return {
|
|
159
|
+
nodeCount: this.nodes.size,
|
|
160
|
+
edgeCount: this.edges.size
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
exports.GraphStore = GraphStore;
|
|
165
|
+
/**
|
|
166
|
+
* Graph RAG - Combines graph traversal with vector similarity
|
|
167
|
+
*/
|
|
168
|
+
class GraphRAG {
|
|
169
|
+
constructor(config = {}) {
|
|
170
|
+
this.graphStore = new GraphStore();
|
|
171
|
+
this.embedFn = config.embedFn;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Add a document as a node
|
|
175
|
+
*/
|
|
176
|
+
async addDocument(doc) {
|
|
177
|
+
const embedding = this.embedFn ? await this.embedFn(doc.content) : undefined;
|
|
178
|
+
this.graphStore.addNode({
|
|
179
|
+
id: doc.id,
|
|
180
|
+
type: doc.type || 'document',
|
|
181
|
+
content: doc.content,
|
|
182
|
+
metadata: doc.metadata,
|
|
183
|
+
embedding
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Add a relationship between documents
|
|
188
|
+
*/
|
|
189
|
+
addRelationship(sourceId, targetId, type, weight) {
|
|
190
|
+
this.graphStore.addEdge({
|
|
191
|
+
id: `${sourceId}-${type}-${targetId}`,
|
|
192
|
+
source: sourceId,
|
|
193
|
+
target: targetId,
|
|
194
|
+
type,
|
|
195
|
+
weight
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Query the graph with a natural language query
|
|
200
|
+
*/
|
|
201
|
+
async query(query, config = {}) {
|
|
202
|
+
const { maxDepth = 2, maxNodes = 10, minScore = 0, includeEdges = true } = config;
|
|
203
|
+
// Get all nodes and score them
|
|
204
|
+
const allNodes = this.graphStore.getAllNodes();
|
|
205
|
+
let scoredNodes;
|
|
206
|
+
if (this.embedFn) {
|
|
207
|
+
// Use embedding similarity
|
|
208
|
+
const queryEmbedding = await this.embedFn(query);
|
|
209
|
+
scoredNodes = allNodes
|
|
210
|
+
.filter(n => n.embedding)
|
|
211
|
+
.map(node => ({
|
|
212
|
+
node,
|
|
213
|
+
score: this.cosineSimilarity(queryEmbedding, node.embedding)
|
|
214
|
+
}));
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
// Use keyword matching
|
|
218
|
+
const queryTerms = query.toLowerCase().split(/\s+/);
|
|
219
|
+
scoredNodes = allNodes.map(node => {
|
|
220
|
+
const content = node.content.toLowerCase();
|
|
221
|
+
let score = 0;
|
|
222
|
+
for (const term of queryTerms) {
|
|
223
|
+
if (content.includes(term))
|
|
224
|
+
score += 1;
|
|
225
|
+
}
|
|
226
|
+
return { node, score: score / queryTerms.length };
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
// Filter and sort by score
|
|
230
|
+
scoredNodes = scoredNodes
|
|
231
|
+
.filter(s => s.score >= minScore)
|
|
232
|
+
.sort((a, b) => b.score - a.score);
|
|
233
|
+
// Get top nodes and expand graph
|
|
234
|
+
const topNodes = scoredNodes.slice(0, Math.min(3, maxNodes));
|
|
235
|
+
const resultNodes = [];
|
|
236
|
+
const resultEdges = [];
|
|
237
|
+
const seenNodeIds = new Set();
|
|
238
|
+
for (const { node } of topNodes) {
|
|
239
|
+
const subgraph = this.graphStore.getSubgraph(node.id, maxDepth);
|
|
240
|
+
for (const n of subgraph.nodes) {
|
|
241
|
+
if (!seenNodeIds.has(n.id) && resultNodes.length < maxNodes) {
|
|
242
|
+
seenNodeIds.add(n.id);
|
|
243
|
+
resultNodes.push(n);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
if (includeEdges) {
|
|
247
|
+
resultEdges.push(...subgraph.edges);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
// Find paths between top nodes
|
|
251
|
+
const paths = [];
|
|
252
|
+
if (topNodes.length >= 2) {
|
|
253
|
+
for (let i = 0; i < topNodes.length - 1; i++) {
|
|
254
|
+
const foundPaths = this.graphStore.findPaths(topNodes[i].node.id, topNodes[i + 1].node.id, maxDepth);
|
|
255
|
+
paths.push(...foundPaths.slice(0, 3));
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return {
|
|
259
|
+
nodes: resultNodes,
|
|
260
|
+
edges: resultEdges,
|
|
261
|
+
paths,
|
|
262
|
+
score: topNodes.length > 0 ? topNodes[0].score : 0
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Get context string from query results
|
|
267
|
+
*/
|
|
268
|
+
getContext(result) {
|
|
269
|
+
const nodeContents = result.nodes.map(n => n.content);
|
|
270
|
+
return nodeContents.join('\n\n---\n\n');
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Get the underlying graph store
|
|
274
|
+
*/
|
|
275
|
+
getGraphStore() {
|
|
276
|
+
return this.graphStore;
|
|
277
|
+
}
|
|
278
|
+
cosineSimilarity(a, b) {
|
|
279
|
+
const dot = a.reduce((sum, x, i) => sum + x * b[i], 0);
|
|
280
|
+
const normA = Math.sqrt(a.reduce((sum, x) => sum + x * x, 0));
|
|
281
|
+
const normB = Math.sqrt(b.reduce((sum, x) => sum + x * x, 0));
|
|
282
|
+
return dot / (normA * normB);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
exports.GraphRAG = GraphRAG;
|
|
286
|
+
// Factory function
|
|
287
|
+
function createGraphRAG(config) {
|
|
288
|
+
return new GraphRAG(config);
|
|
289
|
+
}
|
package/dist/knowledge/index.js
CHANGED
|
@@ -1,4 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
2
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
17
|
exports.BaseKnowledgeBase = void 0;
|
|
4
18
|
class BaseKnowledgeBase {
|
|
@@ -27,3 +41,7 @@ class BaseKnowledgeBase {
|
|
|
27
41
|
}
|
|
28
42
|
}
|
|
29
43
|
exports.BaseKnowledgeBase = BaseKnowledgeBase;
|
|
44
|
+
// Export Reranker
|
|
45
|
+
__exportStar(require("./reranker"), exports);
|
|
46
|
+
// Export Graph RAG
|
|
47
|
+
__exportStar(require("./graph-rag"), exports);
|