@pcircle/memesh 4.1.7 → 4.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/README.de.md +48 -10
- package/README.es.md +82 -17
- package/README.fr.md +49 -11
- package/README.ja.md +83 -17
- package/README.ko.md +86 -20
- package/README.md +12 -7
- package/README.pt.md +48 -10
- package/README.th.md +46 -8
- package/README.vi.md +48 -10
- package/README.zh-CN.md +83 -17
- package/README.zh-TW.md +110 -44
- package/dashboard/dist/index.html +9 -9
- package/dist/cli/view-live.js +2 -2
- package/dist/cli/view.d.ts.map +1 -1
- package/dist/cli/view.js +15 -12
- package/dist/cli/view.js.map +1 -1
- package/dist/core/analytics.d.ts +17 -0
- package/dist/core/analytics.d.ts.map +1 -1
- package/dist/core/analytics.js +40 -0
- package/dist/core/analytics.js.map +1 -1
- package/dist/core/auto-tagger.d.ts +7 -2
- package/dist/core/auto-tagger.d.ts.map +1 -1
- package/dist/core/auto-tagger.js +12 -4
- package/dist/core/auto-tagger.js.map +1 -1
- package/dist/core/config.d.ts +2 -0
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +19 -11
- package/dist/core/config.js.map +1 -1
- package/dist/core/consolidator.d.ts.map +1 -1
- package/dist/core/consolidator.js +13 -4
- package/dist/core/consolidator.js.map +1 -1
- package/dist/core/digest-validator.d.ts +18 -0
- package/dist/core/digest-validator.d.ts.map +1 -0
- package/dist/core/digest-validator.js +79 -0
- package/dist/core/digest-validator.js.map +1 -0
- package/dist/core/doctor.d.ts.map +1 -1
- package/dist/core/doctor.js +28 -11
- package/dist/core/doctor.js.map +1 -1
- package/dist/core/dreamer.d.ts +8 -1
- package/dist/core/dreamer.d.ts.map +1 -1
- package/dist/core/dreamer.js +68 -14
- package/dist/core/dreamer.js.map +1 -1
- package/dist/core/embedder.d.ts.map +1 -1
- package/dist/core/embedder.js +2 -2
- package/dist/core/embedder.js.map +1 -1
- package/dist/core/extractor.d.ts.map +1 -1
- package/dist/core/extractor.js +2 -1
- package/dist/core/extractor.js.map +1 -1
- package/dist/core/failure-analyzer.d.ts +6 -1
- package/dist/core/failure-analyzer.d.ts.map +1 -1
- package/dist/core/failure-analyzer.js +10 -2
- package/dist/core/failure-analyzer.js.map +1 -1
- package/dist/core/install-hooks.d.ts.map +1 -1
- package/dist/core/install-hooks.js +1 -7
- package/dist/core/install-hooks.js.map +1 -1
- package/dist/core/install-id.d.ts.map +1 -1
- package/dist/core/install-id.js +2 -3
- package/dist/core/install-id.js.map +1 -1
- package/dist/core/kg-backfill.d.ts +39 -0
- package/dist/core/kg-backfill.d.ts.map +1 -0
- package/dist/core/kg-backfill.js +362 -0
- package/dist/core/kg-backfill.js.map +1 -0
- package/dist/core/llm-client.d.ts +13 -0
- package/dist/core/llm-client.d.ts.map +1 -1
- package/dist/core/llm-client.js +63 -3
- package/dist/core/llm-client.js.map +1 -1
- package/dist/core/llm-telemetry.d.ts +35 -0
- package/dist/core/llm-telemetry.d.ts.map +1 -0
- package/dist/core/llm-telemetry.js +96 -0
- package/dist/core/llm-telemetry.js.map +1 -0
- package/dist/core/llm-validator.d.ts.map +1 -1
- package/dist/core/llm-validator.js +3 -3
- package/dist/core/llm-validator.js.map +1 -1
- package/dist/core/operations.d.ts.map +1 -1
- package/dist/core/operations.js +4 -35
- package/dist/core/operations.js.map +1 -1
- package/dist/core/paths.d.ts +6 -0
- package/dist/core/paths.d.ts.map +1 -0
- package/dist/core/paths.js +27 -0
- package/dist/core/paths.js.map +1 -0
- package/dist/core/prompt-safety.d.ts.map +1 -1
- package/dist/core/prompt-safety.js.map +1 -1
- package/dist/core/scoring.d.ts +5 -0
- package/dist/core/scoring.d.ts.map +1 -1
- package/dist/core/scoring.js +8 -0
- package/dist/core/scoring.js.map +1 -1
- package/dist/core/serializer.js +1 -1
- package/dist/core/serializer.js.map +1 -1
- package/dist/core/skill-usage-log.js +2 -2
- package/dist/core/skill-usage-log.js.map +1 -1
- package/dist/core/types.d.ts +2 -2
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/verifier.d.ts.map +1 -1
- package/dist/core/verifier.js +4 -4
- package/dist/core/verifier.js.map +1 -1
- package/dist/core/version-check.d.ts.map +1 -1
- package/dist/core/version-check.js +4 -3
- package/dist/core/version-check.js.map +1 -1
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +71 -14
- package/dist/db.js.map +1 -1
- package/dist/knowledge-graph.d.ts +1 -1
- package/dist/knowledge-graph.d.ts.map +1 -1
- package/dist/knowledge-graph.js +1 -1
- package/dist/knowledge-graph.js.map +1 -1
- package/dist/skills-manifest.json +16 -16
- package/dist/storage/fts-index.js +1 -1
- package/dist/storage/fts-index.js.map +1 -1
- package/dist/transports/cli/cli.js +128 -6
- package/dist/transports/cli/cli.js.map +1 -1
- package/dist/transports/http/server.d.ts.map +1 -1
- package/dist/transports/http/server.js +204 -25
- package/dist/transports/http/server.js.map +1 -1
- package/dist/transports/mcp/handlers.d.ts +1 -1
- package/dist/transports/mcp/handlers.d.ts.map +1 -1
- package/dist/transports/mcp/handlers.js +1 -1
- package/dist/transports/mcp/handlers.js.map +1 -1
- package/package.json +2 -2
- package/scripts/hooks/_shared.js +177 -14
- package/scripts/hooks/post-commit.js +50 -8
- package/scripts/hooks/pre-bash-orchestration-nudge.js +8 -3
- package/scripts/hooks/pre-compact.js +13 -16
- package/scripts/hooks/pre-edit-recall.js +28 -13
- package/scripts/hooks/session-start.js +194 -184
- package/scripts/hooks/session-summary.js +376 -41
- package/dist/core/query-expander.d.ts +0 -4
- package/dist/core/query-expander.d.ts.map +0 -1
- package/dist/core/query-expander.js +0 -53
- package/dist/core/query-expander.js.map +0 -1
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
import { getDatabase } from '../db.js';
|
|
2
|
+
const SYSTEM_TAG_PREFIXES = [
|
|
3
|
+
'project:', 'week:', 'severity:', 'scope:', 'source:', 'date:',
|
|
4
|
+
'type:', 'urgency:', 'host:', 'session:', 'release:',
|
|
5
|
+
];
|
|
6
|
+
const SYSTEM_TAG_LITERALS = new Set([
|
|
7
|
+
'session_end', 'auto_saved', 'commit', 'auto-tracked',
|
|
8
|
+
'session-summary', 'session-insight', 'session_keypoint',
|
|
9
|
+
'workflow_checkpoint', 'auto', 'auto-captured',
|
|
10
|
+
'completed', 'plan-completion', 'lesson', 'verification',
|
|
11
|
+
'engineering-judgment', 'reference', 'plan',
|
|
12
|
+
]);
|
|
13
|
+
const DATE_TAG_RE = /^\d{4}-\d{2}-\d{2}/;
|
|
14
|
+
const NAME_STOPWORDS = new Set([
|
|
15
|
+
'the', 'and', 'for', 'with', 'from', 'this', 'that', 'are', 'was', 'has',
|
|
16
|
+
'fix', 'add', 'get', 'set', 'use', 'via', 'not', 'new', 'old', 'update',
|
|
17
|
+
'into', 'onto', 'when', 'then', 'also', 'both', 'each', 'more', 'about',
|
|
18
|
+
'best', 'practices', 'pattern', 'applied', 'standards', 'professional',
|
|
19
|
+
'using', 'based', 'related', 'general', 'common', 'basic', 'simple',
|
|
20
|
+
'verification', 'cleanup', 'refactoring', 'implementation', 'migration',
|
|
21
|
+
'summary', 'overview', 'analysis', 'review', 'report', 'notes',
|
|
22
|
+
'jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec',
|
|
23
|
+
]);
|
|
24
|
+
const NUMERIC_RE = /^\d+$/;
|
|
25
|
+
export function tokenizeName(name) {
|
|
26
|
+
return new Set(name.toLowerCase()
|
|
27
|
+
.split(/[\W_]+/)
|
|
28
|
+
.filter((t) => t.length >= 3 && !NAME_STOPWORDS.has(t) && !NUMERIC_RE.test(t)));
|
|
29
|
+
}
|
|
30
|
+
export function jaccardSimilarity(a, b) {
|
|
31
|
+
if (a.size === 0 || b.size === 0)
|
|
32
|
+
return 0;
|
|
33
|
+
let intersection = 0;
|
|
34
|
+
for (const t of a) {
|
|
35
|
+
if (b.has(t))
|
|
36
|
+
intersection++;
|
|
37
|
+
}
|
|
38
|
+
return intersection / (a.size + b.size - intersection);
|
|
39
|
+
}
|
|
40
|
+
export function isTopicalTag(tag) {
|
|
41
|
+
if (!tag)
|
|
42
|
+
return false;
|
|
43
|
+
const lower = tag.toLowerCase();
|
|
44
|
+
if (DATE_TAG_RE.test(lower))
|
|
45
|
+
return false;
|
|
46
|
+
if (SYSTEM_TAG_LITERALS.has(lower))
|
|
47
|
+
return false;
|
|
48
|
+
for (const prefix of SYSTEM_TAG_PREFIXES) {
|
|
49
|
+
if (lower.startsWith(prefix)) {
|
|
50
|
+
if (prefix === 'topic:' || prefix === 'tech:')
|
|
51
|
+
return true;
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (lower.length < 2)
|
|
56
|
+
return false;
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
export function backfillRelations(opts = {}, db) {
|
|
60
|
+
const conn = db ?? getDatabase();
|
|
61
|
+
const candidates = proposeBackfillCandidates(opts, conn);
|
|
62
|
+
const result = {
|
|
63
|
+
candidatesProposed: candidates.length,
|
|
64
|
+
edgesWritten: 0,
|
|
65
|
+
dryRun: !!opts.dryRun,
|
|
66
|
+
byRule: { tagCooccurrence: 0, projectClustering: 0, sessionCooccurrence: 0, nameTokenSimilarity: 0 },
|
|
67
|
+
};
|
|
68
|
+
if (opts.dryRun)
|
|
69
|
+
return result;
|
|
70
|
+
const insert = conn.prepare('INSERT OR IGNORE INTO relations (from_entity_id, to_entity_id, relation_type) VALUES (?, ?, ?)');
|
|
71
|
+
const tx = conn.transaction((rows) => {
|
|
72
|
+
for (const c of rows) {
|
|
73
|
+
const r = insert.run(c.fromEntityId, c.toEntityId, c.relationType);
|
|
74
|
+
if (r.changes > 0) {
|
|
75
|
+
result.edgesWritten++;
|
|
76
|
+
if (c.relationType === 'related-to')
|
|
77
|
+
result.byRule.tagCooccurrence++;
|
|
78
|
+
else if (c.relationType === 'belongs-to-project')
|
|
79
|
+
result.byRule.projectClustering++;
|
|
80
|
+
else if (c.relationType === 'co-created')
|
|
81
|
+
result.byRule.sessionCooccurrence++;
|
|
82
|
+
else if (c.relationType === 'shares-name-tokens')
|
|
83
|
+
result.byRule.nameTokenSimilarity++;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
tx(candidates);
|
|
88
|
+
return result;
|
|
89
|
+
}
|
|
90
|
+
export function proposeBackfillCandidates(opts = {}, db) {
|
|
91
|
+
const conn = db ?? getDatabase();
|
|
92
|
+
const maxPerSource = opts.maxEdgesPerSource ?? 3;
|
|
93
|
+
const minShared = opts.minSharedTags ?? 2;
|
|
94
|
+
const statusFilter = opts.includeArchived ? "" : "AND e.status = 'active'";
|
|
95
|
+
const projectClause = opts.project ? "AND EXISTS (SELECT 1 FROM tags t2 WHERE t2.entity_id = e.id AND t2.tag = ?)" : "";
|
|
96
|
+
const projectArgs = opts.project ? [`project:${opts.project}`] : [];
|
|
97
|
+
const orphans = conn.prepare(`
|
|
98
|
+
SELECT e.id, e.name, e.type, e.metadata
|
|
99
|
+
FROM entities e
|
|
100
|
+
WHERE 1=1 ${statusFilter}
|
|
101
|
+
${projectClause}
|
|
102
|
+
AND NOT EXISTS (SELECT 1 FROM relations r WHERE r.from_entity_id = e.id OR r.to_entity_id = e.id)
|
|
103
|
+
`).all(...projectArgs);
|
|
104
|
+
if (orphans.length === 0)
|
|
105
|
+
return [];
|
|
106
|
+
const allTagRows = conn.prepare(`
|
|
107
|
+
SELECT t.entity_id, t.tag
|
|
108
|
+
FROM tags t
|
|
109
|
+
JOIN entities e ON e.id = t.entity_id
|
|
110
|
+
WHERE 1=1 ${statusFilter}
|
|
111
|
+
`).all();
|
|
112
|
+
const tagsByEntity = new Map();
|
|
113
|
+
const entitiesByTag = new Map();
|
|
114
|
+
for (const row of allTagRows) {
|
|
115
|
+
if (!isTopicalTag(row.tag))
|
|
116
|
+
continue;
|
|
117
|
+
let set = tagsByEntity.get(row.entity_id);
|
|
118
|
+
if (!set) {
|
|
119
|
+
set = new Set();
|
|
120
|
+
tagsByEntity.set(row.entity_id, set);
|
|
121
|
+
}
|
|
122
|
+
set.add(row.tag);
|
|
123
|
+
let list = entitiesByTag.get(row.tag);
|
|
124
|
+
if (!list) {
|
|
125
|
+
list = [];
|
|
126
|
+
entitiesByTag.set(row.tag, list);
|
|
127
|
+
}
|
|
128
|
+
list.push(row.entity_id);
|
|
129
|
+
}
|
|
130
|
+
const candidates = [];
|
|
131
|
+
const orphanById = new Map();
|
|
132
|
+
for (const o of orphans)
|
|
133
|
+
orphanById.set(o.id, o);
|
|
134
|
+
const allEntities = conn.prepare(`SELECT e.id, e.name, e.type, e.metadata FROM entities e WHERE 1=1 ${statusFilter}`).all();
|
|
135
|
+
const entityById = new Map();
|
|
136
|
+
for (const e of allEntities)
|
|
137
|
+
entityById.set(e.id, e);
|
|
138
|
+
for (const orphan of orphans) {
|
|
139
|
+
const orphanTags = tagsByEntity.get(orphan.id);
|
|
140
|
+
if (!orphanTags || orphanTags.size < minShared)
|
|
141
|
+
continue;
|
|
142
|
+
const overlapByPeer = new Map();
|
|
143
|
+
for (const tag of orphanTags) {
|
|
144
|
+
const peerIds = entitiesByTag.get(tag) ?? [];
|
|
145
|
+
for (const peerId of peerIds) {
|
|
146
|
+
if (peerId === orphan.id)
|
|
147
|
+
continue;
|
|
148
|
+
overlapByPeer.set(peerId, (overlapByPeer.get(peerId) ?? 0) + 1);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
const ranked = [...overlapByPeer.entries()]
|
|
152
|
+
.filter(([_, n]) => n >= minShared)
|
|
153
|
+
.sort((a, b) => b[1] - a[1])
|
|
154
|
+
.slice(0, maxPerSource);
|
|
155
|
+
for (const [peerId, sharedCount] of ranked) {
|
|
156
|
+
const peer = entityById.get(peerId);
|
|
157
|
+
if (!peer)
|
|
158
|
+
continue;
|
|
159
|
+
candidates.push({
|
|
160
|
+
fromEntityId: orphan.id,
|
|
161
|
+
fromName: orphan.name,
|
|
162
|
+
toEntityId: peer.id,
|
|
163
|
+
toName: peer.name,
|
|
164
|
+
relationType: 'related-to',
|
|
165
|
+
reason: `shares ${sharedCount} topical tag${sharedCount > 1 ? 's' : ''}`,
|
|
166
|
+
strength: sharedCount,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
const consumerTypes = new Set(['lesson_learned', 'lesson', 'decision', 'bug_fix', 'pattern', 'mistake', 'best_practice']);
|
|
171
|
+
const anchorTypes = new Set(['release', 'feature', 'architecture', 'plan']);
|
|
172
|
+
const anchorsByProject = new Map();
|
|
173
|
+
const projectAnchorRows = conn.prepare(`
|
|
174
|
+
SELECT e.id, e.name, e.type, e.created_at, t.tag AS project_tag
|
|
175
|
+
FROM entities e
|
|
176
|
+
JOIN tags t ON t.entity_id = e.id AND t.tag LIKE 'project:%'
|
|
177
|
+
WHERE 1=1 ${statusFilter}
|
|
178
|
+
AND e.type IN (${[...anchorTypes].map(() => '?').join(',')})
|
|
179
|
+
`).all(...anchorTypes);
|
|
180
|
+
for (const r of projectAnchorRows) {
|
|
181
|
+
const project = r.project_tag.slice('project:'.length);
|
|
182
|
+
let list = anchorsByProject.get(project);
|
|
183
|
+
if (!list) {
|
|
184
|
+
list = [];
|
|
185
|
+
anchorsByProject.set(project, list);
|
|
186
|
+
}
|
|
187
|
+
list.push({ id: r.id, name: r.name, type: r.type, created_at: r.created_at });
|
|
188
|
+
}
|
|
189
|
+
for (const list of anchorsByProject.values()) {
|
|
190
|
+
list.sort((a, b) => b.created_at.localeCompare(a.created_at));
|
|
191
|
+
}
|
|
192
|
+
const orphanProjectRows = conn.prepare(`
|
|
193
|
+
SELECT t.entity_id, t.tag
|
|
194
|
+
FROM tags t
|
|
195
|
+
JOIN entities e ON e.id = t.entity_id
|
|
196
|
+
WHERE 1=1 ${statusFilter}
|
|
197
|
+
AND t.tag LIKE 'project:%'
|
|
198
|
+
AND NOT EXISTS (SELECT 1 FROM relations r WHERE r.from_entity_id = e.id OR r.to_entity_id = e.id)
|
|
199
|
+
`).all();
|
|
200
|
+
const orphanProject = new Map();
|
|
201
|
+
for (const r of orphanProjectRows)
|
|
202
|
+
orphanProject.set(r.entity_id, r.tag.slice('project:'.length));
|
|
203
|
+
for (const orphan of orphans) {
|
|
204
|
+
if (!consumerTypes.has(orphan.type))
|
|
205
|
+
continue;
|
|
206
|
+
const project = orphanProject.get(orphan.id);
|
|
207
|
+
if (!project)
|
|
208
|
+
continue;
|
|
209
|
+
const anchors = anchorsByProject.get(project);
|
|
210
|
+
if (!anchors || anchors.length === 0)
|
|
211
|
+
continue;
|
|
212
|
+
const anchor = anchors[0];
|
|
213
|
+
if (anchor.id === orphan.id)
|
|
214
|
+
continue;
|
|
215
|
+
candidates.push({
|
|
216
|
+
fromEntityId: orphan.id,
|
|
217
|
+
fromName: orphan.name,
|
|
218
|
+
toEntityId: anchor.id,
|
|
219
|
+
toName: anchor.name,
|
|
220
|
+
relationType: 'belongs-to-project',
|
|
221
|
+
reason: `same-project anchor (${anchor.type})`,
|
|
222
|
+
strength: 1,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
if (opts.includeSessionCooccurrence) {
|
|
226
|
+
const minScore = opts.minSessionSignalScore ?? 0.6;
|
|
227
|
+
const sessionEligibleTypes = new Set([
|
|
228
|
+
'lesson_learned', 'lesson', 'decision', 'architecture', 'architecture_decision',
|
|
229
|
+
'plan', 'feature', 'bug_fix', 'pattern', 'best_practice', 'release',
|
|
230
|
+
]);
|
|
231
|
+
const getSignalScore = (meta) => {
|
|
232
|
+
try {
|
|
233
|
+
const parsed = JSON.parse(meta ?? '{}');
|
|
234
|
+
const s = parsed?.signal_score;
|
|
235
|
+
return typeof s === 'number' ? s : 1.0;
|
|
236
|
+
}
|
|
237
|
+
catch {
|
|
238
|
+
return 1.0;
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
const sessionTagRows = conn.prepare(`
|
|
242
|
+
SELECT t.entity_id, t.tag
|
|
243
|
+
FROM tags t
|
|
244
|
+
JOIN entities e ON e.id = t.entity_id
|
|
245
|
+
WHERE 1=1 ${statusFilter}
|
|
246
|
+
AND t.tag LIKE 'session:%'
|
|
247
|
+
`).all();
|
|
248
|
+
const entitiesBySession = new Map();
|
|
249
|
+
for (const row of sessionTagRows) {
|
|
250
|
+
const ent = entityById.get(row.entity_id);
|
|
251
|
+
if (!ent || !sessionEligibleTypes.has(ent.type))
|
|
252
|
+
continue;
|
|
253
|
+
if (getSignalScore(ent.metadata) < minScore)
|
|
254
|
+
continue;
|
|
255
|
+
let list = entitiesBySession.get(row.tag);
|
|
256
|
+
if (!list) {
|
|
257
|
+
list = [];
|
|
258
|
+
entitiesBySession.set(row.tag, list);
|
|
259
|
+
}
|
|
260
|
+
list.push(row.entity_id);
|
|
261
|
+
}
|
|
262
|
+
const sessionTagsByOrphan = new Map();
|
|
263
|
+
for (const row of sessionTagRows) {
|
|
264
|
+
if (!orphanById.has(row.entity_id))
|
|
265
|
+
continue;
|
|
266
|
+
let list = sessionTagsByOrphan.get(row.entity_id);
|
|
267
|
+
if (!list) {
|
|
268
|
+
list = [];
|
|
269
|
+
sessionTagsByOrphan.set(row.entity_id, list);
|
|
270
|
+
}
|
|
271
|
+
list.push(row.tag);
|
|
272
|
+
}
|
|
273
|
+
for (const orphan of orphans) {
|
|
274
|
+
if (!sessionEligibleTypes.has(orphan.type))
|
|
275
|
+
continue;
|
|
276
|
+
if (getSignalScore(orphan.metadata) < minScore)
|
|
277
|
+
continue;
|
|
278
|
+
const sessionTags = sessionTagsByOrphan.get(orphan.id) ?? [];
|
|
279
|
+
let added = 0;
|
|
280
|
+
const proposedPeers = new Set();
|
|
281
|
+
for (const stag of sessionTags) {
|
|
282
|
+
const peers = (entitiesBySession.get(stag) ?? []).filter((id) => id !== orphan.id);
|
|
283
|
+
for (const peerId of peers) {
|
|
284
|
+
if (added >= maxPerSource)
|
|
285
|
+
break;
|
|
286
|
+
if (proposedPeers.has(peerId))
|
|
287
|
+
continue;
|
|
288
|
+
const peer = entityById.get(peerId);
|
|
289
|
+
if (!peer)
|
|
290
|
+
continue;
|
|
291
|
+
candidates.push({
|
|
292
|
+
fromEntityId: orphan.id,
|
|
293
|
+
fromName: orphan.name,
|
|
294
|
+
toEntityId: peer.id,
|
|
295
|
+
toName: peer.name,
|
|
296
|
+
relationType: 'co-created',
|
|
297
|
+
reason: `co-created in same session (${stag})`,
|
|
298
|
+
strength: 2,
|
|
299
|
+
});
|
|
300
|
+
proposedPeers.add(peerId);
|
|
301
|
+
added++;
|
|
302
|
+
}
|
|
303
|
+
if (added >= maxPerSource)
|
|
304
|
+
break;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
if (opts.includeNameTokenSimilarity) {
|
|
309
|
+
const minJaccard = opts.minNameJaccard ?? 0.50;
|
|
310
|
+
const minSharedTokens = opts.minSharedNameTokens ?? 3;
|
|
311
|
+
const tokensByEntity = new Map();
|
|
312
|
+
for (const e of allEntities) {
|
|
313
|
+
const tokens = tokenizeName(e.name);
|
|
314
|
+
if (tokens.size >= 2)
|
|
315
|
+
tokensByEntity.set(e.id, tokens);
|
|
316
|
+
}
|
|
317
|
+
const proposedNamePairs = new Set();
|
|
318
|
+
for (const orphan of orphans) {
|
|
319
|
+
const orphanTokens = tokensByEntity.get(orphan.id);
|
|
320
|
+
if (!orphanTokens)
|
|
321
|
+
continue;
|
|
322
|
+
const scored = [];
|
|
323
|
+
for (const [candidateId, candidateTokens] of tokensByEntity) {
|
|
324
|
+
if (candidateId === orphan.id)
|
|
325
|
+
continue;
|
|
326
|
+
const pairKey = `${Math.min(orphan.id, candidateId)}-${Math.max(orphan.id, candidateId)}`;
|
|
327
|
+
if (proposedNamePairs.has(pairKey))
|
|
328
|
+
continue;
|
|
329
|
+
let shared = 0;
|
|
330
|
+
for (const t of orphanTokens) {
|
|
331
|
+
if (candidateTokens.has(t))
|
|
332
|
+
shared++;
|
|
333
|
+
}
|
|
334
|
+
if (shared === 0)
|
|
335
|
+
continue;
|
|
336
|
+
const jaccard = jaccardSimilarity(orphanTokens, candidateTokens);
|
|
337
|
+
if (shared >= minSharedTokens || jaccard >= minJaccard) {
|
|
338
|
+
scored.push({ id: candidateId, shared, jaccard });
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
scored.sort((a, b) => b.shared - a.shared || b.jaccard - a.jaccard);
|
|
342
|
+
for (const { id: peerId, shared, jaccard } of scored.slice(0, maxPerSource)) {
|
|
343
|
+
const peer = entityById.get(peerId);
|
|
344
|
+
if (!peer)
|
|
345
|
+
continue;
|
|
346
|
+
const pairKey = `${Math.min(orphan.id, peerId)}-${Math.max(orphan.id, peerId)}`;
|
|
347
|
+
proposedNamePairs.add(pairKey);
|
|
348
|
+
candidates.push({
|
|
349
|
+
fromEntityId: orphan.id,
|
|
350
|
+
fromName: orphan.name,
|
|
351
|
+
toEntityId: peer.id,
|
|
352
|
+
toName: peer.name,
|
|
353
|
+
relationType: 'shares-name-tokens',
|
|
354
|
+
reason: `${shared} shared name token(s), Jaccard=${jaccard.toFixed(2)}`,
|
|
355
|
+
strength: shared,
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
return candidates;
|
|
361
|
+
}
|
|
362
|
+
//# sourceMappingURL=kg-backfill.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kg-backfill.js","sourceRoot":"","sources":["../../src/core/kg-backfill.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
import type { LLMConfig } from './config.js';
|
|
2
|
+
export type LLMErrorClass = 'auth' | 'rate_limit' | 'upstream' | 'bad_request' | 'network' | 'unknown';
|
|
3
|
+
export interface LLMAttempt {
|
|
4
|
+
provider: LLMConfig['provider'];
|
|
5
|
+
model?: string;
|
|
6
|
+
status: 'ok' | 'fail';
|
|
7
|
+
latencyMs: number;
|
|
8
|
+
errorClass?: LLMErrorClass;
|
|
9
|
+
errorMessage?: string;
|
|
10
|
+
index: number;
|
|
11
|
+
}
|
|
2
12
|
export interface CallLLMOptions {
|
|
3
13
|
maxTokens?: number;
|
|
14
|
+
fallbacks?: LLMConfig[];
|
|
15
|
+
onAttempt?: (attempts: LLMAttempt[]) => void;
|
|
4
16
|
}
|
|
5
17
|
export declare function callLLM(prompt: string, config: LLMConfig, opts?: CallLLMOptions): Promise<string>;
|
|
18
|
+
export declare function classifyError(e: Error): LLMErrorClass;
|
|
6
19
|
//# sourceMappingURL=llm-client.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"llm-client.d.ts","sourceRoot":"","sources":["../../src/core/llm-client.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"llm-client.d.ts","sourceRoot":"","sources":["../../src/core/llm-client.ts"],"names":[],"mappings":"AAuCA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAG7C,MAAM,MAAM,aAAa,GACrB,MAAM,GACN,YAAY,GACZ,UAAU,GACV,aAAa,GACb,SAAS,GACT,SAAS,CAAC;AAEd,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,SAAS,CAAC,UAAU,CAAC,CAAC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,IAAI,GAAG,MAAM,CAAC;IAEtB,SAAS,EAAE,MAAM,CAAC;IAElB,UAAU,CAAC,EAAE,aAAa,CAAC;IAE3B,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAE7B,SAAS,CAAC,EAAE,MAAM,CAAC;IAMnB,SAAS,CAAC,EAAE,SAAS,EAAE,CAAC;IAQxB,SAAS,CAAC,EAAE,CAAC,QAAQ,EAAE,UAAU,EAAE,KAAK,IAAI,CAAC;CAC9C;AAoBD,wBAAsB,OAAO,CAC3B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,SAAS,EACjB,IAAI,GAAE,cAAmB,GACxB,OAAO,CAAC,MAAM,CAAC,CAqCjB;AA+ED,wBAAgB,aAAa,CAAC,CAAC,EAAE,KAAK,GAAG,aAAa,CAgBrD"}
|
package/dist/core/llm-client.js
CHANGED
|
@@ -1,9 +1,48 @@
|
|
|
1
1
|
export async function callLLM(prompt, config, opts = {}) {
|
|
2
|
-
const
|
|
2
|
+
const chain = [config, ...(opts.fallbacks ?? [])];
|
|
3
|
+
const attempts = [];
|
|
4
|
+
const reportAttempts = () => {
|
|
5
|
+
if (!opts.onAttempt)
|
|
6
|
+
return;
|
|
7
|
+
try {
|
|
8
|
+
opts.onAttempt(attempts);
|
|
9
|
+
}
|
|
10
|
+
catch { }
|
|
11
|
+
};
|
|
12
|
+
let lastErr = null;
|
|
13
|
+
for (let i = 0; i < chain.length; i++) {
|
|
14
|
+
const cfg = chain[i];
|
|
15
|
+
const t0 = Date.now();
|
|
16
|
+
try {
|
|
17
|
+
const text = await callSingle(prompt, cfg, opts.maxTokens ?? 200);
|
|
18
|
+
attempts.push({
|
|
19
|
+
provider: cfg.provider, model: cfg.model,
|
|
20
|
+
status: 'ok', latencyMs: Date.now() - t0, index: i,
|
|
21
|
+
});
|
|
22
|
+
reportAttempts();
|
|
23
|
+
return text;
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
const e = err instanceof Error ? err : new Error(String(err));
|
|
27
|
+
const klass = classifyError(e);
|
|
28
|
+
attempts.push({
|
|
29
|
+
provider: cfg.provider, model: cfg.model,
|
|
30
|
+
status: 'fail', latencyMs: Date.now() - t0,
|
|
31
|
+
errorClass: klass, errorMessage: redactSecrets(e.message), index: i,
|
|
32
|
+
});
|
|
33
|
+
lastErr = e;
|
|
34
|
+
if (klass === 'bad_request')
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
reportAttempts();
|
|
39
|
+
throw lastErr ?? new Error('callLLM: no providers configured');
|
|
40
|
+
}
|
|
41
|
+
async function callSingle(prompt, config, maxTokens) {
|
|
3
42
|
if (config.provider === 'anthropic') {
|
|
4
43
|
const apiKey = config.apiKey || process.env.ANTHROPIC_API_KEY;
|
|
5
44
|
if (!apiKey)
|
|
6
|
-
throw new Error('
|
|
45
|
+
throw new Error('Anthropic: no API key configured');
|
|
7
46
|
const res = await fetch('https://api.anthropic.com/v1/messages', {
|
|
8
47
|
method: 'POST',
|
|
9
48
|
headers: {
|
|
@@ -25,7 +64,7 @@ export async function callLLM(prompt, config, opts = {}) {
|
|
|
25
64
|
if (config.provider === 'openai') {
|
|
26
65
|
const apiKey = config.apiKey || process.env.OPENAI_API_KEY;
|
|
27
66
|
if (!apiKey)
|
|
28
|
-
throw new Error('
|
|
67
|
+
throw new Error('OpenAI: no API key configured');
|
|
29
68
|
const res = await fetch('https://api.openai.com/v1/chat/completions', {
|
|
30
69
|
method: 'POST',
|
|
31
70
|
headers: {
|
|
@@ -61,4 +100,25 @@ export async function callLLM(prompt, config, opts = {}) {
|
|
|
61
100
|
}
|
|
62
101
|
return '';
|
|
63
102
|
}
|
|
103
|
+
export function classifyError(e) {
|
|
104
|
+
const msg = e.message;
|
|
105
|
+
if (/\b(401|403|invalid_api_key|authentication|x-api-key|unauthorized)\b/i.test(msg))
|
|
106
|
+
return 'auth';
|
|
107
|
+
if (/no API key/i.test(msg))
|
|
108
|
+
return 'auth';
|
|
109
|
+
if (/\b(429|rate.?limit|quota)\b/i.test(msg))
|
|
110
|
+
return 'rate_limit';
|
|
111
|
+
if (/\b(50[023]|service unavailable|bad gateway)\b/i.test(msg))
|
|
112
|
+
return 'upstream';
|
|
113
|
+
if (/\b400\b/.test(msg))
|
|
114
|
+
return 'bad_request';
|
|
115
|
+
if (/(ENOTFOUND|ECONNREFUSED|ECONNRESET|ETIMEDOUT|fetch failed|aborted)/i.test(msg))
|
|
116
|
+
return 'network';
|
|
117
|
+
return 'unknown';
|
|
118
|
+
}
|
|
119
|
+
function redactSecrets(msg) {
|
|
120
|
+
return msg
|
|
121
|
+
.replace(/sk-[A-Za-z0-9_-]{8,}/g, 'sk-<REDACTED>')
|
|
122
|
+
.replace(/Bearer\s+[A-Za-z0-9_.-]{8,}/gi, 'Bearer <REDACTED>');
|
|
123
|
+
}
|
|
64
124
|
//# sourceMappingURL=llm-client.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"llm-client.js","sourceRoot":"","sources":["../../src/core/llm-client.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"llm-client.js","sourceRoot":"","sources":["../../src/core/llm-client.ts"],"names":[],"mappings":"AAqGA,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,MAAc,EACd,MAAiB,EACjB,OAAuB,EAAE;IAEzB,MAAM,KAAK,GAAgB,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,CAAC;IAC/D,MAAM,QAAQ,GAAiB,EAAE,CAAC;IAClC,MAAM,cAAc,GAAG,GAAG,EAAE;QAC1B,IAAI,CAAC,IAAI,CAAC,SAAS;YAAE,OAAO;QAC5B,IAAI,CAAC;YAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAyC,CAAC;IACrF,CAAC,CAAC;IAEF,IAAI,OAAO,GAAiB,IAAI,CAAC;IACjC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACrB,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACtB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC,SAAS,IAAI,GAAG,CAAC,CAAC;YAClE,QAAQ,CAAC,IAAI,CAAC;gBACZ,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK;gBACxC,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,CAAC;aACnD,CAAC,CAAC;YACH,cAAc,EAAE,CAAC;YACjB,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;YAC9D,MAAM,KAAK,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC;YAC/B,QAAQ,CAAC,IAAI,CAAC;gBACZ,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK;gBACxC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE;gBAC1C,UAAU,EAAE,KAAK,EAAE,YAAY,EAAE,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC;aACpE,CAAC,CAAC;YACH,OAAO,GAAG,CAAC,CAAC;YAGZ,IAAI,KAAK,KAAK,aAAa;gBAAE,MAAM;QACrC,CAAC;IACH,CAAC;IAED,cAAc,EAAE,CAAC;IACjB,MAAM,OAAO,IAAI,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;AACjE,CAAC;AAGD,KAAK,UAAU,UAAU,CACvB,MAAc,EACd,MAAiB,EACjB,SAAiB;IAEjB,IAAI,MAAM,CAAC,QAAQ,KAAK,WAAW,EAAE,CAAC;QACpC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;QAC9D,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;QACjE,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,uCAAuC,EAAE;YAC/D,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,WAAW,EAAE,MAAM;gBACnB,mBAAmB,EAAE,YAAY;gBACjC,cAAc,EAAE,kBAAkB;aACnC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,kBAAkB;gBACzC,UAAU,EAAE,SAAS;gBACrB,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;aAC9C,CAAC;SACH,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;QACnE,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAuB,CAAC;QACnD,OAAO,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,IAAI,EAAE,CAAC;IACvC,CAAC;IAED,IAAI,MAAM,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACjC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;QAC3D,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;QAC9D,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,4CAA4C,EAAE;YACpE,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,eAAe,EAAE,UAAU,MAAM,EAAE;gBACnC,cAAc,EAAE,kBAAkB;aACnC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,aAAa;gBACpC,UAAU,EAAE,SAAS;gBACrB,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;aAC9C,CAAC;SACH,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;QAChE,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAoB,CAAC;QAChD,OAAO,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,IAAI,EAAE,CAAC;IACnD,CAAC;IAED,IAAI,MAAM,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACjC,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,wBAAwB,CAAC;QACjE,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,eAAe,EAAE;YAC9C,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,UAAU;gBACjC,MAAM;gBACN,MAAM,EAAE,KAAK;aAKd,CAAC;SACH,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,iBAAiB,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;QAC5D,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAoB,CAAC;QAChD,OAAO,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC;IAC7B,CAAC;IAED,OAAO,EAAE,CAAC;AACZ,CAAC;AASD,MAAM,UAAU,aAAa,CAAC,CAAQ;IACpC,MAAM,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC;IAEtB,IAAI,sEAAsE,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,MAAM,CAAC;IAEpG,IAAI,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,MAAM,CAAC;IAE3C,IAAI,8BAA8B,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,YAAY,CAAC;IAElE,IAAI,gDAAgD,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,UAAU,CAAC;IAGlF,IAAI,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,aAAa,CAAC;IAE9C,IAAI,qEAAqE,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IACtG,OAAO,SAAS,CAAC;AACnB,CAAC;AAQD,SAAS,aAAa,CAAC,GAAW;IAChC,OAAO,GAAG;SACP,OAAO,CAAC,uBAAuB,EAAE,eAAe,CAAC;SACjD,OAAO,CAAC,+BAA+B,EAAE,mBAAmB,CAAC,CAAC;AACnE,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type Database from 'better-sqlite3';
|
|
2
|
+
import type { LLMAttempt } from './llm-client.js';
|
|
3
|
+
export interface RecordTelemetryOpts {
|
|
4
|
+
flow: string;
|
|
5
|
+
project?: string;
|
|
6
|
+
db?: Database.Database;
|
|
7
|
+
}
|
|
8
|
+
export declare function recordTelemetry(attempts: LLMAttempt[], opts: RecordTelemetryOpts): void;
|
|
9
|
+
export interface TelemetrySummary {
|
|
10
|
+
flow: string;
|
|
11
|
+
total_calls: number;
|
|
12
|
+
total_attempts: number;
|
|
13
|
+
successes: number;
|
|
14
|
+
failures: number;
|
|
15
|
+
fallback_used: number;
|
|
16
|
+
median_latency_ms: number | null;
|
|
17
|
+
by_provider: Record<string, {
|
|
18
|
+
ok: number;
|
|
19
|
+
fail: number;
|
|
20
|
+
}>;
|
|
21
|
+
by_error_class: Record<string, number>;
|
|
22
|
+
window_days: number;
|
|
23
|
+
}
|
|
24
|
+
export declare function summariseTelemetry(windowDays?: number, db?: Database.Database): TelemetrySummary[];
|
|
25
|
+
export interface PruneOptions {
|
|
26
|
+
olderThanDays?: number;
|
|
27
|
+
db?: Database.Database;
|
|
28
|
+
}
|
|
29
|
+
export interface PruneResult {
|
|
30
|
+
deletedRows: number;
|
|
31
|
+
cutoffIso: string;
|
|
32
|
+
totalRowsAfter: number;
|
|
33
|
+
}
|
|
34
|
+
export declare function pruneTelemetry(opts?: PruneOptions): PruneResult;
|
|
35
|
+
//# sourceMappingURL=llm-telemetry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llm-telemetry.d.ts","sourceRoot":"","sources":["../../src/core/llm-telemetry.ts"],"names":[],"mappings":"AAgCA,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAGlD,MAAM,WAAW,mBAAmB;IAElC,IAAI,EAAE,MAAM,CAAC;IAEb,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,EAAE,CAAC,EAAE,QAAQ,CAAC,QAAQ,CAAC;CACxB;AAcD,wBAAgB,eAAe,CAAC,QAAQ,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,mBAAmB,GAAG,IAAI,CAyCvF;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC1D,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,WAAW,EAAE,MAAM,CAAC;CACrB;AAQD,wBAAgB,kBAAkB,CAAC,UAAU,SAAK,EAAE,EAAE,CAAC,EAAE,QAAQ,CAAC,QAAQ,GAAG,gBAAgB,EAAE,CAkE9F;AAED,MAAM,WAAW,YAAY;IAE3B,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,EAAE,CAAC,EAAE,QAAQ,CAAC,QAAQ,CAAC;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;CACxB;AAaD,wBAAgB,cAAc,CAAC,IAAI,GAAE,YAAiB,GAAG,WAAW,CAkBnE"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { getDatabase } from '../db.js';
|
|
2
|
+
export function recordTelemetry(attempts, opts) {
|
|
3
|
+
if (!attempts || attempts.length === 0)
|
|
4
|
+
return;
|
|
5
|
+
let db;
|
|
6
|
+
try {
|
|
7
|
+
db = opts.db ?? getDatabase();
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
const stmt = db.prepare(`
|
|
13
|
+
INSERT INTO llm_telemetry (
|
|
14
|
+
flow, provider, model, project, attempt_index, status,
|
|
15
|
+
latency_ms, error_class, error_message, fallback_used
|
|
16
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
17
|
+
`);
|
|
18
|
+
const tx = db.transaction((rows) => {
|
|
19
|
+
for (const a of rows) {
|
|
20
|
+
try {
|
|
21
|
+
stmt.run(opts.flow, a.provider, a.model ?? null, opts.project ?? null, a.index, a.status, a.latencyMs, a.errorClass ?? null, a.errorMessage ?? null, a.index > 0 ? 1 : 0);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
try {
|
|
28
|
+
tx(attempts);
|
|
29
|
+
}
|
|
30
|
+
catch { }
|
|
31
|
+
}
|
|
32
|
+
export function summariseTelemetry(windowDays = 30, db) {
|
|
33
|
+
const conn = db ?? getDatabase();
|
|
34
|
+
const since = new Date(Date.now() - windowDays * 86400000).toISOString();
|
|
35
|
+
const rows = conn.prepare(`
|
|
36
|
+
SELECT flow, provider, status, latency_ms, error_class, attempt_index, fallback_used
|
|
37
|
+
FROM llm_telemetry
|
|
38
|
+
WHERE ts >= ?
|
|
39
|
+
ORDER BY ts ASC
|
|
40
|
+
`).all(since);
|
|
41
|
+
const byFlow = new Map();
|
|
42
|
+
for (const r of rows) {
|
|
43
|
+
let bucket = byFlow.get(r.flow);
|
|
44
|
+
if (!bucket) {
|
|
45
|
+
bucket = { attempts: [], primaryAttempts: 0 };
|
|
46
|
+
byFlow.set(r.flow, bucket);
|
|
47
|
+
}
|
|
48
|
+
bucket.attempts.push(r);
|
|
49
|
+
if (r.attempt_index === 0)
|
|
50
|
+
bucket.primaryAttempts++;
|
|
51
|
+
}
|
|
52
|
+
const out = [];
|
|
53
|
+
for (const [flow, bucket] of byFlow) {
|
|
54
|
+
const successes = bucket.attempts.filter(a => a.status === 'ok').length;
|
|
55
|
+
const failures = bucket.attempts.filter(a => a.status === 'fail').length;
|
|
56
|
+
const fallbackUsed = bucket.attempts.filter(a => a.fallback_used === 1).length;
|
|
57
|
+
const okLatencies = bucket.attempts.filter(a => a.status === 'ok' && a.latency_ms != null).map(a => a.latency_ms).sort((a, b) => a - b);
|
|
58
|
+
const median = okLatencies.length === 0 ? null : okLatencies[Math.floor(okLatencies.length / 2)];
|
|
59
|
+
const byProvider = {};
|
|
60
|
+
for (const a of bucket.attempts) {
|
|
61
|
+
byProvider[a.provider] ??= { ok: 0, fail: 0 };
|
|
62
|
+
byProvider[a.provider][a.status === 'ok' ? 'ok' : 'fail']++;
|
|
63
|
+
}
|
|
64
|
+
const byErrorClass = {};
|
|
65
|
+
for (const a of bucket.attempts) {
|
|
66
|
+
if (a.error_class)
|
|
67
|
+
byErrorClass[a.error_class] = (byErrorClass[a.error_class] ?? 0) + 1;
|
|
68
|
+
}
|
|
69
|
+
out.push({
|
|
70
|
+
flow,
|
|
71
|
+
total_calls: bucket.primaryAttempts,
|
|
72
|
+
total_attempts: bucket.attempts.length,
|
|
73
|
+
successes,
|
|
74
|
+
failures,
|
|
75
|
+
fallback_used: fallbackUsed,
|
|
76
|
+
median_latency_ms: median,
|
|
77
|
+
by_provider: byProvider,
|
|
78
|
+
by_error_class: byErrorClass,
|
|
79
|
+
window_days: windowDays,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
return out.sort((a, b) => b.total_attempts - a.total_attempts);
|
|
83
|
+
}
|
|
84
|
+
export function pruneTelemetry(opts = {}) {
|
|
85
|
+
const olderThanDays = opts.olderThanDays ?? 180;
|
|
86
|
+
const db = opts.db ?? getDatabase();
|
|
87
|
+
const cutoffIso = new Date(Date.now() - olderThanDays * 86400000).toISOString();
|
|
88
|
+
const result = db.prepare('DELETE FROM llm_telemetry WHERE ts < ?').run(cutoffIso);
|
|
89
|
+
const totalRowsAfter = db.prepare('SELECT COUNT(*) AS c FROM llm_telemetry').get().c;
|
|
90
|
+
return {
|
|
91
|
+
deletedRows: result.changes,
|
|
92
|
+
cutoffIso,
|
|
93
|
+
totalRowsAfter,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=llm-telemetry.js.map
|