@jungjaehoon/mama-core 1.1.2 → 1.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config-loader.d.ts +61 -0
- package/dist/config-loader.js +187 -0
- package/dist/db-adapter/base-adapter.d.ts +76 -0
- package/dist/db-adapter/base-adapter.js +11 -0
- package/dist/db-adapter/index.d.ts +24 -0
- package/dist/db-adapter/index.js +29 -0
- package/dist/db-adapter/sqlite-adapter.d.ts +73 -0
- package/dist/db-adapter/sqlite-adapter.js +330 -0
- package/dist/db-adapter/statement.d.ts +119 -0
- package/dist/db-adapter/statement.js +113 -0
- package/dist/db-manager.d.ts +252 -0
- package/dist/db-manager.js +622 -0
- package/dist/debug-logger.d.ts +32 -0
- package/dist/debug-logger.js +83 -0
- package/dist/decision-formatter.d.ts +172 -0
- package/dist/decision-formatter.js +894 -0
- package/dist/decision-tracker.d.ts +218 -0
- package/dist/decision-tracker.js +531 -0
- package/dist/embedding-cache.d.ts +83 -0
- package/dist/embedding-cache.js +182 -0
- package/dist/embedding-client.d.ts +43 -0
- package/dist/embedding-client.js +132 -0
- package/dist/embedding-server/index.d.ts +65 -0
- package/dist/embedding-server/index.js +397 -0
- package/dist/embedding-server/mobile/auth.d.ts +53 -0
- package/dist/embedding-server/mobile/auth.js +140 -0
- package/dist/embedding-server/mobile/daemon.d.ts +128 -0
- package/dist/embedding-server/mobile/daemon.js +303 -0
- package/dist/embedding-server/mobile/output-parser.d.ts +115 -0
- package/dist/embedding-server/mobile/output-parser.js +241 -0
- package/dist/embedding-server/mobile/session-api.d.ts +57 -0
- package/dist/embedding-server/mobile/session-api.js +261 -0
- package/dist/embedding-server/mobile/session-manager.d.ts +135 -0
- package/dist/embedding-server/mobile/session-manager.js +333 -0
- package/dist/embedding-server/mobile/websocket-handler.d.ts +127 -0
- package/dist/embedding-server/mobile/websocket-handler.js +435 -0
- package/dist/embeddings.d.ts +75 -0
- package/dist/embeddings.js +262 -0
- package/dist/errors.d.ts +131 -0
- package/dist/errors.js +225 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +193 -0
- package/dist/mama-api.d.ts +954 -0
- package/dist/mama-api.js +2210 -0
- package/dist/memory-inject.d.ts +24 -0
- package/dist/memory-inject.js +116 -0
- package/dist/memory-store.d.ts +103 -0
- package/dist/memory-store.js +129 -0
- package/dist/notification-manager.d.ts +7 -0
- package/dist/notification-manager.js +12 -0
- package/dist/ollama-client.d.ts +51 -0
- package/dist/ollama-client.js +308 -0
- package/dist/outcome-tracker.d.ts +165 -0
- package/dist/outcome-tracker.js +315 -0
- package/dist/progress-indicator.d.ts +48 -0
- package/dist/progress-indicator.js +82 -0
- package/dist/query-intent.d.ts +27 -0
- package/dist/query-intent.js +144 -0
- package/dist/relevance-scorer.d.ts +124 -0
- package/dist/relevance-scorer.js +243 -0
- package/dist/test-utils.d.ts +66 -0
- package/dist/test-utils.js +166 -0
- package/dist/tier-validator.d.ts +55 -0
- package/dist/tier-validator.js +216 -0
- package/dist/time-formatter.d.ts +25 -0
- package/dist/time-formatter.js +93 -0
- package/package.json +3 -2
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MAMA (Memory-Augmented MCP Architecture) - Relevance Scorer
|
|
3
|
+
*
|
|
4
|
+
* Relevance scoring formula for decision ranking and top-N selection
|
|
5
|
+
* Tasks: 1.1-1.4, 2.1-2.7 (Relevance scoring and top-N selection)
|
|
6
|
+
* AC #1, #4, #5: Decision relevance, failure priority boost, top-N selection
|
|
7
|
+
*
|
|
8
|
+
* @module relevance-scorer
|
|
9
|
+
* @version 1.0
|
|
10
|
+
* @date 2025-11-14
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Decision object for relevance scoring
|
|
14
|
+
*/
|
|
15
|
+
export interface DecisionWithEmbedding {
|
|
16
|
+
id?: string;
|
|
17
|
+
topic?: string;
|
|
18
|
+
decision: string;
|
|
19
|
+
reasoning?: string | null;
|
|
20
|
+
outcome?: string | null;
|
|
21
|
+
failure_reason?: string | null;
|
|
22
|
+
user_involvement?: string;
|
|
23
|
+
confidence?: number;
|
|
24
|
+
created_at: number;
|
|
25
|
+
updated_at?: number;
|
|
26
|
+
embedding?: Float32Array;
|
|
27
|
+
relevanceScore?: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Query context for relevance calculation
|
|
31
|
+
*/
|
|
32
|
+
export interface QueryContext {
|
|
33
|
+
embedding?: Float32Array;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Formatted context result
|
|
37
|
+
*/
|
|
38
|
+
export interface FormattedContext {
|
|
39
|
+
full: Array<{
|
|
40
|
+
decision_id?: string;
|
|
41
|
+
topic?: string;
|
|
42
|
+
decision: string;
|
|
43
|
+
reasoning?: string | null;
|
|
44
|
+
outcome?: string | null;
|
|
45
|
+
failure_reason?: string | null;
|
|
46
|
+
user_involvement?: string;
|
|
47
|
+
confidence?: number;
|
|
48
|
+
relevanceScore?: number;
|
|
49
|
+
created_at: number;
|
|
50
|
+
}>;
|
|
51
|
+
summary: {
|
|
52
|
+
count: number;
|
|
53
|
+
duration_days: number;
|
|
54
|
+
failures: Array<{
|
|
55
|
+
decision: string;
|
|
56
|
+
reason: string | null | undefined;
|
|
57
|
+
}>;
|
|
58
|
+
} | null;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Calculate relevance score for a single decision
|
|
62
|
+
*
|
|
63
|
+
* Task 1.2: Implement calculateRelevance(decision, queryContext) function
|
|
64
|
+
* AC #1, #4: Relevance scoring with failure priority boost
|
|
65
|
+
*
|
|
66
|
+
* Formula:
|
|
67
|
+
* Relevance = (Recency × 0.2) + (Importance × 0.5) + (Semantic × 0.3)
|
|
68
|
+
*
|
|
69
|
+
* Where:
|
|
70
|
+
* - Recency: exp(-days_since / 30) [30-day half-life]
|
|
71
|
+
* - Importance: OUTCOME_WEIGHTS[outcome]
|
|
72
|
+
* - FAILED: 1.0 (highest - failures are most valuable)
|
|
73
|
+
* - PARTIAL: 0.7
|
|
74
|
+
* - SUCCESS: 0.5
|
|
75
|
+
* - null: 0.3 (ongoing, lowest)
|
|
76
|
+
* - Semantic: cosineSimilarity(decision.embedding, query.embedding)
|
|
77
|
+
*
|
|
78
|
+
* @param decision - Decision object
|
|
79
|
+
* @param queryContext - Query context
|
|
80
|
+
* @returns Relevance score (0.0-1.0)
|
|
81
|
+
*/
|
|
82
|
+
export declare function calculateRelevance(decision: DecisionWithEmbedding, queryContext: QueryContext): number;
|
|
83
|
+
/**
|
|
84
|
+
* Select top N most relevant decisions
|
|
85
|
+
*
|
|
86
|
+
* Task 2.1: Add selectTopDecisions(decisions, queryContext, n=3) function
|
|
87
|
+
* AC #1, #5: Top-N selection with threshold filtering
|
|
88
|
+
*
|
|
89
|
+
* @param decisions - Array of decision objects
|
|
90
|
+
* @param queryContext - Query context with embedding
|
|
91
|
+
* @param n - Number of top decisions to return (default: 3)
|
|
92
|
+
* @returns Top N decisions with relevance scores
|
|
93
|
+
*/
|
|
94
|
+
export declare function selectTopDecisions(decisions: DecisionWithEmbedding[], queryContext: QueryContext, n?: number): DecisionWithEmbedding[];
|
|
95
|
+
/**
|
|
96
|
+
* Format decisions with top-N selection and summary
|
|
97
|
+
*
|
|
98
|
+
* Task 8.2-8.3: Format top 3 in full detail, rest as summary
|
|
99
|
+
* AC #5: Top-N selection with summary
|
|
100
|
+
*
|
|
101
|
+
* @param decisions - All decisions (sorted by relevance)
|
|
102
|
+
* @param topN - Number of decisions to show in full detail (default: 3)
|
|
103
|
+
* @returns Formatted context {full: Array, summary: Object}
|
|
104
|
+
*/
|
|
105
|
+
export declare function formatTopNContext(decisions: DecisionWithEmbedding[], topN?: number): FormattedContext;
|
|
106
|
+
/**
|
|
107
|
+
* Test result for relevance scoring
|
|
108
|
+
*/
|
|
109
|
+
export interface TestResult {
|
|
110
|
+
name: string;
|
|
111
|
+
expected: string;
|
|
112
|
+
calculated: string;
|
|
113
|
+
pass: boolean;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Test relevance scoring with sample decisions
|
|
117
|
+
*
|
|
118
|
+
* Task 1.4: Test relevance scoring with sample decisions
|
|
119
|
+
* AC #1, #4: Verify scoring formula and failure priority
|
|
120
|
+
*
|
|
121
|
+
* @returns Test results
|
|
122
|
+
*/
|
|
123
|
+
export declare function testRelevanceScoring(): TestResult[];
|
|
124
|
+
//# sourceMappingURL=relevance-scorer.d.ts.map
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* MAMA (Memory-Augmented MCP Architecture) - Relevance Scorer
|
|
4
|
+
*
|
|
5
|
+
* Relevance scoring formula for decision ranking and top-N selection
|
|
6
|
+
* Tasks: 1.1-1.4, 2.1-2.7 (Relevance scoring and top-N selection)
|
|
7
|
+
* AC #1, #4, #5: Decision relevance, failure priority boost, top-N selection
|
|
8
|
+
*
|
|
9
|
+
* @module relevance-scorer
|
|
10
|
+
* @version 1.0
|
|
11
|
+
* @date 2025-11-14
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.calculateRelevance = calculateRelevance;
|
|
15
|
+
exports.selectTopDecisions = selectTopDecisions;
|
|
16
|
+
exports.formatTopNContext = formatTopNContext;
|
|
17
|
+
exports.testRelevanceScoring = testRelevanceScoring;
|
|
18
|
+
const embeddings_js_1 = require("./embeddings.js");
|
|
19
|
+
/**
|
|
20
|
+
* Outcome weights for importance scoring
|
|
21
|
+
*/
|
|
22
|
+
const OUTCOME_WEIGHTS = {
|
|
23
|
+
FAILED: 1.0, // Highest - failures are most valuable (AC #4)
|
|
24
|
+
PARTIAL: 0.7,
|
|
25
|
+
SUCCESS: 0.5,
|
|
26
|
+
pending: 0.3, // Ongoing/pending, lowest
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Calculate relevance score for a single decision
|
|
30
|
+
*
|
|
31
|
+
* Task 1.2: Implement calculateRelevance(decision, queryContext) function
|
|
32
|
+
* AC #1, #4: Relevance scoring with failure priority boost
|
|
33
|
+
*
|
|
34
|
+
* Formula:
|
|
35
|
+
* Relevance = (Recency × 0.2) + (Importance × 0.5) + (Semantic × 0.3)
|
|
36
|
+
*
|
|
37
|
+
* Where:
|
|
38
|
+
* - Recency: exp(-days_since / 30) [30-day half-life]
|
|
39
|
+
* - Importance: OUTCOME_WEIGHTS[outcome]
|
|
40
|
+
* - FAILED: 1.0 (highest - failures are most valuable)
|
|
41
|
+
* - PARTIAL: 0.7
|
|
42
|
+
* - SUCCESS: 0.5
|
|
43
|
+
* - null: 0.3 (ongoing, lowest)
|
|
44
|
+
* - Semantic: cosineSimilarity(decision.embedding, query.embedding)
|
|
45
|
+
*
|
|
46
|
+
* @param decision - Decision object
|
|
47
|
+
* @param queryContext - Query context
|
|
48
|
+
* @returns Relevance score (0.0-1.0)
|
|
49
|
+
*/
|
|
50
|
+
function calculateRelevance(decision, queryContext) {
|
|
51
|
+
// ═══════════════════════════════════════════════════════════
|
|
52
|
+
// Recency Score (20%)
|
|
53
|
+
// ═══════════════════════════════════════════════════════════
|
|
54
|
+
// Exponential decay with 30-day half-life
|
|
55
|
+
const daysSince = (Date.now() - decision.created_at) / (1000 * 60 * 60 * 24);
|
|
56
|
+
const recencyScore = Math.exp(-daysSince / 30);
|
|
57
|
+
// Decay curve:
|
|
58
|
+
// 0 days = 1.0
|
|
59
|
+
// 30 days = 0.5
|
|
60
|
+
// 60 days = 0.25
|
|
61
|
+
// 90 days = 0.125
|
|
62
|
+
// ═══════════════════════════════════════════════════════════
|
|
63
|
+
// Importance Score (50%) - AC #4: Failure Priority Boost
|
|
64
|
+
// ═══════════════════════════════════════════════════════════
|
|
65
|
+
// Use explicit null check to avoid confusion with object key access
|
|
66
|
+
const outcomeKey = decision.outcome ?? 'pending';
|
|
67
|
+
const importanceScore = OUTCOME_WEIGHTS[outcomeKey] ?? OUTCOME_WEIGHTS.pending;
|
|
68
|
+
// ═══════════════════════════════════════════════════════════
|
|
69
|
+
// Semantic Score (30%)
|
|
70
|
+
// ═══════════════════════════════════════════════════════════
|
|
71
|
+
let semanticScore = 0;
|
|
72
|
+
if (decision.embedding && queryContext.embedding) {
|
|
73
|
+
// Task 1.3: Use cosine similarity function
|
|
74
|
+
semanticScore = (0, embeddings_js_1.cosineSimilarity)(decision.embedding, queryContext.embedding);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
// Fallback: no semantic match if embeddings missing
|
|
78
|
+
semanticScore = 0;
|
|
79
|
+
}
|
|
80
|
+
// ═══════════════════════════════════════════════════════════
|
|
81
|
+
// Weighted Sum (Total: 100%)
|
|
82
|
+
// ═══════════════════════════════════════════════════════════
|
|
83
|
+
const relevance = recencyScore * 0.2 + importanceScore * 0.5 + semanticScore * 0.3;
|
|
84
|
+
return relevance;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Select top N most relevant decisions
|
|
88
|
+
*
|
|
89
|
+
* Task 2.1: Add selectTopDecisions(decisions, queryContext, n=3) function
|
|
90
|
+
* AC #1, #5: Top-N selection with threshold filtering
|
|
91
|
+
*
|
|
92
|
+
* @param decisions - Array of decision objects
|
|
93
|
+
* @param queryContext - Query context with embedding
|
|
94
|
+
* @param n - Number of top decisions to return (default: 3)
|
|
95
|
+
* @returns Top N decisions with relevance scores
|
|
96
|
+
*/
|
|
97
|
+
function selectTopDecisions(decisions, queryContext, n = 3) {
|
|
98
|
+
if (!Array.isArray(decisions) || decisions.length === 0) {
|
|
99
|
+
return [];
|
|
100
|
+
}
|
|
101
|
+
// Task 2.3: Score all results by relevance
|
|
102
|
+
const decisionsWithScores = decisions.map((decision) => ({
|
|
103
|
+
...decision,
|
|
104
|
+
relevanceScore: calculateRelevance(decision, queryContext),
|
|
105
|
+
}));
|
|
106
|
+
// Task 2.4: Sort descending (highest relevance first)
|
|
107
|
+
decisionsWithScores.sort((a, b) => (b.relevanceScore ?? 0) - (a.relevanceScore ?? 0));
|
|
108
|
+
// Task 2.6: Filter out < 0.5 relevance (AC #1)
|
|
109
|
+
const filtered = decisionsWithScores.filter((d) => (d.relevanceScore ?? 0) >= 0.5);
|
|
110
|
+
// Task 2.5: Return top 3 (or top N)
|
|
111
|
+
const topN = filtered.slice(0, n);
|
|
112
|
+
return topN;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Format decisions with top-N selection and summary
|
|
116
|
+
*
|
|
117
|
+
* Task 8.2-8.3: Format top 3 in full detail, rest as summary
|
|
118
|
+
* AC #5: Top-N selection with summary
|
|
119
|
+
*
|
|
120
|
+
* @param decisions - All decisions (sorted by relevance)
|
|
121
|
+
* @param topN - Number of decisions to show in full detail (default: 3)
|
|
122
|
+
* @returns Formatted context {full: Array, summary: Object}
|
|
123
|
+
*/
|
|
124
|
+
function formatTopNContext(decisions, topN = 3) {
|
|
125
|
+
if (!Array.isArray(decisions) || decisions.length === 0) {
|
|
126
|
+
return { full: [], summary: null };
|
|
127
|
+
}
|
|
128
|
+
// Split into top N and rest
|
|
129
|
+
const fullDetailDecisions = decisions.slice(0, topN);
|
|
130
|
+
const summaryDecisions = decisions.slice(topN);
|
|
131
|
+
// Full detail for top N
|
|
132
|
+
const full = fullDetailDecisions.map((d) => ({
|
|
133
|
+
decision_id: d.id,
|
|
134
|
+
topic: d.topic,
|
|
135
|
+
decision: d.decision,
|
|
136
|
+
reasoning: d.reasoning,
|
|
137
|
+
outcome: d.outcome,
|
|
138
|
+
failure_reason: d.failure_reason,
|
|
139
|
+
user_involvement: d.user_involvement,
|
|
140
|
+
confidence: d.confidence,
|
|
141
|
+
relevanceScore: d.relevanceScore,
|
|
142
|
+
created_at: d.created_at,
|
|
143
|
+
}));
|
|
144
|
+
// Summary for rest (count, duration, key failures only)
|
|
145
|
+
let summary = null;
|
|
146
|
+
if (summaryDecisions.length > 0) {
|
|
147
|
+
// Calculate duration (oldest to newest)
|
|
148
|
+
const oldestTimestamp = Math.min(...summaryDecisions.map((d) => d.created_at));
|
|
149
|
+
const newestTimestamp = Math.max(...summaryDecisions.map((d) => d.created_at));
|
|
150
|
+
const durationDays = Math.floor((newestTimestamp - oldestTimestamp) / (1000 * 60 * 60 * 24));
|
|
151
|
+
// Extract key failures
|
|
152
|
+
const failures = summaryDecisions
|
|
153
|
+
.filter((d) => d.outcome === 'FAILED')
|
|
154
|
+
.map((d) => ({ decision: d.decision, reason: d.failure_reason }));
|
|
155
|
+
summary = {
|
|
156
|
+
count: summaryDecisions.length,
|
|
157
|
+
duration_days: durationDays,
|
|
158
|
+
failures: failures.slice(0, 3), // Show max 3 failures
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
return { full, summary };
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Test relevance scoring with sample decisions
|
|
165
|
+
*
|
|
166
|
+
* Task 1.4: Test relevance scoring with sample decisions
|
|
167
|
+
* AC #1, #4: Verify scoring formula and failure priority
|
|
168
|
+
*
|
|
169
|
+
* @returns Test results
|
|
170
|
+
*/
|
|
171
|
+
function testRelevanceScoring() {
|
|
172
|
+
const now = Date.now();
|
|
173
|
+
// Mock embeddings (dummy for testing)
|
|
174
|
+
const queryEmbedding = new Float32Array(384).fill(0.5);
|
|
175
|
+
const decisionEmbedding1 = new Float32Array(384).fill(0.5); // Identical (similarity = 1.0)
|
|
176
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
177
|
+
const _decisionEmbedding2 = new Float32Array(384).fill(0.3); // Different (similarity < 1.0)
|
|
178
|
+
const scenarios = [
|
|
179
|
+
// Scenario 1: Recent FAILED decision (should have highest relevance)
|
|
180
|
+
{
|
|
181
|
+
name: 'Recent FAILED decision',
|
|
182
|
+
decision: {
|
|
183
|
+
created_at: now - 5 * 24 * 60 * 60 * 1000, // 5 days ago
|
|
184
|
+
outcome: 'FAILED',
|
|
185
|
+
embedding: decisionEmbedding1,
|
|
186
|
+
decision: 'test decision',
|
|
187
|
+
},
|
|
188
|
+
queryContext: { embedding: queryEmbedding },
|
|
189
|
+
expected: {
|
|
190
|
+
recency: 0.85, // exp(-5/30) ≈ 0.85
|
|
191
|
+
importance: 1.0, // FAILED = 1.0 (AC #4)
|
|
192
|
+
semantic: 1.0, // Identical embeddings
|
|
193
|
+
relevance: 0.87, // (0.85×0.2) + (1.0×0.5) + (1.0×0.3)
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
// Scenario 2: Recent SUCCESS decision (lower importance)
|
|
197
|
+
{
|
|
198
|
+
name: 'Recent SUCCESS decision',
|
|
199
|
+
decision: {
|
|
200
|
+
created_at: now - 5 * 24 * 60 * 60 * 1000, // 5 days ago
|
|
201
|
+
outcome: 'SUCCESS',
|
|
202
|
+
embedding: decisionEmbedding1,
|
|
203
|
+
decision: 'test decision',
|
|
204
|
+
},
|
|
205
|
+
queryContext: { embedding: queryEmbedding },
|
|
206
|
+
expected: {
|
|
207
|
+
recency: 0.85,
|
|
208
|
+
importance: 0.5, // SUCCESS = 0.5
|
|
209
|
+
semantic: 1.0,
|
|
210
|
+
relevance: 0.62, // (0.85×0.2) + (0.5×0.5) + (1.0×0.3)
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
// Scenario 3: Old FAILED decision (recency decay)
|
|
214
|
+
{
|
|
215
|
+
name: 'Old FAILED decision',
|
|
216
|
+
decision: {
|
|
217
|
+
created_at: now - 60 * 24 * 60 * 60 * 1000, // 60 days ago
|
|
218
|
+
outcome: 'FAILED',
|
|
219
|
+
embedding: decisionEmbedding1,
|
|
220
|
+
decision: 'test decision',
|
|
221
|
+
},
|
|
222
|
+
queryContext: { embedding: queryEmbedding },
|
|
223
|
+
expected: {
|
|
224
|
+
recency: 0.25, // exp(-60/30) ≈ 0.25
|
|
225
|
+
importance: 1.0,
|
|
226
|
+
semantic: 1.0,
|
|
227
|
+
relevance: 0.85, // (0.25×0.2) + (1.0×0.5) + (1.0×0.3)
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
];
|
|
231
|
+
const results = scenarios.map((scenario) => {
|
|
232
|
+
const calculated = calculateRelevance(scenario.decision, scenario.queryContext);
|
|
233
|
+
const pass = Math.abs(calculated - scenario.expected.relevance) < 0.05;
|
|
234
|
+
return {
|
|
235
|
+
name: scenario.name,
|
|
236
|
+
expected: scenario.expected.relevance.toFixed(2),
|
|
237
|
+
calculated: calculated.toFixed(2),
|
|
238
|
+
pass,
|
|
239
|
+
};
|
|
240
|
+
});
|
|
241
|
+
return results;
|
|
242
|
+
}
|
|
243
|
+
//# sourceMappingURL=relevance-scorer.js.map
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MAMA Core Test Utilities
|
|
3
|
+
*
|
|
4
|
+
* Shared test helpers for mama-core consumers.
|
|
5
|
+
* Optimizes test performance by sharing expensive resources (embedding model, DB adapter).
|
|
6
|
+
*
|
|
7
|
+
* @module test-utils
|
|
8
|
+
* @version 1.1
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Preload embedding model (call once in globalSetup)
|
|
12
|
+
*
|
|
13
|
+
* This loads the embedding model into memory so subsequent tests don't pay the ~2s load time.
|
|
14
|
+
* The model stays in memory due to singleton pattern in embeddings.ts.
|
|
15
|
+
*/
|
|
16
|
+
export declare function preloadEmbeddingModel(): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Create isolated test database
|
|
19
|
+
*
|
|
20
|
+
* Creates a unique test database and sets MAMA_DB_PATH.
|
|
21
|
+
* Use in beforeAll() of each test file.
|
|
22
|
+
*
|
|
23
|
+
* @param testName - Unique test identifier (e.g., 'search-narrative')
|
|
24
|
+
* @returns Test database path
|
|
25
|
+
*/
|
|
26
|
+
export declare function initTestDB(testName: string): Promise<string>;
|
|
27
|
+
/**
|
|
28
|
+
* Cleanup test database
|
|
29
|
+
*
|
|
30
|
+
* Call in afterAll() to clean up test database files.
|
|
31
|
+
*
|
|
32
|
+
* @param testDbPath - Path returned by initTestDB
|
|
33
|
+
*/
|
|
34
|
+
export declare function cleanupTestDB(testDbPath?: string): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* Cleanup all test databases
|
|
37
|
+
*
|
|
38
|
+
* Call in globalTeardown to clean up all test databases.
|
|
39
|
+
*/
|
|
40
|
+
export declare function cleanupAllTestDBs(): void;
|
|
41
|
+
/**
|
|
42
|
+
* Check if embeddings are available
|
|
43
|
+
*
|
|
44
|
+
* Returns false if embedding model failed to load (e.g., CI without ONNX).
|
|
45
|
+
* Use to conditionally skip semantic search tests.
|
|
46
|
+
*/
|
|
47
|
+
export declare function isEmbeddingsAvailable(): Promise<boolean>;
|
|
48
|
+
/**
|
|
49
|
+
* Create mock tool context
|
|
50
|
+
*
|
|
51
|
+
* Returns a mock context object for MCP tool tests.
|
|
52
|
+
*/
|
|
53
|
+
export declare function createMockToolContext(): {
|
|
54
|
+
logger: {
|
|
55
|
+
info: () => void;
|
|
56
|
+
warn: () => void;
|
|
57
|
+
error: () => void;
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Suppress console output during tests
|
|
62
|
+
*
|
|
63
|
+
* Returns restore function to call after test.
|
|
64
|
+
*/
|
|
65
|
+
export declare function suppressConsole(): () => void;
|
|
66
|
+
//# sourceMappingURL=test-utils.d.ts.map
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* MAMA Core Test Utilities
|
|
4
|
+
*
|
|
5
|
+
* Shared test helpers for mama-core consumers.
|
|
6
|
+
* Optimizes test performance by sharing expensive resources (embedding model, DB adapter).
|
|
7
|
+
*
|
|
8
|
+
* @module test-utils
|
|
9
|
+
* @version 1.1
|
|
10
|
+
*/
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.preloadEmbeddingModel = preloadEmbeddingModel;
|
|
16
|
+
exports.initTestDB = initTestDB;
|
|
17
|
+
exports.cleanupTestDB = cleanupTestDB;
|
|
18
|
+
exports.cleanupAllTestDBs = cleanupAllTestDBs;
|
|
19
|
+
exports.isEmbeddingsAvailable = isEmbeddingsAvailable;
|
|
20
|
+
exports.createMockToolContext = createMockToolContext;
|
|
21
|
+
exports.suppressConsole = suppressConsole;
|
|
22
|
+
const path_1 = __importDefault(require("path"));
|
|
23
|
+
const fs_1 = __importDefault(require("fs"));
|
|
24
|
+
const os_1 = __importDefault(require("os"));
|
|
25
|
+
const db_manager_js_1 = require("./db-manager.js");
|
|
26
|
+
const debug_logger_js_1 = require("./debug-logger.js");
|
|
27
|
+
// Track test databases for cleanup
|
|
28
|
+
const testDatabases = [];
|
|
29
|
+
// Embedding model preload state
|
|
30
|
+
let embeddingModelPreloaded = false;
|
|
31
|
+
/**
|
|
32
|
+
* Preload embedding model (call once in globalSetup)
|
|
33
|
+
*
|
|
34
|
+
* This loads the embedding model into memory so subsequent tests don't pay the ~2s load time.
|
|
35
|
+
* The model stays in memory due to singleton pattern in embeddings.ts.
|
|
36
|
+
*/
|
|
37
|
+
async function preloadEmbeddingModel() {
|
|
38
|
+
if (embeddingModelPreloaded) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
const { generateEmbedding } = await import('./embeddings.js');
|
|
43
|
+
await generateEmbedding('preload');
|
|
44
|
+
embeddingModelPreloaded = true;
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
// Non-fatal: some CI environments may not have ONNX runtime
|
|
48
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
49
|
+
(0, debug_logger_js_1.warn)(`[test-utils] Embedding model preload failed: ${message}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Create isolated test database
|
|
54
|
+
*
|
|
55
|
+
* Creates a unique test database and sets MAMA_DB_PATH.
|
|
56
|
+
* Use in beforeAll() of each test file.
|
|
57
|
+
*
|
|
58
|
+
* @param testName - Unique test identifier (e.g., 'search-narrative')
|
|
59
|
+
* @returns Test database path
|
|
60
|
+
*/
|
|
61
|
+
async function initTestDB(testName) {
|
|
62
|
+
// Reset any existing DB state first
|
|
63
|
+
(0, db_manager_js_1.resetDBState)({ disconnect: true });
|
|
64
|
+
// Create unique test database path
|
|
65
|
+
const testDbPath = path_1.default.join(os_1.default.tmpdir(), `mama-test-${testName}-${Date.now()}.db`);
|
|
66
|
+
// Clean up any existing files
|
|
67
|
+
cleanupDbFiles(testDbPath);
|
|
68
|
+
// Set environment variable
|
|
69
|
+
process.env.MAMA_DB_PATH = testDbPath;
|
|
70
|
+
// Track for cleanup
|
|
71
|
+
testDatabases.push(testDbPath);
|
|
72
|
+
// Initialize database
|
|
73
|
+
await (0, db_manager_js_1.initDB)();
|
|
74
|
+
return testDbPath;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Cleanup test database
|
|
78
|
+
*
|
|
79
|
+
* Call in afterAll() to clean up test database files.
|
|
80
|
+
*
|
|
81
|
+
* @param testDbPath - Path returned by initTestDB
|
|
82
|
+
*/
|
|
83
|
+
async function cleanupTestDB(testDbPath) {
|
|
84
|
+
// Close database connection
|
|
85
|
+
await (0, db_manager_js_1.closeDB)();
|
|
86
|
+
// Clean up files
|
|
87
|
+
if (testDbPath) {
|
|
88
|
+
cleanupDbFiles(testDbPath);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Cleanup all test databases
|
|
93
|
+
*
|
|
94
|
+
* Call in globalTeardown to clean up all test databases.
|
|
95
|
+
*/
|
|
96
|
+
function cleanupAllTestDBs() {
|
|
97
|
+
for (const dbPath of testDatabases) {
|
|
98
|
+
cleanupDbFiles(dbPath);
|
|
99
|
+
}
|
|
100
|
+
testDatabases.length = 0;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Helper to clean up SQLite database files
|
|
104
|
+
*/
|
|
105
|
+
function cleanupDbFiles(dbPath) {
|
|
106
|
+
const files = [dbPath, `${dbPath}-wal`, `${dbPath}-shm`];
|
|
107
|
+
for (const file of files) {
|
|
108
|
+
try {
|
|
109
|
+
if (fs_1.default.existsSync(file)) {
|
|
110
|
+
fs_1.default.unlinkSync(file);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
// Ignore cleanup errors
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Check if embeddings are available
|
|
120
|
+
*
|
|
121
|
+
* Returns false if embedding model failed to load (e.g., CI without ONNX).
|
|
122
|
+
* Use to conditionally skip semantic search tests.
|
|
123
|
+
*/
|
|
124
|
+
async function isEmbeddingsAvailable() {
|
|
125
|
+
try {
|
|
126
|
+
const { generateEmbedding } = await import('./embeddings.js');
|
|
127
|
+
await generateEmbedding('test');
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Create mock tool context
|
|
136
|
+
*
|
|
137
|
+
* Returns a mock context object for MCP tool tests.
|
|
138
|
+
*/
|
|
139
|
+
function createMockToolContext() {
|
|
140
|
+
return {
|
|
141
|
+
logger: {
|
|
142
|
+
info: () => { },
|
|
143
|
+
warn: () => { },
|
|
144
|
+
error: () => { },
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Suppress console output during tests
|
|
150
|
+
*
|
|
151
|
+
* Returns restore function to call after test.
|
|
152
|
+
*/
|
|
153
|
+
function suppressConsole() {
|
|
154
|
+
const originalLog = console.log;
|
|
155
|
+
const originalWarn = console.warn;
|
|
156
|
+
const originalError = console.error;
|
|
157
|
+
console.log = () => { };
|
|
158
|
+
console.warn = () => { };
|
|
159
|
+
console.error = () => { };
|
|
160
|
+
return () => {
|
|
161
|
+
console.log = originalLog;
|
|
162
|
+
console.warn = originalWarn;
|
|
163
|
+
console.error = originalError;
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=test-utils.js.map
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MAMA Tier Validator
|
|
3
|
+
*
|
|
4
|
+
* Centralized tier validation module for MAMA.
|
|
5
|
+
* Validates system requirements and determines tier status (1 or 2).
|
|
6
|
+
*
|
|
7
|
+
* Tier 1: Full features (Node.js 18+, SQLite, Embeddings, Database)
|
|
8
|
+
* Tier 2: Degraded mode (missing one or more requirements)
|
|
9
|
+
*
|
|
10
|
+
* @module tier-validator
|
|
11
|
+
*/
|
|
12
|
+
export interface CheckResult {
|
|
13
|
+
status: 'pass' | 'fail';
|
|
14
|
+
details: string;
|
|
15
|
+
}
|
|
16
|
+
export interface NamedCheckResult extends CheckResult {
|
|
17
|
+
name: string;
|
|
18
|
+
}
|
|
19
|
+
export interface TierValidation {
|
|
20
|
+
tier: 1 | 2;
|
|
21
|
+
checks: NamedCheckResult[];
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Validates Node.js version requirement
|
|
25
|
+
*/
|
|
26
|
+
export declare function checkNodeVersion(): CheckResult;
|
|
27
|
+
/**
|
|
28
|
+
* Validates SQLite (better-sqlite3) availability
|
|
29
|
+
*/
|
|
30
|
+
export declare function checkSQLite(): CheckResult;
|
|
31
|
+
/**
|
|
32
|
+
* Validates embedding model availability
|
|
33
|
+
*/
|
|
34
|
+
export declare function checkEmbeddings(): CheckResult;
|
|
35
|
+
/**
|
|
36
|
+
* Validates database file accessibility
|
|
37
|
+
*/
|
|
38
|
+
export declare function checkDatabase(): CheckResult;
|
|
39
|
+
/**
|
|
40
|
+
* Validates MAMA tier status
|
|
41
|
+
*
|
|
42
|
+
* Performs all system checks and determines tier:
|
|
43
|
+
* - Tier 1: All checks pass (full features)
|
|
44
|
+
* - Tier 2: One or more checks fail (degraded mode)
|
|
45
|
+
*/
|
|
46
|
+
export declare function validateTier(): Promise<TierValidation>;
|
|
47
|
+
/**
|
|
48
|
+
* Get user-friendly tier description
|
|
49
|
+
*/
|
|
50
|
+
export declare function getTierDescription(tier: number): string;
|
|
51
|
+
/**
|
|
52
|
+
* Get tier status banner
|
|
53
|
+
*/
|
|
54
|
+
export declare function getTierBanner(validation: TierValidation): string;
|
|
55
|
+
//# sourceMappingURL=tier-validator.d.ts.map
|