@voidwire/lore 1.0.8 → 1.1.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/lib/indexers/personal.ts +102 -5
- package/package.json +3 -2
package/lib/indexers/personal.ts
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
import { readFileSync, statSync, existsSync } from "fs";
|
|
14
14
|
import { join } from "path";
|
|
15
15
|
import { checkPath, type IndexerContext } from "../indexer";
|
|
16
|
+
import { complete } from "@voidwire/llm-core";
|
|
16
17
|
|
|
17
18
|
function fileMtime(path: string): string {
|
|
18
19
|
return statSync(path).mtime.toISOString();
|
|
@@ -24,7 +25,74 @@ function toISO(dateStr: string, fallback: string): string {
|
|
|
24
25
|
return s.includes("T") ? s : `${s.slice(0, 10)}T00:00:00Z`;
|
|
25
26
|
}
|
|
26
27
|
|
|
28
|
+
const ENRICH_PROMPTS: Record<string, string> = {
|
|
29
|
+
person: `You are enriching a personal contact entry for search indexing.
|
|
30
|
+
The "relationship" field is the EXACT relationship — do NOT add other relationship types.
|
|
31
|
+
Generate synonyms and alternative phrasings ONLY for the stated relationship.
|
|
32
|
+
Example: relationship "uncle" → uncle, family member, relative, parent's brother, parent's sibling. NOT: cousin, nephew, aunt.
|
|
33
|
+
Example: relationship "daughter" → daughter, child, kid, offspring, family member. NOT: son, niece, nephew.
|
|
34
|
+
Include both singular and plural forms where applicable.
|
|
35
|
+
Keep under 80 words. Output only the description, no headers or formatting.`,
|
|
36
|
+
book: `You are enriching a book entry for search indexing.
|
|
37
|
+
Generate: genre, themes, subject matter, and related topics.
|
|
38
|
+
Include both singular and plural forms of key terms.
|
|
39
|
+
Keep under 80 words. Output only the description, no headers or formatting.`,
|
|
40
|
+
movie: `You are enriching a movie entry for search indexing.
|
|
41
|
+
Generate: genre, themes, notable actors or director if well-known, and related topics.
|
|
42
|
+
Include both singular and plural forms of key terms.
|
|
43
|
+
Keep under 80 words. Output only the description, no headers or formatting.`,
|
|
44
|
+
interest: `You are enriching a personal interest entry for search indexing.
|
|
45
|
+
Generate: related activities, domains, synonyms, and common alternative phrasings.
|
|
46
|
+
Include both singular and plural forms.
|
|
47
|
+
Keep under 80 words. Output only the description, no headers or formatting.`,
|
|
48
|
+
habit: `You are enriching a personal habit/routine entry for search indexing.
|
|
49
|
+
Generate: related routines, synonyms, categories, and common alternative phrasings.
|
|
50
|
+
Include both singular and plural forms.
|
|
51
|
+
Keep under 80 words. Output only the description, no headers or formatting.`,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const ENRICH_TIMEOUT_MS = 30_000;
|
|
55
|
+
const ENRICH_MAX_FAILURES = 3;
|
|
56
|
+
|
|
57
|
+
let enrichmentFailures = 0;
|
|
58
|
+
|
|
59
|
+
async function enrich(type: string, input: string): Promise<string | null> {
|
|
60
|
+
if (enrichmentFailures >= ENRICH_MAX_FAILURES) return null;
|
|
61
|
+
const systemPrompt = ENRICH_PROMPTS[type];
|
|
62
|
+
if (!systemPrompt) return null;
|
|
63
|
+
try {
|
|
64
|
+
const result = await Promise.race([
|
|
65
|
+
complete({
|
|
66
|
+
prompt: input,
|
|
67
|
+
systemPrompt,
|
|
68
|
+
temperature: 0.3,
|
|
69
|
+
maxTokens: 150,
|
|
70
|
+
}),
|
|
71
|
+
new Promise<never>((_, reject) =>
|
|
72
|
+
setTimeout(
|
|
73
|
+
() => reject(new Error("Enrichment timed out")),
|
|
74
|
+
ENRICH_TIMEOUT_MS,
|
|
75
|
+
),
|
|
76
|
+
),
|
|
77
|
+
]);
|
|
78
|
+
enrichmentFailures = 0;
|
|
79
|
+
return result.text.replace(/<\|[^|]+\|>/g, "").trim();
|
|
80
|
+
} catch (e) {
|
|
81
|
+
enrichmentFailures++;
|
|
82
|
+
console.warn(
|
|
83
|
+
`Enrichment failed (${enrichmentFailures}/${ENRICH_MAX_FAILURES}): ${e}`,
|
|
84
|
+
);
|
|
85
|
+
if (enrichmentFailures >= ENRICH_MAX_FAILURES) {
|
|
86
|
+
console.warn(
|
|
87
|
+
"Max enrichment failures reached, disabling for remaining entries",
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
27
94
|
export async function indexPersonal(ctx: IndexerContext): Promise<void> {
|
|
95
|
+
enrichmentFailures = 0;
|
|
28
96
|
const personalDir = ctx.config.paths.personal;
|
|
29
97
|
|
|
30
98
|
if (!checkPath("personal", "paths.personal", personalDir)) return;
|
|
@@ -37,7 +105,12 @@ export async function indexPersonal(ctx: IndexerContext): Promise<void> {
|
|
|
37
105
|
const books = JSON.parse(readFileSync(booksPath, "utf-8"));
|
|
38
106
|
for (const book of books) {
|
|
39
107
|
if (!book.title) continue;
|
|
40
|
-
|
|
108
|
+
let content = `${book.title} by ${book.author || "unknown"}\n${book.notes || ""}`;
|
|
109
|
+
const enriched = await enrich(
|
|
110
|
+
"book",
|
|
111
|
+
JSON.stringify({ title: book.title, author: book.author }),
|
|
112
|
+
);
|
|
113
|
+
if (enriched) content = enriched;
|
|
41
114
|
const timestamp = book.date_read
|
|
42
115
|
? toISO(book.date_read, booksTs)
|
|
43
116
|
: booksTs;
|
|
@@ -65,7 +138,15 @@ export async function indexPersonal(ctx: IndexerContext): Promise<void> {
|
|
|
65
138
|
const people = JSON.parse(readFileSync(peoplePath, "utf-8"));
|
|
66
139
|
for (const person of people) {
|
|
67
140
|
if (!person.name) continue;
|
|
68
|
-
|
|
141
|
+
let content = `${person.name}\n${person.relationship || ""}\n${person.notes || ""}`;
|
|
142
|
+
const enriched = await enrich(
|
|
143
|
+
"person",
|
|
144
|
+
JSON.stringify({
|
|
145
|
+
name: person.name,
|
|
146
|
+
relationship: person.relationship,
|
|
147
|
+
}),
|
|
148
|
+
);
|
|
149
|
+
if (enriched) content = enriched;
|
|
69
150
|
|
|
70
151
|
ctx.insert({
|
|
71
152
|
source: "personal",
|
|
@@ -91,9 +172,14 @@ export async function indexPersonal(ctx: IndexerContext): Promise<void> {
|
|
|
91
172
|
for (const movie of movies) {
|
|
92
173
|
if (!movie.title) continue;
|
|
93
174
|
const year = movie.year || "";
|
|
94
|
-
|
|
175
|
+
let content = year
|
|
95
176
|
? `${movie.title} (${year})\n${movie.notes || ""}`
|
|
96
177
|
: `${movie.title}\n${movie.notes || ""}`;
|
|
178
|
+
const enriched = await enrich(
|
|
179
|
+
"movie",
|
|
180
|
+
JSON.stringify({ title: movie.title, year: movie.year }),
|
|
181
|
+
);
|
|
182
|
+
if (enriched) content = enriched;
|
|
97
183
|
const timestamp = movie.date_watched
|
|
98
184
|
? toISO(movie.date_watched, moviesTs)
|
|
99
185
|
: moviesTs;
|
|
@@ -147,11 +233,17 @@ export async function indexPersonal(ctx: IndexerContext): Promise<void> {
|
|
|
147
233
|
const interests = JSON.parse(readFileSync(interestsPath, "utf-8"));
|
|
148
234
|
for (const interest of interests) {
|
|
149
235
|
if (typeof interest !== "string" || !interest) continue;
|
|
236
|
+
let content = interest;
|
|
237
|
+
const enriched = await enrich(
|
|
238
|
+
"interest",
|
|
239
|
+
JSON.stringify({ name: interest }),
|
|
240
|
+
);
|
|
241
|
+
if (enriched) content = enriched;
|
|
150
242
|
|
|
151
243
|
ctx.insert({
|
|
152
244
|
source: "personal",
|
|
153
245
|
title: `[interest] ${interest}`,
|
|
154
|
-
content
|
|
246
|
+
content,
|
|
155
247
|
topic: "",
|
|
156
248
|
type: "interest",
|
|
157
249
|
timestamp: interestsTs,
|
|
@@ -173,7 +265,12 @@ export async function indexPersonal(ctx: IndexerContext): Promise<void> {
|
|
|
173
265
|
const habitName = habit.habit || "";
|
|
174
266
|
if (!habitName) continue;
|
|
175
267
|
const frequency = habit.frequency || "";
|
|
176
|
-
|
|
268
|
+
let content = frequency ? `${habitName} (${frequency})` : habitName;
|
|
269
|
+
const enriched = await enrich(
|
|
270
|
+
"habit",
|
|
271
|
+
JSON.stringify({ habit: habitName, frequency }),
|
|
272
|
+
);
|
|
273
|
+
if (enriched) content = enriched;
|
|
177
274
|
|
|
178
275
|
ctx.insert({
|
|
179
276
|
source: "personal",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voidwire/lore",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Unified knowledge CLI - Search, list, and capture your indexed knowledge",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.ts",
|
|
@@ -44,7 +44,8 @@
|
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@huggingface/transformers": "^3.2.6",
|
|
47
|
-
"@iarna/toml": "^2.2.5"
|
|
47
|
+
"@iarna/toml": "^2.2.5",
|
|
48
|
+
"@voidwire/llm-core": "^0.3.1"
|
|
48
49
|
},
|
|
49
50
|
"devDependencies": {
|
|
50
51
|
"bun-types": "1.3.5"
|