@loreai/core 0.16.0 → 0.17.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 +11 -0
- package/dist/bun/agents-file.d.ts +13 -1
- package/dist/bun/agents-file.d.ts.map +1 -1
- package/dist/bun/config.d.ts +20 -1
- package/dist/bun/config.d.ts.map +1 -1
- package/dist/bun/data.d.ts +174 -0
- package/dist/bun/data.d.ts.map +1 -0
- package/dist/bun/db.d.ts +65 -0
- package/dist/bun/db.d.ts.map +1 -1
- package/dist/bun/distillation.d.ts +49 -6
- package/dist/bun/distillation.d.ts.map +1 -1
- package/dist/bun/embedding-vendor.d.ts +66 -0
- package/dist/bun/embedding-vendor.d.ts.map +1 -0
- package/dist/bun/embedding-worker-types.d.ts +66 -0
- package/dist/bun/embedding-worker-types.d.ts.map +1 -0
- package/dist/bun/embedding-worker.d.ts +16 -0
- package/dist/bun/embedding-worker.d.ts.map +1 -0
- package/dist/bun/embedding-worker.js +100 -0
- package/dist/bun/embedding-worker.js.map +7 -0
- package/dist/bun/embedding.d.ts +91 -8
- package/dist/bun/embedding.d.ts.map +1 -1
- package/dist/bun/git.d.ts +47 -0
- package/dist/bun/git.d.ts.map +1 -0
- package/dist/bun/gradient.d.ts +19 -1
- package/dist/bun/gradient.d.ts.map +1 -1
- package/dist/bun/index.d.ts +9 -6
- package/dist/bun/index.d.ts.map +1 -1
- package/dist/bun/index.js +13029 -10885
- package/dist/bun/index.js.map +4 -4
- package/dist/bun/lat-reader.d.ts +1 -1
- package/dist/bun/lat-reader.d.ts.map +1 -1
- package/dist/bun/ltm.d.ts.map +1 -1
- package/dist/bun/markdown.d.ts +11 -0
- package/dist/bun/markdown.d.ts.map +1 -1
- package/dist/bun/prompt.d.ts +1 -1
- package/dist/bun/prompt.d.ts.map +1 -1
- package/dist/bun/recall.d.ts +53 -0
- package/dist/bun/recall.d.ts.map +1 -1
- package/dist/bun/search.d.ts +29 -0
- package/dist/bun/search.d.ts.map +1 -1
- package/dist/bun/temporal.d.ts +2 -0
- package/dist/bun/temporal.d.ts.map +1 -1
- package/dist/bun/types.d.ts +15 -0
- package/dist/bun/types.d.ts.map +1 -1
- package/dist/bun/worker-model.d.ts +12 -9
- package/dist/bun/worker-model.d.ts.map +1 -1
- package/dist/node/agents-file.d.ts +13 -1
- package/dist/node/agents-file.d.ts.map +1 -1
- package/dist/node/config.d.ts +20 -1
- package/dist/node/config.d.ts.map +1 -1
- package/dist/node/data.d.ts +174 -0
- package/dist/node/data.d.ts.map +1 -0
- package/dist/node/db.d.ts +65 -0
- package/dist/node/db.d.ts.map +1 -1
- package/dist/node/distillation.d.ts +49 -6
- package/dist/node/distillation.d.ts.map +1 -1
- package/dist/node/embedding-vendor.d.ts +66 -0
- package/dist/node/embedding-vendor.d.ts.map +1 -0
- package/dist/node/embedding-worker-types.d.ts +66 -0
- package/dist/node/embedding-worker-types.d.ts.map +1 -0
- package/dist/node/embedding-worker.d.ts +16 -0
- package/dist/node/embedding-worker.d.ts.map +1 -0
- package/dist/node/embedding-worker.js +100 -0
- package/dist/node/embedding-worker.js.map +7 -0
- package/dist/node/embedding.d.ts +91 -8
- package/dist/node/embedding.d.ts.map +1 -1
- package/dist/node/git.d.ts +47 -0
- package/dist/node/git.d.ts.map +1 -0
- package/dist/node/gradient.d.ts +19 -1
- package/dist/node/gradient.d.ts.map +1 -1
- package/dist/node/index.d.ts +9 -6
- package/dist/node/index.d.ts.map +1 -1
- package/dist/node/index.js +13029 -10885
- package/dist/node/index.js.map +4 -4
- package/dist/node/lat-reader.d.ts +1 -1
- package/dist/node/lat-reader.d.ts.map +1 -1
- package/dist/node/ltm.d.ts.map +1 -1
- package/dist/node/markdown.d.ts +11 -0
- package/dist/node/markdown.d.ts.map +1 -1
- package/dist/node/prompt.d.ts +1 -1
- package/dist/node/prompt.d.ts.map +1 -1
- package/dist/node/recall.d.ts +53 -0
- package/dist/node/recall.d.ts.map +1 -1
- package/dist/node/search.d.ts +29 -0
- package/dist/node/search.d.ts.map +1 -1
- package/dist/node/temporal.d.ts +2 -0
- package/dist/node/temporal.d.ts.map +1 -1
- package/dist/node/types.d.ts +15 -0
- package/dist/node/types.d.ts.map +1 -1
- package/dist/node/worker-model.d.ts +12 -9
- package/dist/node/worker-model.d.ts.map +1 -1
- package/dist/types/agents-file.d.ts +13 -1
- package/dist/types/agents-file.d.ts.map +1 -1
- package/dist/types/config.d.ts +20 -1
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/data.d.ts +174 -0
- package/dist/types/data.d.ts.map +1 -0
- package/dist/types/db.d.ts +65 -0
- package/dist/types/db.d.ts.map +1 -1
- package/dist/types/distillation.d.ts +49 -6
- package/dist/types/distillation.d.ts.map +1 -1
- package/dist/types/embedding-vendor.d.ts +66 -0
- package/dist/types/embedding-vendor.d.ts.map +1 -0
- package/dist/types/embedding-worker-types.d.ts +66 -0
- package/dist/types/embedding-worker-types.d.ts.map +1 -0
- package/dist/types/embedding-worker.d.ts +16 -0
- package/dist/types/embedding-worker.d.ts.map +1 -0
- package/dist/types/embedding.d.ts +91 -8
- package/dist/types/embedding.d.ts.map +1 -1
- package/dist/types/git.d.ts +47 -0
- package/dist/types/git.d.ts.map +1 -0
- package/dist/types/gradient.d.ts +19 -1
- package/dist/types/gradient.d.ts.map +1 -1
- package/dist/types/index.d.ts +9 -6
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/lat-reader.d.ts +1 -1
- package/dist/types/lat-reader.d.ts.map +1 -1
- package/dist/types/ltm.d.ts.map +1 -1
- package/dist/types/markdown.d.ts +11 -0
- package/dist/types/markdown.d.ts.map +1 -1
- package/dist/types/prompt.d.ts +1 -1
- package/dist/types/prompt.d.ts.map +1 -1
- package/dist/types/recall.d.ts +53 -0
- package/dist/types/recall.d.ts.map +1 -1
- package/dist/types/search.d.ts +29 -0
- package/dist/types/search.d.ts.map +1 -1
- package/dist/types/temporal.d.ts +2 -0
- package/dist/types/temporal.d.ts.map +1 -1
- package/dist/types/types.d.ts +15 -0
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/worker-model.d.ts +12 -9
- package/dist/types/worker-model.d.ts.map +1 -1
- package/package.json +5 -2
- package/src/agents-file.ts +87 -4
- package/src/config.ts +68 -5
- package/src/curator.ts +2 -2
- package/src/data.ts +768 -0
- package/src/db.ts +386 -7
- package/src/distillation.ts +178 -35
- package/src/embedding-vendor.ts +102 -0
- package/src/embedding-worker-types.ts +82 -0
- package/src/embedding-worker.ts +185 -0
- package/src/embedding.ts +607 -61
- package/src/git.ts +144 -0
- package/src/gradient.ts +174 -17
- package/src/index.ts +20 -0
- package/src/lat-reader.ts +5 -11
- package/src/ltm.ts +17 -44
- package/src/markdown.ts +15 -0
- package/src/prompt.ts +1 -2
- package/src/recall.ts +401 -70
- package/src/search.ts +71 -1
- package/src/temporal.ts +42 -35
- package/src/types.ts +15 -0
- package/src/worker-model.ts +14 -9
package/dist/node/db.d.ts
CHANGED
|
@@ -1,6 +1,43 @@
|
|
|
1
1
|
import { Database } from "#db/driver";
|
|
2
|
+
/**
|
|
3
|
+
* Extract the repository name from a normalized git remote URL.
|
|
4
|
+
*
|
|
5
|
+
* Examples:
|
|
6
|
+
* "github.com/BYK/LoreAI" → "LoreAI"
|
|
7
|
+
* "github.com/org/repo" → "repo"
|
|
8
|
+
* "github.com" → null (no path components)
|
|
9
|
+
* null → null
|
|
10
|
+
*/
|
|
11
|
+
export declare function repoNameFromRemote(remote: string | null): string | null;
|
|
12
|
+
/** Return the resolved path of the SQLite database file. */
|
|
13
|
+
export declare function dbPath(): string;
|
|
2
14
|
export declare function db(): Database;
|
|
15
|
+
/**
|
|
16
|
+
* Merge all data from `sourceId` project into `targetId` project.
|
|
17
|
+
*
|
|
18
|
+
* Moves knowledge, temporal messages, distillations, LAT sections, and
|
|
19
|
+
* path aliases from source to target. Registers the source project's path
|
|
20
|
+
* as an alias of the target. Deletes the source project row.
|
|
21
|
+
*
|
|
22
|
+
* Used internally during lazy git-remote backfill when two path-only
|
|
23
|
+
* projects are discovered to share the same git remote.
|
|
24
|
+
*/
|
|
25
|
+
export declare function mergeProjectInternal(sourceId: string, targetId: string): void;
|
|
3
26
|
export declare function close(): void;
|
|
27
|
+
/**
|
|
28
|
+
* Look up or create a project by filesystem path, with git-remote awareness.
|
|
29
|
+
*
|
|
30
|
+
* Resolution order:
|
|
31
|
+
* 1. Exact path match in `projects` table (fast path, O(1) index scan)
|
|
32
|
+
* 2. Path alias match in `project_path_aliases` (worktree/clone re-visits)
|
|
33
|
+
* 3. Git remote match — runs `git remote -v` (once per unique path, cached),
|
|
34
|
+
* finds an existing project with the same normalized remote URL
|
|
35
|
+
* 4. Create a new project row
|
|
36
|
+
*
|
|
37
|
+
* When a git-remote match is found (step 3), the new path is registered as
|
|
38
|
+
* an alias so subsequent calls skip the subprocess. If the matched project's
|
|
39
|
+
* git_remote was not yet populated (pre-v14 rows), it is backfilled lazily.
|
|
40
|
+
*/
|
|
4
41
|
export declare function ensureProject(path: string, name?: string): string;
|
|
5
42
|
export declare function projectId(path: string): string | undefined;
|
|
6
43
|
/** Look up a project's display name by its internal ID. */
|
|
@@ -19,6 +56,34 @@ export declare function loadForceMinLayer(sessionID: string): number;
|
|
|
19
56
|
* (consumed) to avoid unbounded growth.
|
|
20
57
|
*/
|
|
21
58
|
export declare function saveForceMinLayer(sessionID: string, layer: number): void;
|
|
59
|
+
/** Persisted cost snapshot for a session. */
|
|
60
|
+
export type SessionCostSnapshot = {
|
|
61
|
+
conversationCost: number;
|
|
62
|
+
workerCost: number;
|
|
63
|
+
conversationTurns: number;
|
|
64
|
+
cacheReadTokens: number;
|
|
65
|
+
cacheWriteTokens: number;
|
|
66
|
+
warmupSavings: number;
|
|
67
|
+
warmupHits: number;
|
|
68
|
+
ttlSavings: number;
|
|
69
|
+
ttlHits: number;
|
|
70
|
+
batchSavings: number;
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* Persist a session's cost snapshot. Uses INSERT OR REPLACE so it works
|
|
74
|
+
* whether or not a row already exists (forceMinLayer may have created one).
|
|
75
|
+
*/
|
|
76
|
+
export declare function saveSessionCosts(sessionID: string, costs: SessionCostSnapshot): void;
|
|
77
|
+
/**
|
|
78
|
+
* Load persisted cost snapshot for a session. Returns null if not stored
|
|
79
|
+
* or if all cost columns are zero (pre-migration row from forceMinLayer only).
|
|
80
|
+
*/
|
|
81
|
+
export declare function loadSessionCosts(sessionID: string): SessionCostSnapshot | null;
|
|
82
|
+
/**
|
|
83
|
+
* Load cost snapshots for all sessions that have non-zero cost data.
|
|
84
|
+
* Returns a map of sessionID → SessionCostSnapshot.
|
|
85
|
+
*/
|
|
86
|
+
export declare function loadAllSessionCosts(): Map<string, SessionCostSnapshot>;
|
|
22
87
|
/** Get a metadata value by key. Returns null if not found. */
|
|
23
88
|
export declare function getMeta(key: string): string | null;
|
|
24
89
|
/** Set a metadata value (upsert). */
|
package/dist/node/db.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAMtC;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAMvE;AA0bD,4DAA4D;AAC5D,wBAAgB,MAAM,IAAI,MAAM,CAI/B;AAID,wBAAgB,EAAE,IAAI,QAAQ,CA8C7B;AAsHD;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf,IAAI,CAoCN;AAED,wBAAgB,KAAK,SAKpB;AAID;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAiEjE;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAW1D;AAED,2DAA2D;AAC3D,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKrD;AAED;;;GAGG;AACH,wBAAgB,UAAU,IAAI,OAAO,CAKpC;AAMD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAK3D;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAYxE;AAED,6CAA6C;AAC7C,MAAM,MAAM,mBAAmB,GAAG;IAChC,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,mBAAmB,GAAG,IAAI,CA2BpF;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,mBAAmB,GAAG,IAAI,CAiC9E;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAsCtE;AAMD,8DAA8D;AAC9D,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKlD;AAED,qCAAqC;AACrC,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAMxD;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAMtC"}
|
|
@@ -15,20 +15,58 @@ type TemporalMessage = temporal.TemporalMessage;
|
|
|
15
15
|
* use as a diagnostic signal, not a hard gate.
|
|
16
16
|
*/
|
|
17
17
|
export declare function compressionRatio(distilledTokens: number, sourceTokens: number): number;
|
|
18
|
+
/**
|
|
19
|
+
* Maximum allowed expansion for distillation output.
|
|
20
|
+
*
|
|
21
|
+
* Tiny segments can't meaningfully compress — distillation adds metadata
|
|
22
|
+
* (timestamps, importance markers, cross-references) that necessarily
|
|
23
|
+
* exceeds the source. Allow generous expansion for small segments while
|
|
24
|
+
* still enforcing compression on large ones.
|
|
25
|
+
*
|
|
26
|
+
* @returns Maximum allowed distilled tokens for a given source token count.
|
|
27
|
+
*/
|
|
28
|
+
export declare function maxAllowedExpansion(sourceTokens: number): number;
|
|
18
29
|
/**
|
|
19
30
|
* Segment detection: group related messages into distillation-sized chunks.
|
|
20
31
|
*
|
|
21
|
-
* When the
|
|
32
|
+
* When the total token count exceeds `maxTokens`, prefers splitting at the
|
|
22
33
|
* largest inter-message time gap (if it's ≥ 3× the median gap) to respect
|
|
23
|
-
* natural conversation boundaries. Falls back to
|
|
24
|
-
*
|
|
34
|
+
* natural conversation boundaries. Falls back to token-boundary splitting
|
|
35
|
+
* when timestamps are uniform.
|
|
25
36
|
*
|
|
26
|
-
* Trailing segments
|
|
27
|
-
* to avoid tiny distillation inputs
|
|
37
|
+
* Trailing segments whose token sum is below {@link MIN_SEGMENT_TOKENS}
|
|
38
|
+
* are merged into the previous segment to avoid tiny distillation inputs
|
|
39
|
+
* with too little context.
|
|
28
40
|
*
|
|
29
41
|
* Exported for testing; `run()` is the production caller.
|
|
30
42
|
*/
|
|
31
|
-
export declare function detectSegments(messages: TemporalMessage[],
|
|
43
|
+
export declare function detectSegments(messages: TemporalMessage[], maxTokens: number): TemporalMessage[][];
|
|
44
|
+
/**
|
|
45
|
+
* Compute the max_tokens budget for a worker LLM call.
|
|
46
|
+
*
|
|
47
|
+
* @param inputTokens Estimated source token count
|
|
48
|
+
* @param ratio Compression ratio (0.0–1.0) — output ≈ ratio × input
|
|
49
|
+
* @param floor Minimum output tokens
|
|
50
|
+
* @param cap Maximum output tokens
|
|
51
|
+
*/
|
|
52
|
+
export declare function workerTokenBudget(inputTokens: number, ratio: number, floor: number, cap: number): number;
|
|
53
|
+
/**
|
|
54
|
+
* Compute the max_tokens budget for gen-0 distillation of raw messages.
|
|
55
|
+
*
|
|
56
|
+
* Uses a √N-based formula (8 × √N) instead of a linear ratio so that the
|
|
57
|
+
* budget grows sub-linearly with input size. This naturally constrains the
|
|
58
|
+
* LLM to produce output at ~R ≈ 2–4 (the square-root boundary) and avoids
|
|
59
|
+
* expansion on small segments where a linear 0.25 ratio + 1024 floor gave
|
|
60
|
+
* the model far too much room.
|
|
61
|
+
*
|
|
62
|
+
* The multiplier (8) gives ~4× headroom above the R=2.0 target, accounting
|
|
63
|
+
* for the detailed observation format (emoji markers, timestamps, entity
|
|
64
|
+
* tags, exact numbers) required by the distillation prompt.
|
|
65
|
+
*
|
|
66
|
+
* @param sourceTokens Estimated source token count from raw messages
|
|
67
|
+
* @returns Token budget clamped to [256, 4096]
|
|
68
|
+
*/
|
|
69
|
+
export declare function distillTokenBudget(sourceTokens: number): number;
|
|
32
70
|
/**
|
|
33
71
|
* Truncate tool outputs within a `TemporalMessage.content` string (produced
|
|
34
72
|
* by `temporal.partsToText`). Plain text and `[reasoning]` chunks pass
|
|
@@ -100,6 +138,7 @@ export type Distillation = {
|
|
|
100
138
|
* rare callers that explicitly want all rows.
|
|
101
139
|
*/
|
|
102
140
|
export declare function loadForSession(projectPath: string, sessionID: string, includeArchived?: boolean): Distillation[];
|
|
141
|
+
export declare function gen0Count(projectPath: string, sessionID: string): number;
|
|
103
142
|
export declare function run(input: {
|
|
104
143
|
llm: LLMClient;
|
|
105
144
|
projectPath: string;
|
|
@@ -121,6 +160,9 @@ export declare function run(input: {
|
|
|
121
160
|
* where the caller is blocking on the result. Background/idle distillation
|
|
122
161
|
* should leave this false to benefit from batch API 50% cost savings. */
|
|
123
162
|
urgent?: boolean;
|
|
163
|
+
/** Whether the LLM call will use batch or direct pricing. Recorded on the
|
|
164
|
+
* distillation row for accurate historical cost estimates. */
|
|
165
|
+
callType?: "batch" | "direct";
|
|
124
166
|
}): Promise<{
|
|
125
167
|
rounds: number;
|
|
126
168
|
distilled: number;
|
|
@@ -142,6 +184,7 @@ export declare function metaDistill(input: {
|
|
|
142
184
|
modelID: string;
|
|
143
185
|
};
|
|
144
186
|
urgent?: boolean;
|
|
187
|
+
callType?: "batch" | "direct";
|
|
145
188
|
}): Promise<DistillationResult | null>;
|
|
146
189
|
/**
|
|
147
190
|
* Backfill `r_compression` and `c_norm` for distillations that were created
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"distillation.d.ts","sourceRoot":"","sources":["../../src/distillation.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,QAAQ,MAAM,YAAY,CAAC;AAavC,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAGzC,OAAO,EAAE,gBAAgB,EAAE,CAAC;AAE5B,KAAK,eAAe,GAAG,QAAQ,CAAC,eAAe,CAAC;AAEhD;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAC9B,eAAe,EAAE,MAAM,EACvB,YAAY,EAAE,MAAM,GACnB,MAAM,CAGR;AAED
|
|
1
|
+
{"version":3,"file":"distillation.d.ts","sourceRoot":"","sources":["../../src/distillation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vendored bge-small registration for the standalone Lore binary.
|
|
3
|
+
*
|
|
4
|
+
* The Bun-compiled `lore` binary uses `bun build --compile` to bundle
|
|
5
|
+
* `fastembed` + `onnxruntime-node` + `@anush008/tokenizers-<platform>`
|
|
6
|
+
* directly into the executable — including the platform-specific
|
|
7
|
+
* `.node` addons which Bun embeds and dlopens from `$bunfs` at runtime.
|
|
8
|
+
*
|
|
9
|
+
* Two pieces don't fit into Bun's automatic bundling and need our help:
|
|
10
|
+
*
|
|
11
|
+
* 1. **Side-load shared libraries**. `onnxruntime_binding.node` does a
|
|
12
|
+
* runtime `dlopen("libonnxruntime.so.1")` (or the .dylib / .dll
|
|
13
|
+
* equivalent) for the actual ONNX Runtime computation library. Bun
|
|
14
|
+
* doesn't follow this kind of dependency. The binary's wrapper
|
|
15
|
+
* pre-loads these libs via `bun:ffi` *before* fastembed evaluates,
|
|
16
|
+
* so when the addon's dlopen fires it finds the cached handle.
|
|
17
|
+
*
|
|
18
|
+
* 2. **Model weights + tokenizer**. fastembed downloads from the HF
|
|
19
|
+
* Hub on first use; we want zero network on first run. The wrapper
|
|
20
|
+
* embeds the bge-small INT8 files as Bun assets, writes them to a
|
|
21
|
+
* real disk dir on first run, and sets `globalThis.__LORE_VENDOR_MODEL__`
|
|
22
|
+
* to that path. This module exposes that registration to the
|
|
23
|
+
* LocalProvider so it can hand the path to fastembed's CUSTOM-mode
|
|
24
|
+
* init (`modelAbsoluteDirPath` + `modelName`).
|
|
25
|
+
*
|
|
26
|
+
* In npm-mode usage from `@loreai/opencode` / `@loreai/pi` the global
|
|
27
|
+
* is unset and `vendorModelInfo()` returns `null`, so the LocalProvider
|
|
28
|
+
* falls through to fastembed's default Qdrant repo + cache.
|
|
29
|
+
*/
|
|
30
|
+
/** What the binary wrapper writes to globalThis after extracting model files. */
|
|
31
|
+
export interface VendorRegistration {
|
|
32
|
+
/** Absolute path to the dir containing the bge-small files
|
|
33
|
+
* (config.json, tokenizer.json, model_quantized.onnx, …). Pass to
|
|
34
|
+
* fastembed as `modelAbsoluteDirPath` in CUSTOM init. */
|
|
35
|
+
modelAbsoluteDirPath: string;
|
|
36
|
+
/** Filename of the ONNX weights inside that dir. Pass to fastembed
|
|
37
|
+
* as `modelName` in CUSTOM init. */
|
|
38
|
+
modelName: string;
|
|
39
|
+
/** Target identifier the binary was built for, e.g. "linux-x64".
|
|
40
|
+
* Diagnostic only — the runtime doesn't branch on it. */
|
|
41
|
+
target: string;
|
|
42
|
+
/** Lore CLI version that produced the binary. Diagnostic only. */
|
|
43
|
+
version: string;
|
|
44
|
+
}
|
|
45
|
+
/** Test-only: programmatically set/clear the registration to exercise
|
|
46
|
+
* both binary-mode and npm-mode code paths without spinning up a real
|
|
47
|
+
* compiled binary. */
|
|
48
|
+
export declare function _setVendorRegistration(reg: VendorRegistration | null): void;
|
|
49
|
+
/** Subset of the registration fastembed needs. Stripped of the
|
|
50
|
+
* diagnostic fields so the LocalProvider has exactly what it should
|
|
51
|
+
* hand to `FlagEmbedding.init`. */
|
|
52
|
+
export interface VendorModelInfo {
|
|
53
|
+
modelAbsoluteDirPath: string;
|
|
54
|
+
modelName: string;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Resolve the bundled-model arguments for fastembed CUSTOM init. Returns
|
|
58
|
+
* `null` when no vendor is registered (npm-mode), so the caller can fall
|
|
59
|
+
* through to fastembed's default cacheDir/HF Hub flow.
|
|
60
|
+
*/
|
|
61
|
+
export declare function vendorModelInfo(): VendorModelInfo | null;
|
|
62
|
+
/** True iff this process is running inside a vendored Lore binary. */
|
|
63
|
+
export declare function isVendoredBinary(): boolean;
|
|
64
|
+
/** The full registration, for diagnostics (`lore --print-vendor-info`). */
|
|
65
|
+
export declare function vendorRegistration(): VendorRegistration | null;
|
|
66
|
+
//# sourceMappingURL=embedding-vendor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embedding-vendor.d.ts","sourceRoot":"","sources":["../../src/embedding-vendor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAMH,iFAAiF;AACjF,MAAM,WAAW,kBAAkB;IACjC;;8DAE0D;IAC1D,oBAAoB,EAAE,MAAM,CAAC;IAC7B;yCACqC;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB;8DAC0D;IAC1D,MAAM,EAAE,MAAM,CAAC;IACf,kEAAkE;IAClE,OAAO,EAAE,MAAM,CAAC;CACjB;AAUD;;uBAEuB;AACvB,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,kBAAkB,GAAG,IAAI,GAAG,IAAI,CAI3E;AAMD;;oCAEoC;AACpC,MAAM,WAAW,eAAe;IAC9B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAgB,eAAe,IAAI,eAAe,GAAG,IAAI,CAOxD;AAED,sEAAsE;AACtE,wBAAgB,gBAAgB,IAAI,OAAO,CAE1C;AAED,2EAA2E;AAC3E,wBAAgB,kBAAkB,IAAI,kBAAkB,GAAG,IAAI,CAE9D"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared message types for the embedding worker thread.
|
|
3
|
+
*
|
|
4
|
+
* The embedding worker (`embedding-worker.ts`) runs fastembed/ONNX inference
|
|
5
|
+
* in a separate `node:worker_threads` Worker so the main thread's event loop
|
|
6
|
+
* stays free during inference. This file defines the message protocol between
|
|
7
|
+
* the main thread (`LocalProvider` in `embedding.ts`) and the worker.
|
|
8
|
+
*
|
|
9
|
+
* Imported by both sides — keep this file free of runtime dependencies.
|
|
10
|
+
*/
|
|
11
|
+
/** Request an embedding batch. */
|
|
12
|
+
export interface EmbedRequest {
|
|
13
|
+
type: "embed";
|
|
14
|
+
/** Monotonic request ID for correlating responses. */
|
|
15
|
+
id: number;
|
|
16
|
+
/** Texts to embed. */
|
|
17
|
+
texts: string[];
|
|
18
|
+
/** "document" for storage, "query" for search. */
|
|
19
|
+
inputType: "document" | "query";
|
|
20
|
+
/** "high" = recall queries (jump the queue), "normal" = backfill. */
|
|
21
|
+
priority: "high" | "normal";
|
|
22
|
+
}
|
|
23
|
+
/** Ask the worker to exit cleanly. */
|
|
24
|
+
export interface ShutdownRequest {
|
|
25
|
+
type: "shutdown";
|
|
26
|
+
}
|
|
27
|
+
export type WorkerInbound = EmbedRequest | ShutdownRequest;
|
|
28
|
+
/** Embedding result — vectors are Float32Array[], sent via structured clone. */
|
|
29
|
+
export interface EmbedResult {
|
|
30
|
+
type: "result";
|
|
31
|
+
/** Matches the request ID. */
|
|
32
|
+
id: number;
|
|
33
|
+
/** One Float32Array per input text. Sent via structured clone
|
|
34
|
+
* (Bun preserves Float32Array identity across threads). */
|
|
35
|
+
vectors: Float32Array[];
|
|
36
|
+
}
|
|
37
|
+
/** A single embed request failed (ONNX error, etc.). */
|
|
38
|
+
export interface EmbedError {
|
|
39
|
+
type: "error";
|
|
40
|
+
/** Matches the request ID. */
|
|
41
|
+
id: number;
|
|
42
|
+
/** Human-readable error message. */
|
|
43
|
+
error: string;
|
|
44
|
+
}
|
|
45
|
+
/** Model initialization failed inside the worker. All pending and future
|
|
46
|
+
* requests should be rejected — the worker is unusable. */
|
|
47
|
+
export interface InitError {
|
|
48
|
+
type: "init-error";
|
|
49
|
+
/** Human-readable error message. */
|
|
50
|
+
error: string;
|
|
51
|
+
}
|
|
52
|
+
export type WorkerOutbound = EmbedResult | EmbedError | InitError;
|
|
53
|
+
/** Passed to the worker via `workerData` at construction time. */
|
|
54
|
+
export interface WorkerInitData {
|
|
55
|
+
/** fastembed model name, e.g. "BGESmallENV15". */
|
|
56
|
+
modelName: string;
|
|
57
|
+
/** Vendored model info for binary mode, or null for npm mode.
|
|
58
|
+
* Mirrors the `globalThis.__LORE_VENDOR_MODEL__` registration which
|
|
59
|
+
* only exists on the main thread — passed explicitly so the worker
|
|
60
|
+
* can hand it to `FlagEmbedding.init()`. */
|
|
61
|
+
vendorModel: {
|
|
62
|
+
modelAbsoluteDirPath: string;
|
|
63
|
+
modelName: string;
|
|
64
|
+
} | null;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=embedding-worker-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embedding-worker-types.d.ts","sourceRoot":"","sources":["../../src/embedding-worker-types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,kCAAkC;AAClC,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,OAAO,CAAC;IACd,sDAAsD;IACtD,EAAE,EAAE,MAAM,CAAC;IACX,sBAAsB;IACtB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,kDAAkD;IAClD,SAAS,EAAE,UAAU,GAAG,OAAO,CAAC;IAChC,qEAAqE;IACrE,QAAQ,EAAE,MAAM,GAAG,QAAQ,CAAC;CAC7B;AAED,sCAAsC;AACtC,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,UAAU,CAAC;CAClB;AAED,MAAM,MAAM,aAAa,GAAG,YAAY,GAAG,eAAe,CAAC;AAM3D,gFAAgF;AAChF,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,QAAQ,CAAC;IACf,8BAA8B;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX;gEAC4D;IAC5D,OAAO,EAAE,YAAY,EAAE,CAAC;CACzB;AAED,wDAAwD;AACxD,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,OAAO,CAAC;IACd,8BAA8B;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,oCAAoC;IACpC,KAAK,EAAE,MAAM,CAAC;CACf;AAED;4DAC4D;AAC5D,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,YAAY,CAAC;IACnB,oCAAoC;IACpC,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG,UAAU,GAAG,SAAS,CAAC;AAMlE,kEAAkE;AAClE,MAAM,WAAW,cAAc;IAC7B,kDAAkD;IAClD,SAAS,EAAE,MAAM,CAAC;IAClB;;;iDAG6C;IAC7C,WAAW,EAAE;QAAE,oBAAoB,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CACzE"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Embedding worker thread — runs fastembed/ONNX inference off the main thread.
|
|
3
|
+
*
|
|
4
|
+
* This file is the entry point for a `node:worker_threads` Worker spawned by
|
|
5
|
+
* `LocalProvider` in `embedding.ts`. It owns the `FlagEmbedding` ONNX model
|
|
6
|
+
* and processes embed requests sequentially from a priority queue. Moving
|
|
7
|
+
* inference here keeps the main thread's event loop free — HTTP requests,
|
|
8
|
+
* SSE streams, and session APIs are no longer blocked during embedding.
|
|
9
|
+
*
|
|
10
|
+
* Communication uses `parentPort` message passing with structured clone.
|
|
11
|
+
* Float32Array vectors are sent back directly (Bun preserves identity).
|
|
12
|
+
*
|
|
13
|
+
* @see embedding-worker-types.ts for the message protocol.
|
|
14
|
+
*/
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=embedding-worker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embedding-worker.d.ts","sourceRoot":"","sources":["../../src/embedding-worker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// src/embedding-worker.ts
|
|
2
|
+
import { parentPort, workerData } from "node:worker_threads";
|
|
3
|
+
var { modelName, vendorModel } = workerData;
|
|
4
|
+
var model = null;
|
|
5
|
+
var initPromise = null;
|
|
6
|
+
var initFailed = false;
|
|
7
|
+
var initError = null;
|
|
8
|
+
async function ensureModel() {
|
|
9
|
+
if (model) return model;
|
|
10
|
+
if (initFailed) throw new Error(initError ?? "fastembed init previously failed");
|
|
11
|
+
if (!initPromise) {
|
|
12
|
+
initPromise = (async () => {
|
|
13
|
+
const fastembed = await import("fastembed");
|
|
14
|
+
const { EmbeddingModel, FlagEmbedding } = fastembed;
|
|
15
|
+
let m;
|
|
16
|
+
if (vendorModel) {
|
|
17
|
+
m = await FlagEmbedding.init({
|
|
18
|
+
model: EmbeddingModel.CUSTOM,
|
|
19
|
+
modelAbsoluteDirPath: vendorModel.modelAbsoluteDirPath,
|
|
20
|
+
modelName: vendorModel.modelName
|
|
21
|
+
});
|
|
22
|
+
} else {
|
|
23
|
+
const enumValue = EmbeddingModel[modelName];
|
|
24
|
+
m = await FlagEmbedding.init({
|
|
25
|
+
model: enumValue ?? modelName
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
model = m;
|
|
29
|
+
})().catch((err) => {
|
|
30
|
+
initFailed = true;
|
|
31
|
+
initError = err instanceof Error ? err.message : String(err);
|
|
32
|
+
initPromise = null;
|
|
33
|
+
post({ type: "init-error", error: initError });
|
|
34
|
+
throw err;
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
await initPromise;
|
|
38
|
+
if (!model) throw new Error("model init completed but model is null");
|
|
39
|
+
return model;
|
|
40
|
+
}
|
|
41
|
+
var queue = [];
|
|
42
|
+
var processing = false;
|
|
43
|
+
function enqueue(msg) {
|
|
44
|
+
if (msg.priority === "high") {
|
|
45
|
+
let insertAt = 0;
|
|
46
|
+
while (insertAt < queue.length && queue[insertAt].priority === "high") {
|
|
47
|
+
insertAt++;
|
|
48
|
+
}
|
|
49
|
+
queue.splice(insertAt, 0, msg);
|
|
50
|
+
} else {
|
|
51
|
+
queue.push(msg);
|
|
52
|
+
}
|
|
53
|
+
drain();
|
|
54
|
+
}
|
|
55
|
+
async function drain() {
|
|
56
|
+
if (processing) return;
|
|
57
|
+
processing = true;
|
|
58
|
+
while (queue.length > 0) {
|
|
59
|
+
const req = queue.shift();
|
|
60
|
+
await processEmbed(req);
|
|
61
|
+
}
|
|
62
|
+
processing = false;
|
|
63
|
+
}
|
|
64
|
+
async function processEmbed(req) {
|
|
65
|
+
try {
|
|
66
|
+
const m = await ensureModel();
|
|
67
|
+
let vectors;
|
|
68
|
+
if (req.inputType === "query" && req.texts.length === 1) {
|
|
69
|
+
const vec = await m.queryEmbed(req.texts[0]);
|
|
70
|
+
vectors = [new Float32Array(vec)];
|
|
71
|
+
} else {
|
|
72
|
+
vectors = [];
|
|
73
|
+
for await (const batch of m.passageEmbed(req.texts)) {
|
|
74
|
+
for (const vec of batch) {
|
|
75
|
+
vectors.push(new Float32Array(vec));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
post({ type: "result", id: req.id, vectors });
|
|
80
|
+
} catch (err) {
|
|
81
|
+
if (!initFailed) {
|
|
82
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
83
|
+
post({ type: "error", id: req.id, error: msg });
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function post(msg) {
|
|
88
|
+
parentPort.postMessage(msg);
|
|
89
|
+
}
|
|
90
|
+
parentPort.on("message", (msg) => {
|
|
91
|
+
switch (msg.type) {
|
|
92
|
+
case "embed":
|
|
93
|
+
enqueue(msg);
|
|
94
|
+
break;
|
|
95
|
+
case "shutdown":
|
|
96
|
+
process.exit(0);
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
//# sourceMappingURL=embedding-worker.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/embedding-worker.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Embedding worker thread \u2014 runs fastembed/ONNX inference off the main thread.\n *\n * This file is the entry point for a `node:worker_threads` Worker spawned by\n * `LocalProvider` in `embedding.ts`. It owns the `FlagEmbedding` ONNX model\n * and processes embed requests sequentially from a priority queue. Moving\n * inference here keeps the main thread's event loop free \u2014 HTTP requests,\n * SSE streams, and session APIs are no longer blocked during embedding.\n *\n * Communication uses `parentPort` message passing with structured clone.\n * Float32Array vectors are sent back directly (Bun preserves identity).\n *\n * @see embedding-worker-types.ts for the message protocol.\n */\n\nimport { parentPort, workerData } from \"node:worker_threads\";\nimport type {\n WorkerInbound,\n WorkerOutbound,\n WorkerInitData,\n EmbedRequest,\n} from \"./embedding-worker-types\";\n\n// ---------------------------------------------------------------------------\n// workerData\n// ---------------------------------------------------------------------------\n\nconst { modelName, vendorModel } = workerData as WorkerInitData;\n\n// ---------------------------------------------------------------------------\n// Model lifecycle \u2014 lazy init on first embed request\n// ---------------------------------------------------------------------------\n\n/** The fastembed model, typed to the subset of methods we use. */\ntype FastembedModel = {\n queryEmbed(text: string): Promise<number[]>;\n passageEmbed(texts: string[], batchSize?: number): AsyncGenerator<number[][]>;\n};\n\nlet model: FastembedModel | null = null;\nlet initPromise: Promise<void> | null = null;\nlet initFailed = false;\nlet initError: string | null = null;\n\n/**\n * Ensure the fastembed model is loaded. Lazy \u2014 first call triggers the\n * dynamic import + FlagEmbedding.init(), subsequent calls return immediately.\n * On failure, marks the worker as permanently broken and posts `init-error`.\n */\nasync function ensureModel(): Promise<FastembedModel> {\n if (model) return model;\n if (initFailed) throw new Error(initError ?? \"fastembed init previously failed\");\n\n if (!initPromise) {\n initPromise = (async () => {\n const fastembed = await import(\"fastembed\");\n const { EmbeddingModel, FlagEmbedding } = fastembed;\n\n let m: unknown;\n if (vendorModel) {\n // Binary mode: use pre-extracted model files.\n m = await FlagEmbedding.init({\n model: EmbeddingModel.CUSTOM,\n modelAbsoluteDirPath: vendorModel.modelAbsoluteDirPath,\n modelName: vendorModel.modelName,\n });\n } else {\n // npm mode: resolve model name against fastembed's enum.\n const enumValue = (EmbeddingModel as Record<string, string>)[modelName];\n m = await FlagEmbedding.init({\n model: enumValue ?? modelName,\n } as { model: typeof EmbeddingModel.BGESmallENV15 });\n }\n model = m as FastembedModel;\n })().catch((err) => {\n initFailed = true;\n initError = err instanceof Error ? err.message : String(err);\n initPromise = null;\n // Notify main thread \u2014 all pending + future requests should fail.\n post({ type: \"init-error\", error: initError });\n throw err;\n });\n }\n\n await initPromise;\n if (!model) throw new Error(\"model init completed but model is null\");\n return model;\n}\n\n// ---------------------------------------------------------------------------\n// Priority queue \u2014 high-priority (recall) jumps ahead of normal (backfill)\n// ---------------------------------------------------------------------------\n\nconst queue: EmbedRequest[] = [];\nlet processing = false;\n\n/**\n * Enqueue an embed request. High-priority requests are inserted after any\n * existing high-priority items but before all normal-priority items (FIFO\n * within each priority level). Triggers drain if not already running.\n */\nfunction enqueue(msg: EmbedRequest): void {\n if (msg.priority === \"high\") {\n // Insert after the last \"high\" item \u2014 keeps FIFO within high priority.\n let insertAt = 0;\n while (insertAt < queue.length && queue[insertAt].priority === \"high\") {\n insertAt++;\n }\n queue.splice(insertAt, 0, msg);\n } else {\n queue.push(msg);\n }\n drain();\n}\n\n/**\n * Process queued requests one at a time. ONNX inference is synchronous\n * inside the NAPI call, so parallelism within the worker buys nothing.\n * The queue gives us a natural point to interleave high-priority requests\n * between normal-priority batches.\n */\nasync function drain(): Promise<void> {\n if (processing) return;\n processing = true;\n\n while (queue.length > 0) {\n const req = queue.shift()!;\n await processEmbed(req);\n }\n\n processing = false;\n}\n\n// ---------------------------------------------------------------------------\n// Embed processing\n// ---------------------------------------------------------------------------\n\nasync function processEmbed(req: EmbedRequest): Promise<void> {\n try {\n const m = await ensureModel();\n\n let vectors: Float32Array[];\n\n if (req.inputType === \"query\" && req.texts.length === 1) {\n // Single query \u2014 use queryEmbed for better quality.\n const vec = await m.queryEmbed(req.texts[0]);\n vectors = [new Float32Array(vec)];\n } else {\n // Batch document embedding via async generator.\n vectors = [];\n for await (const batch of m.passageEmbed(req.texts)) {\n for (const vec of batch) {\n vectors.push(new Float32Array(vec));\n }\n }\n }\n\n post({ type: \"result\", id: req.id, vectors });\n } catch (err) {\n // Don't re-post init-error \u2014 it was already sent in ensureModel().\n if (!initFailed) {\n const msg = err instanceof Error ? err.message : String(err);\n post({ type: \"error\", id: req.id, error: msg });\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Message handling\n// ---------------------------------------------------------------------------\n\nfunction post(msg: WorkerOutbound): void {\n parentPort!.postMessage(msg);\n}\n\nparentPort!.on(\"message\", (msg: WorkerInbound) => {\n switch (msg.type) {\n case \"embed\":\n enqueue(msg);\n break;\n case \"shutdown\":\n process.exit(0);\n break;\n }\n});\n"],
|
|
5
|
+
"mappings": ";AAeA,SAAS,YAAY,kBAAkB;AAYvC,IAAM,EAAE,WAAW,YAAY,IAAI;AAYnC,IAAI,QAA+B;AACnC,IAAI,cAAoC;AACxC,IAAI,aAAa;AACjB,IAAI,YAA2B;AAO/B,eAAe,cAAuC;AACpD,MAAI,MAAO,QAAO;AAClB,MAAI,WAAY,OAAM,IAAI,MAAM,aAAa,kCAAkC;AAE/E,MAAI,CAAC,aAAa;AAChB,mBAAe,YAAY;AACzB,YAAM,YAAY,MAAM,OAAO,WAAW;AAC1C,YAAM,EAAE,gBAAgB,cAAc,IAAI;AAE1C,UAAI;AACJ,UAAI,aAAa;AAEf,YAAI,MAAM,cAAc,KAAK;AAAA,UAC3B,OAAO,eAAe;AAAA,UACtB,sBAAsB,YAAY;AAAA,UAClC,WAAW,YAAY;AAAA,QACzB,CAAC;AAAA,MACH,OAAO;AAEL,cAAM,YAAa,eAA0C,SAAS;AACtE,YAAI,MAAM,cAAc,KAAK;AAAA,UAC3B,OAAO,aAAa;AAAA,QACtB,CAAmD;AAAA,MACrD;AACA,cAAQ;AAAA,IACV,GAAG,EAAE,MAAM,CAAC,QAAQ;AAClB,mBAAa;AACb,kBAAY,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,oBAAc;AAEd,WAAK,EAAE,MAAM,cAAc,OAAO,UAAU,CAAC;AAC7C,YAAM;AAAA,IACR,CAAC;AAAA,EACH;AAEA,QAAM;AACN,MAAI,CAAC,MAAO,OAAM,IAAI,MAAM,wCAAwC;AACpE,SAAO;AACT;AAMA,IAAM,QAAwB,CAAC;AAC/B,IAAI,aAAa;AAOjB,SAAS,QAAQ,KAAyB;AACxC,MAAI,IAAI,aAAa,QAAQ;AAE3B,QAAI,WAAW;AACf,WAAO,WAAW,MAAM,UAAU,MAAM,QAAQ,EAAE,aAAa,QAAQ;AACrE;AAAA,IACF;AACA,UAAM,OAAO,UAAU,GAAG,GAAG;AAAA,EAC/B,OAAO;AACL,UAAM,KAAK,GAAG;AAAA,EAChB;AACA,QAAM;AACR;AAQA,eAAe,QAAuB;AACpC,MAAI,WAAY;AAChB,eAAa;AAEb,SAAO,MAAM,SAAS,GAAG;AACvB,UAAM,MAAM,MAAM,MAAM;AACxB,UAAM,aAAa,GAAG;AAAA,EACxB;AAEA,eAAa;AACf;AAMA,eAAe,aAAa,KAAkC;AAC5D,MAAI;AACF,UAAM,IAAI,MAAM,YAAY;AAE5B,QAAI;AAEJ,QAAI,IAAI,cAAc,WAAW,IAAI,MAAM,WAAW,GAAG;AAEvD,YAAM,MAAM,MAAM,EAAE,WAAW,IAAI,MAAM,CAAC,CAAC;AAC3C,gBAAU,CAAC,IAAI,aAAa,GAAG,CAAC;AAAA,IAClC,OAAO;AAEL,gBAAU,CAAC;AACX,uBAAiB,SAAS,EAAE,aAAa,IAAI,KAAK,GAAG;AACnD,mBAAW,OAAO,OAAO;AACvB,kBAAQ,KAAK,IAAI,aAAa,GAAG,CAAC;AAAA,QACpC;AAAA,MACF;AAAA,IACF;AAEA,SAAK,EAAE,MAAM,UAAU,IAAI,IAAI,IAAI,QAAQ,CAAC;AAAA,EAC9C,SAAS,KAAK;AAEZ,QAAI,CAAC,YAAY;AACf,YAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,WAAK,EAAE,MAAM,SAAS,IAAI,IAAI,IAAI,OAAO,IAAI,CAAC;AAAA,IAChD;AAAA,EACF;AACF;AAMA,SAAS,KAAK,KAA2B;AACvC,aAAY,YAAY,GAAG;AAC7B;AAEA,WAAY,GAAG,WAAW,CAAC,QAAuB;AAChD,UAAQ,IAAI,MAAM;AAAA,IAChB,KAAK;AACH,cAAQ,GAAG;AACX;AAAA,IACF,KAAK;AACH,cAAQ,KAAK,CAAC;AACd;AAAA,EACJ;AACF,CAAC;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/node/embedding.d.ts
CHANGED
|
@@ -11,19 +11,83 @@ export interface EmbeddingProvider {
|
|
|
11
11
|
embed(texts: string[], inputType: "document" | "query"): Promise<Float32Array[]>;
|
|
12
12
|
readonly maxBatchSize: number;
|
|
13
13
|
}
|
|
14
|
-
/**
|
|
15
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Thrown when `LocalProvider` is requested but `fastembed` cannot be loaded.
|
|
16
|
+
* `fastembed` is an optionalDependency of `@loreai/core`: if its postinstall
|
|
17
|
+
* fails (e.g. CUDA 13 hits the upstream `onnxruntime-node` bug — see #185),
|
|
18
|
+
* the package install still succeeds but local embeddings are disabled.
|
|
19
|
+
* Callers in `recall.ts` / `ltm.ts` / `distillation.ts` already gate on
|
|
20
|
+
* `isAvailable()`, which flips to `false` after this error fires once.
|
|
21
|
+
*/
|
|
22
|
+
export declare class LocalProviderUnavailableError extends Error {
|
|
23
|
+
constructor(cause?: unknown);
|
|
24
|
+
}
|
|
25
|
+
/** For tests: reset the fastembed probe cache. */
|
|
26
|
+
export declare function _resetFastembedProbe(): void;
|
|
27
|
+
/** For tests: simulate fastembed being unresolvable, without mocking the
|
|
28
|
+
* dynamic import. After this call, `tryLoadFastembed()` short-circuits to
|
|
29
|
+
* `null` and `isAvailable()` returns false for the local provider. */
|
|
30
|
+
export declare function _markFastembedUnavailable(): void;
|
|
31
|
+
/** Reset cached provider — called when config changes.
|
|
32
|
+
* Shuts down the worker thread if the current provider is a LocalProvider.
|
|
33
|
+
* Returns a promise that resolves once any worker has fully exited.
|
|
34
|
+
* Callers that need clean teardown (tests) should await the result. */
|
|
35
|
+
export declare function resetProvider(): Promise<void>;
|
|
36
|
+
/** Shut down the current provider and prevent any new provider from being
|
|
37
|
+
* created. After this call, `embed()` throws and `isAvailable()` returns
|
|
38
|
+
* false. Test-only: prevents fire-and-forget embeds (queued by other test
|
|
39
|
+
* files) from spawning a new worker after cleanup. */
|
|
40
|
+
export declare function _shutdownAndDisable(): Promise<void>;
|
|
41
|
+
/** Save the current cached provider reference (including the live worker)
|
|
42
|
+
* and clear the cache so the next `getProvider()` call creates a fresh one.
|
|
43
|
+
* Returns an opaque token that must be passed to `_restoreProvider()` to
|
|
44
|
+
* put the original provider back — without this, the worker is orphaned and
|
|
45
|
+
* a second ONNX load in the same Bun process will crash.
|
|
46
|
+
*
|
|
47
|
+
* Test-only helper: lets suites temporarily swap in a mock/unavailable
|
|
48
|
+
* provider without killing the real worker. */
|
|
49
|
+
export declare function _saveAndClearProvider(): unknown;
|
|
50
|
+
/** Restore a provider previously saved by `_saveAndClearProvider()`. Any
|
|
51
|
+
* provider created between save and restore is discarded (callers must
|
|
52
|
+
* ensure it's not a LocalProvider with a live worker — those suites only
|
|
53
|
+
* use `_markFastembedUnavailable()` so no worker is spawned). */
|
|
54
|
+
export declare function _restoreProvider(token: unknown): void;
|
|
55
|
+
/**
|
|
56
|
+
* Build a remote `EmbeddingProvider` from whichever API key is in env.
|
|
57
|
+
* Returns `null` when neither `VOYAGE_API_KEY` nor `OPENAI_API_KEY` is set,
|
|
58
|
+
* which is the signal for callers to fall through to FTS-only behaviour.
|
|
59
|
+
*
|
|
60
|
+
* Voyage wins ties because it's the higher-quality option for code search;
|
|
61
|
+
* users who want OpenAI specifically can pin `search.embeddings.provider`
|
|
62
|
+
* in `.lore.json` and skip the fallback path entirely.
|
|
63
|
+
*/
|
|
64
|
+
export declare function pickRemoteFallback(): {
|
|
65
|
+
name: "voyage" | "openai";
|
|
66
|
+
provider: EmbeddingProvider;
|
|
67
|
+
} | null;
|
|
16
68
|
/** Returns true if embedding is available.
|
|
17
69
|
* Active when the configured provider's API key is set, unless explicitly
|
|
18
|
-
* disabled via `search.embeddings.enabled: false` in .lore.json.
|
|
70
|
+
* disabled via `search.embeddings.enabled: false` in .lore.json.
|
|
71
|
+
*
|
|
72
|
+
* For the `local` provider, also returns false once we've discovered the
|
|
73
|
+
* optional `fastembed` peer is missing — callers (recall, ltm, distillation)
|
|
74
|
+
* use this gate to skip embedding work and fall back to FTS-only search. */
|
|
19
75
|
export declare function isAvailable(): boolean;
|
|
20
76
|
/**
|
|
21
77
|
* Generate embeddings for the given texts using the configured provider.
|
|
22
78
|
*
|
|
79
|
+
* If the configured provider is `local` and `fastembed` turns out to be
|
|
80
|
+
* unavailable at runtime (failed install, vendor extraction blocked, etc.),
|
|
81
|
+
* automatically swap to a remote provider when `VOYAGE_API_KEY` or
|
|
82
|
+
* `OPENAI_API_KEY` is set in env. The swap is permanent for the rest of
|
|
83
|
+
* the process — `cachedProvider` is replaced so subsequent calls skip the
|
|
84
|
+
* local-then-fail path.
|
|
85
|
+
*
|
|
23
86
|
* @param texts Array of texts to embed
|
|
24
87
|
* @param inputType "document" for storage, "query" for search
|
|
25
88
|
* @returns Float32Array per input text
|
|
26
|
-
* @throws On API errors or
|
|
89
|
+
* @throws On API errors or when no provider (local or remote) is
|
|
90
|
+
* available
|
|
27
91
|
*/
|
|
28
92
|
export declare function embed(texts: string[], inputType: "document" | "query"): Promise<Float32Array[]>;
|
|
29
93
|
/**
|
|
@@ -64,6 +128,24 @@ export declare function embedKnowledgeEntry(id: string, title: string, content:
|
|
|
64
128
|
* The distillation remains searchable via FTS even if embedding fails.
|
|
65
129
|
*/
|
|
66
130
|
export declare function embedDistillation(id: string, observations: string): void;
|
|
131
|
+
/**
|
|
132
|
+
* Embed a temporal message and store the result in the DB.
|
|
133
|
+
* Fire-and-forget — errors are logged, never thrown.
|
|
134
|
+
* Only called for undistilled messages; once distilled, the embedding
|
|
135
|
+
* is NULLed (semantic content captured by distillation embedding).
|
|
136
|
+
*/
|
|
137
|
+
export declare function embedTemporalMessage(id: string, content: string): void;
|
|
138
|
+
/**
|
|
139
|
+
* Search undistilled temporal messages with embeddings by cosine similarity.
|
|
140
|
+
* Returns top-k entries sorted by similarity descending.
|
|
141
|
+
*
|
|
142
|
+
* Only scans undistilled messages (distilled=0) — once a message is
|
|
143
|
+
* distilled, its semantic content is captured by the distillation
|
|
144
|
+
* embedding and the temporal embedding is cleared.
|
|
145
|
+
*
|
|
146
|
+
* Scoped to a single project. Optionally scoped to a single session.
|
|
147
|
+
*/
|
|
148
|
+
export declare function vectorSearchTemporal(queryEmbedding: Float32Array, projectId: string, limit?: number, sessionId?: string): VectorHit[];
|
|
67
149
|
/**
|
|
68
150
|
* Check if embedding config has changed since the last backfill.
|
|
69
151
|
* If so, clear all existing embeddings (they're incompatible) and
|
|
@@ -77,10 +159,11 @@ export declare function checkConfigChange(): boolean;
|
|
|
77
159
|
*
|
|
78
160
|
* This is the canonical entry point that every host adapter (OpenCode, Pi,
|
|
79
161
|
* future ACP) should call once during init. It:
|
|
80
|
-
* 1.
|
|
81
|
-
* 2.
|
|
82
|
-
* 3. Backfills
|
|
83
|
-
* 4.
|
|
162
|
+
* 1. Waits a short grace period so first-connect HTTP requests can finish
|
|
163
|
+
* 2. Detects config changes (provider swap) and clears stale embeddings
|
|
164
|
+
* 3. Backfills knowledge entries missing embeddings
|
|
165
|
+
* 4. Backfills non-archived distillations missing embeddings
|
|
166
|
+
* 5. Logs a one-line coverage summary to stderr (always visible, not gated)
|
|
84
167
|
*
|
|
85
168
|
* Fire-and-forget: callers should `.catch()` — embedding failures must not
|
|
86
169
|
* block plugin initialization.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"embedding.d.ts","sourceRoot":"","sources":["../../src/embedding.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"embedding.d.ts","sourceRoot":"","sources":["../../src/embedding.ts"],"names":[],"mappings":"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"}
|