@wirecat/cli-core 0.434.0 → 0.436.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/CHANGELOG.md +47 -0
- package/dist/cli/messenger/context.js +1 -0
- package/dist/cli/messenger/conversation-answers.d.ts +1 -0
- package/dist/cli/messenger/conversations-command.js +2 -2
- package/dist/cli/messenger/message-answers.d.ts +1 -0
- package/dist/cli/messenger/message-writes.d.ts +2 -0
- package/dist/cli/messenger/messages-search-command.js +6 -10
- package/dist/cli/messenger/models-command.js +34 -7
- package/dist/cli/messenger/search-answers.d.ts +3 -0
- package/dist/cli/messenger/search-answers.js +40 -8
- package/dist/cli/messenger/search-command.js +3 -1
- package/dist/cli/messenger/store-maintenance-command.js +7 -4
- package/dist/cli/model-catalogue-command.js +1 -1
- package/dist/cli/profile.d.ts +4 -1
- package/dist/cli/profile.js +22 -7
- package/dist/cli/program.js +8 -4
- package/dist/cli/settings.d.ts +4 -0
- package/dist/cli/settings.js +5 -0
- package/dist/cli/shared-command-exports.d.ts +4 -0
- package/dist/embeddings/child.js +15 -5
- package/dist/embeddings/embed.d.ts +16 -2
- package/dist/embeddings/embed.js +57 -28
- package/dist/embeddings/models.d.ts +18 -0
- package/dist/embeddings/models.js +69 -0
- package/dist/embeddings/process.d.ts +6 -1
- package/dist/embeddings/process.js +23 -7
- package/dist/embeddings/rerank.d.ts +10 -0
- package/dist/embeddings/rerank.js +31 -0
- package/dist/entities/conversation-results.d.ts +2 -0
- package/dist/entities/evidence.d.ts +8 -0
- package/dist/entities/index.d.ts +27 -1
- package/dist/entities/listener.d.ts +4 -0
- package/dist/entities/message-context.d.ts +3 -0
- package/dist/entities/message-write.d.ts +4 -0
- package/dist/entities/message.d.ts +2 -0
- package/dist/entities/message.js +2 -0
- package/dist/entities/model.d.ts +5 -2
- package/dist/entities/model.js +5 -2
- package/dist/entities/store-maintenance.d.ts +1 -0
- package/dist/entities/store-maintenance.js +8 -1
- package/dist/entities/store-records.d.ts +1 -0
- package/dist/mcp/tool.d.ts +1 -1
- package/dist/mcp/tool.js +1 -0
- package/dist/services/deps.d.ts +3 -0
- package/dist/services/embeddings-maintenance.d.ts +10 -2
- package/dist/services/index.d.ts +2 -0
- package/dist/services/index.js +1 -0
- package/dist/services/messages-archive-search.d.ts +2 -2
- package/dist/services/messages-archive-search.js +4 -4
- package/dist/services/messages-discovery.d.ts +33 -4
- package/dist/services/messages-discovery.js +88 -3
- package/dist/services/messages-meaning.d.ts +2 -1
- package/dist/services/messages-meaning.js +7 -3
- package/dist/services/messages-rerank.d.ts +7 -0
- package/dist/services/messages-rerank.js +42 -0
- package/dist/services/messages-search.d.ts +5 -0
- package/dist/services/messages.d.ts +6 -2
- package/dist/services/messages.js +4 -3
- package/dist/services/model-downloads.d.ts +3 -1
- package/dist/services/model-downloads.js +20 -1
- package/dist/services/notes-search.d.ts +7 -2
- package/dist/services/notes-search.js +7 -4
- package/dist/services/search-all-meetings.d.ts +3 -2
- package/dist/services/search-all-meetings.js +8 -8
- package/dist/services/search-all.d.ts +7 -1
- package/dist/services/search-all.js +109 -45
- package/dist/store/exports.d.ts +1 -1
- package/dist/store/exports.js +1 -1
- package/dist/store/sqlite/accounts.js +2 -2
- package/dist/store/sqlite/chats.d.ts +1 -1
- package/dist/store/sqlite/chats.js +11 -4
- package/dist/store/sqlite/document-pictures.js +2 -7
- package/dist/store/sqlite/email-index.d.ts +1 -3
- package/dist/store/sqlite/email-index.js +5 -21
- package/dist/store/sqlite/emails.js +4 -3
- package/dist/store/sqlite/image-chunks.d.ts +2 -2
- package/dist/store/sqlite/image-chunks.js +10 -13
- package/dist/store/sqlite/meeting-vectors.js +11 -9
- package/dist/store/sqlite/message-chunks.js +3 -8
- package/dist/store/sqlite/note-index.js +2 -7
- package/dist/store/sqlite/schema.d.ts +1 -1
- package/dist/store/sqlite/schema.js +1 -1
- package/dist/store/sqlite/vector-index.d.ts +9 -0
- package/dist/store/sqlite/vector-index.js +25 -0
- package/dist/store/sqlite/vectors.d.ts +2 -2
- package/dist/store/sqlite/vectors.js +11 -8
- package/package.json +1 -1
|
@@ -8,6 +8,7 @@ import { indexRow } from "./index-state.js";
|
|
|
8
8
|
import { speech } from "./lucene-fields.js";
|
|
9
9
|
import { savedStemmers } from "./stems.js";
|
|
10
10
|
import { write } from "./transaction.js";
|
|
11
|
+
import { dropUnusedVectors } from "./vector-index.js";
|
|
11
12
|
export const CORPORA = {
|
|
12
13
|
document: {
|
|
13
14
|
type: "document",
|
|
@@ -114,8 +115,6 @@ const writer = (database, corpus, stemmer) => {
|
|
|
114
115
|
stems: database.prepare(`INSERT INTO ${corpus.type}_stems (rowid, stems, scope) VALUES (?, ?, ?)`),
|
|
115
116
|
chunk: database.prepare("INSERT INTO chunks (chunkable_type, chunkable_id, position, start_offset, end_offset, content_hash, scope, account_id, created_at, updated_at) " +
|
|
116
117
|
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"),
|
|
117
|
-
purge: database.prepare(`DELETE FROM embeddings WHERE content_hash = ?
|
|
118
|
-
AND NOT EXISTS (SELECT 1 FROM chunks WHERE content_hash = ?)`),
|
|
119
118
|
dequeue: database.prepare(`DELETE FROM ${corpus.type}_index_pending WHERE indexable_type = ? AND id = ?`),
|
|
120
119
|
};
|
|
121
120
|
return (id) => {
|
|
@@ -134,7 +133,6 @@ const writer = (database, corpus, stemmer) => {
|
|
|
134
133
|
const stems = terms ? terms.stems : text ? stemmer.indexText(text) : "";
|
|
135
134
|
if (stems !== "")
|
|
136
135
|
statements.stems.run(id, stems, scope);
|
|
137
|
-
const after = new Set();
|
|
138
136
|
if (text.trim() && corpus.pieces !== false) {
|
|
139
137
|
const at = Date.now();
|
|
140
138
|
const sections = row?.metadata == null ? [] : storedSections(String(row.metadata));
|
|
@@ -143,13 +141,10 @@ const writer = (database, corpus, stemmer) => {
|
|
|
143
141
|
: splitText(text, CHUNK_CHARS);
|
|
144
142
|
ranges.forEach(({ start, end }, position) => {
|
|
145
143
|
const hash = chunkHash(text.slice(start, end));
|
|
146
|
-
after.add(hash);
|
|
147
144
|
statements.chunk.run(corpus.type, id, position, start, end, hash, scope, int(row?.account_id), at, at);
|
|
148
145
|
});
|
|
149
146
|
}
|
|
150
|
-
|
|
151
|
-
if (!after.has(hash))
|
|
152
|
-
statements.purge.run(hash, hash);
|
|
147
|
+
dropUnusedVectors(database, before);
|
|
153
148
|
if (!live && corpus.type === "document")
|
|
154
149
|
dropDocumentPicture(database, id);
|
|
155
150
|
statements.dequeue.run(queuedAs(corpus), id);
|
|
@@ -15535,7 +15535,7 @@ export declare const conversationState: import("drizzle-orm/sqlite-core").SQLite
|
|
|
15535
15535
|
dialect: 'sqlite';
|
|
15536
15536
|
isAlias: false;
|
|
15537
15537
|
}>;
|
|
15538
|
-
/** One model's vector for one chunk text; no owner
|
|
15538
|
+
/** One model's vector for one chunk text; no owner: chunks of any kind share it, and it goes once none holds the text. */
|
|
15539
15539
|
export declare const embeddings: import("drizzle-orm/sqlite-core").SQLiteTableWithColumns<{
|
|
15540
15540
|
name: "embeddings";
|
|
15541
15541
|
schema: undefined;
|
|
@@ -1556,7 +1556,7 @@ export const conversationState = sqliteTable("conversation_state", {
|
|
|
1556
1556
|
/** The build readers see; a higher one is being written, or failed. */
|
|
1557
1557
|
currentBuild: integer("current_build"),
|
|
1558
1558
|
});
|
|
1559
|
-
/** One model's vector for one chunk text; no owner
|
|
1559
|
+
/** One model's vector for one chunk text; no owner: chunks of any kind share it, and it goes once none holds the text. */
|
|
1560
1560
|
export const embeddings = sqliteTable("embeddings", {
|
|
1561
1561
|
/** `<provider>:<model>:<dims>` — vectors of different models never mix. */
|
|
1562
1562
|
model: text("model").notNull(),
|
|
@@ -65,3 +65,12 @@ export declare const vectorIndexCounts: (database: CacheDatabase) => {
|
|
|
65
65
|
missing: number;
|
|
66
66
|
extra: number;
|
|
67
67
|
}[];
|
|
68
|
+
/**
|
|
69
|
+
* The one rule for a vector whose text may be gone: it goes once no chunk of any kind holds its hash, for every model.
|
|
70
|
+
* Call it with the hashes a write released, after that write's new chunks are in, so a text that comes back keeps its
|
|
71
|
+
* vector. The key is (model, content_hash): naming each model reaches the row by the key; the hash alone scans every
|
|
72
|
+
* vector.
|
|
73
|
+
*/
|
|
74
|
+
export declare const dropUnusedVectors: (database: CacheDatabase, hashes: Iterable<string>) => void;
|
|
75
|
+
/** Every vector no chunk holds, left by a build before `dropUnusedVectors` covered every write; how many. */
|
|
76
|
+
export declare const dropAllUnusedVectors: (database: CacheDatabase) => number;
|
|
@@ -178,3 +178,28 @@ export const vectorIndexCounts = (database) => database.prepare("SELECT id, mode
|
|
|
178
178
|
extra: count(`${held} EXCEPT ${wanted}`),
|
|
179
179
|
};
|
|
180
180
|
});
|
|
181
|
+
/**
|
|
182
|
+
* The one rule for a vector whose text may be gone: it goes once no chunk of any kind holds its hash, for every model.
|
|
183
|
+
* Call it with the hashes a write released, after that write's new chunks are in, so a text that comes back keeps its
|
|
184
|
+
* vector. The key is (model, content_hash): naming each model reaches the row by the key; the hash alone scans every
|
|
185
|
+
* vector.
|
|
186
|
+
*/
|
|
187
|
+
export const dropUnusedVectors = (database, hashes) => {
|
|
188
|
+
const unique = new Set(hashes);
|
|
189
|
+
if (unique.size === 0)
|
|
190
|
+
return;
|
|
191
|
+
const models = database
|
|
192
|
+
.prepare(`WITH RECURSIVE m(model) AS (SELECT min(model) FROM embeddings
|
|
193
|
+
UNION ALL SELECT (SELECT min(model) FROM embeddings WHERE model > m.model) FROM m WHERE m.model IS NOT NULL)
|
|
194
|
+
SELECT model FROM m WHERE model IS NOT NULL`)
|
|
195
|
+
.all()
|
|
196
|
+
.map(({ model }) => String(model));
|
|
197
|
+
const drop = database.prepare("DELETE FROM embeddings WHERE model = ? AND content_hash = ? AND NOT EXISTS (SELECT 1 FROM chunks WHERE content_hash = ?)");
|
|
198
|
+
for (const hash of unique)
|
|
199
|
+
for (const model of models)
|
|
200
|
+
drop.run(model, hash, hash);
|
|
201
|
+
};
|
|
202
|
+
/** Every vector no chunk holds, left by a build before `dropUnusedVectors` covered every write; how many. */
|
|
203
|
+
export const dropAllUnusedVectors = (database) => database
|
|
204
|
+
.prepare("DELETE FROM embeddings WHERE NOT EXISTS (SELECT 1 FROM chunks k WHERE k.content_hash = embeddings.content_hash) RETURNING 1")
|
|
205
|
+
.all().length;
|
|
@@ -50,8 +50,8 @@ export declare const vectorStatus: ({ orm }: StoreContext, chatKey: number, mode
|
|
|
50
50
|
embedded: number;
|
|
51
51
|
};
|
|
52
52
|
/**
|
|
53
|
-
* Drops the vectors of the chat's chunks, or only one model's — but not one
|
|
54
|
-
* points at: the same text in two chats has one vector.
|
|
53
|
+
* Drops the vectors of the chat's conversation and message chunks, or only one model's — but not one a chunk outside
|
|
54
|
+
* the chat still points at: the same text in two chats has one vector. The chunks themselves stay, to be embedded again.
|
|
55
55
|
*/
|
|
56
56
|
export declare const clearVectors: ({ orm }: StoreContext, chatKey: number, model: string | undefined) => number;
|
|
57
57
|
export interface NearestChunk {
|
|
@@ -138,16 +138,19 @@ export const vectorStatus = ({ orm }, chatKey, model) => {
|
|
|
138
138
|
return { chunks: Number(row?.chunks ?? 0), embedded: Number(row?.embedded ?? 0) };
|
|
139
139
|
};
|
|
140
140
|
/**
|
|
141
|
-
* Drops the vectors of the chat's chunks, or only one model's — but not one
|
|
142
|
-
* points at: the same text in two chats has one vector.
|
|
141
|
+
* Drops the vectors of the chat's conversation and message chunks, or only one model's — but not one a chunk outside
|
|
142
|
+
* the chat still points at: the same text in two chats has one vector. The chunks themselves stay, to be embedded again.
|
|
143
143
|
*/
|
|
144
|
-
export const clearVectors = ({ orm }, chatKey, model) => orm.all(sql `
|
|
144
|
+
export const clearVectors = ({ orm }, chatKey, model) => orm.all(sql `WITH mine(id) AS (
|
|
145
|
+
SELECT k.id FROM chunks k JOIN conversations c ON k.chunkable_type = 'conversation' AND c.id = k.chunkable_id
|
|
146
|
+
WHERE c.chat_id = ${chatKey}
|
|
147
|
+
UNION ALL SELECT k.id FROM chunks k JOIN messages m ON k.chunkable_type = 'message' AND m.id = k.chunkable_id
|
|
148
|
+
WHERE m.chat_id = ${chatKey})
|
|
149
|
+
DELETE FROM embeddings
|
|
145
150
|
WHERE ${model === undefined ? sql `1` : sql `model = ${model}`}
|
|
146
|
-
AND content_hash IN (SELECT
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
LEFT JOIN conversations c ON k.chunkable_type = 'conversation' AND c.id = k.chunkable_id
|
|
150
|
-
WHERE c.id IS NULL OR c.chat_id <> ${chatKey})
|
|
151
|
+
AND content_hash IN (SELECT content_hash FROM chunks WHERE id IN (SELECT id FROM mine))
|
|
152
|
+
AND NOT EXISTS (SELECT 1 FROM chunks o WHERE o.content_hash = embeddings.content_hash
|
|
153
|
+
AND o.id NOT IN (SELECT id FROM mine))
|
|
151
154
|
RETURNING 1 AS n`).length;
|
|
152
155
|
/** Rows read per step of a search, so a chat of any size is scanned in bounded memory. */
|
|
153
156
|
const SCAN_PAGE = 5_000;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wirecat/cli-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.436.0",
|
|
4
4
|
"description": "The messenger-neutral half of a messaging CLI: domain model, message locators, rendering, name resolution, the SQLite seam, the command skeleton and shared read commands, the send guard, run records and the message store",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Viacheslav Ptsarev",
|