@jungjaehoon/mama-core 1.4.1 → 1.5.0
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/db/migrations/026-create-canonical-entity-tables.sql +99 -0
- package/db/migrations/027-create-entity-review-support.sql +44 -0
- package/db/migrations/028-create-entity-audit-tables.sql +45 -0
- package/db/migrations/029-extend-entity-observations-observation-type.sql +60 -0
- package/db/migrations/030-case-first-memory-substrate.sql +901 -0
- package/db/migrations/031-wiki-page-index-provenance-columns.sql +2 -0
- package/dist/canonicalize.d.ts +38 -0
- package/dist/canonicalize.js +106 -0
- package/dist/cases/case-links.d.ts +116 -0
- package/dist/cases/case-links.js +794 -0
- package/dist/cases/composition-overrides.d.ts +90 -0
- package/dist/cases/composition-overrides.js +724 -0
- package/dist/cases/corrections.d.ts +156 -0
- package/dist/cases/corrections.js +1045 -0
- package/dist/cases/freshness.d.ts +57 -0
- package/dist/cases/freshness.js +283 -0
- package/dist/cases/live-state.d.ts +48 -0
- package/dist/cases/live-state.js +256 -0
- package/dist/cases/membership-explain.d.ts +56 -0
- package/dist/cases/membership-explain.js +140 -0
- package/dist/cases/membership-matcher.d.ts +23 -0
- package/dist/cases/membership-matcher.js +193 -0
- package/dist/cases/merge-split.d.ts +61 -0
- package/dist/cases/merge-split.js +368 -0
- package/dist/cases/role-inference.d.ts +10 -0
- package/dist/cases/role-inference.js +85 -0
- package/dist/cases/search-rollup.d.ts +23 -0
- package/dist/cases/search-rollup.js +225 -0
- package/dist/cases/sqlite-transaction.d.ts +10 -0
- package/dist/cases/sqlite-transaction.js +62 -0
- package/dist/cases/store.d.ts +74 -0
- package/dist/cases/store.js +816 -0
- package/dist/cases/target-ref.d.ts +25 -0
- package/dist/cases/target-ref.js +22 -0
- package/dist/cases/timeline-range.d.ts +6 -0
- package/dist/cases/timeline-range.js +322 -0
- package/dist/cases/tombstone-sweeper.d.ts +13 -0
- package/dist/cases/tombstone-sweeper.js +128 -0
- package/dist/cases/types.d.ts +244 -0
- package/dist/cases/types.js +47 -0
- package/dist/cases/wiki-page-index.d.ts +50 -0
- package/dist/cases/wiki-page-index.js +350 -0
- package/dist/config-loader.d.ts +1 -0
- package/dist/config-loader.js +43 -0
- package/dist/connectors/event-index.d.ts +44 -0
- package/dist/connectors/event-index.js +366 -0
- package/dist/connectors/types.d.ts +69 -0
- package/dist/connectors/types.js +3 -0
- package/dist/db-manager.d.ts +4 -0
- package/dist/db-manager.js +10 -3
- package/dist/decision-formatter.d.ts +2 -0
- package/dist/decision-formatter.js +11 -8
- package/dist/embedding-server/mobile/daemon.d.ts +1 -0
- package/dist/embedding-server/mobile/daemon.js +11 -1
- package/dist/entities/audit-metrics.d.ts +73 -0
- package/dist/entities/audit-metrics.js +128 -0
- package/dist/entities/candidate-generator.d.ts +9 -0
- package/dist/entities/candidate-generator.js +436 -0
- package/dist/entities/entity-impact.d.ts +41 -0
- package/dist/entities/entity-impact.js +140 -0
- package/dist/entities/entity-linked-decision-counts.d.ts +2 -0
- package/dist/entities/entity-linked-decision-counts.js +29 -0
- package/dist/entities/entity-list.d.ts +23 -0
- package/dist/entities/entity-list.js +99 -0
- package/dist/entities/entity-orphan-list.d.ts +26 -0
- package/dist/entities/entity-orphan-list.js +91 -0
- package/dist/entities/entity-search.d.ts +21 -0
- package/dist/entities/entity-search.js +150 -0
- package/dist/entities/errors.d.ts +65 -0
- package/dist/entities/errors.js +124 -0
- package/dist/entities/exact-merge-backfill.d.ts +11 -0
- package/dist/entities/exact-merge-backfill.js +161 -0
- package/dist/entities/lineage-backfill.d.ts +12 -0
- package/dist/entities/lineage-backfill.js +101 -0
- package/dist/entities/lineage-store.d.ts +51 -0
- package/dist/entities/lineage-store.js +308 -0
- package/dist/entities/normalization.d.ts +14 -0
- package/dist/entities/normalization.js +63 -0
- package/dist/entities/policy-store.d.ts +17 -0
- package/dist/entities/policy-store.js +263 -0
- package/dist/entities/policy-types.d.ts +72 -0
- package/dist/entities/policy-types.js +59 -0
- package/dist/entities/projection.d.ts +8 -0
- package/dist/entities/projection.js +82 -0
- package/dist/entities/provenance-query.d.ts +33 -0
- package/dist/entities/provenance-query.js +167 -0
- package/dist/entities/read-identity.d.ts +31 -0
- package/dist/entities/read-identity.js +264 -0
- package/dist/entities/recall-bridge.d.ts +5 -0
- package/dist/entities/recall-bridge.js +171 -0
- package/dist/entities/resolution-engine.d.ts +15 -0
- package/dist/entities/resolution-engine.js +69 -0
- package/dist/entities/rollback-preview.d.ts +39 -0
- package/dist/entities/rollback-preview.js +235 -0
- package/dist/entities/source-locator.d.ts +3 -0
- package/dist/entities/source-locator.js +32 -0
- package/dist/entities/store.d.ts +75 -0
- package/dist/entities/store.js +465 -0
- package/dist/entities/types.d.ts +162 -0
- package/dist/entities/types.js +30 -0
- package/dist/index.d.ts +47 -2
- package/dist/index.js +66 -25
- package/dist/mama-api.d.ts +22 -1
- package/dist/mama-api.js +272 -64
- package/dist/memory/api.d.ts +28 -1
- package/dist/memory/api.js +293 -31
- package/dist/memory/types.d.ts +11 -2
- package/dist/search/feedback-store.d.ts +59 -0
- package/dist/search/feedback-store.js +211 -0
- package/dist/search/question-type.d.ts +5 -0
- package/dist/search/question-type.js +41 -0
- package/dist/search/ranker-features.d.ts +23 -0
- package/dist/search/ranker-features.js +104 -0
- package/dist/search/ranker-rescore.d.ts +31 -0
- package/dist/search/ranker-rescore.js +226 -0
- package/dist/search/ranker-trainer.d.ts +94 -0
- package/dist/search/ranker-trainer.js +860 -0
- package/dist/test-utils.d.ts +14 -0
- package/dist/test-utils.js +52 -0
- package/package.json +21 -3
|
@@ -140,7 +140,7 @@ Top ${full.length} Most Relevant Decisions:
|
|
|
140
140
|
`.trim();
|
|
141
141
|
for (let i = 0; i < full.length; i++) {
|
|
142
142
|
const d = full[i];
|
|
143
|
-
const duration = calculateDuration(d
|
|
143
|
+
const duration = calculateDuration(getDecisionTimestamp(d));
|
|
144
144
|
const outcomeEmoji = getOutcomeEmoji(d.outcome ?? null);
|
|
145
145
|
const relevancePercent = Math.round((d.relevanceScore || 0) * 100);
|
|
146
146
|
context += `\n\n${i + 1}. ${d.decision} (${duration}, relevance: ${relevancePercent}%) ${outcomeEmoji}`;
|
|
@@ -166,7 +166,7 @@ Top ${full.length} Most Relevant Decisions:
|
|
|
166
166
|
* Format small decision history (3 or fewer)
|
|
167
167
|
*/
|
|
168
168
|
function formatSmallHistory(current, history) {
|
|
169
|
-
const duration = calculateDuration(current
|
|
169
|
+
const duration = calculateDuration(getDecisionTimestamp(current));
|
|
170
170
|
let context = `
|
|
171
171
|
🧠 DECISION HISTORY: ${current.topic}
|
|
172
172
|
|
|
@@ -284,6 +284,9 @@ function calculateDuration(timestamp) {
|
|
|
284
284
|
}
|
|
285
285
|
return `${diffDays} day${diffDays !== 1 ? 's' : ''} ago`;
|
|
286
286
|
}
|
|
287
|
+
function getDecisionTimestamp(decision) {
|
|
288
|
+
return decision.event_datetime ?? decision.created_at;
|
|
289
|
+
}
|
|
287
290
|
/**
|
|
288
291
|
* Calculate duration between two timestamps in days
|
|
289
292
|
*/
|
|
@@ -560,7 +563,7 @@ function formatTeaserList(decisions, topN = 3) {
|
|
|
560
563
|
output += `\n "${preview}"`;
|
|
561
564
|
// Recency metadata (NEW - Gaussian Decay)
|
|
562
565
|
if (d.recency_age_days !== undefined && d.created_at) {
|
|
563
|
-
const timeAgo = calculateDuration(d
|
|
566
|
+
const timeAgo = calculateDuration(getDecisionTimestamp(d));
|
|
564
567
|
const recencyScore = d.recency_score ? Math.round(d.recency_score * 100) : null;
|
|
565
568
|
const finalScore = d.final_score ? Math.round(d.final_score * 100) : null;
|
|
566
569
|
output += `\n ⏰ ${timeAgo}`;
|
|
@@ -582,7 +585,7 @@ function formatTeaser(decision) {
|
|
|
582
585
|
if (!decision) {
|
|
583
586
|
return null;
|
|
584
587
|
}
|
|
585
|
-
const timeAgo = calculateDuration(decision
|
|
588
|
+
const timeAgo = calculateDuration(getDecisionTimestamp(decision));
|
|
586
589
|
// Extract preview (first 60 chars)
|
|
587
590
|
const preview = decision.decision.length > 60 ? decision.decision.substring(0, 60) + '...' : decision.decision;
|
|
588
591
|
// Extract files from trust_context or show generic
|
|
@@ -633,7 +636,7 @@ function formatRecall(decisions, semanticEdges = null) {
|
|
|
633
636
|
* Format single decision with full detail
|
|
634
637
|
*/
|
|
635
638
|
function formatSingleDecision(decision) {
|
|
636
|
-
const timeAgo = calculateDuration(decision
|
|
639
|
+
const timeAgo = calculateDuration(getDecisionTimestamp(decision));
|
|
637
640
|
const confidencePercent = Math.round((decision.confidence || 0) * 100);
|
|
638
641
|
const outcomeEmoji = getOutcomeEmoji(decision.outcome ?? null);
|
|
639
642
|
const outcomeText = decision.outcome || 'Not yet tracked';
|
|
@@ -725,7 +728,7 @@ function formatDecisionHistory(decisions, semanticEdges = null) {
|
|
|
725
728
|
📋 Decision History: ${topic}
|
|
726
729
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
727
730
|
|
|
728
|
-
Latest Decision (${calculateDuration(latest
|
|
731
|
+
Latest Decision (${calculateDuration(getDecisionTimestamp(latest))}):
|
|
729
732
|
${latest.decision}
|
|
730
733
|
`.trim();
|
|
731
734
|
// Show brief reasoning if available
|
|
@@ -777,7 +780,7 @@ ${latest.decision}
|
|
|
777
780
|
output += `\nPrevious Decisions (${older.length}):\n`;
|
|
778
781
|
for (let i = 0; i < Math.min(older.length, 5); i++) {
|
|
779
782
|
const d = older[i];
|
|
780
|
-
const timeAgo = calculateDuration(d
|
|
783
|
+
const timeAgo = calculateDuration(getDecisionTimestamp(d));
|
|
781
784
|
const emoji = getOutcomeEmoji(d.outcome ?? null);
|
|
782
785
|
output += `\n${i + 2}. ${d.decision} (${timeAgo}) ${emoji}`;
|
|
783
786
|
if (d.outcome === 'FAILED' && d.failure_reason) {
|
|
@@ -876,7 +879,7 @@ function formatList(decisions, options = {}) {
|
|
|
876
879
|
output += '━'.repeat(60) + '\n';
|
|
877
880
|
for (let i = 0; i < items.length; i++) {
|
|
878
881
|
const d = items[i];
|
|
879
|
-
const timeAgo = calculateDuration(d
|
|
882
|
+
const timeAgo = calculateDuration(getDecisionTimestamp(d));
|
|
880
883
|
const type = d.user_involvement === 'approved' ? '👤 User' : '🤖 Assistant';
|
|
881
884
|
const status = d.outcome ? getOutcomeEmoji(d.outcome) + ' ' + d.outcome : '⏳ Pending';
|
|
882
885
|
const confidence = Math.round((d.confidence || 0) * 100);
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
18
|
exports.ClaudeDaemon = exports.ANSI_REGEX = void 0;
|
|
19
19
|
const events_1 = require("events");
|
|
20
|
+
const crypto_1 = require("crypto");
|
|
20
21
|
const child_process_1 = require("child_process");
|
|
21
22
|
/**
|
|
22
23
|
* ANSI escape code regex pattern
|
|
@@ -38,6 +39,7 @@ class ClaudeDaemon extends events_1.EventEmitter {
|
|
|
38
39
|
_pid;
|
|
39
40
|
_isRunning;
|
|
40
41
|
buffer;
|
|
42
|
+
cliSessionId;
|
|
41
43
|
/**
|
|
42
44
|
* Create a new ClaudeDaemon instance
|
|
43
45
|
* @param projectDir - Working directory for Claude Code
|
|
@@ -51,6 +53,7 @@ class ClaudeDaemon extends events_1.EventEmitter {
|
|
|
51
53
|
this._pid = null;
|
|
52
54
|
this._isRunning = false;
|
|
53
55
|
this.buffer = '';
|
|
56
|
+
this.cliSessionId = (0, crypto_1.randomUUID)();
|
|
54
57
|
}
|
|
55
58
|
/**
|
|
56
59
|
* Check if the daemon is currently running
|
|
@@ -76,13 +79,20 @@ class ClaudeDaemon extends events_1.EventEmitter {
|
|
|
76
79
|
return new Promise((resolve, reject) => {
|
|
77
80
|
let settled = false;
|
|
78
81
|
try {
|
|
79
|
-
//
|
|
82
|
+
// Requires a modern Claude CLI build with stream-json support
|
|
83
|
+
// for --print/--verbose/--session-id/--setting-sources.
|
|
80
84
|
const args = [
|
|
85
|
+
'--print',
|
|
86
|
+
'--verbose',
|
|
81
87
|
'--dangerously-skip-permissions',
|
|
82
88
|
'--input-format',
|
|
83
89
|
'stream-json',
|
|
84
90
|
'--output-format',
|
|
85
91
|
'stream-json',
|
|
92
|
+
'--session-id',
|
|
93
|
+
this.cliSessionId,
|
|
94
|
+
'--setting-sources',
|
|
95
|
+
'project,local',
|
|
86
96
|
];
|
|
87
97
|
// Spawn Claude Code CLI
|
|
88
98
|
this.process = (0, child_process_1.spawn)('claude', args, {
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Entity audit metric helpers.
|
|
3
|
+
*
|
|
4
|
+
* Pure functions — no DB, no embeddings. The audit queue assembles the
|
|
5
|
+
* snapshot from live tables and fixtures, then hands it to these helpers
|
|
6
|
+
* for deterministic metric calculation + baseline classification.
|
|
7
|
+
*/
|
|
8
|
+
export declare const FALSE_MERGE_HARD_CEILING = 0.02;
|
|
9
|
+
export type EntityAuditClassification = 'improved' | 'stable' | 'regressed' | 'inconclusive';
|
|
10
|
+
export interface AuditCandidateSnapshot {
|
|
11
|
+
id: string;
|
|
12
|
+
status: 'pending' | 'auto_merged' | 'approved' | 'rejected' | 'deferred';
|
|
13
|
+
score_total: number;
|
|
14
|
+
left_canonical_id: string | null;
|
|
15
|
+
right_canonical_id: string | null;
|
|
16
|
+
left_kind: string | null;
|
|
17
|
+
right_kind: string | null;
|
|
18
|
+
}
|
|
19
|
+
export interface AuditGoldPair {
|
|
20
|
+
canonical_id: string;
|
|
21
|
+
left_obs_id: string;
|
|
22
|
+
right_obs_id: string;
|
|
23
|
+
}
|
|
24
|
+
export interface AuditCandidateGoldLink {
|
|
25
|
+
candidate_id: string;
|
|
26
|
+
matched_pair_key: string;
|
|
27
|
+
score_total: number;
|
|
28
|
+
}
|
|
29
|
+
export interface AuditSnapshot {
|
|
30
|
+
candidates: AuditCandidateSnapshot[];
|
|
31
|
+
gold_pairs: AuditGoldPair[];
|
|
32
|
+
candidate_gold_matches: AuditCandidateGoldLink[];
|
|
33
|
+
gold_canonical_projection_counts: Record<string, number>;
|
|
34
|
+
}
|
|
35
|
+
export interface AuditMetricSummary {
|
|
36
|
+
false_merge_rate: number;
|
|
37
|
+
false_merge_numerator: number;
|
|
38
|
+
false_merge_denominator: number;
|
|
39
|
+
cross_language_candidate_recall_at_10: number;
|
|
40
|
+
cross_language_recall_numerator: number;
|
|
41
|
+
cross_language_recall_denominator: number;
|
|
42
|
+
ontology_violation_count: number;
|
|
43
|
+
projection_fragmentation_rate: number;
|
|
44
|
+
}
|
|
45
|
+
export interface AuditRunClassificationInput {
|
|
46
|
+
current: AuditMetricSummary;
|
|
47
|
+
baseline: AuditMetricSummary | null;
|
|
48
|
+
}
|
|
49
|
+
export declare function computeFalseMergeRate(candidates: AuditCandidateSnapshot[]): {
|
|
50
|
+
rate: number;
|
|
51
|
+
numerator: number;
|
|
52
|
+
denominator: number;
|
|
53
|
+
};
|
|
54
|
+
export declare function computeCrossLanguageRecallAtK(goldPairs: AuditGoldPair[], matches: AuditCandidateGoldLink[], k: number): {
|
|
55
|
+
recall: number;
|
|
56
|
+
numerator: number;
|
|
57
|
+
denominator: number;
|
|
58
|
+
};
|
|
59
|
+
export declare function computeCrossLanguageRecallAt5(goldPairs: AuditGoldPair[], matches: AuditCandidateGoldLink[]): {
|
|
60
|
+
recall: number;
|
|
61
|
+
numerator: number;
|
|
62
|
+
denominator: number;
|
|
63
|
+
};
|
|
64
|
+
export declare function computeCrossLanguageRecallAt10(goldPairs: AuditGoldPair[], matches: AuditCandidateGoldLink[]): {
|
|
65
|
+
recall: number;
|
|
66
|
+
numerator: number;
|
|
67
|
+
denominator: number;
|
|
68
|
+
};
|
|
69
|
+
export declare function computeOntologyViolationCount(candidates: AuditCandidateSnapshot[]): number;
|
|
70
|
+
export declare function computeProjectionFragmentationRate(counts: Record<string, number>): number;
|
|
71
|
+
export declare function computeAuditMetrics(snapshot: AuditSnapshot): AuditMetricSummary;
|
|
72
|
+
export declare function classifyAuditRun(input: AuditRunClassificationInput): EntityAuditClassification;
|
|
73
|
+
//# sourceMappingURL=audit-metrics.d.ts.map
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Entity audit metric helpers.
|
|
4
|
+
*
|
|
5
|
+
* Pure functions — no DB, no embeddings. The audit queue assembles the
|
|
6
|
+
* snapshot from live tables and fixtures, then hands it to these helpers
|
|
7
|
+
* for deterministic metric calculation + baseline classification.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.FALSE_MERGE_HARD_CEILING = void 0;
|
|
11
|
+
exports.computeFalseMergeRate = computeFalseMergeRate;
|
|
12
|
+
exports.computeCrossLanguageRecallAtK = computeCrossLanguageRecallAtK;
|
|
13
|
+
exports.computeCrossLanguageRecallAt5 = computeCrossLanguageRecallAt5;
|
|
14
|
+
exports.computeCrossLanguageRecallAt10 = computeCrossLanguageRecallAt10;
|
|
15
|
+
exports.computeOntologyViolationCount = computeOntologyViolationCount;
|
|
16
|
+
exports.computeProjectionFragmentationRate = computeProjectionFragmentationRate;
|
|
17
|
+
exports.computeAuditMetrics = computeAuditMetrics;
|
|
18
|
+
exports.classifyAuditRun = classifyAuditRun;
|
|
19
|
+
exports.FALSE_MERGE_HARD_CEILING = 0.02;
|
|
20
|
+
const RESOLVED_MERGE_STATUSES = new Set([
|
|
21
|
+
'auto_merged',
|
|
22
|
+
'approved',
|
|
23
|
+
]);
|
|
24
|
+
function computeFalseMergeRate(candidates) {
|
|
25
|
+
let denominator = 0;
|
|
26
|
+
let numerator = 0;
|
|
27
|
+
for (const candidate of candidates) {
|
|
28
|
+
if (!RESOLVED_MERGE_STATUSES.has(candidate.status))
|
|
29
|
+
continue;
|
|
30
|
+
denominator += 1;
|
|
31
|
+
if (candidate.left_canonical_id &&
|
|
32
|
+
candidate.right_canonical_id &&
|
|
33
|
+
candidate.left_canonical_id !== candidate.right_canonical_id) {
|
|
34
|
+
numerator += 1;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
const rate = denominator === 0 ? 0 : numerator / denominator;
|
|
38
|
+
return { rate, numerator, denominator };
|
|
39
|
+
}
|
|
40
|
+
function computeCrossLanguageRecallAtK(goldPairs, matches, k) {
|
|
41
|
+
const denominator = goldPairs.length;
|
|
42
|
+
if (denominator === 0) {
|
|
43
|
+
return { recall: 0, numerator: 0, denominator: 0 };
|
|
44
|
+
}
|
|
45
|
+
const topRanked = [...matches]
|
|
46
|
+
.sort((a, b) => b.score_total - a.score_total)
|
|
47
|
+
.slice(0, Math.max(0, k))
|
|
48
|
+
.map((m) => m.matched_pair_key);
|
|
49
|
+
const matched = new Set(topRanked);
|
|
50
|
+
let numerator = 0;
|
|
51
|
+
for (const pair of goldPairs) {
|
|
52
|
+
const key = `${pair.canonical_id}:${pair.left_obs_id}:${pair.right_obs_id}`;
|
|
53
|
+
if (matched.has(key)) {
|
|
54
|
+
numerator += 1;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return { recall: numerator / denominator, numerator, denominator };
|
|
58
|
+
}
|
|
59
|
+
function computeCrossLanguageRecallAt5(goldPairs, matches) {
|
|
60
|
+
return computeCrossLanguageRecallAtK(goldPairs, matches, 5);
|
|
61
|
+
}
|
|
62
|
+
function computeCrossLanguageRecallAt10(goldPairs, matches) {
|
|
63
|
+
return computeCrossLanguageRecallAtK(goldPairs, matches, 10);
|
|
64
|
+
}
|
|
65
|
+
function computeOntologyViolationCount(candidates) {
|
|
66
|
+
let count = 0;
|
|
67
|
+
for (const candidate of candidates) {
|
|
68
|
+
if (candidate.left_kind &&
|
|
69
|
+
candidate.right_kind &&
|
|
70
|
+
candidate.left_kind !== candidate.right_kind) {
|
|
71
|
+
count += 1;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return count;
|
|
75
|
+
}
|
|
76
|
+
function computeProjectionFragmentationRate(counts) {
|
|
77
|
+
const keys = Object.keys(counts);
|
|
78
|
+
if (keys.length === 0)
|
|
79
|
+
return 0;
|
|
80
|
+
let fragmented = 0;
|
|
81
|
+
for (const key of keys) {
|
|
82
|
+
if ((counts[key] ?? 0) > 1)
|
|
83
|
+
fragmented += 1;
|
|
84
|
+
}
|
|
85
|
+
return fragmented / keys.length;
|
|
86
|
+
}
|
|
87
|
+
function computeAuditMetrics(snapshot) {
|
|
88
|
+
const falseMerge = computeFalseMergeRate(snapshot.candidates);
|
|
89
|
+
const recall = computeCrossLanguageRecallAt10(snapshot.gold_pairs, snapshot.candidate_gold_matches);
|
|
90
|
+
const ontology = computeOntologyViolationCount(snapshot.candidates);
|
|
91
|
+
const fragmentation = computeProjectionFragmentationRate(snapshot.gold_canonical_projection_counts);
|
|
92
|
+
return {
|
|
93
|
+
false_merge_rate: falseMerge.rate,
|
|
94
|
+
false_merge_numerator: falseMerge.numerator,
|
|
95
|
+
false_merge_denominator: falseMerge.denominator,
|
|
96
|
+
cross_language_candidate_recall_at_10: recall.recall,
|
|
97
|
+
cross_language_recall_numerator: recall.numerator,
|
|
98
|
+
cross_language_recall_denominator: recall.denominator,
|
|
99
|
+
ontology_violation_count: ontology,
|
|
100
|
+
projection_fragmentation_rate: fragmentation,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function classifyAuditRun(input) {
|
|
104
|
+
const { current, baseline } = input;
|
|
105
|
+
if (current.false_merge_rate > exports.FALSE_MERGE_HARD_CEILING) {
|
|
106
|
+
return 'regressed';
|
|
107
|
+
}
|
|
108
|
+
if (!baseline) {
|
|
109
|
+
return 'inconclusive';
|
|
110
|
+
}
|
|
111
|
+
if (current.false_merge_rate > baseline.false_merge_rate) {
|
|
112
|
+
return 'regressed';
|
|
113
|
+
}
|
|
114
|
+
const recallUp = current.cross_language_candidate_recall_at_10 > baseline.cross_language_candidate_recall_at_10;
|
|
115
|
+
const fragmentationDown = current.projection_fragmentation_rate < baseline.projection_fragmentation_rate;
|
|
116
|
+
const ontologyDown = current.ontology_violation_count < baseline.ontology_violation_count;
|
|
117
|
+
if (recallUp || fragmentationDown || ontologyDown) {
|
|
118
|
+
const anyRegression = current.cross_language_candidate_recall_at_10 <
|
|
119
|
+
baseline.cross_language_candidate_recall_at_10 ||
|
|
120
|
+
current.projection_fragmentation_rate > baseline.projection_fragmentation_rate ||
|
|
121
|
+
current.ontology_violation_count > baseline.ontology_violation_count;
|
|
122
|
+
if (anyRegression)
|
|
123
|
+
return 'stable';
|
|
124
|
+
return 'improved';
|
|
125
|
+
}
|
|
126
|
+
return 'stable';
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=audit-metrics.js.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { EntityObservation, EntityResolutionCandidate } from './types.js';
|
|
2
|
+
export declare const ENTITY_EMBEDDING_TOPN = 50;
|
|
3
|
+
export interface CandidateGeneratorOptions {
|
|
4
|
+
embeddingScorer?: (left: EntityObservation, right: EntityObservation) => Promise<number>;
|
|
5
|
+
topN?: number;
|
|
6
|
+
}
|
|
7
|
+
export declare function dedupeObservationsBySource(observations: EntityObservation[]): EntityObservation[];
|
|
8
|
+
export declare function generateResolutionCandidates(observations: EntityObservation[], options?: CandidateGeneratorOptions): Promise<EntityResolutionCandidate[]>;
|
|
9
|
+
//# sourceMappingURL=candidate-generator.d.ts.map
|