claude-memory-layer 1.0.2 → 1.0.4
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/.claude/settings.local.json +3 -1
- package/.history/package_20260201133143.json +45 -0
- package/CLAUDE.md +14 -0
- package/dist/cli/index.js +836 -9
- package/dist/cli/index.js.map +4 -4
- package/dist/core/index.js +782 -3
- package/dist/core/index.js.map +4 -4
- package/dist/hooks/session-end.js +836 -9
- package/dist/hooks/session-end.js.map +4 -4
- package/dist/hooks/session-start.js +836 -9
- package/dist/hooks/session-start.js.map +4 -4
- package/dist/hooks/stop.js +836 -9
- package/dist/hooks/stop.js.map +4 -4
- package/dist/hooks/user-prompt-submit.js +839 -10
- package/dist/hooks/user-prompt-submit.js.map +4 -4
- package/dist/services/memory-service.js +836 -9
- package/dist/services/memory-service.js.map +4 -4
- package/package.json +1 -1
- package/src/core/index.ts +6 -0
- package/src/core/retriever.ts +118 -2
- package/src/core/shared-event-store.ts +114 -0
- package/src/core/shared-promoter.ts +249 -0
- package/src/core/shared-store.ts +289 -0
- package/src/core/shared-vector-store.ts +203 -0
- package/src/core/types.ts +69 -2
- package/src/hooks/user-prompt-submit.ts +6 -2
- package/src/services/memory-service.ts +145 -6
package/package.json
CHANGED
package/src/core/index.ts
CHANGED
|
@@ -30,3 +30,9 @@ export * from './graduation.js';
|
|
|
30
30
|
|
|
31
31
|
// Task Entity System
|
|
32
32
|
export * from './task/index.js';
|
|
33
|
+
|
|
34
|
+
// Shared Store (Cross-Project Knowledge)
|
|
35
|
+
export * from './shared-event-store.js';
|
|
36
|
+
export * from './shared-store.js';
|
|
37
|
+
export * from './shared-vector-store.js';
|
|
38
|
+
export * from './shared-promoter.js';
|
package/src/core/retriever.ts
CHANGED
|
@@ -7,7 +7,9 @@ import { EventStore } from './event-store.js';
|
|
|
7
7
|
import { VectorStore, SearchResult } from './vector-store.js';
|
|
8
8
|
import { Embedder } from './embedder.js';
|
|
9
9
|
import { Matcher } from './matcher.js';
|
|
10
|
-
import
|
|
10
|
+
import { SharedStore } from './shared-store.js';
|
|
11
|
+
import { SharedVectorStore } from './shared-vector-store.js';
|
|
12
|
+
import type { MemoryEvent, MatchResult, Config, SharedTroubleshootingEntry } from './types.js';
|
|
11
13
|
|
|
12
14
|
export interface RetrievalOptions {
|
|
13
15
|
topK: number;
|
|
@@ -30,6 +32,15 @@ export interface MemoryWithContext {
|
|
|
30
32
|
sessionContext?: string;
|
|
31
33
|
}
|
|
32
34
|
|
|
35
|
+
export interface UnifiedRetrievalOptions extends RetrievalOptions {
|
|
36
|
+
includeShared?: boolean;
|
|
37
|
+
projectHash?: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface UnifiedRetrievalResult extends RetrievalResult {
|
|
41
|
+
sharedMemories?: SharedTroubleshootingEntry[];
|
|
42
|
+
}
|
|
43
|
+
|
|
33
44
|
const DEFAULT_OPTIONS: RetrievalOptions = {
|
|
34
45
|
topK: 5,
|
|
35
46
|
minScore: 0.7,
|
|
@@ -37,22 +48,40 @@ const DEFAULT_OPTIONS: RetrievalOptions = {
|
|
|
37
48
|
includeSessionContext: true
|
|
38
49
|
};
|
|
39
50
|
|
|
51
|
+
export interface SharedStoreOptions {
|
|
52
|
+
sharedStore?: SharedStore;
|
|
53
|
+
sharedVectorStore?: SharedVectorStore;
|
|
54
|
+
}
|
|
55
|
+
|
|
40
56
|
export class Retriever {
|
|
41
57
|
private readonly eventStore: EventStore;
|
|
42
58
|
private readonly vectorStore: VectorStore;
|
|
43
59
|
private readonly embedder: Embedder;
|
|
44
60
|
private readonly matcher: Matcher;
|
|
61
|
+
private sharedStore?: SharedStore;
|
|
62
|
+
private sharedVectorStore?: SharedVectorStore;
|
|
45
63
|
|
|
46
64
|
constructor(
|
|
47
65
|
eventStore: EventStore,
|
|
48
66
|
vectorStore: VectorStore,
|
|
49
67
|
embedder: Embedder,
|
|
50
|
-
matcher: Matcher
|
|
68
|
+
matcher: Matcher,
|
|
69
|
+
sharedOptions?: SharedStoreOptions
|
|
51
70
|
) {
|
|
52
71
|
this.eventStore = eventStore;
|
|
53
72
|
this.vectorStore = vectorStore;
|
|
54
73
|
this.embedder = embedder;
|
|
55
74
|
this.matcher = matcher;
|
|
75
|
+
this.sharedStore = sharedOptions?.sharedStore;
|
|
76
|
+
this.sharedVectorStore = sharedOptions?.sharedVectorStore;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Set shared stores after construction
|
|
81
|
+
*/
|
|
82
|
+
setSharedStores(sharedStore: SharedStore, sharedVectorStore: SharedVectorStore): void {
|
|
83
|
+
this.sharedStore = sharedStore;
|
|
84
|
+
this.sharedVectorStore = sharedVectorStore;
|
|
56
85
|
}
|
|
57
86
|
|
|
58
87
|
/**
|
|
@@ -94,6 +123,93 @@ export class Retriever {
|
|
|
94
123
|
};
|
|
95
124
|
}
|
|
96
125
|
|
|
126
|
+
/**
|
|
127
|
+
* Retrieve with unified search (project + shared)
|
|
128
|
+
*/
|
|
129
|
+
async retrieveUnified(
|
|
130
|
+
query: string,
|
|
131
|
+
options: Partial<UnifiedRetrievalOptions> = {}
|
|
132
|
+
): Promise<UnifiedRetrievalResult> {
|
|
133
|
+
// Get project-local results first
|
|
134
|
+
const projectResult = await this.retrieve(query, options);
|
|
135
|
+
|
|
136
|
+
// If shared search is not requested or stores not available, return project results only
|
|
137
|
+
if (!options.includeShared || !this.sharedStore || !this.sharedVectorStore) {
|
|
138
|
+
return projectResult;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
// Generate query embedding (reuse if possible)
|
|
143
|
+
const queryEmbedding = await this.embedder.embed(query);
|
|
144
|
+
|
|
145
|
+
// Vector search in shared store
|
|
146
|
+
const sharedVectorResults = await this.sharedVectorStore.search(
|
|
147
|
+
queryEmbedding.vector,
|
|
148
|
+
{
|
|
149
|
+
limit: options.topK || 5,
|
|
150
|
+
minScore: options.minScore || 0.7,
|
|
151
|
+
excludeProjectHash: options.projectHash
|
|
152
|
+
}
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
// Get full entries from shared store
|
|
156
|
+
const sharedMemories: SharedTroubleshootingEntry[] = [];
|
|
157
|
+
for (const result of sharedVectorResults) {
|
|
158
|
+
const entry = await this.sharedStore.get(result.entryId);
|
|
159
|
+
if (entry) {
|
|
160
|
+
// Exclude entries from current project if specified
|
|
161
|
+
if (!options.projectHash || entry.sourceProjectHash !== options.projectHash) {
|
|
162
|
+
sharedMemories.push(entry);
|
|
163
|
+
// Record usage for ranking
|
|
164
|
+
await this.sharedStore.recordUsage(entry.entryId);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Build unified context
|
|
170
|
+
const unifiedContext = this.buildUnifiedContext(projectResult, sharedMemories);
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
...projectResult,
|
|
174
|
+
context: unifiedContext,
|
|
175
|
+
totalTokens: this.estimateTokens(unifiedContext),
|
|
176
|
+
sharedMemories
|
|
177
|
+
};
|
|
178
|
+
} catch (error) {
|
|
179
|
+
// If shared search fails, return project results only
|
|
180
|
+
console.error('Shared search failed:', error);
|
|
181
|
+
return projectResult;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Build unified context combining project and shared memories
|
|
187
|
+
*/
|
|
188
|
+
private buildUnifiedContext(
|
|
189
|
+
projectResult: RetrievalResult,
|
|
190
|
+
sharedMemories: SharedTroubleshootingEntry[]
|
|
191
|
+
): string {
|
|
192
|
+
let context = projectResult.context;
|
|
193
|
+
|
|
194
|
+
if (sharedMemories.length > 0) {
|
|
195
|
+
context += '\n\n## Cross-Project Knowledge\n\n';
|
|
196
|
+
for (const memory of sharedMemories.slice(0, 3)) {
|
|
197
|
+
context += `### ${memory.title}\n`;
|
|
198
|
+
if (memory.symptoms.length > 0) {
|
|
199
|
+
context += `**Symptoms:** ${memory.symptoms.join(', ')}\n`;
|
|
200
|
+
}
|
|
201
|
+
context += `**Root Cause:** ${memory.rootCause}\n`;
|
|
202
|
+
context += `**Solution:** ${memory.solution}\n`;
|
|
203
|
+
if (memory.technologies && memory.technologies.length > 0) {
|
|
204
|
+
context += `**Technologies:** ${memory.technologies.join(', ')}\n`;
|
|
205
|
+
}
|
|
206
|
+
context += `_Confidence: ${(memory.confidence * 100).toFixed(0)}%_\n\n`;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return context;
|
|
211
|
+
}
|
|
212
|
+
|
|
97
213
|
/**
|
|
98
214
|
* Retrieve memories from a specific session
|
|
99
215
|
*/
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SharedEventStore - Global database for cross-project knowledge
|
|
3
|
+
* Location: ~/.claude-code/memory/shared/
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
createDatabase,
|
|
8
|
+
dbRun,
|
|
9
|
+
dbClose,
|
|
10
|
+
type Database
|
|
11
|
+
} from './db-wrapper.js';
|
|
12
|
+
|
|
13
|
+
export class SharedEventStore {
|
|
14
|
+
private db: Database;
|
|
15
|
+
private initialized = false;
|
|
16
|
+
|
|
17
|
+
constructor(private dbPath: string) {
|
|
18
|
+
this.db = createDatabase(dbPath);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async initialize(): Promise<void> {
|
|
22
|
+
if (this.initialized) return;
|
|
23
|
+
|
|
24
|
+
// Shared troubleshooting entries table
|
|
25
|
+
await dbRun(this.db, `
|
|
26
|
+
CREATE TABLE IF NOT EXISTS shared_troubleshooting (
|
|
27
|
+
entry_id VARCHAR PRIMARY KEY,
|
|
28
|
+
source_project_hash VARCHAR NOT NULL,
|
|
29
|
+
source_entry_id VARCHAR NOT NULL,
|
|
30
|
+
title VARCHAR NOT NULL,
|
|
31
|
+
symptoms JSON NOT NULL,
|
|
32
|
+
root_cause TEXT NOT NULL,
|
|
33
|
+
solution TEXT NOT NULL,
|
|
34
|
+
topics JSON NOT NULL,
|
|
35
|
+
technologies JSON,
|
|
36
|
+
confidence REAL NOT NULL DEFAULT 0.8,
|
|
37
|
+
usage_count INTEGER DEFAULT 0,
|
|
38
|
+
last_used_at TIMESTAMP,
|
|
39
|
+
promoted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
40
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
41
|
+
UNIQUE(source_project_hash, source_entry_id)
|
|
42
|
+
)
|
|
43
|
+
`);
|
|
44
|
+
|
|
45
|
+
// Future extensibility: best practices table
|
|
46
|
+
await dbRun(this.db, `
|
|
47
|
+
CREATE TABLE IF NOT EXISTS shared_best_practices (
|
|
48
|
+
entry_id VARCHAR PRIMARY KEY,
|
|
49
|
+
source_project_hash VARCHAR NOT NULL,
|
|
50
|
+
source_entry_id VARCHAR NOT NULL,
|
|
51
|
+
title VARCHAR NOT NULL,
|
|
52
|
+
content_json JSON NOT NULL,
|
|
53
|
+
topics JSON NOT NULL,
|
|
54
|
+
confidence REAL NOT NULL DEFAULT 0.8,
|
|
55
|
+
usage_count INTEGER DEFAULT 0,
|
|
56
|
+
promoted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
57
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
58
|
+
UNIQUE(source_project_hash, source_entry_id)
|
|
59
|
+
)
|
|
60
|
+
`);
|
|
61
|
+
|
|
62
|
+
// Future extensibility: common errors table
|
|
63
|
+
await dbRun(this.db, `
|
|
64
|
+
CREATE TABLE IF NOT EXISTS shared_common_errors (
|
|
65
|
+
entry_id VARCHAR PRIMARY KEY,
|
|
66
|
+
source_project_hash VARCHAR NOT NULL,
|
|
67
|
+
source_entry_id VARCHAR NOT NULL,
|
|
68
|
+
title VARCHAR NOT NULL,
|
|
69
|
+
error_pattern TEXT NOT NULL,
|
|
70
|
+
solution TEXT NOT NULL,
|
|
71
|
+
topics JSON NOT NULL,
|
|
72
|
+
technologies JSON,
|
|
73
|
+
confidence REAL NOT NULL DEFAULT 0.8,
|
|
74
|
+
usage_count INTEGER DEFAULT 0,
|
|
75
|
+
promoted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
76
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
77
|
+
UNIQUE(source_project_hash, source_entry_id)
|
|
78
|
+
)
|
|
79
|
+
`);
|
|
80
|
+
|
|
81
|
+
// Indexes for troubleshooting
|
|
82
|
+
await dbRun(this.db, `
|
|
83
|
+
CREATE INDEX IF NOT EXISTS idx_shared_ts_confidence
|
|
84
|
+
ON shared_troubleshooting(confidence DESC)
|
|
85
|
+
`);
|
|
86
|
+
await dbRun(this.db, `
|
|
87
|
+
CREATE INDEX IF NOT EXISTS idx_shared_ts_usage
|
|
88
|
+
ON shared_troubleshooting(usage_count DESC)
|
|
89
|
+
`);
|
|
90
|
+
await dbRun(this.db, `
|
|
91
|
+
CREATE INDEX IF NOT EXISTS idx_shared_ts_source
|
|
92
|
+
ON shared_troubleshooting(source_project_hash)
|
|
93
|
+
`);
|
|
94
|
+
|
|
95
|
+
this.initialized = true;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
getDatabase(): Database {
|
|
99
|
+
return this.db;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
isInitialized(): boolean {
|
|
103
|
+
return this.initialized;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async close(): Promise<void> {
|
|
107
|
+
await dbClose(this.db);
|
|
108
|
+
this.initialized = false;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function createSharedEventStore(dbPath: string): SharedEventStore {
|
|
113
|
+
return new SharedEventStore(dbPath);
|
|
114
|
+
}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SharedPromoter - Handles auto-promotion of verified troubleshooting entries
|
|
3
|
+
* Promotes entries from project-local storage to cross-project shared storage
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { randomUUID } from 'crypto';
|
|
7
|
+
import { SharedStore } from './shared-store.js';
|
|
8
|
+
import { SharedVectorStore } from './shared-vector-store.js';
|
|
9
|
+
import { Embedder } from './embedder.js';
|
|
10
|
+
import type { Entry, SharedTroubleshootingInput, SharedStoreConfig } from './types.js';
|
|
11
|
+
|
|
12
|
+
export interface TroubleshootingContent {
|
|
13
|
+
symptoms?: string[];
|
|
14
|
+
rootCause?: string;
|
|
15
|
+
solution?: string;
|
|
16
|
+
technologies?: string[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface PromotionResult {
|
|
20
|
+
success: boolean;
|
|
21
|
+
entryId?: string;
|
|
22
|
+
error?: string;
|
|
23
|
+
skipped?: boolean;
|
|
24
|
+
skipReason?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export class SharedPromoter {
|
|
28
|
+
constructor(
|
|
29
|
+
private sharedStore: SharedStore,
|
|
30
|
+
private sharedVectorStore: SharedVectorStore,
|
|
31
|
+
private embedder: Embedder,
|
|
32
|
+
private config?: SharedStoreConfig
|
|
33
|
+
) {}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Check if an entry is eligible for promotion
|
|
37
|
+
*/
|
|
38
|
+
isEligibleForPromotion(entry: Entry): boolean {
|
|
39
|
+
// Must be troubleshooting type
|
|
40
|
+
if (entry.entryType !== 'troubleshooting') {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Must be at least 'verified' stage
|
|
45
|
+
if (entry.stage !== 'verified' && entry.stage !== 'certified') {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Must be active status
|
|
50
|
+
if (entry.status !== 'active') {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Promote a verified troubleshooting entry to shared storage
|
|
59
|
+
*/
|
|
60
|
+
async promoteEntry(
|
|
61
|
+
entry: Entry,
|
|
62
|
+
projectHash: string
|
|
63
|
+
): Promise<PromotionResult> {
|
|
64
|
+
// Validate eligibility
|
|
65
|
+
if (!this.isEligibleForPromotion(entry)) {
|
|
66
|
+
return {
|
|
67
|
+
success: false,
|
|
68
|
+
skipped: true,
|
|
69
|
+
skipReason: `Entry not eligible: type=${entry.entryType}, stage=${entry.stage}, status=${entry.status}`
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Check if already promoted
|
|
74
|
+
const exists = await this.sharedStore.exists(projectHash, entry.entryId);
|
|
75
|
+
if (exists) {
|
|
76
|
+
return {
|
|
77
|
+
success: true,
|
|
78
|
+
skipped: true,
|
|
79
|
+
skipReason: 'Entry already exists in shared store'
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
const content = entry.contentJson as TroubleshootingContent;
|
|
85
|
+
const confidence = this.calculateConfidence(entry);
|
|
86
|
+
|
|
87
|
+
// Check minimum confidence threshold
|
|
88
|
+
const minConfidence = this.config?.minConfidenceForPromotion ?? 0.8;
|
|
89
|
+
if (confidence < minConfidence) {
|
|
90
|
+
return {
|
|
91
|
+
success: false,
|
|
92
|
+
skipped: true,
|
|
93
|
+
skipReason: `Confidence ${confidence} below threshold ${minConfidence}`
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const input: SharedTroubleshootingInput = {
|
|
98
|
+
sourceProjectHash: projectHash,
|
|
99
|
+
sourceEntryId: entry.entryId,
|
|
100
|
+
title: entry.title,
|
|
101
|
+
symptoms: content.symptoms || [],
|
|
102
|
+
rootCause: content.rootCause || '',
|
|
103
|
+
solution: content.solution || '',
|
|
104
|
+
topics: this.extractTopics(entry),
|
|
105
|
+
technologies: content.technologies || [],
|
|
106
|
+
confidence
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
// Promote to shared store
|
|
110
|
+
const entryId = await this.sharedStore.promoteEntry(input);
|
|
111
|
+
|
|
112
|
+
// Create embedding for vector search
|
|
113
|
+
const embeddingContent = this.createEmbeddingContent(input);
|
|
114
|
+
const embedding = await this.embedder.embed(embeddingContent);
|
|
115
|
+
|
|
116
|
+
await this.sharedVectorStore.upsert({
|
|
117
|
+
id: randomUUID(),
|
|
118
|
+
entryId,
|
|
119
|
+
entryType: 'troubleshooting',
|
|
120
|
+
content: embeddingContent,
|
|
121
|
+
vector: embedding.vector,
|
|
122
|
+
topics: input.topics,
|
|
123
|
+
sourceProjectHash: projectHash
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
success: true,
|
|
128
|
+
entryId
|
|
129
|
+
};
|
|
130
|
+
} catch (error) {
|
|
131
|
+
return {
|
|
132
|
+
success: false,
|
|
133
|
+
error: error instanceof Error ? error.message : String(error)
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Batch promote multiple entries
|
|
140
|
+
*/
|
|
141
|
+
async promoteEntries(
|
|
142
|
+
entries: Entry[],
|
|
143
|
+
projectHash: string
|
|
144
|
+
): Promise<Map<string, PromotionResult>> {
|
|
145
|
+
const results = new Map<string, PromotionResult>();
|
|
146
|
+
|
|
147
|
+
for (const entry of entries) {
|
|
148
|
+
const result = await this.promoteEntry(entry, projectHash);
|
|
149
|
+
results.set(entry.entryId, result);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return results;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Extract topics from entry
|
|
157
|
+
*/
|
|
158
|
+
private extractTopics(entry: Entry): string[] {
|
|
159
|
+
const topics: string[] = [];
|
|
160
|
+
|
|
161
|
+
// Extract from title (meaningful words)
|
|
162
|
+
const titleWords = entry.title
|
|
163
|
+
.toLowerCase()
|
|
164
|
+
.split(/[\s\-_]+/)
|
|
165
|
+
.filter(w => w.length > 3 && !this.isStopWord(w));
|
|
166
|
+
topics.push(...titleWords);
|
|
167
|
+
|
|
168
|
+
// Extract from content if available
|
|
169
|
+
const content = entry.contentJson as Record<string, unknown>;
|
|
170
|
+
if (content.topics && Array.isArray(content.topics)) {
|
|
171
|
+
topics.push(...content.topics.map(t => String(t).toLowerCase()));
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Extract technologies as topics
|
|
175
|
+
if (content.technologies && Array.isArray(content.technologies)) {
|
|
176
|
+
topics.push(...content.technologies.map(t => String(t).toLowerCase()));
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Dedupe and return
|
|
180
|
+
return [...new Set(topics)];
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Check if word is a stop word
|
|
185
|
+
*/
|
|
186
|
+
private isStopWord(word: string): boolean {
|
|
187
|
+
const stopWords = new Set([
|
|
188
|
+
'the', 'and', 'for', 'with', 'this', 'that', 'from', 'have', 'been',
|
|
189
|
+
'were', 'are', 'was', 'had', 'has', 'will', 'would', 'could', 'should',
|
|
190
|
+
'when', 'where', 'what', 'which', 'while', 'error', 'problem', 'issue'
|
|
191
|
+
]);
|
|
192
|
+
return stopWords.has(word);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Calculate confidence score for entry
|
|
197
|
+
*/
|
|
198
|
+
private calculateConfidence(entry: Entry): number {
|
|
199
|
+
let confidence = 0.8; // Base confidence for verified entries
|
|
200
|
+
|
|
201
|
+
// Boost if certified
|
|
202
|
+
if (entry.stage === 'certified') {
|
|
203
|
+
confidence = 0.95;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Could add more factors:
|
|
207
|
+
// - Number of evidence items
|
|
208
|
+
// - Age of entry (older verified entries may be more reliable)
|
|
209
|
+
// - Usage count if tracked
|
|
210
|
+
|
|
211
|
+
return Math.min(confidence, 1.0);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Create embedding content from input
|
|
216
|
+
*/
|
|
217
|
+
private createEmbeddingContent(input: SharedTroubleshootingInput): string {
|
|
218
|
+
const parts: string[] = [];
|
|
219
|
+
|
|
220
|
+
parts.push(`Problem: ${input.title}`);
|
|
221
|
+
|
|
222
|
+
if (input.symptoms.length > 0) {
|
|
223
|
+
parts.push(`Symptoms: ${input.symptoms.join(', ')}`);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (input.rootCause) {
|
|
227
|
+
parts.push(`Root Cause: ${input.rootCause}`);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (input.solution) {
|
|
231
|
+
parts.push(`Solution: ${input.solution}`);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (input.technologies && input.technologies.length > 0) {
|
|
235
|
+
parts.push(`Technologies: ${input.technologies.join(', ')}`);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return parts.join('\n');
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export function createSharedPromoter(
|
|
243
|
+
sharedStore: SharedStore,
|
|
244
|
+
sharedVectorStore: SharedVectorStore,
|
|
245
|
+
embedder: Embedder,
|
|
246
|
+
config?: SharedStoreConfig
|
|
247
|
+
): SharedPromoter {
|
|
248
|
+
return new SharedPromoter(sharedStore, sharedVectorStore, embedder, config);
|
|
249
|
+
}
|