@gmickel/gno 1.45.1 → 2.0.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 +1 -1
- package/THIRD_PARTY_NOTICES.md +46 -0
- package/assets/skill/SKILL.md +7 -6
- package/assets/skill/cli-reference.md +14 -6
- package/assets/skill/mcp-reference.md +4 -1
- package/assets/spa-production.json.gz +0 -0
- package/browser-extension/artifacts/gno-browser-clipper-v2.0.0.zip +0 -0
- package/browser-extension/artifacts/gno-browser-clipper-v2.0.0.zip.sha256 +1 -0
- package/browser-extension/dist/chunk-4tc9v0ja.js +74 -0
- package/browser-extension/dist/manifest.json +1 -1
- package/browser-extension/dist/preview.html +1 -1
- package/browser-extension/dist/service-worker.js +32 -33
- package/bunfig.toml +2 -0
- package/package.json +40 -26
- package/spec/cli.md +30 -11
- package/spec/db/schema.sql +146 -1
- package/spec/mcp.md +26 -0
- package/src/app/context-runtime-types.ts +3 -0
- package/src/app/context-runtime.ts +2 -0
- package/src/cli/commands/ask.ts +6 -1
- package/src/cli/commands/daemon.ts +21 -8
- package/src/cli/commands/embed.ts +77 -41
- package/src/cli/commands/mcp/install.ts +20 -0
- package/src/cli/commands/mcp/paths.ts +25 -0
- package/src/cli/commands/mcp/status.ts +6 -0
- package/src/cli/detach.ts +3 -2
- package/src/cli/program.ts +6 -0
- package/src/config/types.ts +3 -3
- package/src/converters/adapters/markitdownTs/adapter.ts +1 -2
- package/src/converters/adapters/officeparser/adapter.ts +1 -2
- package/src/converters/versions.ts +6 -8
- package/src/core/context-evidence.ts +8 -4
- package/src/core/job-manager.ts +95 -13
- package/src/core/network-boundary-inventory.ts +10 -0
- package/src/core/shutdown-budget.ts +45 -0
- package/src/embed/backlog.ts +107 -4
- package/src/embed/batch.ts +42 -2
- package/src/embed/fingerprint.ts +16 -0
- package/src/embed/retry.ts +113 -5
- package/src/embed/variant-backlog.ts +105 -0
- package/src/embed/variant-plan.ts +62 -0
- package/src/embed/variant-retry.ts +113 -0
- package/src/ingestion/graph-reconciliation.ts +327 -0
- package/src/ingestion/sync.ts +9 -272
- package/src/llm/http-inference.ts +6 -0
- package/src/llm/httpEmbedding.ts +37 -6
- package/src/llm/httpGeneration.ts +18 -3
- package/src/llm/httpRerank.ts +23 -5
- package/src/llm/inference-cancellation.ts +168 -0
- package/src/llm/inference-scope.ts +202 -0
- package/src/llm/lazy-ports.ts +115 -0
- package/src/llm/native-worker/client.ts +541 -0
- package/src/llm/native-worker/dispatcher.ts +228 -0
- package/src/llm/native-worker/embedding-identity.ts +33 -0
- package/src/llm/native-worker/entry.ts +173 -0
- package/src/llm/native-worker/errors.ts +32 -0
- package/src/llm/native-worker/evaluation.ts +16 -0
- package/src/llm/native-worker/owned-exit.ts +108 -0
- package/src/llm/native-worker/owner.ts +141 -0
- package/src/llm/native-worker/ports.ts +317 -0
- package/src/llm/native-worker/protocol.ts +442 -0
- package/src/llm/native-worker/runtime-config.ts +92 -0
- package/src/llm/nodeLlamaCpp/adapter.ts +77 -20
- package/src/llm/nodeLlamaCpp/embedding.ts +130 -46
- package/src/llm/nodeLlamaCpp/generation.ts +34 -5
- package/src/llm/nodeLlamaCpp/lifecycle-options.ts +99 -0
- package/src/llm/nodeLlamaCpp/lifecycle.ts +209 -204
- package/src/llm/nodeLlamaCpp/rerank-capacity.ts +111 -0
- package/src/llm/nodeLlamaCpp/rerank.ts +118 -27
- package/src/llm/nodeLlamaCpp/simulator-handle.ts +73 -0
- package/src/llm/nodeLlamaCpp/simulator-install.ts +124 -0
- package/src/llm/nodeLlamaCpp/simulator-session.ts +240 -0
- package/src/llm/nodeLlamaCpp/simulator-types.ts +80 -0
- package/src/llm/types.ts +35 -5
- package/src/mcp/context.ts +27 -0
- package/src/mcp/http-transport.ts +12 -10
- package/src/mcp/server.ts +3 -0
- package/src/mcp/tool-profile.ts +30 -8
- package/src/mcp/tools/context.ts +8 -11
- package/src/mcp/tools/embed.ts +1 -1
- package/src/mcp/tools/index-cmd.ts +1 -1
- package/src/mcp/tools/index.ts +10 -8
- package/src/mcp/tools/query.ts +14 -30
- package/src/mcp/tools/vsearch.ts +1 -1
- package/src/pipeline/answer.ts +23 -3
- package/src/pipeline/claim-verifier.ts +6 -0
- package/src/pipeline/expansion.ts +43 -40
- package/src/pipeline/explain.ts +6 -2
- package/src/pipeline/filters.ts +63 -0
- package/src/pipeline/fusion.ts +29 -9
- package/src/pipeline/graph-retrieval.ts +29 -9
- package/src/pipeline/hybrid.ts +198 -55
- package/src/pipeline/hydration.ts +161 -0
- package/src/pipeline/owner-fusion.ts +87 -0
- package/src/pipeline/rerank.ts +35 -11
- package/src/pipeline/search.ts +13 -2
- package/src/pipeline/types.ts +5 -3
- package/src/pipeline/vsearch.ts +87 -7
- package/src/sdk/client.ts +47 -3
- package/src/sdk/embed.ts +63 -39
- package/src/serve/background-runtime.ts +1 -1
- package/src/serve/context.ts +41 -56
- package/src/serve/embed-scheduler.ts +58 -35
- package/src/serve/public/components/IndexingProgress.tsx +46 -60
- package/src/serve/public/globals.built.css +1 -1
- package/src/serve/public/lib/shiki-language-ids.ts +14 -0
- package/src/serve/resident-admission.ts +36 -36
- package/src/serve/resident-background-work.ts +20 -2
- package/src/serve/resident-request.ts +11 -5
- package/src/serve/resident-runtime.ts +97 -61
- package/src/serve/resident-shutdown.ts +153 -0
- package/src/serve/routes/api.ts +3 -1
- package/src/serve/server.ts +47 -26
- package/src/store/migrations/028-vector-variants.ts +54 -0
- package/src/store/migrations/029-graph-reference-state.ts +77 -0
- package/src/store/migrations/index.ts +4 -0
- package/src/store/sqlite/adapter.ts +251 -183
- package/src/store/sqlite/eligibility.ts +174 -0
- package/src/store/sqlite/graph-edge-application.ts +66 -0
- package/src/store/sqlite/graph-reference-state.ts +194 -0
- package/src/store/sqlite/legacy-vector-ownership.ts +79 -0
- package/src/store/types.ts +80 -12
- package/src/store/vector/eligibility.ts +36 -0
- package/src/store/vector/freshness.ts +33 -6
- package/src/store/vector/lazy.ts +81 -0
- package/src/store/vector/sqlite-vec.ts +106 -54
- package/src/store/vector/stats.ts +14 -3
- package/src/store/vector/types.ts +35 -2
- package/src/store/vector/variant-search.ts +192 -0
- package/src/store/vector/variants.ts +451 -0
- package/vendor/converters/markitdown-ts/LICENSE +21 -0
- package/vendor/converters/markitdown-ts/dist/index.cjs +1180 -0
- package/vendor/converters/markitdown-ts/dist/index.d.cts +46 -0
- package/vendor/converters/markitdown-ts/dist/index.d.mts +46 -0
- package/vendor/converters/markitdown-ts/dist/index.d.ts +46 -0
- package/vendor/converters/markitdown-ts/dist/index.mjs +1152 -0
- package/vendor/converters/markitdown-ts/package.json +77 -0
- package/vendor/converters/officeparser/LICENSE +21 -0
- package/vendor/converters/officeparser/dist/OfficeConverter.d.ts +47 -0
- package/vendor/converters/officeparser/dist/OfficeConverter.js +76 -0
- package/vendor/converters/officeparser/dist/OfficeGenerator.d.ts +23 -0
- package/vendor/converters/officeparser/dist/OfficeGenerator.js +73 -0
- package/vendor/converters/officeparser/dist/OfficeParser.d.ts +106 -0
- package/vendor/converters/officeparser/dist/OfficeParser.js +332 -0
- package/vendor/converters/officeparser/dist/cli.d.ts +28 -0
- package/vendor/converters/officeparser/dist/cli.js +381 -0
- package/vendor/converters/officeparser/dist/defaults.d.ts +41 -0
- package/vendor/converters/officeparser/dist/defaults.js +218 -0
- package/vendor/converters/officeparser/dist/generators/BaseGenerator.d.ts +107 -0
- package/vendor/converters/officeparser/dist/generators/BaseGenerator.js +248 -0
- package/vendor/converters/officeparser/dist/generators/ChunkingGenerator.d.ts +82 -0
- package/vendor/converters/officeparser/dist/generators/ChunkingGenerator.js +797 -0
- package/vendor/converters/officeparser/dist/generators/CsvGenerator.d.ts +38 -0
- package/vendor/converters/officeparser/dist/generators/CsvGenerator.js +245 -0
- package/vendor/converters/officeparser/dist/generators/EpubGenerator.d.ts +43 -0
- package/vendor/converters/officeparser/dist/generators/EpubGenerator.js +315 -0
- package/vendor/converters/officeparser/dist/generators/HtmlGenerator.d.ts +59 -0
- package/vendor/converters/officeparser/dist/generators/HtmlGenerator.js +1942 -0
- package/vendor/converters/officeparser/dist/generators/MarkdownGenerator.d.ts +96 -0
- package/vendor/converters/officeparser/dist/generators/MarkdownGenerator.js +1175 -0
- package/vendor/converters/officeparser/dist/generators/PdfGenerator.d.ts +22 -0
- package/vendor/converters/officeparser/dist/generators/PdfGenerator.js +194 -0
- package/vendor/converters/officeparser/dist/generators/RtfGenerator.d.ts +29 -0
- package/vendor/converters/officeparser/dist/generators/RtfGenerator.js +316 -0
- package/vendor/converters/officeparser/dist/generators/TextGenerator.d.ts +13 -0
- package/vendor/converters/officeparser/dist/generators/TextGenerator.js +201 -0
- package/vendor/converters/officeparser/dist/index.d.ts +60 -0
- package/vendor/converters/officeparser/dist/index.js +72 -0
- package/vendor/converters/officeparser/dist/index.mjs +18 -0
- package/vendor/converters/officeparser/dist/officeparser.browser.d.ts +2621 -0
- package/vendor/converters/officeparser/dist/officeparser.browser.iife.js +1336 -0
- package/vendor/converters/officeparser/dist/officeparser.browser.mjs +1335 -0
- package/vendor/converters/officeparser/dist/officeparser.browser.slim.d.ts +2621 -0
- package/vendor/converters/officeparser/dist/officeparser.browser.slim.iife.js +1336 -0
- package/vendor/converters/officeparser/dist/officeparser.browser.slim.mjs +1335 -0
- package/vendor/converters/officeparser/dist/parsers/CsvParser.d.ts +9 -0
- package/vendor/converters/officeparser/dist/parsers/CsvParser.js +115 -0
- package/vendor/converters/officeparser/dist/parsers/EpubParser.d.ts +8 -0
- package/vendor/converters/officeparser/dist/parsers/EpubParser.js +217 -0
- package/vendor/converters/officeparser/dist/parsers/ExcelParser.d.ts +32 -0
- package/vendor/converters/officeparser/dist/parsers/ExcelParser.js +736 -0
- package/vendor/converters/officeparser/dist/parsers/HtmlParser.d.ts +2 -0
- package/vendor/converters/officeparser/dist/parsers/HtmlParser.js +1287 -0
- package/vendor/converters/officeparser/dist/parsers/MarkdownParser.d.ts +2 -0
- package/vendor/converters/officeparser/dist/parsers/MarkdownParser.js +1272 -0
- package/vendor/converters/officeparser/dist/parsers/OpenOfficeParser.d.ts +31 -0
- package/vendor/converters/officeparser/dist/parsers/OpenOfficeParser.js +1819 -0
- package/vendor/converters/officeparser/dist/parsers/PdfParser.d.ts +67 -0
- package/vendor/converters/officeparser/dist/parsers/PdfParser.js +848 -0
- package/vendor/converters/officeparser/dist/parsers/PowerPointParser.d.ts +32 -0
- package/vendor/converters/officeparser/dist/parsers/PowerPointParser.js +950 -0
- package/vendor/converters/officeparser/dist/parsers/RtfParser.d.ts +187 -0
- package/vendor/converters/officeparser/dist/parsers/RtfParser.js +1801 -0
- package/vendor/converters/officeparser/dist/parsers/WordParser.d.ts +79 -0
- package/vendor/converters/officeparser/dist/parsers/WordParser.js +1177 -0
- package/vendor/converters/officeparser/dist/sbom.cdx.json +1763 -0
- package/vendor/converters/officeparser/dist/types.d.ts +2507 -0
- package/vendor/converters/officeparser/dist/types.js +107 -0
- package/vendor/converters/officeparser/dist/utils/astUtils.d.ts +16 -0
- package/vendor/converters/officeparser/dist/utils/astUtils.js +33 -0
- package/vendor/converters/officeparser/dist/utils/chartUtils.d.ts +6 -0
- package/vendor/converters/officeparser/dist/utils/chartUtils.js +257 -0
- package/vendor/converters/officeparser/dist/utils/configUtils.d.ts +44 -0
- package/vendor/converters/officeparser/dist/utils/configUtils.js +315 -0
- package/vendor/converters/officeparser/dist/utils/dateUtils.d.ts +17 -0
- package/vendor/converters/officeparser/dist/utils/dateUtils.js +69 -0
- package/vendor/converters/officeparser/dist/utils/envUtils.d.ts +29 -0
- package/vendor/converters/officeparser/dist/utils/envUtils.js +152 -0
- package/vendor/converters/officeparser/dist/utils/errorUtils.d.ts +72 -0
- package/vendor/converters/officeparser/dist/utils/errorUtils.js +245 -0
- package/vendor/converters/officeparser/dist/utils/imageUtils.d.ts +66 -0
- package/vendor/converters/officeparser/dist/utils/imageUtils.js +133 -0
- package/vendor/converters/officeparser/dist/utils/mathUtils.d.ts +42 -0
- package/vendor/converters/officeparser/dist/utils/mathUtils.js +385 -0
- package/vendor/converters/officeparser/dist/utils/moduleLoader.d.ts +18 -0
- package/vendor/converters/officeparser/dist/utils/moduleLoader.js +106 -0
- package/vendor/converters/officeparser/dist/utils/ocrUtils.d.ts +42 -0
- package/vendor/converters/officeparser/dist/utils/ocrUtils.js +428 -0
- package/vendor/converters/officeparser/dist/utils/sanitize.d.ts +148 -0
- package/vendor/converters/officeparser/dist/utils/sanitize.js +344 -0
- package/vendor/converters/officeparser/dist/utils/sheetUtils.d.ts +7 -0
- package/vendor/converters/officeparser/dist/utils/sheetUtils.js +35 -0
- package/vendor/converters/officeparser/dist/utils/styleMapper.d.ts +36 -0
- package/vendor/converters/officeparser/dist/utils/styleMapper.js +224 -0
- package/vendor/converters/officeparser/dist/utils/xmlUtils.d.ts +163 -0
- package/vendor/converters/officeparser/dist/utils/xmlUtils.js +461 -0
- package/vendor/converters/officeparser/dist/utils/zipUtils.d.ts +134 -0
- package/vendor/converters/officeparser/dist/utils/zipUtils.js +337 -0
- package/vendor/converters/officeparser/package.json +147 -0
- package/vendor/converters/upstream-manifest.json +124 -0
- package/vendor/dependency-fixes/README.md +77 -0
- package/vendor/dependency-fixes/vendor-converters.py +83 -0
- package/browser-extension/artifacts/gno-browser-clipper-v1.45.1.zip +0 -0
- package/browser-extension/artifacts/gno-browser-clipper-v1.45.1.zip.sha256 +0 -1
- package/browser-extension/dist/chunk-627emwpj.js +0 -75
- /package/browser-extension/dist/{chunk-ydfx5d7p.css → chunk-z74y8n8c.css} +0 -0
package/src/embed/retry.ts
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
import type { EmbeddingPort } from "../llm/types";
|
|
2
2
|
import type { StoreResult } from "../store/types";
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
BacklogItem,
|
|
5
|
+
VectorIndexPort,
|
|
6
|
+
VectorRow,
|
|
7
|
+
VectorStatsPort,
|
|
8
|
+
} from "../store/vector";
|
|
4
9
|
|
|
5
10
|
import { isSqliteLockContention } from "../core/file-lock";
|
|
11
|
+
import { assertInferenceActive } from "../llm/inference-scope";
|
|
6
12
|
import { formatDocForEmbedding } from "../pipeline/contextual";
|
|
13
|
+
import { err, ok } from "../store/types";
|
|
14
|
+
import { getVectorStatsDatabase } from "../store/vector/stats";
|
|
7
15
|
import { embedTextsWithRecovery } from "./batch";
|
|
8
16
|
|
|
9
17
|
export const MAX_EMBED_CHUNK_ATTEMPTS = 2;
|
|
@@ -42,12 +50,21 @@ export interface EmbedStoreBatchResult {
|
|
|
42
50
|
// src/cli/commands/index-cmd.ts) must surface contentionErrors in their
|
|
43
51
|
// summary and exit non-zero — the integrator wires that.
|
|
44
52
|
|
|
45
|
-
export function chunkRetryKey(
|
|
53
|
+
export function chunkRetryKey(
|
|
54
|
+
item: Pick<BacklogItem, "mirrorHash" | "seq"> & {
|
|
55
|
+
documentId?: number;
|
|
56
|
+
inputHash?: string;
|
|
57
|
+
}
|
|
58
|
+
) {
|
|
59
|
+
if (item.documentId !== undefined && item.inputHash !== undefined) {
|
|
60
|
+
return `${item.documentId}\0${item.mirrorHash}\0${item.seq}\0${item.inputHash}`;
|
|
61
|
+
}
|
|
46
62
|
return `${item.mirrorHash}\0${item.seq}`;
|
|
47
63
|
}
|
|
48
64
|
|
|
49
65
|
export function addUniqueSamples(target: string[], samples: string[]): void {
|
|
50
66
|
for (const sample of samples) {
|
|
67
|
+
assertInferenceActive();
|
|
51
68
|
if (target.length >= MAX_EMBED_FAILURE_SAMPLES) {
|
|
52
69
|
break;
|
|
53
70
|
}
|
|
@@ -127,6 +144,7 @@ export async function upsertVectorsWithContentionRetry(
|
|
|
127
144
|
vectors: VectorRow[],
|
|
128
145
|
delays?: number[]
|
|
129
146
|
): Promise<StoreResult<void>> {
|
|
147
|
+
assertInferenceActive();
|
|
130
148
|
let storeResult = await vectorIndex.upsertVectors(vectors);
|
|
131
149
|
let attempts = 1;
|
|
132
150
|
while (
|
|
@@ -138,6 +156,7 @@ export async function upsertVectorsWithContentionRetry(
|
|
|
138
156
|
if (delayMs > 0) {
|
|
139
157
|
await Bun.sleep(delayMs);
|
|
140
158
|
}
|
|
159
|
+
assertInferenceActive();
|
|
141
160
|
storeResult = await vectorIndex.upsertVectors(vectors);
|
|
142
161
|
attempts += 1;
|
|
143
162
|
}
|
|
@@ -150,10 +169,22 @@ export async function embedAndStoreBatch(params: {
|
|
|
150
169
|
items: BacklogItem[];
|
|
151
170
|
modelUri: string;
|
|
152
171
|
embedFingerprint: string;
|
|
172
|
+
statsPort?: VectorStatsPort;
|
|
173
|
+
identityStillCurrent?: () => boolean;
|
|
153
174
|
/** Test seam: override contention-retry delays in milliseconds. */
|
|
154
175
|
delays?: number[];
|
|
155
176
|
}): Promise<EmbedStoreBatchResult> {
|
|
156
177
|
const { embedPort, vectorIndex, items, modelUri, embedFingerprint } = params;
|
|
178
|
+
const db = params.statsPort && getVectorStatsDatabase(params.statsPort);
|
|
179
|
+
if (db && !vectorIndex.upsertVectorsChecked)
|
|
180
|
+
return {
|
|
181
|
+
embedded: 0,
|
|
182
|
+
errors: items.length,
|
|
183
|
+
contentionErrors: 0,
|
|
184
|
+
retryItems: [],
|
|
185
|
+
errorSamples: ["Atomic vector checkpoint unavailable"],
|
|
186
|
+
batchFailed: false,
|
|
187
|
+
};
|
|
157
188
|
const embedResult = await embedTextsWithRecovery(
|
|
158
189
|
embedPort,
|
|
159
190
|
items.map((item) =>
|
|
@@ -180,6 +211,7 @@ export async function embedAndStoreBatch(params: {
|
|
|
180
211
|
const vectors: VectorRow[] = [];
|
|
181
212
|
const retryItems: BacklogItem[] = [];
|
|
182
213
|
for (const [idx, item] of items.entries()) {
|
|
214
|
+
assertInferenceActive();
|
|
183
215
|
const embedding = embedResult.value.vectors[idx];
|
|
184
216
|
if (!embedding) {
|
|
185
217
|
retryItems.push(item);
|
|
@@ -207,8 +239,75 @@ export async function embedAndStoreBatch(params: {
|
|
|
207
239
|
};
|
|
208
240
|
}
|
|
209
241
|
|
|
242
|
+
let written = 0;
|
|
243
|
+
let committedRows: VectorRow[] = [];
|
|
244
|
+
const itemsByKey = new Map(items.map((item) => [chunkRetryKey(item), item]));
|
|
245
|
+
const checkpoint = (rows: VectorRow[]): VectorRow[] => {
|
|
246
|
+
assertInferenceActive();
|
|
247
|
+
if (params.identityStillCurrent && !params.identityStillCurrent())
|
|
248
|
+
return [];
|
|
249
|
+
if (!db) return rows;
|
|
250
|
+
// A concurrently activated verified partition forbids a late legacy write.
|
|
251
|
+
if (
|
|
252
|
+
db
|
|
253
|
+
.query(
|
|
254
|
+
"SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'vector_partitions'"
|
|
255
|
+
)
|
|
256
|
+
.get() &&
|
|
257
|
+
db
|
|
258
|
+
.query(
|
|
259
|
+
"SELECT 1 FROM vector_partitions WHERE model = ? AND state = 'active' AND activated_epoch IS NOT NULL LIMIT 1"
|
|
260
|
+
)
|
|
261
|
+
.get(modelUri)
|
|
262
|
+
)
|
|
263
|
+
return [];
|
|
264
|
+
return rows.filter((row) => {
|
|
265
|
+
const item = itemsByKey.get(chunkRetryKey(row));
|
|
266
|
+
const current = db
|
|
267
|
+
.query<{ text: string; title: string | null }, [string, number]>(`
|
|
268
|
+
SELECT c.text, d.title FROM content_chunks c
|
|
269
|
+
JOIN documents d ON d.mirror_hash = c.mirror_hash AND d.active = 1
|
|
270
|
+
WHERE c.mirror_hash = ? AND c.seq = ? ORDER BY d.id LIMIT 1
|
|
271
|
+
`)
|
|
272
|
+
.get(row.mirrorHash, row.seq);
|
|
273
|
+
return (
|
|
274
|
+
!!item &&
|
|
275
|
+
!!current &&
|
|
276
|
+
formatDocForEmbedding(
|
|
277
|
+
current.text,
|
|
278
|
+
current.title ?? undefined,
|
|
279
|
+
modelUri
|
|
280
|
+
) ===
|
|
281
|
+
formatDocForEmbedding(item.text, item.title ?? undefined, modelUri)
|
|
282
|
+
);
|
|
283
|
+
});
|
|
284
|
+
};
|
|
210
285
|
const storeResult = await upsertVectorsWithContentionRetry(
|
|
211
|
-
|
|
286
|
+
{
|
|
287
|
+
upsertVectors: async (rows) => {
|
|
288
|
+
if (vectorIndex.upsertVectorsChecked) {
|
|
289
|
+
const result = await vectorIndex.upsertVectorsChecked(
|
|
290
|
+
rows,
|
|
291
|
+
(candidates) => {
|
|
292
|
+
committedRows = checkpoint(candidates);
|
|
293
|
+
return committedRows;
|
|
294
|
+
}
|
|
295
|
+
);
|
|
296
|
+
if (!result.ok) return result;
|
|
297
|
+
written = result.value;
|
|
298
|
+
return ok(undefined);
|
|
299
|
+
}
|
|
300
|
+
if (db)
|
|
301
|
+
return err("INVALID_INPUT", "Atomic vector checkpoint unavailable");
|
|
302
|
+
const valid = checkpoint(rows);
|
|
303
|
+
const result = await vectorIndex.upsertVectors(valid);
|
|
304
|
+
if (result.ok) {
|
|
305
|
+
written = valid.length;
|
|
306
|
+
committedRows = valid;
|
|
307
|
+
}
|
|
308
|
+
return result;
|
|
309
|
+
},
|
|
310
|
+
},
|
|
212
311
|
vectors,
|
|
213
312
|
params.delays
|
|
214
313
|
);
|
|
@@ -237,11 +336,20 @@ export async function embedAndStoreBatch(params: {
|
|
|
237
336
|
};
|
|
238
337
|
}
|
|
239
338
|
|
|
339
|
+
const vectorKeys = new Set(vectors.map((row) => chunkRetryKey(row)));
|
|
340
|
+
const committedKeys = new Set(committedRows.map((row) => chunkRetryKey(row)));
|
|
240
341
|
return {
|
|
241
|
-
embedded:
|
|
342
|
+
embedded: written,
|
|
242
343
|
errors: 0,
|
|
243
344
|
contentionErrors: 0,
|
|
244
|
-
retryItems
|
|
345
|
+
retryItems: [
|
|
346
|
+
...retryItems,
|
|
347
|
+
...items.filter(
|
|
348
|
+
(item) =>
|
|
349
|
+
vectorKeys.has(chunkRetryKey(item)) &&
|
|
350
|
+
!committedKeys.has(chunkRetryKey(item))
|
|
351
|
+
),
|
|
352
|
+
],
|
|
245
353
|
errorSamples: embedResult.value.failureSamples,
|
|
246
354
|
suggestion: embedResult.value.retrySuggestion,
|
|
247
355
|
batchFailed: embedResult.value.batchFailed,
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import type { StoreResult } from "../store/types";
|
|
2
|
+
import type { VectorVariantStore } from "../store/vector/variants";
|
|
3
|
+
import type { EmbedBacklogDeps, EmbedBacklogResult } from "./backlog";
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
assertInferenceActive,
|
|
7
|
+
isBackgroundInference,
|
|
8
|
+
} from "../llm/inference-scope";
|
|
9
|
+
import { err, ok } from "../store/types";
|
|
10
|
+
import { getVectorStatsDatabase } from "../store/vector/stats";
|
|
11
|
+
import { variantBacklogPage } from "./variant-plan";
|
|
12
|
+
import { embedVariantBatch } from "./variant-retry";
|
|
13
|
+
|
|
14
|
+
/** One bounded owner pass plus one retry; mutations stay durably pending for the next pass. */
|
|
15
|
+
export async function embedVariantBacklog(
|
|
16
|
+
deps: EmbedBacklogDeps,
|
|
17
|
+
store: VectorVariantStore
|
|
18
|
+
): Promise<StoreResult<EmbedBacklogResult>> {
|
|
19
|
+
const identityStillCurrent = deps.identityStillCurrent;
|
|
20
|
+
if (!identityStillCurrent)
|
|
21
|
+
return err(
|
|
22
|
+
"INVALID_INPUT",
|
|
23
|
+
"Variant embedding requires an effective runtime identity check"
|
|
24
|
+
);
|
|
25
|
+
const db = getVectorStatsDatabase(deps.statsPort);
|
|
26
|
+
if (deps.collection && !db)
|
|
27
|
+
return err(
|
|
28
|
+
"INVALID_INPUT",
|
|
29
|
+
"Collection-scoped variant backlog requires document ownership storage"
|
|
30
|
+
);
|
|
31
|
+
const background = isBackgroundInference();
|
|
32
|
+
const batchSize = background
|
|
33
|
+
? Math.min(deps.batchSize ?? 32, 32)
|
|
34
|
+
: (deps.batchSize ?? 32);
|
|
35
|
+
if (!Number.isSafeInteger(batchSize) || batchSize < 1)
|
|
36
|
+
return err("INVALID_INPUT", "Invalid embedding batch size");
|
|
37
|
+
const total = { embedded: 0, errors: 0, contentionErrors: 0 };
|
|
38
|
+
let after: { documentId: number; seq: number } | undefined;
|
|
39
|
+
try {
|
|
40
|
+
while (identityStillCurrent()) {
|
|
41
|
+
assertInferenceActive();
|
|
42
|
+
const pending = variantBacklogPage(deps, store, batchSize, after);
|
|
43
|
+
if (!pending.length) break;
|
|
44
|
+
const last = pending.at(-1)!;
|
|
45
|
+
after = { documentId: last.documentId, seq: last.seq };
|
|
46
|
+
const owners = deps.collection
|
|
47
|
+
? pending.filter(
|
|
48
|
+
(owner) =>
|
|
49
|
+
!!db!
|
|
50
|
+
.query(
|
|
51
|
+
"SELECT 1 FROM documents WHERE id = ? AND collection = ? AND active = 1"
|
|
52
|
+
)
|
|
53
|
+
.get(owner.documentId, deps.collection!)
|
|
54
|
+
)
|
|
55
|
+
: pending;
|
|
56
|
+
let result = await embedVariantBatch({
|
|
57
|
+
store,
|
|
58
|
+
embedPort: deps.embedPort,
|
|
59
|
+
owners,
|
|
60
|
+
identityStillCurrent,
|
|
61
|
+
force: deps.force,
|
|
62
|
+
});
|
|
63
|
+
total.embedded += result.embedded;
|
|
64
|
+
total.errors += result.errors;
|
|
65
|
+
total.contentionErrors += result.contentionErrors;
|
|
66
|
+
if (background) total.errors += result.retryOwners.length;
|
|
67
|
+
if (!background && result.retryOwners.length) {
|
|
68
|
+
result = await embedVariantBatch({
|
|
69
|
+
store,
|
|
70
|
+
embedPort: deps.embedPort,
|
|
71
|
+
owners: result.retryOwners,
|
|
72
|
+
identityStillCurrent,
|
|
73
|
+
force: deps.force,
|
|
74
|
+
});
|
|
75
|
+
total.embedded += result.embedded;
|
|
76
|
+
total.errors += result.errors + result.retryOwners.length;
|
|
77
|
+
total.contentionErrors += result.contentionErrors;
|
|
78
|
+
}
|
|
79
|
+
deps.onProgress?.(total.embedded, total.errors);
|
|
80
|
+
if (background) await Bun.sleep(0);
|
|
81
|
+
}
|
|
82
|
+
assertInferenceActive();
|
|
83
|
+
// Capture the epoch before checking completeness; activate rechecks under write lock.
|
|
84
|
+
const epoch = store.epoch();
|
|
85
|
+
if (identityStillCurrent() && !store.pending({ limit: 1 }).length) {
|
|
86
|
+
try {
|
|
87
|
+
if (!store.isActive()) store.syncIndex();
|
|
88
|
+
if (!identityStillCurrent()) return ok(total);
|
|
89
|
+
store.activate(epoch);
|
|
90
|
+
} catch (cause) {
|
|
91
|
+
return ok({
|
|
92
|
+
...total,
|
|
93
|
+
syncError: cause instanceof Error ? cause.message : String(cause),
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
assertInferenceActive();
|
|
98
|
+
return ok(total);
|
|
99
|
+
} catch (cause) {
|
|
100
|
+
return err(
|
|
101
|
+
"QUERY_FAILED",
|
|
102
|
+
cause instanceof Error ? cause.message : String(cause)
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { VectorVariantStore } from "../store/vector/variants";
|
|
2
|
+
import type { EmbedBacklogDeps } from "./backlog";
|
|
3
|
+
|
|
4
|
+
import { getVectorStatsDatabase } from "../store/vector/stats";
|
|
5
|
+
|
|
6
|
+
/** Counts and execution enumerate the same exact current owner partition. */
|
|
7
|
+
export function variantBacklogPage(
|
|
8
|
+
deps: EmbedBacklogDeps,
|
|
9
|
+
store: VectorVariantStore,
|
|
10
|
+
limit: number,
|
|
11
|
+
after?: { documentId: number; seq: number }
|
|
12
|
+
) {
|
|
13
|
+
if (!deps.force) return store.pending({ limit, after });
|
|
14
|
+
const db = getVectorStatsDatabase(deps.statsPort);
|
|
15
|
+
if (!db)
|
|
16
|
+
throw new Error("Forced variants require document ownership storage");
|
|
17
|
+
const rows = db
|
|
18
|
+
.query<
|
|
19
|
+
{ documentId: number; seq: number },
|
|
20
|
+
[number, number, number, number]
|
|
21
|
+
>(`
|
|
22
|
+
SELECT d.id AS documentId, c.seq FROM documents d
|
|
23
|
+
JOIN content_chunks c ON c.mirror_hash = d.mirror_hash
|
|
24
|
+
WHERE d.active = 1 AND (d.id > ? OR (d.id = ? AND c.seq > ?))
|
|
25
|
+
ORDER BY d.id, c.seq LIMIT ?
|
|
26
|
+
`)
|
|
27
|
+
.all(
|
|
28
|
+
after?.documentId ?? -1,
|
|
29
|
+
after?.documentId ?? -1,
|
|
30
|
+
after?.seq ?? -1,
|
|
31
|
+
limit
|
|
32
|
+
);
|
|
33
|
+
return rows.flatMap((row) => {
|
|
34
|
+
const owner = store.current(row.documentId, row.seq);
|
|
35
|
+
return owner ? [owner] : [];
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function countVariantBacklog(deps: EmbedBacklogDeps): number {
|
|
40
|
+
const store = deps.variantStore;
|
|
41
|
+
if (!store) throw new Error("Verified variant identity required");
|
|
42
|
+
const db = getVectorStatsDatabase(deps.statsPort);
|
|
43
|
+
let after: { documentId: number; seq: number } | undefined;
|
|
44
|
+
let count = 0;
|
|
45
|
+
while (true) {
|
|
46
|
+
const owners = variantBacklogPage(deps, store, 256, after);
|
|
47
|
+
if (!owners.length) return count;
|
|
48
|
+
for (const owner of owners) {
|
|
49
|
+
if (
|
|
50
|
+
!deps.collection ||
|
|
51
|
+
db
|
|
52
|
+
?.query(
|
|
53
|
+
"SELECT 1 FROM documents WHERE id = ? AND collection = ? AND active = 1"
|
|
54
|
+
)
|
|
55
|
+
.get(owner.documentId, deps.collection)
|
|
56
|
+
)
|
|
57
|
+
count += 1;
|
|
58
|
+
}
|
|
59
|
+
const last = owners.at(-1)!;
|
|
60
|
+
after = { documentId: last.documentId, seq: last.seq };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import type { EmbeddingPort } from "../llm/types";
|
|
2
|
+
import type { VectorOwnerInput } from "../store/vector/types";
|
|
3
|
+
import type { VectorVariantStore } from "../store/vector/variants";
|
|
4
|
+
|
|
5
|
+
import { err, ok } from "../store/types";
|
|
6
|
+
import { embedTextsWithRecovery } from "./batch";
|
|
7
|
+
import {
|
|
8
|
+
chunkRetryKey,
|
|
9
|
+
isUpsertLockContention,
|
|
10
|
+
upsertVectorsWithContentionRetry,
|
|
11
|
+
} from "./retry";
|
|
12
|
+
|
|
13
|
+
function isCurrent(
|
|
14
|
+
store: VectorVariantStore,
|
|
15
|
+
owner: VectorOwnerInput
|
|
16
|
+
): boolean {
|
|
17
|
+
const current = store.current(owner.documentId, owner.seq);
|
|
18
|
+
return (
|
|
19
|
+
current?.mirrorHash === owner.mirrorHash &&
|
|
20
|
+
current.inputHash === owner.inputHash &&
|
|
21
|
+
current.formattedInput === owner.formattedInput
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Exact inputs are inferred once; successful current owners checkpoint atomically. */
|
|
26
|
+
export async function embedVariantBatch(params: {
|
|
27
|
+
store: VectorVariantStore;
|
|
28
|
+
embedPort: EmbeddingPort;
|
|
29
|
+
owners: VectorOwnerInput[];
|
|
30
|
+
identityStillCurrent: () => boolean;
|
|
31
|
+
delays?: number[];
|
|
32
|
+
force?: boolean;
|
|
33
|
+
}): Promise<{
|
|
34
|
+
embedded: number;
|
|
35
|
+
errors: number;
|
|
36
|
+
contentionErrors: number;
|
|
37
|
+
retryOwners: VectorOwnerInput[];
|
|
38
|
+
}> {
|
|
39
|
+
const { store, embedPort, identityStillCurrent } = params;
|
|
40
|
+
const empty = {
|
|
41
|
+
embedded: 0,
|
|
42
|
+
errors: 0,
|
|
43
|
+
contentionErrors: 0,
|
|
44
|
+
retryOwners: [] as VectorOwnerInput[],
|
|
45
|
+
};
|
|
46
|
+
if (!identityStillCurrent()) return empty;
|
|
47
|
+
const owners = [
|
|
48
|
+
...new Map(
|
|
49
|
+
params.owners.map((owner) => [chunkRetryKey(owner), owner])
|
|
50
|
+
).values(),
|
|
51
|
+
].filter((owner) => isCurrent(store, owner));
|
|
52
|
+
const inputs = new Map<string, string>();
|
|
53
|
+
const reusable = new Set<string>();
|
|
54
|
+
for (const owner of owners) {
|
|
55
|
+
if (!params.force && store.reusable(owner)) reusable.add(owner.inputHash);
|
|
56
|
+
else inputs.set(owner.inputHash, owner.formattedInput);
|
|
57
|
+
}
|
|
58
|
+
const keys = [...inputs.keys()];
|
|
59
|
+
const result = await embedTextsWithRecovery(embedPort, [...inputs.values()]);
|
|
60
|
+
if (!identityStillCurrent()) return empty;
|
|
61
|
+
const current = owners.filter((owner) => isCurrent(store, owner));
|
|
62
|
+
const vectors = new Map<string, Float32Array>();
|
|
63
|
+
if (result.ok) {
|
|
64
|
+
for (const [index, key] of keys.entries()) {
|
|
65
|
+
const vector = result.value.vectors[index];
|
|
66
|
+
if (vector) vectors.set(key, new Float32Array(vector));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const retryOwners = current.filter(
|
|
70
|
+
(owner) => !reusable.has(owner.inputHash) && !vectors.has(owner.inputHash)
|
|
71
|
+
);
|
|
72
|
+
const rows = current
|
|
73
|
+
.filter(
|
|
74
|
+
(owner) => reusable.has(owner.inputHash) || vectors.has(owner.inputHash)
|
|
75
|
+
)
|
|
76
|
+
.map((owner) => ({ owner, embedding: vectors.get(owner.inputHash) }));
|
|
77
|
+
if (!rows.length) return { ...empty, retryOwners };
|
|
78
|
+
let written = 0;
|
|
79
|
+
const persisted = await upsertVectorsWithContentionRetry(
|
|
80
|
+
{
|
|
81
|
+
upsertVectors: () => {
|
|
82
|
+
try {
|
|
83
|
+
// A contention wait may allow document or runtime mutations. Revalidate each attempt.
|
|
84
|
+
if (!identityStillCurrent()) return Promise.resolve(ok(undefined));
|
|
85
|
+
const valid = rows.filter(({ owner }) => isCurrent(store, owner));
|
|
86
|
+
store.write(valid);
|
|
87
|
+
written = valid.length;
|
|
88
|
+
return Promise.resolve(ok(undefined));
|
|
89
|
+
} catch (cause) {
|
|
90
|
+
return Promise.resolve(
|
|
91
|
+
err(
|
|
92
|
+
"QUERY_FAILED",
|
|
93
|
+
cause instanceof Error ? cause.message : String(cause),
|
|
94
|
+
cause
|
|
95
|
+
)
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
[],
|
|
101
|
+
params.delays
|
|
102
|
+
);
|
|
103
|
+
if (!persisted.ok)
|
|
104
|
+
return {
|
|
105
|
+
...empty,
|
|
106
|
+
retryOwners,
|
|
107
|
+
errors: isUpsertLockContention(persisted.error) ? 0 : rows.length,
|
|
108
|
+
contentionErrors: isUpsertLockContention(persisted.error)
|
|
109
|
+
? rows.length
|
|
110
|
+
: 0,
|
|
111
|
+
};
|
|
112
|
+
return { ...empty, embedded: written, retryOwners };
|
|
113
|
+
}
|