claude-brain 0.30.2 → 0.30.3
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 +241 -191
- package/VERSION +1 -1
- package/assets/CLAUDE-unified.md +11 -11
- package/assets/CLAUDE.md +29 -29
- package/package.json +7 -3
- package/packs/backend/node.json +173 -173
- package/packs/core/javascript.json +176 -176
- package/packs/core/typescript.json +222 -222
- package/packs/frontend/react.json +254 -254
- package/packs/meta/testing.json +172 -172
- package/scripts/postinstall.mjs +531 -531
- package/src/automation/decision-detector.ts +452 -452
- package/src/automation/phase12-manager.ts +456 -456
- package/src/automation/proactive-recall.ts +373 -373
- package/src/automation/project-detector.ts +310 -310
- package/src/automation/repo-scanner.ts +210 -205
- package/src/cli/auto-setup.ts +75 -75
- package/src/cli/auto-start.ts +266 -266
- package/src/cli/bin.ts +264 -264
- package/src/cli/commands/autostart.ts +90 -90
- package/src/cli/commands/chroma.ts +578 -577
- package/src/cli/commands/export-training.ts +70 -70
- package/src/cli/commands/export.ts +130 -130
- package/src/cli/commands/git-hook.ts +183 -183
- package/src/cli/commands/hooks.ts +217 -217
- package/src/cli/commands/init.ts +123 -123
- package/src/cli/commands/install-mcp.ts +122 -111
- package/src/cli/commands/models.ts +979 -979
- package/src/cli/commands/pack.ts +200 -200
- package/src/cli/commands/refresh.ts +344 -339
- package/src/cli/commands/reindex.ts +120 -120
- package/src/cli/commands/serve.ts +466 -463
- package/src/cli/commands/start.ts +44 -44
- package/src/cli/commands/status.ts +220 -203
- package/src/cli/commands/uninstall-mcp.ts +45 -41
- package/src/cli/commands/update.ts +130 -124
- package/src/cli/migrate-chroma.ts +106 -106
- package/src/cli/ui/animations.ts +80 -80
- package/src/cli/ui/components.ts +82 -82
- package/src/cli/ui/index.ts +4 -4
- package/src/cli/ui/logo.ts +36 -36
- package/src/cli/ui/theme.ts +55 -55
- package/src/code-intelligence/indexer.ts +352 -352
- package/src/code-intelligence/linker.ts +178 -178
- package/src/code-intelligence/parser.ts +484 -484
- package/src/code-intelligence/query.ts +291 -291
- package/src/code-intelligence/schema.ts +83 -83
- package/src/code-intelligence/types.ts +95 -95
- package/src/config/defaults.ts +52 -52
- package/src/config/home.ts +56 -56
- package/src/config/index.ts +5 -5
- package/src/config/loader.ts +192 -192
- package/src/config/schema.ts +446 -415
- package/src/config/validator.ts +182 -182
- package/src/context/assembler.ts +407 -400
- package/src/context/index.ts +79 -79
- package/src/context/progress-tracker.ts +174 -174
- package/src/context/standards-manager.ts +287 -287
- package/src/context/validator.ts +58 -58
- package/src/diagnostics/index.ts +122 -121
- package/src/health/index.ts +233 -232
- package/src/hooks/brain-hook.ts +134 -131
- package/src/hooks/capture.ts +168 -168
- package/src/hooks/claude-code-mastery.md +112 -112
- package/src/hooks/context-hook.ts +260 -245
- package/src/hooks/deduplicator.ts +72 -72
- package/src/hooks/git-capture.ts +109 -109
- package/src/hooks/git-hook-installer.ts +211 -207
- package/src/hooks/index.ts +20 -20
- package/src/hooks/installer.ts +306 -288
- package/src/hooks/interceptor-hook.ts +204 -201
- package/src/hooks/passive-classifier.ts +397 -397
- package/src/hooks/queue.ts +160 -129
- package/src/hooks/session-tracker.ts +312 -312
- package/src/hooks/types.ts +52 -52
- package/src/index.ts +7 -7
- package/src/intelligence/cross-project/generalizer.ts +283 -283
- package/src/intelligence/cross-project/index.ts +7 -7
- package/src/intelligence/hf-downloader.ts +222 -222
- package/src/intelligence/hf-manifest.json +78 -78
- package/src/intelligence/index.ts +24 -24
- package/src/intelligence/inference-router.ts +762 -762
- package/src/intelligence/model-manager.ts +263 -245
- package/src/intelligence/optimization/index.ts +10 -10
- package/src/intelligence/optimization/precompute.ts +202 -202
- package/src/intelligence/optimization/semantic-cache.ts +213 -207
- package/src/intelligence/prediction/index.ts +7 -7
- package/src/intelligence/prediction/recommender.ts +276 -268
- package/src/intelligence/reasoning/chain-retrieval.ts +243 -247
- package/src/intelligence/reasoning/index.ts +7 -7
- package/src/intelligence/temporal/evolution.ts +193 -197
- package/src/intelligence/temporal/index.ts +16 -16
- package/src/intelligence/temporal/query-processor.ts +190 -190
- package/src/intelligence/temporal/timeline.ts +272 -259
- package/src/intelligence/temporal/trends.ts +263 -263
- package/src/intelligence/tokenizer.ts +118 -118
- package/src/knowledge/entity-extractor.ts +447 -443
- package/src/knowledge/graph/builder.ts +185 -185
- package/src/knowledge/graph/linker.ts +201 -201
- package/src/knowledge/graph/memory-graph.ts +359 -359
- package/src/knowledge/graph/schema.ts +99 -99
- package/src/knowledge/graph/search.ts +166 -166
- package/src/knowledge/relationship-extractor.ts +108 -108
- package/src/memory/chroma/client.ts +211 -192
- package/src/memory/chroma/collection-manager.ts +92 -92
- package/src/memory/chroma/config.ts +57 -57
- package/src/memory/chroma/embeddings.ts +177 -175
- package/src/memory/chroma/index.ts +82 -82
- package/src/memory/chroma/migration.ts +270 -270
- package/src/memory/chroma/schemas.ts +69 -69
- package/src/memory/chroma/search.ts +319 -315
- package/src/memory/chroma/store.ts +755 -747
- package/src/memory/compression.ts +121 -121
- package/src/memory/consolidation/archiver.ts +162 -165
- package/src/memory/consolidation/merger.ts +182 -186
- package/src/memory/consolidation/scorer.ts +136 -136
- package/src/memory/database.ts +9 -0
- package/src/memory/dual-write.ts +145 -0
- package/src/memory/embeddings.ts +226 -226
- package/src/memory/episodic/detector.ts +108 -108
- package/src/memory/episodic/manager.ts +347 -351
- package/src/memory/episodic/summarizer.ts +179 -179
- package/src/memory/episodic/types.ts +52 -52
- package/src/memory/fts5-search.ts +692 -633
- package/src/memory/index.ts +943 -1060
- package/src/memory/migrations/add-fts5.ts +118 -108
- package/src/memory/patterns.ts +438 -438
- package/src/memory/pruning.ts +60 -60
- package/src/memory/schema.ts +88 -88
- package/src/memory/store.ts +911 -787
- package/src/orchestrator/handlers/decision-handler.ts +204 -204
- package/src/packs/index.ts +9 -9
- package/src/packs/loader.ts +134 -134
- package/src/packs/manager.ts +204 -204
- package/src/packs/ranker.ts +78 -78
- package/src/packs/types.ts +81 -81
- package/src/phase12/index.ts +5 -5
- package/src/retrieval/bm25/index.ts +300 -297
- package/src/retrieval/bm25/tokenizer.ts +184 -184
- package/src/retrieval/feedback/adaptive.ts +221 -221
- package/src/retrieval/feedback/index.ts +16 -16
- package/src/retrieval/feedback/metrics.ts +221 -221
- package/src/retrieval/feedback/store.ts +283 -283
- package/src/retrieval/fusion/index.ts +194 -194
- package/src/retrieval/fusion/rrf.ts +165 -165
- package/src/retrieval/index.ts +12 -12
- package/src/retrieval/pipeline.ts +375 -375
- package/src/retrieval/query/expander.ts +203 -203
- package/src/retrieval/query/index.ts +27 -27
- package/src/retrieval/query/intent-classifier.ts +252 -252
- package/src/retrieval/query/temporal-parser.ts +295 -295
- package/src/retrieval/reranker/index.ts +189 -188
- package/src/retrieval/reranker/model.ts +99 -95
- package/src/retrieval/service.ts +125 -125
- package/src/retrieval/types.ts +162 -162
- package/src/routing/entity-extractor.ts +454 -454
- package/src/routing/handlers/exploration-handler.ts +369 -0
- package/src/routing/handlers/index.ts +19 -0
- package/src/routing/handlers/memory-handler.ts +273 -0
- package/src/routing/handlers/mutation-handler.ts +241 -0
- package/src/routing/handlers/recall-handler.ts +642 -0
- package/src/routing/handlers/shared.ts +515 -0
- package/src/routing/handlers/types.ts +48 -0
- package/src/routing/intent-classifier.ts +552 -552
- package/src/routing/response-filter.ts +399 -391
- package/src/routing/router.ts +245 -2193
- package/src/routing/search-engine.ts +521 -514
- package/src/routing/types.ts +104 -94
- package/src/scripts/health-check.ts +118 -118
- package/src/scripts/setup.ts +122 -122
- package/src/server/auto-updater.ts +283 -276
- package/src/server/handlers/call-tool.ts +159 -159
- package/src/server/handlers/list-tools.ts +35 -35
- package/src/server/handlers/tools/auto-remember.ts +165 -165
- package/src/server/handlers/tools/brain.ts +86 -86
- package/src/server/handlers/tools/create-project.ts +135 -135
- package/src/server/handlers/tools/get-code-standards.ts +123 -123
- package/src/server/handlers/tools/get-corrections.ts +152 -152
- package/src/server/handlers/tools/get-patterns.ts +156 -156
- package/src/server/handlers/tools/get-project-context.ts +75 -75
- package/src/server/handlers/tools/index.ts +30 -30
- package/src/server/handlers/tools/init-project.ts +756 -756
- package/src/server/handlers/tools/list-projects.ts +126 -126
- package/src/server/handlers/tools/recall-similar.ts +87 -87
- package/src/server/handlers/tools/recognize-pattern.ts +132 -132
- package/src/server/handlers/tools/record-correction.ts +131 -131
- package/src/server/handlers/tools/remember-decision.ts +168 -168
- package/src/server/handlers/tools/schemas.ts +179 -179
- package/src/server/handlers/tools/search-code.ts +122 -122
- package/src/server/handlers/tools/smart-context.ts +146 -146
- package/src/server/handlers/tools/update-progress.ts +131 -131
- package/src/server/http-api.ts +215 -1229
- package/src/server/mcp-proxy.ts +85 -84
- package/src/server/mcp-server.ts +285 -284
- package/src/server/middleware/auth.ts +39 -0
- package/src/server/middleware/error-handler.ts +37 -0
- package/src/server/middleware/rate-limit.ts +53 -0
- package/src/server/middleware/validate.ts +42 -0
- package/src/server/pid-manager.ts +137 -136
- package/src/server/providers/resources.ts +581 -581
- package/src/server/routes/code.ts +228 -0
- package/src/server/routes/context.ts +26 -0
- package/src/server/routes/health.ts +19 -0
- package/src/server/routes/helpers.ts +100 -0
- package/src/server/routes/hooks.ts +197 -0
- package/src/server/routes/mcp.ts +47 -0
- package/src/server/routes/memory.ts +397 -0
- package/src/server/routes/models.ts +96 -0
- package/src/server/routes/projects.ts +89 -0
- package/src/server/routes/types.ts +21 -0
- package/src/server/schemas/api-schemas.ts +202 -0
- package/src/server/services.ts +720 -720
- package/src/server/utils/memory-indicator.ts +84 -84
- package/src/server/utils/response-formatter.ts +129 -129
- package/src/server/web-viewer.ts +1145 -1115
- package/src/setup/index.ts +38 -38
- package/src/tools/registry.ts +115 -115
- package/src/tools/schemas.ts +666 -666
- package/src/tools/types.ts +412 -412
- package/src/training/data-store.ts +320 -298
- package/src/training/retrain-pipeline.ts +399 -394
- package/src/utils/error-handler.ts +136 -136
- package/src/utils/index.ts +58 -58
- package/src/utils/kill-port.ts +55 -53
- package/src/utils/phase12-helper.ts +56 -56
- package/src/utils/safe-path.ts +43 -0
- package/src/utils/timing.ts +47 -47
- package/src/utils/transaction.ts +63 -63
- package/src/vault/index.ts +4 -3
- package/src/vault/paths.ts +106 -106
- package/src/vault/query.ts +4 -1
- package/src/vault/reader.ts +44 -1
- package/src/vault/watcher.ts +24 -1
- package/src/vault/writer.ts +487 -413
- package/skills/persistent-memory/SKILL.md +0 -148
- package/skills/persistent-memory/references/tool-reference.md +0 -90
|
@@ -1,99 +1,99 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Knowledge Graph Schema
|
|
3
|
-
* Type definitions for the knowledge graph data structures
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
export type EntityType =
|
|
7
|
-
| 'project'
|
|
8
|
-
| 'technology'
|
|
9
|
-
| 'concept'
|
|
10
|
-
| 'person'
|
|
11
|
-
| 'file'
|
|
12
|
-
| 'decision'
|
|
13
|
-
| 'pattern'
|
|
14
|
-
| 'correction'
|
|
15
|
-
| 'date'
|
|
16
|
-
|
|
17
|
-
export type RelationshipType =
|
|
18
|
-
| 'uses'
|
|
19
|
-
| 'depends_on'
|
|
20
|
-
| 'replaces'
|
|
21
|
-
| 'contradicts'
|
|
22
|
-
| 'relates_to'
|
|
23
|
-
| 'part_of'
|
|
24
|
-
| 'created_by'
|
|
25
|
-
| 'decided_in'
|
|
26
|
-
| 'similar_to'
|
|
27
|
-
| 'precedes'
|
|
28
|
-
| 'follows'
|
|
29
|
-
|
|
30
|
-
export interface GraphNode {
|
|
31
|
-
id: string
|
|
32
|
-
name: string
|
|
33
|
-
type: EntityType
|
|
34
|
-
properties: Record<string, unknown>
|
|
35
|
-
created_at: string
|
|
36
|
-
updated_at: string
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface GraphEdge {
|
|
40
|
-
id: string
|
|
41
|
-
source: string
|
|
42
|
-
target: string
|
|
43
|
-
relationship: RelationshipType
|
|
44
|
-
weight: number
|
|
45
|
-
properties: Record<string, unknown>
|
|
46
|
-
created_at: string
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export interface TraversalOptions {
|
|
50
|
-
maxDepth?: number
|
|
51
|
-
nodeTypes?: EntityType[]
|
|
52
|
-
relationshipTypes?: RelationshipType[]
|
|
53
|
-
limit?: number
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export interface GraphSearchOptions {
|
|
57
|
-
query?: string
|
|
58
|
-
nodeType?: EntityType
|
|
59
|
-
relationshipType?: RelationshipType
|
|
60
|
-
startNodeId?: string
|
|
61
|
-
hops?: number
|
|
62
|
-
limit?: number
|
|
63
|
-
alpha?: number
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export interface GraphSearchResult {
|
|
67
|
-
nodes: GraphNode[]
|
|
68
|
-
edges: GraphEdge[]
|
|
69
|
-
paths: string[][]
|
|
70
|
-
scores: Map<string, number>
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export interface KnowledgeGraph {
|
|
74
|
-
addNode(node: Omit<GraphNode, 'id' | 'created_at' | 'updated_at'>): GraphNode
|
|
75
|
-
getNode(id: string): GraphNode | undefined
|
|
76
|
-
updateNode(id: string, updates: Partial<Omit<GraphNode, 'id' | 'created_at'>>): GraphNode | undefined
|
|
77
|
-
deleteNode(id: string): boolean
|
|
78
|
-
|
|
79
|
-
addEdge(edge: Omit<GraphEdge, 'id' | 'created_at'>): GraphEdge
|
|
80
|
-
getEdges(nodeId: string, direction?: 'outgoing' | 'incoming' | 'both'): GraphEdge[]
|
|
81
|
-
deleteEdge(id: string): boolean
|
|
82
|
-
|
|
83
|
-
traverse(startId: string, options?: TraversalOptions): GraphNode[]
|
|
84
|
-
shortestPath(sourceId: string, targetId: string): string[] | null
|
|
85
|
-
findNodes(query: { type?: EntityType; name?: string; properties?: Record<string, unknown> }): GraphNode[]
|
|
86
|
-
|
|
87
|
-
getNodeCount(): number
|
|
88
|
-
getEdgeCount(): number
|
|
89
|
-
|
|
90
|
-
save(path: string): Promise<void>
|
|
91
|
-
load(path: string): Promise<void>
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export interface SerializedGraph {
|
|
95
|
-
nodes: GraphNode[]
|
|
96
|
-
edges: GraphEdge[]
|
|
97
|
-
version: string
|
|
98
|
-
saved_at: string
|
|
99
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Knowledge Graph Schema
|
|
3
|
+
* Type definitions for the knowledge graph data structures
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export type EntityType =
|
|
7
|
+
| 'project'
|
|
8
|
+
| 'technology'
|
|
9
|
+
| 'concept'
|
|
10
|
+
| 'person'
|
|
11
|
+
| 'file'
|
|
12
|
+
| 'decision'
|
|
13
|
+
| 'pattern'
|
|
14
|
+
| 'correction'
|
|
15
|
+
| 'date'
|
|
16
|
+
|
|
17
|
+
export type RelationshipType =
|
|
18
|
+
| 'uses'
|
|
19
|
+
| 'depends_on'
|
|
20
|
+
| 'replaces'
|
|
21
|
+
| 'contradicts'
|
|
22
|
+
| 'relates_to'
|
|
23
|
+
| 'part_of'
|
|
24
|
+
| 'created_by'
|
|
25
|
+
| 'decided_in'
|
|
26
|
+
| 'similar_to'
|
|
27
|
+
| 'precedes'
|
|
28
|
+
| 'follows'
|
|
29
|
+
|
|
30
|
+
export interface GraphNode {
|
|
31
|
+
id: string
|
|
32
|
+
name: string
|
|
33
|
+
type: EntityType
|
|
34
|
+
properties: Record<string, unknown>
|
|
35
|
+
created_at: string
|
|
36
|
+
updated_at: string
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface GraphEdge {
|
|
40
|
+
id: string
|
|
41
|
+
source: string
|
|
42
|
+
target: string
|
|
43
|
+
relationship: RelationshipType
|
|
44
|
+
weight: number
|
|
45
|
+
properties: Record<string, unknown>
|
|
46
|
+
created_at: string
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface TraversalOptions {
|
|
50
|
+
maxDepth?: number
|
|
51
|
+
nodeTypes?: EntityType[]
|
|
52
|
+
relationshipTypes?: RelationshipType[]
|
|
53
|
+
limit?: number
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface GraphSearchOptions {
|
|
57
|
+
query?: string
|
|
58
|
+
nodeType?: EntityType
|
|
59
|
+
relationshipType?: RelationshipType
|
|
60
|
+
startNodeId?: string
|
|
61
|
+
hops?: number
|
|
62
|
+
limit?: number
|
|
63
|
+
alpha?: number
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface GraphSearchResult {
|
|
67
|
+
nodes: GraphNode[]
|
|
68
|
+
edges: GraphEdge[]
|
|
69
|
+
paths: string[][]
|
|
70
|
+
scores: Map<string, number>
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface KnowledgeGraph {
|
|
74
|
+
addNode(node: Omit<GraphNode, 'id' | 'created_at' | 'updated_at'>): GraphNode
|
|
75
|
+
getNode(id: string): GraphNode | undefined
|
|
76
|
+
updateNode(id: string, updates: Partial<Omit<GraphNode, 'id' | 'created_at'>>): GraphNode | undefined
|
|
77
|
+
deleteNode(id: string): boolean
|
|
78
|
+
|
|
79
|
+
addEdge(edge: Omit<GraphEdge, 'id' | 'created_at'>): GraphEdge
|
|
80
|
+
getEdges(nodeId: string, direction?: 'outgoing' | 'incoming' | 'both'): GraphEdge[]
|
|
81
|
+
deleteEdge(id: string): boolean
|
|
82
|
+
|
|
83
|
+
traverse(startId: string, options?: TraversalOptions): GraphNode[]
|
|
84
|
+
shortestPath(sourceId: string, targetId: string): string[] | null
|
|
85
|
+
findNodes(query: { type?: EntityType; name?: string; properties?: Record<string, unknown> }): GraphNode[]
|
|
86
|
+
|
|
87
|
+
getNodeCount(): number
|
|
88
|
+
getEdgeCount(): number
|
|
89
|
+
|
|
90
|
+
save(path: string): Promise<void>
|
|
91
|
+
load(path: string): Promise<void>
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface SerializedGraph {
|
|
95
|
+
nodes: GraphNode[]
|
|
96
|
+
edges: GraphEdge[]
|
|
97
|
+
version: string
|
|
98
|
+
saved_at: string
|
|
99
|
+
}
|
|
@@ -1,166 +1,166 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Graph Search Engine
|
|
3
|
-
* Combines vector similarity with graph proximity for enhanced search
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { Logger } from 'pino'
|
|
7
|
-
import type { InMemoryKnowledgeGraph } from './memory-graph'
|
|
8
|
-
import type { GraphNode, GraphEdge, GraphSearchOptions, GraphSearchResult } from './schema'
|
|
9
|
-
|
|
10
|
-
export class GraphSearchEngine {
|
|
11
|
-
private graph: InMemoryKnowledgeGraph
|
|
12
|
-
|
|
13
|
-
constructor(graph: InMemoryKnowledgeGraph, _logger: Logger) {
|
|
14
|
-
this.graph = graph
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
search(options: GraphSearchOptions): GraphSearchResult {
|
|
18
|
-
const {
|
|
19
|
-
query,
|
|
20
|
-
nodeType,
|
|
21
|
-
startNodeId,
|
|
22
|
-
hops = 2,
|
|
23
|
-
limit = 20,
|
|
24
|
-
alpha = 0.6
|
|
25
|
-
} = options
|
|
26
|
-
|
|
27
|
-
const scores = new Map<string, number>()
|
|
28
|
-
const resultNodes: GraphNode[] = []
|
|
29
|
-
const resultEdges: GraphEdge[] = []
|
|
30
|
-
const paths: string[][] = []
|
|
31
|
-
|
|
32
|
-
if (startNodeId) {
|
|
33
|
-
// Graph expansion from a starting node
|
|
34
|
-
return this.expandFromNode(startNodeId, hops, limit, nodeType)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (query) {
|
|
38
|
-
// Text-based search: find nodes matching query, then expand
|
|
39
|
-
const matchingNodes = this.findMatchingNodes(query, nodeType)
|
|
40
|
-
|
|
41
|
-
for (const node of matchingNodes) {
|
|
42
|
-
const nameScore = this.calculateNameScore(node.name, query)
|
|
43
|
-
scores.set(node.id, nameScore)
|
|
44
|
-
|
|
45
|
-
// Expand from each matching node
|
|
46
|
-
const neighbors = this.graph.traverse(node.id, {
|
|
47
|
-
maxDepth: hops,
|
|
48
|
-
nodeTypes: nodeType ? [nodeType] : undefined,
|
|
49
|
-
limit: Math.ceil(limit / Math.max(matchingNodes.length, 1))
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
for (const neighbor of neighbors) {
|
|
53
|
-
if (!scores.has(neighbor.id)) {
|
|
54
|
-
// Graph proximity score: decreases with distance
|
|
55
|
-
const path = this.graph.shortestPath(node.id, neighbor.id)
|
|
56
|
-
const distance = path ? path.length - 1 : hops + 1
|
|
57
|
-
const graphScore = 1 / (1 + distance)
|
|
58
|
-
|
|
59
|
-
// Combined score
|
|
60
|
-
const combinedScore = alpha * nameScore + (1 - alpha) * graphScore
|
|
61
|
-
scores.set(neighbor.id, Math.max(scores.get(neighbor.id) || 0, combinedScore))
|
|
62
|
-
|
|
63
|
-
if (path && path.length > 1) {
|
|
64
|
-
paths.push(path)
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Collect nodes sorted by score
|
|
71
|
-
const sortedEntries = Array.from(scores.entries())
|
|
72
|
-
.sort(([, a], [, b]) => b - a)
|
|
73
|
-
.slice(0, limit)
|
|
74
|
-
|
|
75
|
-
for (const [nodeId] of sortedEntries) {
|
|
76
|
-
const node = this.graph.getNode(nodeId)
|
|
77
|
-
if (node) resultNodes.push(node)
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// Collect edges between result nodes
|
|
81
|
-
const resultNodeIds = new Set(resultNodes.map(n => n.id))
|
|
82
|
-
for (const node of resultNodes) {
|
|
83
|
-
const edges = this.graph.getEdges(node.id)
|
|
84
|
-
for (const edge of edges) {
|
|
85
|
-
if (resultNodeIds.has(edge.source) && resultNodeIds.has(edge.target)) {
|
|
86
|
-
if (!resultEdges.some(e => e.id === edge.id)) {
|
|
87
|
-
resultEdges.push(edge)
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return { nodes: resultNodes, edges: resultEdges, paths, scores }
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
findRelated(nodeId: string, hops: number = 2): GraphSearchResult {
|
|
98
|
-
return this.expandFromNode(nodeId, hops, 50)
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
private expandFromNode(
|
|
102
|
-
nodeId: string,
|
|
103
|
-
hops: number,
|
|
104
|
-
limit: number,
|
|
105
|
-
nodeType?: string
|
|
106
|
-
): GraphSearchResult {
|
|
107
|
-
const scores = new Map<string, number>()
|
|
108
|
-
const nodes = this.graph.traverse(nodeId, {
|
|
109
|
-
maxDepth: hops,
|
|
110
|
-
nodeTypes: nodeType ? [nodeType as any] : undefined,
|
|
111
|
-
limit
|
|
112
|
-
})
|
|
113
|
-
|
|
114
|
-
const resultEdges: GraphEdge[] = []
|
|
115
|
-
const paths: string[][] = []
|
|
116
|
-
const nodeIds = new Set(nodes.map(n => n.id))
|
|
117
|
-
|
|
118
|
-
for (const node of nodes) {
|
|
119
|
-
const path = this.graph.shortestPath(nodeId, node.id)
|
|
120
|
-
const distance = path ? path.length - 1 : 0
|
|
121
|
-
scores.set(node.id, 1 / (1 + distance))
|
|
122
|
-
|
|
123
|
-
if (path && path.length > 1) {
|
|
124
|
-
paths.push(path)
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
const edges = this.graph.getEdges(node.id)
|
|
128
|
-
for (const edge of edges) {
|
|
129
|
-
if (nodeIds.has(edge.source) && nodeIds.has(edge.target)) {
|
|
130
|
-
if (!resultEdges.some(e => e.id === edge.id)) {
|
|
131
|
-
resultEdges.push(edge)
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
return { nodes, edges: resultEdges, paths, scores }
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
private findMatchingNodes(query: string, nodeType?: string): GraphNode[] {
|
|
141
|
-
const lowerQuery = query.toLowerCase()
|
|
142
|
-
const queryTerms = lowerQuery.split(/\s+/).filter(t => t.length > 1)
|
|
143
|
-
|
|
144
|
-
return this.graph.findNodes({
|
|
145
|
-
type: nodeType as any,
|
|
146
|
-
name: undefined
|
|
147
|
-
}).filter(node => {
|
|
148
|
-
const lowerName = node.name.toLowerCase()
|
|
149
|
-
return queryTerms.some(term => lowerName.includes(term) || term.includes(lowerName))
|
|
150
|
-
})
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
private calculateNameScore(nodeName: string, query: string): number {
|
|
154
|
-
const lowerName = nodeName.toLowerCase()
|
|
155
|
-
const lowerQuery = query.toLowerCase()
|
|
156
|
-
|
|
157
|
-
if (lowerName === lowerQuery) return 1.0
|
|
158
|
-
if (lowerQuery.includes(lowerName)) return 0.9
|
|
159
|
-
if (lowerName.includes(lowerQuery)) return 0.8
|
|
160
|
-
|
|
161
|
-
// Partial term matching
|
|
162
|
-
const queryTerms = lowerQuery.split(/\s+/)
|
|
163
|
-
const matchCount = queryTerms.filter(t => lowerName.includes(t)).length
|
|
164
|
-
return matchCount > 0 ? 0.5 + (0.3 * matchCount / queryTerms.length) : 0.3
|
|
165
|
-
}
|
|
166
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Graph Search Engine
|
|
3
|
+
* Combines vector similarity with graph proximity for enhanced search
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Logger } from 'pino'
|
|
7
|
+
import type { InMemoryKnowledgeGraph } from './memory-graph'
|
|
8
|
+
import type { GraphNode, GraphEdge, GraphSearchOptions, GraphSearchResult } from './schema'
|
|
9
|
+
|
|
10
|
+
export class GraphSearchEngine {
|
|
11
|
+
private graph: InMemoryKnowledgeGraph
|
|
12
|
+
|
|
13
|
+
constructor(graph: InMemoryKnowledgeGraph, _logger: Logger) {
|
|
14
|
+
this.graph = graph
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
search(options: GraphSearchOptions): GraphSearchResult {
|
|
18
|
+
const {
|
|
19
|
+
query,
|
|
20
|
+
nodeType,
|
|
21
|
+
startNodeId,
|
|
22
|
+
hops = 2,
|
|
23
|
+
limit = 20,
|
|
24
|
+
alpha = 0.6
|
|
25
|
+
} = options
|
|
26
|
+
|
|
27
|
+
const scores = new Map<string, number>()
|
|
28
|
+
const resultNodes: GraphNode[] = []
|
|
29
|
+
const resultEdges: GraphEdge[] = []
|
|
30
|
+
const paths: string[][] = []
|
|
31
|
+
|
|
32
|
+
if (startNodeId) {
|
|
33
|
+
// Graph expansion from a starting node
|
|
34
|
+
return this.expandFromNode(startNodeId, hops, limit, nodeType)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (query) {
|
|
38
|
+
// Text-based search: find nodes matching query, then expand
|
|
39
|
+
const matchingNodes = this.findMatchingNodes(query, nodeType)
|
|
40
|
+
|
|
41
|
+
for (const node of matchingNodes) {
|
|
42
|
+
const nameScore = this.calculateNameScore(node.name, query)
|
|
43
|
+
scores.set(node.id, nameScore)
|
|
44
|
+
|
|
45
|
+
// Expand from each matching node
|
|
46
|
+
const neighbors = this.graph.traverse(node.id, {
|
|
47
|
+
maxDepth: hops,
|
|
48
|
+
nodeTypes: nodeType ? [nodeType] : undefined,
|
|
49
|
+
limit: Math.ceil(limit / Math.max(matchingNodes.length, 1))
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
for (const neighbor of neighbors) {
|
|
53
|
+
if (!scores.has(neighbor.id)) {
|
|
54
|
+
// Graph proximity score: decreases with distance
|
|
55
|
+
const path = this.graph.shortestPath(node.id, neighbor.id)
|
|
56
|
+
const distance = path ? path.length - 1 : hops + 1
|
|
57
|
+
const graphScore = 1 / (1 + distance)
|
|
58
|
+
|
|
59
|
+
// Combined score
|
|
60
|
+
const combinedScore = alpha * nameScore + (1 - alpha) * graphScore
|
|
61
|
+
scores.set(neighbor.id, Math.max(scores.get(neighbor.id) || 0, combinedScore))
|
|
62
|
+
|
|
63
|
+
if (path && path.length > 1) {
|
|
64
|
+
paths.push(path)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Collect nodes sorted by score
|
|
71
|
+
const sortedEntries = Array.from(scores.entries())
|
|
72
|
+
.sort(([, a], [, b]) => b - a)
|
|
73
|
+
.slice(0, limit)
|
|
74
|
+
|
|
75
|
+
for (const [nodeId] of sortedEntries) {
|
|
76
|
+
const node = this.graph.getNode(nodeId)
|
|
77
|
+
if (node) resultNodes.push(node)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Collect edges between result nodes
|
|
81
|
+
const resultNodeIds = new Set(resultNodes.map(n => n.id))
|
|
82
|
+
for (const node of resultNodes) {
|
|
83
|
+
const edges = this.graph.getEdges(node.id)
|
|
84
|
+
for (const edge of edges) {
|
|
85
|
+
if (resultNodeIds.has(edge.source) && resultNodeIds.has(edge.target)) {
|
|
86
|
+
if (!resultEdges.some(e => e.id === edge.id)) {
|
|
87
|
+
resultEdges.push(edge)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return { nodes: resultNodes, edges: resultEdges, paths, scores }
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
findRelated(nodeId: string, hops: number = 2): GraphSearchResult {
|
|
98
|
+
return this.expandFromNode(nodeId, hops, 50)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
private expandFromNode(
|
|
102
|
+
nodeId: string,
|
|
103
|
+
hops: number,
|
|
104
|
+
limit: number,
|
|
105
|
+
nodeType?: string
|
|
106
|
+
): GraphSearchResult {
|
|
107
|
+
const scores = new Map<string, number>()
|
|
108
|
+
const nodes = this.graph.traverse(nodeId, {
|
|
109
|
+
maxDepth: hops,
|
|
110
|
+
nodeTypes: nodeType ? [nodeType as any] : undefined,
|
|
111
|
+
limit
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
const resultEdges: GraphEdge[] = []
|
|
115
|
+
const paths: string[][] = []
|
|
116
|
+
const nodeIds = new Set(nodes.map(n => n.id))
|
|
117
|
+
|
|
118
|
+
for (const node of nodes) {
|
|
119
|
+
const path = this.graph.shortestPath(nodeId, node.id)
|
|
120
|
+
const distance = path ? path.length - 1 : 0
|
|
121
|
+
scores.set(node.id, 1 / (1 + distance))
|
|
122
|
+
|
|
123
|
+
if (path && path.length > 1) {
|
|
124
|
+
paths.push(path)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const edges = this.graph.getEdges(node.id)
|
|
128
|
+
for (const edge of edges) {
|
|
129
|
+
if (nodeIds.has(edge.source) && nodeIds.has(edge.target)) {
|
|
130
|
+
if (!resultEdges.some(e => e.id === edge.id)) {
|
|
131
|
+
resultEdges.push(edge)
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return { nodes, edges: resultEdges, paths, scores }
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
private findMatchingNodes(query: string, nodeType?: string): GraphNode[] {
|
|
141
|
+
const lowerQuery = query.toLowerCase()
|
|
142
|
+
const queryTerms = lowerQuery.split(/\s+/).filter(t => t.length > 1)
|
|
143
|
+
|
|
144
|
+
return this.graph.findNodes({
|
|
145
|
+
type: nodeType as any,
|
|
146
|
+
name: undefined
|
|
147
|
+
}).filter(node => {
|
|
148
|
+
const lowerName = node.name.toLowerCase()
|
|
149
|
+
return queryTerms.some(term => lowerName.includes(term) || term.includes(lowerName))
|
|
150
|
+
})
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
private calculateNameScore(nodeName: string, query: string): number {
|
|
154
|
+
const lowerName = nodeName.toLowerCase()
|
|
155
|
+
const lowerQuery = query.toLowerCase()
|
|
156
|
+
|
|
157
|
+
if (lowerName === lowerQuery) return 1.0
|
|
158
|
+
if (lowerQuery.includes(lowerName)) return 0.9
|
|
159
|
+
if (lowerName.includes(lowerQuery)) return 0.8
|
|
160
|
+
|
|
161
|
+
// Partial term matching
|
|
162
|
+
const queryTerms = lowerQuery.split(/\s+/)
|
|
163
|
+
const matchCount = queryTerms.filter(t => lowerName.includes(t)).length
|
|
164
|
+
return matchCount > 0 ? 0.5 + (0.3 * matchCount / queryTerms.length) : 0.3
|
|
165
|
+
}
|
|
166
|
+
}
|