@timmeck/brain-core 2.36.23 → 2.36.25
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/command-center.html +259 -153
- package/dist/active-learning/__tests__/active-learning.test.d.ts +1 -0
- package/dist/active-learning/__tests__/active-learning.test.js +132 -0
- package/dist/active-learning/__tests__/active-learning.test.js.map +1 -0
- package/dist/active-learning/active-learner.d.ts +79 -0
- package/dist/active-learning/active-learner.js +224 -0
- package/dist/active-learning/active-learner.js.map +1 -0
- package/dist/active-learning/index.d.ts +2 -0
- package/dist/active-learning/index.js +2 -0
- package/dist/active-learning/index.js.map +1 -0
- package/dist/code-health/__tests__/code-health.test.d.ts +1 -0
- package/dist/code-health/__tests__/code-health.test.js +123 -0
- package/dist/code-health/__tests__/code-health.test.js.map +1 -0
- package/dist/code-health/health-monitor.d.ts +55 -0
- package/dist/code-health/health-monitor.js +180 -0
- package/dist/code-health/health-monitor.js.map +1 -0
- package/dist/code-health/index.d.ts +2 -0
- package/dist/code-health/index.js +2 -0
- package/dist/code-health/index.js.map +1 -0
- package/dist/consensus/__tests__/consensus.test.d.ts +1 -0
- package/dist/consensus/__tests__/consensus.test.js +159 -0
- package/dist/consensus/__tests__/consensus.test.js.map +1 -0
- package/dist/consensus/consensus-engine.d.ts +81 -0
- package/dist/consensus/consensus-engine.js +237 -0
- package/dist/consensus/consensus-engine.js.map +1 -0
- package/dist/consensus/index.d.ts +2 -0
- package/dist/consensus/index.js +2 -0
- package/dist/consensus/index.js.map +1 -0
- package/dist/feedback/__tests__/feedback-engine.test.d.ts +1 -0
- package/dist/feedback/__tests__/feedback-engine.test.js +156 -0
- package/dist/feedback/__tests__/feedback-engine.test.js.map +1 -0
- package/dist/feedback/feedback-engine.d.ts +61 -0
- package/dist/feedback/feedback-engine.js +203 -0
- package/dist/feedback/feedback-engine.js.map +1 -0
- package/dist/feedback/index.d.ts +2 -0
- package/dist/feedback/index.js +2 -0
- package/dist/feedback/index.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -1
- package/dist/knowledge-graph/__tests__/knowledge-graph.test.d.ts +1 -0
- package/dist/knowledge-graph/__tests__/knowledge-graph.test.js +215 -0
- package/dist/knowledge-graph/__tests__/knowledge-graph.test.js.map +1 -0
- package/dist/knowledge-graph/fact-extractor.d.ts +23 -0
- package/dist/knowledge-graph/fact-extractor.js +70 -0
- package/dist/knowledge-graph/fact-extractor.js.map +1 -0
- package/dist/knowledge-graph/graph-engine.d.ts +78 -0
- package/dist/knowledge-graph/graph-engine.js +276 -0
- package/dist/knowledge-graph/graph-engine.js.map +1 -0
- package/dist/knowledge-graph/index.d.ts +4 -0
- package/dist/knowledge-graph/index.js +3 -0
- package/dist/knowledge-graph/index.js.map +1 -0
- package/dist/proactive/__tests__/proactive-engine.test.d.ts +1 -0
- package/dist/proactive/__tests__/proactive-engine.test.js +183 -0
- package/dist/proactive/__tests__/proactive-engine.test.js.map +1 -0
- package/dist/proactive/index.d.ts +2 -0
- package/dist/proactive/index.js +2 -0
- package/dist/proactive/index.js.map +1 -0
- package/dist/proactive/proactive-engine.d.ts +86 -0
- package/dist/proactive/proactive-engine.js +252 -0
- package/dist/proactive/proactive-engine.js.map +1 -0
- package/dist/rag/__tests__/rag-engine.test.d.ts +1 -0
- package/dist/rag/__tests__/rag-engine.test.js +235 -0
- package/dist/rag/__tests__/rag-engine.test.js.map +1 -0
- package/dist/rag/index.d.ts +4 -0
- package/dist/rag/index.js +3 -0
- package/dist/rag/index.js.map +1 -0
- package/dist/rag/rag-engine.d.ts +98 -0
- package/dist/rag/rag-engine.js +310 -0
- package/dist/rag/rag-engine.js.map +1 -0
- package/dist/rag/rag-indexer.d.ts +52 -0
- package/dist/rag/rag-indexer.js +144 -0
- package/dist/rag/rag-indexer.js.map +1 -0
- package/dist/research/__tests__/autonomy-features.test.d.ts +1 -0
- package/dist/research/__tests__/autonomy-features.test.js +155 -0
- package/dist/research/__tests__/autonomy-features.test.js.map +1 -0
- package/dist/research/__tests__/semantic-compressor.test.d.ts +1 -0
- package/dist/research/__tests__/semantic-compressor.test.js +153 -0
- package/dist/research/__tests__/semantic-compressor.test.js.map +1 -0
- package/dist/research/semantic-compressor.d.ts +55 -0
- package/dist/research/semantic-compressor.js +227 -0
- package/dist/research/semantic-compressor.js.map +1 -0
- package/dist/teaching/__tests__/teaching.test.d.ts +1 -0
- package/dist/teaching/__tests__/teaching.test.js +151 -0
- package/dist/teaching/__tests__/teaching.test.js.map +1 -0
- package/dist/teaching/curriculum.d.ts +32 -0
- package/dist/teaching/curriculum.js +89 -0
- package/dist/teaching/curriculum.js.map +1 -0
- package/dist/teaching/index.d.ts +4 -0
- package/dist/teaching/index.js +3 -0
- package/dist/teaching/index.js.map +1 -0
- package/dist/teaching/teaching-protocol.d.ts +74 -0
- package/dist/teaching/teaching-protocol.js +164 -0
- package/dist/teaching/teaching-protocol.js.map +1 -0
- package/dist/tool-learning/__tests__/tool-learning.test.d.ts +1 -0
- package/dist/tool-learning/__tests__/tool-learning.test.js +187 -0
- package/dist/tool-learning/__tests__/tool-learning.test.js.map +1 -0
- package/dist/tool-learning/index.d.ts +4 -0
- package/dist/tool-learning/index.js +3 -0
- package/dist/tool-learning/index.js.map +1 -0
- package/dist/tool-learning/tool-patterns.d.ts +47 -0
- package/dist/tool-learning/tool-patterns.js +125 -0
- package/dist/tool-learning/tool-patterns.js.map +1 -0
- package/dist/tool-learning/tool-tracker.d.ts +58 -0
- package/dist/tool-learning/tool-tracker.js +135 -0
- package/dist/tool-learning/tool-tracker.js.map +1 -0
- package/dist/user-model/__tests__/user-model.test.d.ts +1 -0
- package/dist/user-model/__tests__/user-model.test.js +142 -0
- package/dist/user-model/__tests__/user-model.test.js.map +1 -0
- package/dist/user-model/adaptive-context.d.ts +18 -0
- package/dist/user-model/adaptive-context.js +46 -0
- package/dist/user-model/adaptive-context.js.map +1 -0
- package/dist/user-model/index.d.ts +4 -0
- package/dist/user-model/index.js +3 -0
- package/dist/user-model/index.js.map +1 -0
- package/dist/user-model/user-model.d.ts +53 -0
- package/dist/user-model/user-model.js +204 -0
- package/dist/user-model/user-model.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { getLogger } from '../utils/logger.js';
|
|
2
|
+
const EXTRACTION_PATTERNS = [
|
|
3
|
+
{ regex: /(.+?)\s+causes\s+(.+)/i, predicate: 'causes' },
|
|
4
|
+
{ regex: /(.+?)\s+solves\s+(.+)/i, predicate: 'solves' },
|
|
5
|
+
{ regex: /(.+?)\s+requires\s+(.+)/i, predicate: 'requires' },
|
|
6
|
+
{ regex: /(.+?)\s+improves\s+(.+)/i, predicate: 'improves' },
|
|
7
|
+
{ regex: /(.+?)\s+prevents\s+(.+)/i, predicate: 'prevents' },
|
|
8
|
+
{ regex: /when\s+(.+?)\s+then\s+(.+)/i, predicate: 'leads_to' },
|
|
9
|
+
];
|
|
10
|
+
// ── Extractor ───────────────────────────────────────────
|
|
11
|
+
export class FactExtractor {
|
|
12
|
+
db;
|
|
13
|
+
config;
|
|
14
|
+
log = getLogger();
|
|
15
|
+
llm = null;
|
|
16
|
+
constructor(db, config) {
|
|
17
|
+
this.db = db;
|
|
18
|
+
this.config = config;
|
|
19
|
+
this.log.info(`[FactExtractor] Initialized for ${this.config.brainName}`);
|
|
20
|
+
}
|
|
21
|
+
// ── Setters ──────────────────────────────────────────
|
|
22
|
+
setLLMService(llm) { this.llm = llm; }
|
|
23
|
+
// ── Extraction Methods ───────────────────────────────
|
|
24
|
+
extractFromInsight(text, sourceId) {
|
|
25
|
+
const facts = [];
|
|
26
|
+
// Split text into sentences for multi-sentence processing
|
|
27
|
+
const sentences = text.split(/[.;!?\n]+/).map(s => s.trim()).filter(s => s.length > 0);
|
|
28
|
+
for (const sentence of sentences) {
|
|
29
|
+
for (const pattern of EXTRACTION_PATTERNS) {
|
|
30
|
+
const match = sentence.match(pattern.regex);
|
|
31
|
+
if (match && match[1] && match[2]) {
|
|
32
|
+
facts.push({
|
|
33
|
+
subject: match[1].trim(),
|
|
34
|
+
predicate: pattern.predicate,
|
|
35
|
+
object: match[2].trim(),
|
|
36
|
+
context: `insight:${sourceId}`,
|
|
37
|
+
confidence: 0.6,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
this.log.debug(`[FactExtractor] Extracted ${facts.length} fact(s) from insight ${sourceId}`);
|
|
43
|
+
return facts;
|
|
44
|
+
}
|
|
45
|
+
extractFromRule(condition, action, sourceId) {
|
|
46
|
+
const facts = [];
|
|
47
|
+
facts.push({
|
|
48
|
+
subject: condition.trim(),
|
|
49
|
+
predicate: 'triggers',
|
|
50
|
+
object: action.trim(),
|
|
51
|
+
context: `rule:${sourceId}`,
|
|
52
|
+
confidence: 0.8,
|
|
53
|
+
});
|
|
54
|
+
this.log.debug(`[FactExtractor] Extracted rule fact: ${condition} triggers ${action}`);
|
|
55
|
+
return facts;
|
|
56
|
+
}
|
|
57
|
+
extractFromErrorSolution(error, solution, context, sourceId) {
|
|
58
|
+
const facts = [];
|
|
59
|
+
facts.push({
|
|
60
|
+
subject: error.trim(),
|
|
61
|
+
predicate: 'solved_by',
|
|
62
|
+
object: solution.trim(),
|
|
63
|
+
context: `error:${sourceId}:${context}`,
|
|
64
|
+
confidence: 0.7,
|
|
65
|
+
});
|
|
66
|
+
this.log.debug(`[FactExtractor] Extracted error-solution fact: ${error} solved_by ${solution}`);
|
|
67
|
+
return facts;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=fact-extractor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fact-extractor.js","sourceRoot":"","sources":["../../src/knowledge-graph/fact-extractor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type Database from 'better-sqlite3';
|
|
2
|
+
import type { ThoughtStream } from '../consciousness/thought-stream.js';
|
|
3
|
+
import type { LLMService } from '../llm/llm-service.js';
|
|
4
|
+
export interface KnowledgeGraphConfig {
|
|
5
|
+
brainName: string;
|
|
6
|
+
/** Maximum transitive inference depth. Default: 3 */
|
|
7
|
+
maxInferenceDepth?: number;
|
|
8
|
+
/** Minimum confidence to consider a fact "high confidence". Default: 0.5 */
|
|
9
|
+
highConfidenceThreshold?: number;
|
|
10
|
+
}
|
|
11
|
+
export interface KnowledgeFact {
|
|
12
|
+
id?: number;
|
|
13
|
+
subject: string;
|
|
14
|
+
predicate: string;
|
|
15
|
+
object: string;
|
|
16
|
+
context: string | null;
|
|
17
|
+
confidence: number;
|
|
18
|
+
evidence_count: number;
|
|
19
|
+
source_type: string | null;
|
|
20
|
+
source_id: string | null;
|
|
21
|
+
created_at: string;
|
|
22
|
+
updated_at: string;
|
|
23
|
+
}
|
|
24
|
+
export interface FactQuery {
|
|
25
|
+
subject?: string;
|
|
26
|
+
predicate?: string;
|
|
27
|
+
object?: string;
|
|
28
|
+
}
|
|
29
|
+
export interface InferenceChain {
|
|
30
|
+
path: KnowledgeFact[];
|
|
31
|
+
startSubject: string;
|
|
32
|
+
endObject: string;
|
|
33
|
+
predicate: string;
|
|
34
|
+
confidence: number;
|
|
35
|
+
}
|
|
36
|
+
export interface Contradiction {
|
|
37
|
+
subject: string;
|
|
38
|
+
predicate: string;
|
|
39
|
+
facts: KnowledgeFact[];
|
|
40
|
+
}
|
|
41
|
+
export interface KnowledgeGraphStatus {
|
|
42
|
+
totalFacts: number;
|
|
43
|
+
predicateDistribution: Record<string, number>;
|
|
44
|
+
avgConfidence: number;
|
|
45
|
+
}
|
|
46
|
+
export declare function runKnowledgeGraphMigration(db: Database.Database): void;
|
|
47
|
+
export declare class KnowledgeGraphEngine {
|
|
48
|
+
private readonly db;
|
|
49
|
+
private readonly config;
|
|
50
|
+
private readonly log;
|
|
51
|
+
private ts;
|
|
52
|
+
private llm;
|
|
53
|
+
private readonly stmtInsertFact;
|
|
54
|
+
private readonly stmtFindExact;
|
|
55
|
+
private readonly stmtUpdateFact;
|
|
56
|
+
private readonly stmtQueryAll;
|
|
57
|
+
private readonly stmtQueryBySubject;
|
|
58
|
+
private readonly stmtQueryByPredicate;
|
|
59
|
+
private readonly stmtQueryByObject;
|
|
60
|
+
private readonly stmtQueryBySubjectPredicate;
|
|
61
|
+
private readonly stmtQueryBySubjectObject;
|
|
62
|
+
private readonly stmtQueryByPredicateObject;
|
|
63
|
+
private readonly stmtQueryExact;
|
|
64
|
+
private readonly stmtGetBySubjectPredicate;
|
|
65
|
+
private readonly stmtGetNeighbors;
|
|
66
|
+
private readonly stmtTotalFacts;
|
|
67
|
+
private readonly stmtPredicateDistribution;
|
|
68
|
+
private readonly stmtAvgConfidence;
|
|
69
|
+
constructor(db: Database.Database, config: KnowledgeGraphConfig);
|
|
70
|
+
setThoughtStream(ts: ThoughtStream): void;
|
|
71
|
+
setLLMService(llm: LLMService): void;
|
|
72
|
+
addFact(subject: string, predicate: string, object: string, context?: string, confidence?: number, sourceType?: string, sourceId?: string): KnowledgeFact;
|
|
73
|
+
query(filter: FactQuery): KnowledgeFact[];
|
|
74
|
+
infer(subject: string, predicate: string): InferenceChain[];
|
|
75
|
+
contradictions(): Contradiction[];
|
|
76
|
+
subgraph(topic: string, depth: number): KnowledgeFact[];
|
|
77
|
+
getStatus(): KnowledgeGraphStatus;
|
|
78
|
+
}
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import { getLogger } from '../utils/logger.js';
|
|
2
|
+
// ── Migration ───────────────────────────────────────────
|
|
3
|
+
export function runKnowledgeGraphMigration(db) {
|
|
4
|
+
db.exec(`
|
|
5
|
+
CREATE TABLE IF NOT EXISTS knowledge_facts (
|
|
6
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
7
|
+
subject TEXT NOT NULL,
|
|
8
|
+
predicate TEXT NOT NULL,
|
|
9
|
+
object TEXT NOT NULL,
|
|
10
|
+
context TEXT,
|
|
11
|
+
confidence REAL DEFAULT 0.5,
|
|
12
|
+
evidence_count INTEGER DEFAULT 1,
|
|
13
|
+
source_type TEXT,
|
|
14
|
+
source_id TEXT,
|
|
15
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
16
|
+
updated_at TEXT DEFAULT (datetime('now'))
|
|
17
|
+
);
|
|
18
|
+
CREATE INDEX IF NOT EXISTS idx_knowledge_facts_subject ON knowledge_facts(subject);
|
|
19
|
+
CREATE INDEX IF NOT EXISTS idx_knowledge_facts_predicate ON knowledge_facts(predicate);
|
|
20
|
+
`);
|
|
21
|
+
}
|
|
22
|
+
// ── Engine ──────────────────────────────────────────────
|
|
23
|
+
export class KnowledgeGraphEngine {
|
|
24
|
+
db;
|
|
25
|
+
config;
|
|
26
|
+
log = getLogger();
|
|
27
|
+
ts = null;
|
|
28
|
+
llm = null;
|
|
29
|
+
// ── Prepared statements ──────────────────────────────
|
|
30
|
+
stmtInsertFact;
|
|
31
|
+
stmtFindExact;
|
|
32
|
+
stmtUpdateFact;
|
|
33
|
+
stmtQueryAll;
|
|
34
|
+
stmtQueryBySubject;
|
|
35
|
+
stmtQueryByPredicate;
|
|
36
|
+
stmtQueryByObject;
|
|
37
|
+
stmtQueryBySubjectPredicate;
|
|
38
|
+
stmtQueryBySubjectObject;
|
|
39
|
+
stmtQueryByPredicateObject;
|
|
40
|
+
stmtQueryExact;
|
|
41
|
+
stmtGetBySubjectPredicate;
|
|
42
|
+
stmtGetNeighbors;
|
|
43
|
+
stmtTotalFacts;
|
|
44
|
+
stmtPredicateDistribution;
|
|
45
|
+
stmtAvgConfidence;
|
|
46
|
+
constructor(db, config) {
|
|
47
|
+
this.db = db;
|
|
48
|
+
this.config = {
|
|
49
|
+
brainName: config.brainName,
|
|
50
|
+
maxInferenceDepth: config.maxInferenceDepth ?? 3,
|
|
51
|
+
highConfidenceThreshold: config.highConfidenceThreshold ?? 0.5,
|
|
52
|
+
};
|
|
53
|
+
runKnowledgeGraphMigration(db);
|
|
54
|
+
// Prepare statements
|
|
55
|
+
this.stmtInsertFact = db.prepare(`
|
|
56
|
+
INSERT INTO knowledge_facts (subject, predicate, object, context, confidence, evidence_count, source_type, source_id)
|
|
57
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
58
|
+
`);
|
|
59
|
+
this.stmtFindExact = db.prepare(`
|
|
60
|
+
SELECT * FROM knowledge_facts WHERE subject = ? AND predicate = ? AND object = ?
|
|
61
|
+
`);
|
|
62
|
+
this.stmtUpdateFact = db.prepare(`
|
|
63
|
+
UPDATE knowledge_facts SET evidence_count = ?, confidence = ?, context = ?, updated_at = datetime('now')
|
|
64
|
+
WHERE id = ?
|
|
65
|
+
`);
|
|
66
|
+
this.stmtQueryAll = db.prepare('SELECT * FROM knowledge_facts ORDER BY confidence DESC');
|
|
67
|
+
this.stmtQueryBySubject = db.prepare('SELECT * FROM knowledge_facts WHERE subject = ? ORDER BY confidence DESC');
|
|
68
|
+
this.stmtQueryByPredicate = db.prepare('SELECT * FROM knowledge_facts WHERE predicate = ? ORDER BY confidence DESC');
|
|
69
|
+
this.stmtQueryByObject = db.prepare('SELECT * FROM knowledge_facts WHERE object = ? ORDER BY confidence DESC');
|
|
70
|
+
this.stmtQueryBySubjectPredicate = db.prepare('SELECT * FROM knowledge_facts WHERE subject = ? AND predicate = ? ORDER BY confidence DESC');
|
|
71
|
+
this.stmtQueryBySubjectObject = db.prepare('SELECT * FROM knowledge_facts WHERE subject = ? AND object = ? ORDER BY confidence DESC');
|
|
72
|
+
this.stmtQueryByPredicateObject = db.prepare('SELECT * FROM knowledge_facts WHERE predicate = ? AND object = ? ORDER BY confidence DESC');
|
|
73
|
+
this.stmtQueryExact = db.prepare('SELECT * FROM knowledge_facts WHERE subject = ? AND predicate = ? AND object = ? ORDER BY confidence DESC');
|
|
74
|
+
this.stmtGetBySubjectPredicate = db.prepare('SELECT * FROM knowledge_facts WHERE subject = ? AND predicate = ? AND confidence > ? ORDER BY confidence DESC');
|
|
75
|
+
this.stmtGetNeighbors = db.prepare('SELECT * FROM knowledge_facts WHERE subject = ? OR object = ?');
|
|
76
|
+
this.stmtTotalFacts = db.prepare('SELECT COUNT(*) as cnt FROM knowledge_facts');
|
|
77
|
+
this.stmtPredicateDistribution = db.prepare('SELECT predicate, COUNT(*) as cnt FROM knowledge_facts GROUP BY predicate ORDER BY cnt DESC');
|
|
78
|
+
this.stmtAvgConfidence = db.prepare('SELECT AVG(confidence) as avg FROM knowledge_facts');
|
|
79
|
+
this.log.info(`[KnowledgeGraph] Initialized for ${this.config.brainName}`);
|
|
80
|
+
}
|
|
81
|
+
// ── Setters ──────────────────────────────────────────
|
|
82
|
+
setThoughtStream(ts) { this.ts = ts; }
|
|
83
|
+
setLLMService(llm) { this.llm = llm; }
|
|
84
|
+
// ── Core Operations ──────────────────────────────────
|
|
85
|
+
addFact(subject, predicate, object, context, confidence, sourceType, sourceId) {
|
|
86
|
+
const existing = this.stmtFindExact.get(subject, predicate, object);
|
|
87
|
+
if (existing) {
|
|
88
|
+
const newEvidenceCount = existing.evidence_count + 1;
|
|
89
|
+
const newConfidence = confidence !== undefined
|
|
90
|
+
? Math.min(1, (existing.confidence + confidence) / 2 + 0.05)
|
|
91
|
+
: Math.min(1, existing.confidence + 0.05);
|
|
92
|
+
const newContext = context ?? existing.context;
|
|
93
|
+
this.stmtUpdateFact.run(newEvidenceCount, newConfidence, newContext, existing.id);
|
|
94
|
+
this.log.debug(`[KnowledgeGraph] Updated fact: (${subject}, ${predicate}, ${object}) evidence=${newEvidenceCount}`);
|
|
95
|
+
this.ts?.emit('knowledge-graph', 'analyzing', `Updated fact: ${subject} ${predicate} ${object}`, 'routine');
|
|
96
|
+
return {
|
|
97
|
+
...existing,
|
|
98
|
+
evidence_count: newEvidenceCount,
|
|
99
|
+
confidence: newConfidence,
|
|
100
|
+
context: newContext,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
const result = this.stmtInsertFact.run(subject, predicate, object, context ?? null, confidence ?? 0.5, 1, sourceType ?? null, sourceId ?? null);
|
|
104
|
+
this.log.debug(`[KnowledgeGraph] Added fact: (${subject}, ${predicate}, ${object})`);
|
|
105
|
+
this.ts?.emit('knowledge-graph', 'discovering', `New fact: ${subject} ${predicate} ${object}`, 'notable');
|
|
106
|
+
return {
|
|
107
|
+
id: Number(result.lastInsertRowid),
|
|
108
|
+
subject,
|
|
109
|
+
predicate,
|
|
110
|
+
object,
|
|
111
|
+
context: context ?? null,
|
|
112
|
+
confidence: confidence ?? 0.5,
|
|
113
|
+
evidence_count: 1,
|
|
114
|
+
source_type: sourceType ?? null,
|
|
115
|
+
source_id: sourceId ?? null,
|
|
116
|
+
created_at: new Date().toISOString(),
|
|
117
|
+
updated_at: new Date().toISOString(),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
query(filter) {
|
|
121
|
+
const hasSubject = filter.subject !== undefined;
|
|
122
|
+
const hasPredicate = filter.predicate !== undefined;
|
|
123
|
+
const hasObject = filter.object !== undefined;
|
|
124
|
+
if (hasSubject && hasPredicate && hasObject) {
|
|
125
|
+
return this.stmtQueryExact.all(filter.subject, filter.predicate, filter.object);
|
|
126
|
+
}
|
|
127
|
+
if (hasSubject && hasPredicate) {
|
|
128
|
+
return this.stmtQueryBySubjectPredicate.all(filter.subject, filter.predicate);
|
|
129
|
+
}
|
|
130
|
+
if (hasSubject && hasObject) {
|
|
131
|
+
return this.stmtQueryBySubjectObject.all(filter.subject, filter.object);
|
|
132
|
+
}
|
|
133
|
+
if (hasPredicate && hasObject) {
|
|
134
|
+
return this.stmtQueryByPredicateObject.all(filter.predicate, filter.object);
|
|
135
|
+
}
|
|
136
|
+
if (hasSubject) {
|
|
137
|
+
return this.stmtQueryBySubject.all(filter.subject);
|
|
138
|
+
}
|
|
139
|
+
if (hasPredicate) {
|
|
140
|
+
return this.stmtQueryByPredicate.all(filter.predicate);
|
|
141
|
+
}
|
|
142
|
+
if (hasObject) {
|
|
143
|
+
return this.stmtQueryByObject.all(filter.object);
|
|
144
|
+
}
|
|
145
|
+
return this.stmtQueryAll.all();
|
|
146
|
+
}
|
|
147
|
+
infer(subject, predicate) {
|
|
148
|
+
const chains = [];
|
|
149
|
+
const maxDepth = this.config.maxInferenceDepth;
|
|
150
|
+
// BFS-style transitive inference
|
|
151
|
+
// If A-rel-B and B-rel-C, then A-rel-C (transitively)
|
|
152
|
+
const visited = new Set();
|
|
153
|
+
const queue = [];
|
|
154
|
+
// Get direct facts for subject+predicate
|
|
155
|
+
const directFacts = this.stmtQueryBySubjectPredicate.all(subject, predicate);
|
|
156
|
+
for (const fact of directFacts) {
|
|
157
|
+
visited.add(fact.object);
|
|
158
|
+
queue.push({ node: fact.object, path: [fact], depth: 1 });
|
|
159
|
+
}
|
|
160
|
+
while (queue.length > 0) {
|
|
161
|
+
const current = queue.shift();
|
|
162
|
+
if (current.depth >= maxDepth) {
|
|
163
|
+
// At max depth, record the chain if it has more than 1 hop
|
|
164
|
+
if (current.path.length > 1) {
|
|
165
|
+
const chainConfidence = current.path.reduce((acc, f) => acc * f.confidence, 1);
|
|
166
|
+
chains.push({
|
|
167
|
+
path: current.path,
|
|
168
|
+
startSubject: subject,
|
|
169
|
+
endObject: current.node,
|
|
170
|
+
predicate,
|
|
171
|
+
confidence: chainConfidence,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
// Follow the chain: current.node -predicate-> ?
|
|
177
|
+
const nextFacts = this.stmtQueryBySubjectPredicate.all(current.node, predicate);
|
|
178
|
+
if (nextFacts.length === 0 && current.path.length > 1) {
|
|
179
|
+
// Dead end with multi-hop path - record chain
|
|
180
|
+
const chainConfidence = current.path.reduce((acc, f) => acc * f.confidence, 1);
|
|
181
|
+
chains.push({
|
|
182
|
+
path: current.path,
|
|
183
|
+
startSubject: subject,
|
|
184
|
+
endObject: current.node,
|
|
185
|
+
predicate,
|
|
186
|
+
confidence: chainConfidence,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
for (const fact of nextFacts) {
|
|
190
|
+
if (!visited.has(fact.object)) {
|
|
191
|
+
visited.add(fact.object);
|
|
192
|
+
queue.push({
|
|
193
|
+
node: fact.object,
|
|
194
|
+
path: [...current.path, fact],
|
|
195
|
+
depth: current.depth + 1,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
this.log.debug(`[KnowledgeGraph] Inferred ${chains.length} chain(s) for (${subject}, ${predicate}, ?)`);
|
|
201
|
+
return chains;
|
|
202
|
+
}
|
|
203
|
+
contradictions() {
|
|
204
|
+
const threshold = this.config.highConfidenceThreshold;
|
|
205
|
+
const result = [];
|
|
206
|
+
// Get all subject+predicate combos that have multiple high-confidence objects
|
|
207
|
+
const allFacts = this.stmtQueryAll.all();
|
|
208
|
+
// Group by subject+predicate
|
|
209
|
+
const groups = new Map();
|
|
210
|
+
for (const fact of allFacts) {
|
|
211
|
+
const key = `${fact.subject}||${fact.predicate}`;
|
|
212
|
+
const list = groups.get(key) ?? [];
|
|
213
|
+
list.push(fact);
|
|
214
|
+
groups.set(key, list);
|
|
215
|
+
}
|
|
216
|
+
for (const [, facts] of groups) {
|
|
217
|
+
// Filter to high-confidence facts
|
|
218
|
+
const highConf = facts.filter(f => f.confidence > threshold);
|
|
219
|
+
if (highConf.length > 1) {
|
|
220
|
+
// Check if objects differ
|
|
221
|
+
const uniqueObjects = new Set(highConf.map(f => f.object));
|
|
222
|
+
if (uniqueObjects.size > 1) {
|
|
223
|
+
result.push({
|
|
224
|
+
subject: highConf[0].subject,
|
|
225
|
+
predicate: highConf[0].predicate,
|
|
226
|
+
facts: highConf,
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
this.log.debug(`[KnowledgeGraph] Found ${result.length} contradiction(s)`);
|
|
232
|
+
this.ts?.emit('knowledge-graph', 'analyzing', `Found ${result.length} contradiction(s)`, result.length > 0 ? 'notable' : 'routine');
|
|
233
|
+
return result;
|
|
234
|
+
}
|
|
235
|
+
subgraph(topic, depth) {
|
|
236
|
+
const visited = new Set();
|
|
237
|
+
const result = [];
|
|
238
|
+
const queue = [{ node: topic, currentDepth: 0 }];
|
|
239
|
+
visited.add(topic);
|
|
240
|
+
while (queue.length > 0) {
|
|
241
|
+
const current = queue.shift();
|
|
242
|
+
if (current.currentDepth >= depth)
|
|
243
|
+
continue;
|
|
244
|
+
const neighbors = this.stmtGetNeighbors.all(current.node, current.node);
|
|
245
|
+
for (const fact of neighbors) {
|
|
246
|
+
// Add fact if not already in result (by id)
|
|
247
|
+
if (!result.some(r => r.id === fact.id)) {
|
|
248
|
+
result.push(fact);
|
|
249
|
+
}
|
|
250
|
+
// Explore both subject and object sides
|
|
251
|
+
const nextNode = fact.subject === current.node ? fact.object : fact.subject;
|
|
252
|
+
if (!visited.has(nextNode)) {
|
|
253
|
+
visited.add(nextNode);
|
|
254
|
+
queue.push({ node: nextNode, currentDepth: current.currentDepth + 1 });
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
this.log.debug(`[KnowledgeGraph] Subgraph for "${topic}" depth=${depth}: ${result.length} fact(s)`);
|
|
259
|
+
return result;
|
|
260
|
+
}
|
|
261
|
+
getStatus() {
|
|
262
|
+
const totalRow = this.stmtTotalFacts.get();
|
|
263
|
+
const distRows = this.stmtPredicateDistribution.all();
|
|
264
|
+
const avgRow = this.stmtAvgConfidence.get();
|
|
265
|
+
const predicateDistribution = {};
|
|
266
|
+
for (const row of distRows) {
|
|
267
|
+
predicateDistribution[row.predicate] = row.cnt;
|
|
268
|
+
}
|
|
269
|
+
return {
|
|
270
|
+
totalFacts: totalRow.cnt,
|
|
271
|
+
predicateDistribution,
|
|
272
|
+
avgConfidence: avgRow.avg ?? 0,
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
//# sourceMappingURL=graph-engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph-engine.js","sourceRoot":"","sources":["../../src/knowledge-graph/graph-engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { KnowledgeGraphEngine, runKnowledgeGraphMigration } from './graph-engine.js';
|
|
2
|
+
export type { KnowledgeGraphConfig, KnowledgeFact, FactQuery, InferenceChain, Contradiction, KnowledgeGraphStatus, } from './graph-engine.js';
|
|
3
|
+
export { FactExtractor } from './fact-extractor.js';
|
|
4
|
+
export type { ExtractedFact, FactExtractorConfig, } from './fact-extractor.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/knowledge-graph/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAC;AAUrF,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import Database from 'better-sqlite3';
|
|
3
|
+
vi.mock('../../utils/logger.js', () => ({
|
|
4
|
+
getLogger: () => ({
|
|
5
|
+
info: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn(),
|
|
6
|
+
}),
|
|
7
|
+
}));
|
|
8
|
+
import { ProactiveEngine, runProactiveMigration } from '../proactive-engine.js';
|
|
9
|
+
function createDb() {
|
|
10
|
+
return new Database(':memory:');
|
|
11
|
+
}
|
|
12
|
+
describe('ProactiveEngine', () => {
|
|
13
|
+
let db;
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
db = createDb();
|
|
16
|
+
});
|
|
17
|
+
afterEach(() => {
|
|
18
|
+
db.close();
|
|
19
|
+
});
|
|
20
|
+
it('creates a suggestion and retrieves it', () => {
|
|
21
|
+
const engine = new ProactiveEngine(db, { brainName: 'test' });
|
|
22
|
+
const ok = engine.createSuggestion('info', 'Test Suggestion', 'A description', 'Do something', 0.7);
|
|
23
|
+
expect(ok).toBe(true);
|
|
24
|
+
const suggestions = engine.getSuggestions();
|
|
25
|
+
expect(suggestions).toHaveLength(1);
|
|
26
|
+
expect(suggestions[0].title).toBe('Test Suggestion');
|
|
27
|
+
expect(suggestions[0].priority).toBe(0.7);
|
|
28
|
+
expect(suggestions[0].type).toBe('info');
|
|
29
|
+
});
|
|
30
|
+
it('returns suggestions sorted by priority descending', () => {
|
|
31
|
+
const engine = new ProactiveEngine(db, { brainName: 'test' });
|
|
32
|
+
engine.createSuggestion('low', 'Low Priority', undefined, undefined, 0.2);
|
|
33
|
+
engine.createSuggestion('high', 'High Priority', undefined, undefined, 0.9);
|
|
34
|
+
engine.createSuggestion('mid', 'Mid Priority', undefined, undefined, 0.5);
|
|
35
|
+
const suggestions = engine.getSuggestions();
|
|
36
|
+
expect(suggestions).toHaveLength(3);
|
|
37
|
+
expect(suggestions[0].title).toBe('High Priority');
|
|
38
|
+
expect(suggestions[1].title).toBe('Mid Priority');
|
|
39
|
+
expect(suggestions[2].title).toBe('Low Priority');
|
|
40
|
+
});
|
|
41
|
+
it('dismisses a suggestion', () => {
|
|
42
|
+
const engine = new ProactiveEngine(db, { brainName: 'test' });
|
|
43
|
+
engine.createSuggestion('info', 'To Dismiss', 'desc');
|
|
44
|
+
const before = engine.getSuggestions();
|
|
45
|
+
expect(before).toHaveLength(1);
|
|
46
|
+
engine.dismiss(before[0].id);
|
|
47
|
+
const after = engine.getSuggestions();
|
|
48
|
+
expect(after).toHaveLength(0);
|
|
49
|
+
});
|
|
50
|
+
it('respects rate limiting (max per hour)', () => {
|
|
51
|
+
const engine = new ProactiveEngine(db, {
|
|
52
|
+
brainName: 'test',
|
|
53
|
+
maxSuggestionsPerHour: 2,
|
|
54
|
+
});
|
|
55
|
+
expect(engine.createSuggestion('a', 'First')).toBe(true);
|
|
56
|
+
expect(engine.createSuggestion('b', 'Second')).toBe(true);
|
|
57
|
+
expect(engine.createSuggestion('c', 'Third')).toBe(false); // Rate limited
|
|
58
|
+
});
|
|
59
|
+
it('checks recurring errors trigger', () => {
|
|
60
|
+
const engine = new ProactiveEngine(db, {
|
|
61
|
+
brainName: 'test',
|
|
62
|
+
recurringThreshold: 3,
|
|
63
|
+
});
|
|
64
|
+
// Create error_memory table and populate
|
|
65
|
+
db.exec(`
|
|
66
|
+
CREATE TABLE error_memory (
|
|
67
|
+
id INTEGER PRIMARY KEY,
|
|
68
|
+
fingerprint TEXT NOT NULL,
|
|
69
|
+
message TEXT NOT NULL
|
|
70
|
+
)
|
|
71
|
+
`);
|
|
72
|
+
// Insert 3 errors with same fingerprint
|
|
73
|
+
const insert = db.prepare('INSERT INTO error_memory (fingerprint, message) VALUES (?, ?)');
|
|
74
|
+
insert.run('null-ref-001', 'Cannot read property of null');
|
|
75
|
+
insert.run('null-ref-001', 'Cannot read property of null');
|
|
76
|
+
insert.run('null-ref-001', 'Cannot read property of null');
|
|
77
|
+
const created = engine.analyze({ db });
|
|
78
|
+
expect(created).toBeGreaterThanOrEqual(1);
|
|
79
|
+
const suggestions = engine.getSuggestions();
|
|
80
|
+
const errorSuggestion = suggestions.find(s => s.type === 'recurring_error');
|
|
81
|
+
expect(errorSuggestion).toBeDefined();
|
|
82
|
+
expect(errorSuggestion.title).toContain('null-ref-001');
|
|
83
|
+
});
|
|
84
|
+
it('checks stale knowledge trigger', () => {
|
|
85
|
+
const engine = new ProactiveEngine(db, {
|
|
86
|
+
brainName: 'test',
|
|
87
|
+
staleDays: 30,
|
|
88
|
+
});
|
|
89
|
+
// Create insights table with old high-confidence insight
|
|
90
|
+
db.exec(`
|
|
91
|
+
CREATE TABLE insights (
|
|
92
|
+
id INTEGER PRIMARY KEY,
|
|
93
|
+
topic TEXT NOT NULL,
|
|
94
|
+
created_at TEXT NOT NULL,
|
|
95
|
+
confidence REAL NOT NULL
|
|
96
|
+
)
|
|
97
|
+
`);
|
|
98
|
+
db.prepare(`
|
|
99
|
+
INSERT INTO insights (topic, created_at, confidence)
|
|
100
|
+
VALUES (?, datetime('now', '-60 days'), 0.9)
|
|
101
|
+
`).run('old-topic');
|
|
102
|
+
const created = engine.analyze({ db });
|
|
103
|
+
expect(created).toBeGreaterThanOrEqual(1);
|
|
104
|
+
const suggestions = engine.getSuggestions();
|
|
105
|
+
const staleSuggestion = suggestions.find(s => s.type === 'stale_knowledge');
|
|
106
|
+
expect(staleSuggestion).toBeDefined();
|
|
107
|
+
expect(staleSuggestion.title).toContain('old-topic');
|
|
108
|
+
});
|
|
109
|
+
it('getStatus returns correct counts', () => {
|
|
110
|
+
const engine = new ProactiveEngine(db, { brainName: 'test' });
|
|
111
|
+
engine.createSuggestion('a', 'Active 1');
|
|
112
|
+
engine.createSuggestion('b', 'Active 2');
|
|
113
|
+
const suggestions = engine.getSuggestions();
|
|
114
|
+
engine.dismiss(suggestions[0].id);
|
|
115
|
+
const status = engine.getStatus();
|
|
116
|
+
expect(status.totalSuggestions).toBe(2);
|
|
117
|
+
expect(status.activeSuggestions).toBe(1);
|
|
118
|
+
expect(status.dismissedCount).toBe(1);
|
|
119
|
+
expect(status.lastAnalysis).toBeNull(); // No analyze() called
|
|
120
|
+
});
|
|
121
|
+
it('migration is idempotent', () => {
|
|
122
|
+
const engine1 = new ProactiveEngine(db, { brainName: 'test' });
|
|
123
|
+
engine1.createSuggestion('test', 'Survives Migration');
|
|
124
|
+
// Run migration again
|
|
125
|
+
runProactiveMigration(db);
|
|
126
|
+
const engine2 = new ProactiveEngine(db, { brainName: 'test' });
|
|
127
|
+
const suggestions = engine2.getSuggestions();
|
|
128
|
+
expect(suggestions).toHaveLength(1);
|
|
129
|
+
expect(suggestions[0].title).toBe('Survives Migration');
|
|
130
|
+
});
|
|
131
|
+
it('returns zero suggestions from empty analysis', () => {
|
|
132
|
+
const engine = new ProactiveEngine(db, { brainName: 'test' });
|
|
133
|
+
// No error_memory, insights, or tool_usage tables exist
|
|
134
|
+
const created = engine.analyze({ db });
|
|
135
|
+
expect(created).toBe(0);
|
|
136
|
+
});
|
|
137
|
+
it('dismissed suggestions not returned by default', () => {
|
|
138
|
+
const engine = new ProactiveEngine(db, { brainName: 'test' });
|
|
139
|
+
engine.createSuggestion('a', 'Visible');
|
|
140
|
+
engine.createSuggestion('b', 'Hidden');
|
|
141
|
+
const all = engine.getSuggestions();
|
|
142
|
+
engine.dismiss(all.find(s => s.title === 'Hidden').id);
|
|
143
|
+
const active = engine.getSuggestions();
|
|
144
|
+
expect(active).toHaveLength(1);
|
|
145
|
+
expect(active[0].title).toBe('Visible');
|
|
146
|
+
const withDismissed = engine.getSuggestions(20, true);
|
|
147
|
+
expect(withDismissed).toHaveLength(2);
|
|
148
|
+
});
|
|
149
|
+
it('deduplicates suggestions with same title', () => {
|
|
150
|
+
const engine = new ProactiveEngine(db, { brainName: 'test' });
|
|
151
|
+
expect(engine.createSuggestion('a', 'Same Title')).toBe(true);
|
|
152
|
+
expect(engine.createSuggestion('b', 'Same Title')).toBe(false); // Duplicate
|
|
153
|
+
const suggestions = engine.getSuggestions();
|
|
154
|
+
expect(suggestions).toHaveLength(1);
|
|
155
|
+
});
|
|
156
|
+
it('checks quick wins trigger (tool_usage table)', () => {
|
|
157
|
+
const engine = new ProactiveEngine(db, { brainName: 'test' });
|
|
158
|
+
// Create tool_usage table
|
|
159
|
+
db.exec(`
|
|
160
|
+
CREATE TABLE IF NOT EXISTS tool_usage (
|
|
161
|
+
id INTEGER PRIMARY KEY,
|
|
162
|
+
tool_name TEXT NOT NULL,
|
|
163
|
+
context TEXT,
|
|
164
|
+
duration_ms INTEGER,
|
|
165
|
+
outcome TEXT DEFAULT 'success',
|
|
166
|
+
metadata TEXT,
|
|
167
|
+
created_at TEXT DEFAULT (datetime('now'))
|
|
168
|
+
)
|
|
169
|
+
`);
|
|
170
|
+
// Insert a tool with high success rate but low usage
|
|
171
|
+
const insert = db.prepare('INSERT INTO tool_usage (tool_name, outcome) VALUES (?, ?)');
|
|
172
|
+
insert.run('rare-but-good', 'success');
|
|
173
|
+
insert.run('rare-but-good', 'success');
|
|
174
|
+
insert.run('rare-but-good', 'success');
|
|
175
|
+
const created = engine.analyze({ db });
|
|
176
|
+
expect(created).toBeGreaterThanOrEqual(1);
|
|
177
|
+
const suggestions = engine.getSuggestions();
|
|
178
|
+
const quickWin = suggestions.find(s => s.type === 'quick_win');
|
|
179
|
+
expect(quickWin).toBeDefined();
|
|
180
|
+
expect(quickWin.title).toContain('rare-but-good');
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
//# sourceMappingURL=proactive-engine.test.js.map
|