@equationalapplications/core-llm-wiki 4.3.0 → 4.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/README.md +65 -0
- package/dist/index.d.mts +25 -1
- package/dist/index.d.ts +25 -1
- package/dist/index.js +109 -54
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +106 -55
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1058,14 +1058,14 @@ After running the migration SQL, restart your application.`
|
|
|
1058
1058
|
);
|
|
1059
1059
|
}
|
|
1060
1060
|
}
|
|
1061
|
-
const
|
|
1061
|
+
const mismatchScope = this._entityInClause(entityIds);
|
|
1062
1062
|
const mismatchedCount = await this.db.getFirstAsync(
|
|
1063
1063
|
`SELECT COUNT(*) AS cnt FROM ${this.prefix}entries
|
|
1064
|
-
WHERE ${
|
|
1064
|
+
WHERE ${mismatchScope.clause} AND deleted_at IS NULL
|
|
1065
1065
|
AND embedding_blob IS NOT NULL
|
|
1066
1066
|
AND (CAST(length(embedding_blob) AS INTEGER) % 4 = 0)
|
|
1067
1067
|
AND (CAST(length(embedding_blob) AS INTEGER) / 4) != ?`,
|
|
1068
|
-
[...
|
|
1068
|
+
[...mismatchScope.params, queryVec.length]
|
|
1069
1069
|
);
|
|
1070
1070
|
if (mismatchedCount && mismatchedCount.cnt > 0) {
|
|
1071
1071
|
throw new Error(
|
|
@@ -1125,16 +1125,16 @@ After running the migration SQL, restart your application.`
|
|
|
1125
1125
|
}
|
|
1126
1126
|
} else {
|
|
1127
1127
|
if (useRanker) {
|
|
1128
|
-
const
|
|
1128
|
+
const entityScope = this._entityInClause(scoredEntityIds);
|
|
1129
1129
|
candidateRows = await this.db.getAllAsync(
|
|
1130
|
-
`SELECT id, entity_id, updated_at, access_count FROM ${this.prefix}entries WHERE ${
|
|
1131
|
-
|
|
1130
|
+
`SELECT id, entity_id, updated_at, access_count FROM ${this.prefix}entries WHERE ${entityScope.clause} AND deleted_at IS NULL`,
|
|
1131
|
+
entityScope.params
|
|
1132
1132
|
);
|
|
1133
1133
|
} else {
|
|
1134
|
-
const
|
|
1134
|
+
const entityScope = this._entityInClause(scoredEntityIds);
|
|
1135
1135
|
candidateRows = await this.db.getAllAsync(
|
|
1136
|
-
`SELECT id, entity_id, embedding_blob, embedding, updated_at, access_count FROM ${this.prefix}entries WHERE ${
|
|
1137
|
-
|
|
1136
|
+
`SELECT id, entity_id, embedding_blob, embedding, updated_at, access_count FROM ${this.prefix}entries WHERE ${entityScope.clause} AND deleted_at IS NULL`,
|
|
1137
|
+
entityScope.params
|
|
1138
1138
|
);
|
|
1139
1139
|
}
|
|
1140
1140
|
if (weight !== void 0 && weight < 1) {
|
|
@@ -1153,32 +1153,42 @@ After running the migration SQL, restart your application.`
|
|
|
1153
1153
|
const entityCacheKey = entityIds.length === 1 ? entityIds[0] : entityIds.join("\0");
|
|
1154
1154
|
let scored;
|
|
1155
1155
|
if (useRanker) {
|
|
1156
|
-
const
|
|
1156
|
+
const candidateRowsByEntity = /* @__PURE__ */ new Map();
|
|
1157
|
+
for (const row of candidateRows) {
|
|
1158
|
+
const rows = candidateRowsByEntity.get(row.entity_id) ?? [];
|
|
1159
|
+
rows.push(row);
|
|
1160
|
+
candidateRowsByEntity.set(row.entity_id, rows);
|
|
1161
|
+
}
|
|
1157
1162
|
try {
|
|
1158
|
-
const
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
}
|
|
1176
|
-
scored = scored.map((s) => {
|
|
1177
|
-
const meta = metaMap.get(s.id);
|
|
1178
|
-
return { ...s, updated_at: meta?.updated_at ?? null, access_count: meta?.access_count ?? null };
|
|
1179
|
-
});
|
|
1180
|
-
}
|
|
1163
|
+
const rankerResultsByEntity = await Promise.all(
|
|
1164
|
+
scoredEntityIds.filter((id) => (candidateRowsByEntity.get(id)?.length ?? 0) > 0).map(async (scopedEntityId) => {
|
|
1165
|
+
const rowsForEntity = candidateRowsByEntity.get(scopedEntityId) ?? [];
|
|
1166
|
+
const candidateIds = effectivePreFilterLimit !== void 0 ? rowsForEntity.map((row) => row.id) : void 0;
|
|
1167
|
+
const ranked = await this._rankWithVectorRanker({
|
|
1168
|
+
entityId: scopedEntityId,
|
|
1169
|
+
queryVec,
|
|
1170
|
+
candidateIds,
|
|
1171
|
+
candidateRows: rowsForEntity,
|
|
1172
|
+
weight,
|
|
1173
|
+
miniSearchScores,
|
|
1174
|
+
limit: Math.max(maxResults * 2, maxResults + 50)
|
|
1175
|
+
});
|
|
1176
|
+
return ranked.map((row) => ({ ...row, entity_id: scopedEntityId }));
|
|
1177
|
+
})
|
|
1178
|
+
);
|
|
1179
|
+
scored = rankerResultsByEntity.flat();
|
|
1181
1180
|
const scoredIds = new Set(scored.map((s) => s.id));
|
|
1181
|
+
const metadataById = new Map(
|
|
1182
|
+
candidateRows.filter((row) => scoredIds.has(row.id)).map((row) => [row.id, row])
|
|
1183
|
+
);
|
|
1184
|
+
scored = scored.map((row) => {
|
|
1185
|
+
const metadata = metadataById.get(row.id);
|
|
1186
|
+
return {
|
|
1187
|
+
...row,
|
|
1188
|
+
updated_at: metadata?.updated_at ?? null,
|
|
1189
|
+
access_count: metadata?.access_count ?? null
|
|
1190
|
+
};
|
|
1191
|
+
});
|
|
1182
1192
|
const isHybrid = weight !== void 0 && weight < 1;
|
|
1183
1193
|
const maxBackfill = isHybrid ? maxResults : Math.max(0, maxResults - scored.length);
|
|
1184
1194
|
if (maxBackfill > 0) {
|
|
@@ -1326,21 +1336,17 @@ After running the migration SQL, restart your application.`
|
|
|
1326
1336
|
});
|
|
1327
1337
|
const keywordOversampledLimit = Math.max(maxResults * 2, maxResults + 50);
|
|
1328
1338
|
const topResults = msResults.slice(0, keywordOversampledLimit);
|
|
1329
|
-
const
|
|
1330
|
-
const candidateMap =
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
}
|
|
1335
|
-
}
|
|
1336
|
-
scored = topResults.map((r) => {
|
|
1337
|
-
const meta = candidateMap.get(r.id);
|
|
1339
|
+
const topResultIds = new Set(topResults.map((r) => r.id));
|
|
1340
|
+
const candidateMap = new Map(candidateRows.filter((r) => topResultIds.has(r.id)).map((row) => [row.id, row]));
|
|
1341
|
+
scored = topResults.map((result) => {
|
|
1342
|
+
const metadata = candidateMap.get(result.id);
|
|
1343
|
+
const entityForScore = metadata?.entity_id ?? result.entity_id ?? "";
|
|
1338
1344
|
return {
|
|
1339
|
-
id:
|
|
1340
|
-
entity_id:
|
|
1341
|
-
score:
|
|
1342
|
-
access_count:
|
|
1343
|
-
updated_at:
|
|
1345
|
+
id: result.id,
|
|
1346
|
+
entity_id: entityForScore,
|
|
1347
|
+
score: result.score ?? 0,
|
|
1348
|
+
access_count: metadata?.access_count ?? null,
|
|
1349
|
+
updated_at: metadata?.updated_at ?? null
|
|
1344
1350
|
};
|
|
1345
1351
|
});
|
|
1346
1352
|
} else {
|
|
@@ -2781,16 +2787,20 @@ function scoreFactFor(fact, weights, now) {
|
|
|
2781
2787
|
const recencyDecay = Math.exp(-ageDays / 30);
|
|
2782
2788
|
return confW * weights.confidence + Math.log(1 + fact.access_count) * weights.accessCount + recencyDecay * weights.recency;
|
|
2783
2789
|
}
|
|
2784
|
-
function renderFactMarkdown(fact, includeConfidence, includeTags) {
|
|
2790
|
+
function renderFactMarkdown(fact, includeConfidence, includeTags, includeEntityIds, score) {
|
|
2785
2791
|
const confPart = includeConfidence ? ` (${fact.confidence})` : "";
|
|
2786
2792
|
const tagPart = includeTags && fact.tags.length > 0 ? ` [${fact.tags.join(", ")}]` : "";
|
|
2787
|
-
|
|
2793
|
+
const sourcePart = includeEntityIds ? ` {entity_id=${fact.entity_id}}` : "";
|
|
2794
|
+
const scorePart = score !== void 0 ? ` {score=${score.toFixed(4)}}` : "";
|
|
2795
|
+
return `- **${fact.title}**${confPart}${tagPart}${sourcePart}${scorePart}
|
|
2788
2796
|
${fact.body.replace(/\n/g, "\n ")}`;
|
|
2789
2797
|
}
|
|
2790
|
-
function renderFactPlain(fact, includeConfidence, includeTags) {
|
|
2798
|
+
function renderFactPlain(fact, includeConfidence, includeTags, includeEntityIds, score) {
|
|
2791
2799
|
const confPart = includeConfidence ? ` (${fact.confidence})` : "";
|
|
2792
2800
|
const tagPart = includeTags && fact.tags.length > 0 ? ` [${fact.tags.join(", ")}]` : "";
|
|
2793
|
-
|
|
2801
|
+
const sourcePart = includeEntityIds ? ` {entity_id=${fact.entity_id}}` : "";
|
|
2802
|
+
const scorePart = score !== void 0 ? ` {score=${score.toFixed(4)}}` : "";
|
|
2803
|
+
return `${fact.title}${confPart}${tagPart}${sourcePart}${scorePart}: ${fact.body}`;
|
|
2794
2804
|
}
|
|
2795
2805
|
function renderTaskMarkdown(task) {
|
|
2796
2806
|
return `- [P${task.priority}] ${task.description.replace(/\n/g, "\n ")} (${task.status})`;
|
|
@@ -2814,6 +2824,8 @@ function formatContext(bundle, options) {
|
|
|
2814
2824
|
maxEvents: options?.maxEvents ?? 10,
|
|
2815
2825
|
includeConfidence: options?.includeConfidence ?? true,
|
|
2816
2826
|
includeTags: options?.includeTags ?? true,
|
|
2827
|
+
includeEntityIds: options?.includeEntityIds ?? false,
|
|
2828
|
+
includeFactScores: options?.includeFactScores ?? false,
|
|
2817
2829
|
factWeights: {
|
|
2818
2830
|
confidence: options?.factWeights?.confidence ?? 1,
|
|
2819
2831
|
accessCount: options?.factWeights?.accessCount ?? 0.3,
|
|
@@ -2825,7 +2837,7 @@ function formatContext(bundle, options) {
|
|
|
2825
2837
|
validateMaxOption(opts.maxEvents, "maxEvents");
|
|
2826
2838
|
const weights = opts.factWeights;
|
|
2827
2839
|
const now = Date.now();
|
|
2828
|
-
const sortedFacts = [...bundle.facts].sort((a, b) => scoreFactFor(b, weights, now) - scoreFactFor(a, weights, now)).slice(0, opts.maxFacts);
|
|
2840
|
+
const sortedFacts = bundle.factScores ? [...bundle.facts].slice(0, opts.maxFacts) : [...bundle.facts].sort((a, b) => scoreFactFor(b, weights, now) - scoreFactFor(a, weights, now)).slice(0, opts.maxFacts);
|
|
2829
2841
|
const sortedTasks = [...bundle.tasks].sort((a, b) => b.priority - a.priority || a.created_at - b.created_at).slice(0, opts.maxTasks);
|
|
2830
2842
|
const sortedEvents = [...bundle.events].sort((a, b) => b.created_at - a.created_at).slice(0, opts.maxEvents);
|
|
2831
2843
|
if (sortedFacts.length === 0 && sortedTasks.length === 0 && sortedEvents.length === 0) {
|
|
@@ -2839,7 +2851,7 @@ function formatContext(bundle, options) {
|
|
|
2839
2851
|
lines.push("");
|
|
2840
2852
|
lines.push("### Known Facts");
|
|
2841
2853
|
for (const fact of sortedFacts) {
|
|
2842
|
-
lines.push(renderFactMarkdown(fact, opts.includeConfidence, opts.includeTags));
|
|
2854
|
+
lines.push(renderFactMarkdown(fact, opts.includeConfidence, opts.includeTags, opts.includeEntityIds, opts.includeFactScores ? bundle.factScores?.[fact.id] : void 0));
|
|
2843
2855
|
}
|
|
2844
2856
|
}
|
|
2845
2857
|
if (sortedTasks.length > 0) {
|
|
@@ -2860,7 +2872,7 @@ function formatContext(bundle, options) {
|
|
|
2860
2872
|
if (sortedFacts.length > 0) {
|
|
2861
2873
|
lines.push("KNOWN FACTS:");
|
|
2862
2874
|
for (const fact of sortedFacts) {
|
|
2863
|
-
lines.push(renderFactPlain(fact, opts.includeConfidence, opts.includeTags));
|
|
2875
|
+
lines.push(renderFactPlain(fact, opts.includeConfidence, opts.includeTags, opts.includeEntityIds, opts.includeFactScores ? bundle.factScores?.[fact.id] : void 0));
|
|
2864
2876
|
}
|
|
2865
2877
|
}
|
|
2866
2878
|
if (sortedTasks.length > 0) {
|
|
@@ -2979,11 +2991,50 @@ function formatMemoryDump(dump) {
|
|
|
2979
2991
|
};
|
|
2980
2992
|
}
|
|
2981
2993
|
|
|
2994
|
+
// src/librarianPrompt.ts
|
|
2995
|
+
var DEFAULT_LIBRARIAN_SYNTHESIS_PROMPT = `You are a careful memory synthesis assistant.
|
|
2996
|
+
Use only the retrieved context when answering the request.
|
|
2997
|
+
Preserve source provenance when facts come from different entity namespaces.
|
|
2998
|
+
|
|
2999
|
+
Request:
|
|
3000
|
+
{{query}}
|
|
3001
|
+
|
|
3002
|
+
Retrieved context:
|
|
3003
|
+
{{context}}
|
|
3004
|
+
|
|
3005
|
+
Open tasks:
|
|
3006
|
+
{{tasks}}`;
|
|
3007
|
+
function hydrateLibrarianPrompt(template, variables) {
|
|
3008
|
+
return template.replace(/\{\{(context|tasks|query)\}\}/g, (_, key) => variables[key]);
|
|
3009
|
+
}
|
|
3010
|
+
function validateLibrarianPromptTemplate(template, options) {
|
|
3011
|
+
if (!options.custom) return [];
|
|
3012
|
+
const warnings = [];
|
|
3013
|
+
if (!template.includes("{{context}}")) {
|
|
3014
|
+
warnings.push("Custom Librarian systemPrompt omits {{context}}; retrieved memory will not be injected.");
|
|
3015
|
+
}
|
|
3016
|
+
if (!template.includes("{{query}}")) {
|
|
3017
|
+
warnings.push("Custom Librarian systemPrompt omits {{query}}; the original request will not be injected.");
|
|
3018
|
+
}
|
|
3019
|
+
if (options.taskCount > 0 && !template.includes("{{tasks}}")) {
|
|
3020
|
+
warnings.push("Custom Librarian systemPrompt omits {{tasks}} while retrieved tasks are available.");
|
|
3021
|
+
}
|
|
3022
|
+
return warnings;
|
|
3023
|
+
}
|
|
3024
|
+
function mapLibrarianOptionsToReadOptions(options) {
|
|
3025
|
+
const readOptions = {};
|
|
3026
|
+
if (options.entityWeights !== void 0) readOptions.tierWeights = options.entityWeights;
|
|
3027
|
+
if (options.includeZeroWeightEntities !== void 0) {
|
|
3028
|
+
readOptions.includeZeroWeightEntities = options.includeZeroWeightEntities;
|
|
3029
|
+
}
|
|
3030
|
+
return readOptions;
|
|
3031
|
+
}
|
|
3032
|
+
|
|
2982
3033
|
// src/index.ts
|
|
2983
3034
|
function createWiki(db, options) {
|
|
2984
3035
|
return new WikiMemory(db, options);
|
|
2985
3036
|
}
|
|
2986
3037
|
|
|
2987
|
-
export { PrunePartialFailureError, WikiBusyError, WikiMemory, createWiki, formatContext, formatMemoryDump, parseEmbedding };
|
|
3038
|
+
export { DEFAULT_LIBRARIAN_SYNTHESIS_PROMPT, PrunePartialFailureError, WikiBusyError, WikiMemory, createWiki, formatContext, formatMemoryDump, hydrateLibrarianPrompt, mapLibrarianOptionsToReadOptions, parseEmbedding, validateLibrarianPromptTemplate };
|
|
2988
3039
|
//# sourceMappingURL=index.mjs.map
|
|
2989
3040
|
//# sourceMappingURL=index.mjs.map
|