@timmeck/brain-core 2.36.24 → 2.36.26
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/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__/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/dist/utils/logger.js +4 -3
- package/dist/utils/logger.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type Database from 'better-sqlite3';
|
|
2
|
+
export interface ToolSequence {
|
|
3
|
+
sequence: string[];
|
|
4
|
+
count: number;
|
|
5
|
+
}
|
|
6
|
+
export interface ToolPrediction {
|
|
7
|
+
tool: string;
|
|
8
|
+
probability: number;
|
|
9
|
+
}
|
|
10
|
+
export interface ToolPair {
|
|
11
|
+
toolA: string;
|
|
12
|
+
toolB: string;
|
|
13
|
+
count: number;
|
|
14
|
+
}
|
|
15
|
+
/** A single from→to transition with its count. */
|
|
16
|
+
export interface ToolTransition {
|
|
17
|
+
from: string;
|
|
18
|
+
to: string;
|
|
19
|
+
count: number;
|
|
20
|
+
}
|
|
21
|
+
export declare class ToolPatternAnalyzer {
|
|
22
|
+
private db;
|
|
23
|
+
private log;
|
|
24
|
+
private stmtAllUsage;
|
|
25
|
+
private stmtWindowUsage;
|
|
26
|
+
constructor(db: Database.Database);
|
|
27
|
+
/**
|
|
28
|
+
* Find common tool sequences (N-grams) of the given window size.
|
|
29
|
+
* Returns sequences sorted by frequency descending.
|
|
30
|
+
*/
|
|
31
|
+
getSequences(windowSize?: number): ToolSequence[];
|
|
32
|
+
/**
|
|
33
|
+
* Build a Markov transition matrix: which tool follows which.
|
|
34
|
+
* Returns Map<toolA, Map<toolB, count>>.
|
|
35
|
+
*/
|
|
36
|
+
getTransitions(): Map<string, Map<string, number>>;
|
|
37
|
+
/**
|
|
38
|
+
* Predict the top 3 most likely next tools given the current tool,
|
|
39
|
+
* based on transition probabilities from the Markov chain.
|
|
40
|
+
*/
|
|
41
|
+
predictNext(currentTool: string): ToolPrediction[];
|
|
42
|
+
/**
|
|
43
|
+
* Find tool pairs that frequently occur together within 5-minute windows.
|
|
44
|
+
* Returns pairs sorted by co-occurrence count descending.
|
|
45
|
+
*/
|
|
46
|
+
getFrequentPairs(): ToolPair[];
|
|
47
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// ── Tool Pattern Analyzer — Sequence & Transition Analysis ───
|
|
2
|
+
//
|
|
3
|
+
// Analyzes tool usage sequences to find patterns, build Markov
|
|
4
|
+
// transition matrices, and predict likely next tools.
|
|
5
|
+
import { getLogger } from '../utils/logger.js';
|
|
6
|
+
// ── Engine ──────────────────────────────────────────────
|
|
7
|
+
export class ToolPatternAnalyzer {
|
|
8
|
+
db;
|
|
9
|
+
log = getLogger();
|
|
10
|
+
// Prepared statements
|
|
11
|
+
stmtAllUsage;
|
|
12
|
+
stmtWindowUsage;
|
|
13
|
+
constructor(db) {
|
|
14
|
+
this.db = db;
|
|
15
|
+
// These statements query the tool_usage table created by ToolTracker
|
|
16
|
+
this.stmtAllUsage = db.prepare(`
|
|
17
|
+
SELECT tool_name, created_at FROM tool_usage ORDER BY created_at ASC
|
|
18
|
+
`);
|
|
19
|
+
this.stmtWindowUsage = db.prepare(`
|
|
20
|
+
SELECT tool_name, created_at FROM tool_usage ORDER BY created_at ASC
|
|
21
|
+
`);
|
|
22
|
+
this.log.info('[tool-patterns] Analyzer initialized');
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Find common tool sequences (N-grams) of the given window size.
|
|
26
|
+
* Returns sequences sorted by frequency descending.
|
|
27
|
+
*/
|
|
28
|
+
getSequences(windowSize = 3) {
|
|
29
|
+
const rows = this.stmtAllUsage.all();
|
|
30
|
+
if (rows.length < windowSize)
|
|
31
|
+
return [];
|
|
32
|
+
const seqMap = new Map();
|
|
33
|
+
for (let i = 0; i <= rows.length - windowSize; i++) {
|
|
34
|
+
const seq = rows.slice(i, i + windowSize).map(r => r.tool_name);
|
|
35
|
+
const key = seq.join(' → ');
|
|
36
|
+
seqMap.set(key, (seqMap.get(key) ?? 0) + 1);
|
|
37
|
+
}
|
|
38
|
+
const sequences = [];
|
|
39
|
+
for (const [key, count] of seqMap.entries()) {
|
|
40
|
+
if (count >= 2) {
|
|
41
|
+
sequences.push({ sequence: key.split(' → '), count });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
sequences.sort((a, b) => b.count - a.count);
|
|
45
|
+
return sequences;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Build a Markov transition matrix: which tool follows which.
|
|
49
|
+
* Returns Map<toolA, Map<toolB, count>>.
|
|
50
|
+
*/
|
|
51
|
+
getTransitions() {
|
|
52
|
+
const rows = this.stmtAllUsage.all();
|
|
53
|
+
const transitions = new Map();
|
|
54
|
+
for (let i = 0; i < rows.length - 1; i++) {
|
|
55
|
+
const from = rows[i].tool_name;
|
|
56
|
+
const to = rows[i + 1].tool_name;
|
|
57
|
+
if (!transitions.has(from)) {
|
|
58
|
+
transitions.set(from, new Map());
|
|
59
|
+
}
|
|
60
|
+
const inner = transitions.get(from);
|
|
61
|
+
inner.set(to, (inner.get(to) ?? 0) + 1);
|
|
62
|
+
}
|
|
63
|
+
return transitions;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Predict the top 3 most likely next tools given the current tool,
|
|
67
|
+
* based on transition probabilities from the Markov chain.
|
|
68
|
+
*/
|
|
69
|
+
predictNext(currentTool) {
|
|
70
|
+
const transitions = this.getTransitions();
|
|
71
|
+
const fromMap = transitions.get(currentTool);
|
|
72
|
+
if (!fromMap || fromMap.size === 0)
|
|
73
|
+
return [];
|
|
74
|
+
// Calculate total transitions from this tool
|
|
75
|
+
let total = 0;
|
|
76
|
+
for (const count of fromMap.values()) {
|
|
77
|
+
total += count;
|
|
78
|
+
}
|
|
79
|
+
const predictions = [];
|
|
80
|
+
for (const [tool, count] of fromMap.entries()) {
|
|
81
|
+
predictions.push({
|
|
82
|
+
tool,
|
|
83
|
+
probability: count / total,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
predictions.sort((a, b) => b.probability - a.probability);
|
|
87
|
+
return predictions.slice(0, 3);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Find tool pairs that frequently occur together within 5-minute windows.
|
|
91
|
+
* Returns pairs sorted by co-occurrence count descending.
|
|
92
|
+
*/
|
|
93
|
+
getFrequentPairs() {
|
|
94
|
+
const rows = this.stmtWindowUsage.all();
|
|
95
|
+
if (rows.length < 2)
|
|
96
|
+
return [];
|
|
97
|
+
const pairMap = new Map();
|
|
98
|
+
const WINDOW_MS = 5 * 60 * 1000; // 5 minutes
|
|
99
|
+
for (let i = 0; i < rows.length; i++) {
|
|
100
|
+
const timeI = new Date(rows[i].created_at).getTime();
|
|
101
|
+
for (let j = i + 1; j < rows.length; j++) {
|
|
102
|
+
const timeJ = new Date(rows[j].created_at).getTime();
|
|
103
|
+
if (timeJ - timeI > WINDOW_MS)
|
|
104
|
+
break;
|
|
105
|
+
const toolA = rows[i].tool_name;
|
|
106
|
+
const toolB = rows[j].tool_name;
|
|
107
|
+
if (toolA === toolB)
|
|
108
|
+
continue;
|
|
109
|
+
// Normalize pair key (alphabetical order) so A-B and B-A count together
|
|
110
|
+
const key = toolA < toolB ? `${toolA}|${toolB}` : `${toolB}|${toolA}`;
|
|
111
|
+
pairMap.set(key, (pairMap.get(key) ?? 0) + 1);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const pairs = [];
|
|
115
|
+
for (const [key, count] of pairMap.entries()) {
|
|
116
|
+
if (count >= 2) {
|
|
117
|
+
const [toolA, toolB] = key.split('|');
|
|
118
|
+
pairs.push({ toolA, toolB, count });
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
pairs.sort((a, b) => b.count - a.count);
|
|
122
|
+
return pairs;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=tool-patterns.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-patterns.js","sourceRoot":"","sources":["../../src/tool-learning/tool-patterns.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type Database from 'better-sqlite3';
|
|
2
|
+
import type { ThoughtStream } from '../consciousness/thought-stream.js';
|
|
3
|
+
export type ToolOutcome = 'success' | 'failure' | 'partial';
|
|
4
|
+
export interface ToolUsageRecord {
|
|
5
|
+
id?: number;
|
|
6
|
+
tool_name: string;
|
|
7
|
+
context: string | null;
|
|
8
|
+
duration_ms: number | null;
|
|
9
|
+
outcome: ToolOutcome;
|
|
10
|
+
metadata: Record<string, unknown> | null;
|
|
11
|
+
created_at: string;
|
|
12
|
+
}
|
|
13
|
+
export interface ToolStats {
|
|
14
|
+
tool: string;
|
|
15
|
+
totalUses: number;
|
|
16
|
+
successRate: number;
|
|
17
|
+
avgDuration: number;
|
|
18
|
+
lastUsed: string;
|
|
19
|
+
}
|
|
20
|
+
export interface ToolRecommendation {
|
|
21
|
+
tool: string;
|
|
22
|
+
score: number;
|
|
23
|
+
successRate: number;
|
|
24
|
+
frequency: number;
|
|
25
|
+
}
|
|
26
|
+
export interface ToolTrackerStatus {
|
|
27
|
+
totalTracked: number;
|
|
28
|
+
uniqueTools: number;
|
|
29
|
+
avgSuccessRate: number;
|
|
30
|
+
}
|
|
31
|
+
export interface ToolTrackerConfig {
|
|
32
|
+
brainName: string;
|
|
33
|
+
}
|
|
34
|
+
export declare function runToolTrackerMigration(db: Database.Database): void;
|
|
35
|
+
export declare class ToolTracker {
|
|
36
|
+
private db;
|
|
37
|
+
private config;
|
|
38
|
+
private ts;
|
|
39
|
+
private log;
|
|
40
|
+
private stmtInsert;
|
|
41
|
+
private stmtStatsSingle;
|
|
42
|
+
private stmtStatsAll;
|
|
43
|
+
private stmtRecommend;
|
|
44
|
+
private stmtTotalTracked;
|
|
45
|
+
private stmtUniqueTools;
|
|
46
|
+
private stmtAvgSuccessRate;
|
|
47
|
+
constructor(db: Database.Database, config: ToolTrackerConfig);
|
|
48
|
+
/** Set the ThoughtStream for consciousness integration. */
|
|
49
|
+
setThoughtStream(stream: ThoughtStream): void;
|
|
50
|
+
/** Record a tool usage event. */
|
|
51
|
+
recordUsage(tool: string, context: string | null, duration: number | null, outcome?: ToolOutcome, metadata?: Record<string, unknown>): void;
|
|
52
|
+
/** Get statistics for a specific tool, or all tools if none specified. */
|
|
53
|
+
getToolStats(tool?: string): ToolStats | ToolStats[];
|
|
54
|
+
/** Recommend top-5 tools for a given context based on success rate * frequency. */
|
|
55
|
+
recommend(context: string): ToolRecommendation[];
|
|
56
|
+
/** Get tracker status summary. */
|
|
57
|
+
getStatus(): ToolTrackerStatus;
|
|
58
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
// ── Tool Tracker — Usage Recording & Recommendation ──────────
|
|
2
|
+
//
|
|
3
|
+
// Tracks tool usage with context, duration, and outcome.
|
|
4
|
+
// Provides statistics and context-based recommendations.
|
|
5
|
+
import { getLogger } from '../utils/logger.js';
|
|
6
|
+
// ── Migration ───────────────────────────────────────────
|
|
7
|
+
export function runToolTrackerMigration(db) {
|
|
8
|
+
db.exec(`
|
|
9
|
+
CREATE TABLE IF NOT EXISTS tool_usage (
|
|
10
|
+
id INTEGER PRIMARY KEY,
|
|
11
|
+
tool_name TEXT NOT NULL,
|
|
12
|
+
context TEXT,
|
|
13
|
+
duration_ms INTEGER,
|
|
14
|
+
outcome TEXT DEFAULT 'success' CHECK(outcome IN ('success','failure','partial')),
|
|
15
|
+
metadata TEXT,
|
|
16
|
+
created_at TEXT DEFAULT (datetime('now'))
|
|
17
|
+
);
|
|
18
|
+
CREATE INDEX IF NOT EXISTS idx_tool_usage_tool_name ON tool_usage(tool_name);
|
|
19
|
+
`);
|
|
20
|
+
}
|
|
21
|
+
// ── Engine ──────────────────────────────────────────────
|
|
22
|
+
export class ToolTracker {
|
|
23
|
+
db;
|
|
24
|
+
config;
|
|
25
|
+
ts = null;
|
|
26
|
+
log = getLogger();
|
|
27
|
+
// Prepared statements
|
|
28
|
+
stmtInsert;
|
|
29
|
+
stmtStatsSingle;
|
|
30
|
+
stmtStatsAll;
|
|
31
|
+
stmtRecommend;
|
|
32
|
+
stmtTotalTracked;
|
|
33
|
+
stmtUniqueTools;
|
|
34
|
+
stmtAvgSuccessRate;
|
|
35
|
+
constructor(db, config) {
|
|
36
|
+
this.db = db;
|
|
37
|
+
this.config = config;
|
|
38
|
+
runToolTrackerMigration(db);
|
|
39
|
+
// Prepare all statements
|
|
40
|
+
this.stmtInsert = db.prepare(`
|
|
41
|
+
INSERT INTO tool_usage (tool_name, context, duration_ms, outcome, metadata)
|
|
42
|
+
VALUES (?, ?, ?, ?, ?)
|
|
43
|
+
`);
|
|
44
|
+
this.stmtStatsSingle = db.prepare(`
|
|
45
|
+
SELECT
|
|
46
|
+
tool_name AS tool,
|
|
47
|
+
COUNT(*) AS totalUses,
|
|
48
|
+
CAST(SUM(CASE WHEN outcome = 'success' THEN 1 ELSE 0 END) AS REAL) / COUNT(*) AS successRate,
|
|
49
|
+
COALESCE(AVG(duration_ms), 0) AS avgDuration,
|
|
50
|
+
MAX(created_at) AS lastUsed
|
|
51
|
+
FROM tool_usage
|
|
52
|
+
WHERE tool_name = ?
|
|
53
|
+
GROUP BY tool_name
|
|
54
|
+
`);
|
|
55
|
+
this.stmtStatsAll = db.prepare(`
|
|
56
|
+
SELECT
|
|
57
|
+
tool_name AS tool,
|
|
58
|
+
COUNT(*) AS totalUses,
|
|
59
|
+
CAST(SUM(CASE WHEN outcome = 'success' THEN 1 ELSE 0 END) AS REAL) / COUNT(*) AS successRate,
|
|
60
|
+
COALESCE(AVG(duration_ms), 0) AS avgDuration,
|
|
61
|
+
MAX(created_at) AS lastUsed
|
|
62
|
+
FROM tool_usage
|
|
63
|
+
GROUP BY tool_name
|
|
64
|
+
ORDER BY totalUses DESC
|
|
65
|
+
`);
|
|
66
|
+
this.stmtRecommend = db.prepare(`
|
|
67
|
+
SELECT
|
|
68
|
+
tool_name AS tool,
|
|
69
|
+
COUNT(*) AS frequency,
|
|
70
|
+
CAST(SUM(CASE WHEN outcome = 'success' THEN 1 ELSE 0 END) AS REAL) / COUNT(*) AS successRate
|
|
71
|
+
FROM tool_usage
|
|
72
|
+
WHERE context LIKE ?
|
|
73
|
+
GROUP BY tool_name
|
|
74
|
+
ORDER BY (CAST(SUM(CASE WHEN outcome = 'success' THEN 1 ELSE 0 END) AS REAL) / COUNT(*)) * COUNT(*) DESC
|
|
75
|
+
LIMIT 5
|
|
76
|
+
`);
|
|
77
|
+
this.stmtTotalTracked = db.prepare(`
|
|
78
|
+
SELECT COUNT(*) AS total FROM tool_usage
|
|
79
|
+
`);
|
|
80
|
+
this.stmtUniqueTools = db.prepare(`
|
|
81
|
+
SELECT COUNT(DISTINCT tool_name) AS unique_tools FROM tool_usage
|
|
82
|
+
`);
|
|
83
|
+
this.stmtAvgSuccessRate = db.prepare(`
|
|
84
|
+
SELECT COALESCE(
|
|
85
|
+
CAST(SUM(CASE WHEN outcome = 'success' THEN 1 ELSE 0 END) AS REAL) / NULLIF(COUNT(*), 0),
|
|
86
|
+
0
|
|
87
|
+
) AS avgRate
|
|
88
|
+
FROM tool_usage
|
|
89
|
+
`);
|
|
90
|
+
this.log.info(`[tool-tracker] Initialized for ${config.brainName}`);
|
|
91
|
+
}
|
|
92
|
+
/** Set the ThoughtStream for consciousness integration. */
|
|
93
|
+
setThoughtStream(stream) {
|
|
94
|
+
this.ts = stream;
|
|
95
|
+
}
|
|
96
|
+
/** Record a tool usage event. */
|
|
97
|
+
recordUsage(tool, context, duration, outcome = 'success', metadata) {
|
|
98
|
+
this.stmtInsert.run(tool, context, duration, outcome, metadata ? JSON.stringify(metadata) : null);
|
|
99
|
+
this.ts?.emit('tool-tracker', 'analyzing', `Recorded ${tool} usage: ${outcome}${duration ? ` (${duration}ms)` : ''}`, 'routine');
|
|
100
|
+
}
|
|
101
|
+
/** Get statistics for a specific tool, or all tools if none specified. */
|
|
102
|
+
getToolStats(tool) {
|
|
103
|
+
if (tool) {
|
|
104
|
+
const row = this.stmtStatsSingle.get(tool);
|
|
105
|
+
if (!row) {
|
|
106
|
+
return { tool, totalUses: 0, successRate: 0, avgDuration: 0, lastUsed: '' };
|
|
107
|
+
}
|
|
108
|
+
return row;
|
|
109
|
+
}
|
|
110
|
+
return this.stmtStatsAll.all();
|
|
111
|
+
}
|
|
112
|
+
/** Recommend top-5 tools for a given context based on success rate * frequency. */
|
|
113
|
+
recommend(context) {
|
|
114
|
+
const pattern = `%${context}%`;
|
|
115
|
+
const rows = this.stmtRecommend.all(pattern);
|
|
116
|
+
return rows.map(r => ({
|
|
117
|
+
tool: r.tool,
|
|
118
|
+
score: r.successRate * r.frequency,
|
|
119
|
+
successRate: r.successRate,
|
|
120
|
+
frequency: r.frequency,
|
|
121
|
+
}));
|
|
122
|
+
}
|
|
123
|
+
/** Get tracker status summary. */
|
|
124
|
+
getStatus() {
|
|
125
|
+
const total = this.stmtTotalTracked.get().total;
|
|
126
|
+
const unique = this.stmtUniqueTools.get().unique_tools;
|
|
127
|
+
const avgRate = this.stmtAvgSuccessRate.get().avgRate;
|
|
128
|
+
return {
|
|
129
|
+
totalTracked: total,
|
|
130
|
+
uniqueTools: unique,
|
|
131
|
+
avgSuccessRate: avgRate,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=tool-tracker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-tracker.js","sourceRoot":"","sources":["../../src/tool-learning/tool-tracker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,142 @@
|
|
|
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 { UserModel, runUserModelMigration } from '../user-model.js';
|
|
9
|
+
import { AdaptiveContext } from '../adaptive-context.js';
|
|
10
|
+
function createDb() {
|
|
11
|
+
return new Database(':memory:');
|
|
12
|
+
}
|
|
13
|
+
describe('UserModel', () => {
|
|
14
|
+
let db;
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
db = createDb();
|
|
17
|
+
});
|
|
18
|
+
afterEach(() => {
|
|
19
|
+
db.close();
|
|
20
|
+
});
|
|
21
|
+
it('sets and gets a preference', () => {
|
|
22
|
+
const model = new UserModel(db, { brainName: 'test' });
|
|
23
|
+
model.setPreference('theme', 'dark');
|
|
24
|
+
expect(model.getPreference('theme')).toBe('dark');
|
|
25
|
+
});
|
|
26
|
+
it('returns null for unknown preference', () => {
|
|
27
|
+
const model = new UserModel(db, { brainName: 'test' });
|
|
28
|
+
expect(model.getPreference('nonexistent')).toBeNull();
|
|
29
|
+
});
|
|
30
|
+
it('updateFromInteraction tracks tool frequency', () => {
|
|
31
|
+
const model = new UserModel(db, { brainName: 'test' });
|
|
32
|
+
model.updateFromInteraction('mcp.search', 'finding code', 'success');
|
|
33
|
+
model.updateFromInteraction('mcp.search', 'finding docs', 'success');
|
|
34
|
+
const profile = model.getProfile();
|
|
35
|
+
expect(profile.topTools).toContain('mcp.search');
|
|
36
|
+
});
|
|
37
|
+
it('infers beginner skill level for < 3 successful uses', () => {
|
|
38
|
+
const model = new UserModel(db, { brainName: 'test' });
|
|
39
|
+
model.updateFromInteraction('trading.analyze', 'stocks', 'success');
|
|
40
|
+
model.updateFromInteraction('trading.analyze', 'stocks', 'success');
|
|
41
|
+
const profile = model.getProfile();
|
|
42
|
+
expect(profile.skillDomains.get('trading')).toBe('beginner');
|
|
43
|
+
});
|
|
44
|
+
it('infers intermediate skill level for 3-10 successful uses', () => {
|
|
45
|
+
const model = new UserModel(db, { brainName: 'test' });
|
|
46
|
+
for (let i = 0; i < 5; i++) {
|
|
47
|
+
model.updateFromInteraction('trading.analyze', 'stocks', 'success');
|
|
48
|
+
}
|
|
49
|
+
const profile = model.getProfile();
|
|
50
|
+
expect(profile.skillDomains.get('trading')).toBe('intermediate');
|
|
51
|
+
});
|
|
52
|
+
it('infers expert skill level for > 10 successful uses', () => {
|
|
53
|
+
const model = new UserModel(db, { brainName: 'test' });
|
|
54
|
+
for (let i = 0; i < 12; i++) {
|
|
55
|
+
model.updateFromInteraction('coding.refactor', 'cleanup', 'success');
|
|
56
|
+
}
|
|
57
|
+
const profile = model.getProfile();
|
|
58
|
+
expect(profile.skillDomains.get('coding')).toBe('expert');
|
|
59
|
+
});
|
|
60
|
+
it('getProfile returns complete structure', () => {
|
|
61
|
+
const model = new UserModel(db, { brainName: 'test' });
|
|
62
|
+
model.updateFromInteraction('mcp.search', 'ctx', 'success');
|
|
63
|
+
const profile = model.getProfile();
|
|
64
|
+
expect(profile.skillDomains).toBeInstanceOf(Map);
|
|
65
|
+
expect(Array.isArray(profile.topTools)).toBe(true);
|
|
66
|
+
expect(Array.isArray(profile.activeHours)).toBe(true);
|
|
67
|
+
expect(Array.isArray(profile.errorPatterns)).toBe(true);
|
|
68
|
+
});
|
|
69
|
+
it('migration is idempotent', () => {
|
|
70
|
+
const model1 = new UserModel(db, { brainName: 'test' });
|
|
71
|
+
model1.setPreference('lang', 'en');
|
|
72
|
+
// Run migration again
|
|
73
|
+
runUserModelMigration(db);
|
|
74
|
+
const model2 = new UserModel(db, { brainName: 'test' });
|
|
75
|
+
expect(model2.getPreference('lang')).toBe('en');
|
|
76
|
+
});
|
|
77
|
+
it('getStatus returns correct counts', () => {
|
|
78
|
+
const model = new UserModel(db, { brainName: 'test' });
|
|
79
|
+
const emptyStatus = model.getStatus();
|
|
80
|
+
expect(emptyStatus.totalKeys).toBe(0);
|
|
81
|
+
expect(emptyStatus.domains).toBe(0);
|
|
82
|
+
expect(emptyStatus.lastUpdated).toBeNull();
|
|
83
|
+
model.updateFromInteraction('mcp.search', 'ctx', 'success');
|
|
84
|
+
const status = model.getStatus();
|
|
85
|
+
expect(status.totalKeys).toBeGreaterThan(0);
|
|
86
|
+
expect(status.domains).toBeGreaterThanOrEqual(1);
|
|
87
|
+
expect(status.lastUpdated).not.toBeNull();
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
describe('AdaptiveContext', () => {
|
|
91
|
+
it('returns concise for all-expert profile', () => {
|
|
92
|
+
const ctx = new AdaptiveContext();
|
|
93
|
+
const profile = {
|
|
94
|
+
skillDomains: new Map([['coding', 'expert'], ['trading', 'expert']]),
|
|
95
|
+
topTools: [],
|
|
96
|
+
activeHours: [],
|
|
97
|
+
errorPatterns: [],
|
|
98
|
+
};
|
|
99
|
+
const result = ctx.enrichPrompt('How do I do X?', profile);
|
|
100
|
+
expect(result).toContain('Be concise');
|
|
101
|
+
expect(result).toContain('How do I do X?');
|
|
102
|
+
expect(ctx.getDetailLevel(profile)).toBe('concise');
|
|
103
|
+
});
|
|
104
|
+
it('returns detailed for profile with any beginner domain', () => {
|
|
105
|
+
const ctx = new AdaptiveContext();
|
|
106
|
+
const profile = {
|
|
107
|
+
skillDomains: new Map([['coding', 'expert'], ['trading', 'beginner']]),
|
|
108
|
+
topTools: [],
|
|
109
|
+
activeHours: [],
|
|
110
|
+
errorPatterns: [],
|
|
111
|
+
};
|
|
112
|
+
const result = ctx.enrichPrompt('How do I do X?', profile);
|
|
113
|
+
expect(result).toContain('Explain in detail');
|
|
114
|
+
expect(result).toContain('How do I do X?');
|
|
115
|
+
expect(ctx.getDetailLevel(profile)).toBe('detailed');
|
|
116
|
+
});
|
|
117
|
+
it('returns unmodified prompt for default/mixed profile', () => {
|
|
118
|
+
const ctx = new AdaptiveContext();
|
|
119
|
+
const profile = {
|
|
120
|
+
skillDomains: new Map([['coding', 'intermediate'], ['trading', 'expert']]),
|
|
121
|
+
topTools: [],
|
|
122
|
+
activeHours: [],
|
|
123
|
+
errorPatterns: [],
|
|
124
|
+
};
|
|
125
|
+
const result = ctx.enrichPrompt('How do I do X?', profile);
|
|
126
|
+
expect(result).toBe('How do I do X?');
|
|
127
|
+
expect(ctx.getDetailLevel(profile)).toBe('normal');
|
|
128
|
+
});
|
|
129
|
+
it('returns normal for empty profile', () => {
|
|
130
|
+
const ctx = new AdaptiveContext();
|
|
131
|
+
const profile = {
|
|
132
|
+
skillDomains: new Map(),
|
|
133
|
+
topTools: [],
|
|
134
|
+
activeHours: [],
|
|
135
|
+
errorPatterns: [],
|
|
136
|
+
};
|
|
137
|
+
const result = ctx.enrichPrompt('Hello', profile);
|
|
138
|
+
expect(result).toBe('Hello');
|
|
139
|
+
expect(ctx.getDetailLevel(profile)).toBe('normal');
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
//# sourceMappingURL=user-model.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user-model.test.js","sourceRoot":"","sources":["../../../src/user-model/__tests__/user-model.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACzE,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAEtC,EAAE,CAAC,IAAI,CAAC,uBAAuB,EAAE,GAAG,EAAE,CAAC,CAAC;IACtC,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;QAChB,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,CAAC,EAAE,EAAE;KAC7D,CAAC;CACH,CAAC,CAAC,CAAC;AAEJ,OAAO,EAAE,SAAS,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAGzD,SAAS,QAAQ;IACf,OAAO,IAAI,QAAQ,CAAC,UAAU,CAAC,CAAC;AAClC,CAAC;AAED,QAAQ,CAAC,WAAW,EAAE,GAAG,EAAE;IACzB,IAAI,EAAqB,CAAC;IAE1B,UAAU,CAAC,GAAG,EAAE;QACd,EAAE,GAAG,QAAQ,EAAE,CAAC;IAClB,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,EAAE,CAAC,KAAK,EAAE,CAAC;IACb,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,MAAM,KAAK,GAAG,IAAI,SAAS,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;QACvD,KAAK,CAAC,aAAa,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAErC,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC7C,MAAM,KAAK,GAAG,IAAI,SAAS,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;QACvD,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,KAAK,GAAG,IAAI,SAAS,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;QACvD,KAAK,CAAC,qBAAqB,CAAC,YAAY,EAAE,cAAc,EAAE,SAAS,CAAC,CAAC;QACrE,KAAK,CAAC,qBAAqB,CAAC,YAAY,EAAE,cAAc,EAAE,SAAS,CAAC,CAAC;QAErE,MAAM,OAAO,GAAG,KAAK,CAAC,UAAU,EAAE,CAAC;QACnC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,MAAM,KAAK,GAAG,IAAI,SAAS,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;QACvD,KAAK,CAAC,qBAAqB,CAAC,iBAAiB,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;QACpE,KAAK,CAAC,qBAAqB,CAAC,iBAAiB,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;QAEpE,MAAM,OAAO,GAAG,KAAK,CAAC,UAAU,EAAE,CAAC;QACnC,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;QAClE,MAAM,KAAK,GAAG,IAAI,SAAS,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;QACvD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAC3B,KAAK,CAAC,qBAAqB,CAAC,iBAAiB,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;QACtE,CAAC;QAED,MAAM,OAAO,GAAG,KAAK,CAAC,UAAU,EAAE,CAAC;QACnC,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;QAC5D,MAAM,KAAK,GAAG,IAAI,SAAS,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;QACvD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;YAC5B,KAAK,CAAC,qBAAqB,CAAC,iBAAiB,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;QACvE,CAAC;QAED,MAAM,OAAO,GAAG,KAAK,CAAC,UAAU,EAAE,CAAC;QACnC,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,MAAM,KAAK,GAAG,IAAI,SAAS,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;QACvD,KAAK,CAAC,qBAAqB,CAAC,YAAY,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;QAE5D,MAAM,OAAO,GAAG,KAAK,CAAC,UAAU,EAAE,CAAC;QACnC,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;QACjD,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnD,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtD,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yBAAyB,EAAE,GAAG,EAAE;QACjC,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;QACxD,MAAM,CAAC,aAAa,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAEnC,sBAAsB;QACtB,qBAAqB,CAAC,EAAE,CAAC,CAAC;QAC1B,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;QAExD,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC1C,MAAM,KAAK,GAAG,IAAI,SAAS,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;QAEvD,MAAM,WAAW,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC;QACtC,MAAM,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACtC,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpC,MAAM,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC,QAAQ,EAAE,CAAC;QAE3C,KAAK,CAAC,qBAAqB,CAAC,YAAY,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;QAE5D,MAAM,MAAM,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC;QACjC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAC5C,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC;QACjD,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;IAC5C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;IAC/B,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,MAAM,GAAG,GAAG,IAAI,eAAe,EAAE,CAAC;QAClC,MAAM,OAAO,GAAgB;YAC3B,YAAY,EAAE,IAAI,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC;YACpE,QAAQ,EAAE,EAAE;YACZ,WAAW,EAAE,EAAE;YACf,aAAa,EAAE,EAAE;SAClB,CAAC;QAEF,MAAM,MAAM,GAAG,GAAG,CAAC,YAAY,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC;QAC3D,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;QACvC,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;QAC3C,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uDAAuD,EAAE,GAAG,EAAE;QAC/D,MAAM,GAAG,GAAG,IAAI,eAAe,EAAE,CAAC;QAClC,MAAM,OAAO,GAAgB;YAC3B,YAAY,EAAE,IAAI,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC;YACtE,QAAQ,EAAE,EAAE;YACZ,WAAW,EAAE,EAAE;YACf,aAAa,EAAE,EAAE;SAClB,CAAC;QAEF,MAAM,MAAM,GAAG,GAAG,CAAC,YAAY,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC;QAC3D,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;QAC9C,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;QAC3C,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,MAAM,GAAG,GAAG,IAAI,eAAe,EAAE,CAAC;QAClC,MAAM,OAAO,GAAgB;YAC3B,YAAY,EAAE,IAAI,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,cAAc,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC;YAC1E,QAAQ,EAAE,EAAE;YACZ,WAAW,EAAE,EAAE;YACf,aAAa,EAAE,EAAE;SAClB,CAAC;QAEF,MAAM,MAAM,GAAG,GAAG,CAAC,YAAY,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC;QAC3D,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;QACtC,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC1C,MAAM,GAAG,GAAG,IAAI,eAAe,EAAE,CAAC;QAClC,MAAM,OAAO,GAAgB;YAC3B,YAAY,EAAE,IAAI,GAAG,EAAE;YACvB,QAAQ,EAAE,EAAE;YACZ,WAAW,EAAE,EAAE;YACf,aAAa,EAAE,EAAE;SAClB,CAAC;QAEF,MAAM,MAAM,GAAG,GAAG,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAClD,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC7B,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { UserProfile } from './user-model.js';
|
|
2
|
+
export type DetailLevel = 'concise' | 'normal' | 'detailed';
|
|
3
|
+
export declare class AdaptiveContext {
|
|
4
|
+
/**
|
|
5
|
+
* Enrich a base prompt based on the user's profile.
|
|
6
|
+
* - If all domains are 'expert': prepend concise instructions.
|
|
7
|
+
* - If any domain is 'beginner': prepend detailed instructions.
|
|
8
|
+
* - Default: return prompt unmodified.
|
|
9
|
+
*/
|
|
10
|
+
enrichPrompt(basePrompt: string, profile: UserProfile): string;
|
|
11
|
+
/**
|
|
12
|
+
* Determine the appropriate detail level from a user profile.
|
|
13
|
+
* - 'concise': all domains are expert level
|
|
14
|
+
* - 'detailed': any domain is beginner level
|
|
15
|
+
* - 'normal': default / mixed intermediate
|
|
16
|
+
*/
|
|
17
|
+
getDetailLevel(profile: UserProfile): DetailLevel;
|
|
18
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// ── Adaptive Context — Prompt Enrichment Based on User Profile ─
|
|
2
|
+
//
|
|
3
|
+
// Adjusts prompt detail level based on the user's skill profile.
|
|
4
|
+
// Expert users get concise responses; beginners get detailed explanations.
|
|
5
|
+
// ── Functions ───────────────────────────────────────────
|
|
6
|
+
export class AdaptiveContext {
|
|
7
|
+
/**
|
|
8
|
+
* Enrich a base prompt based on the user's profile.
|
|
9
|
+
* - If all domains are 'expert': prepend concise instructions.
|
|
10
|
+
* - If any domain is 'beginner': prepend detailed instructions.
|
|
11
|
+
* - Default: return prompt unmodified.
|
|
12
|
+
*/
|
|
13
|
+
enrichPrompt(basePrompt, profile) {
|
|
14
|
+
const level = this.getDetailLevel(profile);
|
|
15
|
+
switch (level) {
|
|
16
|
+
case 'concise':
|
|
17
|
+
return `Be concise. Skip basics.\n\n${basePrompt}`;
|
|
18
|
+
case 'detailed':
|
|
19
|
+
return `Explain in detail with examples.\n\n${basePrompt}`;
|
|
20
|
+
default:
|
|
21
|
+
return basePrompt;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Determine the appropriate detail level from a user profile.
|
|
26
|
+
* - 'concise': all domains are expert level
|
|
27
|
+
* - 'detailed': any domain is beginner level
|
|
28
|
+
* - 'normal': default / mixed intermediate
|
|
29
|
+
*/
|
|
30
|
+
getDetailLevel(profile) {
|
|
31
|
+
const domains = profile.skillDomains;
|
|
32
|
+
if (domains.size === 0)
|
|
33
|
+
return 'normal';
|
|
34
|
+
const levels = Array.from(domains.values());
|
|
35
|
+
// If any domain is beginner, give detailed responses
|
|
36
|
+
if (levels.some(l => l === 'beginner')) {
|
|
37
|
+
return 'detailed';
|
|
38
|
+
}
|
|
39
|
+
// If ALL domains are expert, be concise
|
|
40
|
+
if (levels.every(l => l === 'expert')) {
|
|
41
|
+
return 'concise';
|
|
42
|
+
}
|
|
43
|
+
return 'normal';
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=adaptive-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adaptive-context.js","sourceRoot":"","sources":["../../src/user-model/adaptive-context.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,EAAE;AACF,iEAAiE;AACjE,2EAA2E;AAQ3E,2DAA2D;AAE3D,MAAM,OAAO,eAAe;IAC1B;;;;;OAKG;IACH,YAAY,CAAC,UAAkB,EAAE,OAAoB;QACnD,MAAM,KAAK,GAAG,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;QAE3C,QAAQ,KAAK,EAAE,CAAC;YACd,KAAK,SAAS;gBACZ,OAAO,+BAA+B,UAAU,EAAE,CAAC;YACrD,KAAK,UAAU;gBACb,OAAO,uCAAuC,UAAU,EAAE,CAAC;YAC7D;gBACE,OAAO,UAAU,CAAC;QACtB,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACH,cAAc,CAAC,OAAoB;QACjC,MAAM,OAAO,GAAG,OAAO,CAAC,YAAY,CAAC;QAErC,IAAI,OAAO,CAAC,IAAI,KAAK,CAAC;YAAE,OAAO,QAAQ,CAAC;QAExC,MAAM,MAAM,GAAiB,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QAE1D,qDAAqD;QACrD,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,UAAU,CAAC,EAAE,CAAC;YACvC,OAAO,UAAU,CAAC;QACpB,CAAC;QAED,wCAAwC;QACxC,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,QAAQ,CAAC,EAAE,CAAC;YACtC,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { UserModel, runUserModelMigration } from './user-model.js';
|
|
2
|
+
export { AdaptiveContext } from './adaptive-context.js';
|
|
3
|
+
export type { SkillLevel, UserProfile, UserModelConfig, UserModelStatus, } from './user-model.js';
|
|
4
|
+
export type { DetailLevel, } from './adaptive-context.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/user-model/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC"}
|