knolo-core 0.2.3 → 3.1.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/dist/builder.js CHANGED
@@ -1,48 +1,70 @@
1
1
  /*
2
2
  * builder.ts
3
3
  *
4
- * Build `.knolo` packs from input docs. Now persists optional headings/docIds
5
- * and stores avgBlockLen in meta for faster/easier normalization at query-time.
4
+ * Build `.knolo` packs from input docs. Persists headings/docIds/token lengths
5
+ * and stores avgBlockLen in meta for stable query-time normalization.
6
6
  */
7
7
  import { buildIndex } from './indexer.js';
8
8
  import { tokenize } from './tokenize.js';
9
9
  import { getTextEncoder } from './utils/utf8.js';
10
- export async function buildPack(docs) {
10
+ import { encodeScaleF16, quantizeEmbeddingInt8L2Norm } from './semantic.js';
11
+ import { validateAgentRegistry } from './agent.js';
12
+ export async function buildPack(docs, opts = {}) {
13
+ const normalizedDocs = validateDocs(docs);
11
14
  // Prepare blocks (strip MD) and carry heading/docId for optional boosts.
12
- const blocks = docs.map((d, i) => ({
15
+ const blocks = normalizedDocs.map((d, i) => ({
13
16
  id: i,
14
17
  text: stripMd(d.text),
15
18
  heading: d.heading,
16
19
  }));
17
20
  // Build index
18
21
  const { lexicon, postings } = buildIndex(blocks);
19
- // Compute avg token length once (store in meta)
20
- const totalTokens = blocks.reduce((sum, b) => sum + tokenize(b.text).length, 0);
22
+ const blockTokenLens = blocks.map((b) => tokenize(b.text).length);
23
+ const totalTokens = blockTokenLens.reduce((sum, len) => sum + len, 0);
21
24
  const avgBlockLen = blocks.length ? totalTokens / blocks.length : 1;
25
+ const agents = normalizeAgents(opts.agents);
22
26
  const meta = {
23
- version: 2,
27
+ version: 3,
24
28
  stats: {
25
- docs: docs.length,
29
+ docs: normalizedDocs.length,
26
30
  blocks: blocks.length,
27
31
  terms: lexicon.length,
28
32
  avgBlockLen,
29
33
  },
34
+ ...(agents ? { agents } : {}),
30
35
  };
31
- // Persist blocks as objects to optionally carry heading/docId
36
+ // Persist blocks as objects to optionally carry heading/docId/token length.
32
37
  const blocksPayload = blocks.map((b, i) => ({
33
38
  text: b.text,
34
39
  heading: b.heading ?? null,
35
- docId: docs[i]?.id ?? null,
40
+ docId: normalizedDocs[i]?.id ?? null,
41
+ namespace: normalizedDocs[i]?.namespace ?? null,
42
+ len: blockTokenLens[i] ?? 0,
36
43
  }));
37
44
  // Encode sections
38
45
  const enc = getTextEncoder();
39
46
  const metaBytes = enc.encode(JSON.stringify(meta));
40
47
  const lexBytes = enc.encode(JSON.stringify(lexicon));
41
48
  const blocksBytes = enc.encode(JSON.stringify(blocksPayload));
42
- const totalLength = 4 + metaBytes.length +
43
- 4 + lexBytes.length +
44
- 4 + postings.length * 4 +
45
- 4 + blocksBytes.length;
49
+ const semanticEnabled = Boolean(opts.semantic?.enabled);
50
+ const semanticSection = semanticEnabled && opts.semantic
51
+ ? buildSemanticSection(blocks.length, opts.semantic)
52
+ : undefined;
53
+ const semBytes = semanticSection
54
+ ? enc.encode(JSON.stringify(semanticSection.semJson))
55
+ : undefined;
56
+ const semBlob = semanticSection?.semBlob;
57
+ const totalLength = 4 +
58
+ metaBytes.length +
59
+ 4 +
60
+ lexBytes.length +
61
+ 4 +
62
+ postings.length * 4 +
63
+ 4 +
64
+ blocksBytes.length +
65
+ (semanticEnabled && semBytes && semBlob
66
+ ? 4 + semBytes.length + 4 + semBlob.length
67
+ : 0);
46
68
  const out = new Uint8Array(totalLength);
47
69
  const dv = new DataView(out.buffer);
48
70
  let offset = 0;
@@ -67,8 +89,100 @@ export async function buildPack(docs) {
67
89
  dv.setUint32(offset, blocksBytes.length, true);
68
90
  offset += 4;
69
91
  out.set(blocksBytes, offset);
92
+ offset += blocksBytes.length;
93
+ if (semanticEnabled && semBytes && semBlob) {
94
+ dv.setUint32(offset, semBytes.length, true);
95
+ offset += 4;
96
+ out.set(semBytes, offset);
97
+ offset += semBytes.length;
98
+ dv.setUint32(offset, semBlob.length, true);
99
+ offset += 4;
100
+ out.set(semBlob, offset);
101
+ }
70
102
  return out;
71
103
  }
104
+ function normalizeAgents(input) {
105
+ if (!input)
106
+ return undefined;
107
+ const registry = Array.isArray(input)
108
+ ? { version: 1, agents: input }
109
+ : input;
110
+ validateAgentRegistry(registry);
111
+ return registry;
112
+ }
113
+ function buildSemanticSection(blockCount, semantic) {
114
+ const { embeddings } = semantic;
115
+ if (!Array.isArray(embeddings) || embeddings.length !== blockCount) {
116
+ throw new Error(`semantic.embeddings must be provided with one embedding per block (expected ${blockCount}).`);
117
+ }
118
+ const quantizationType = semantic.quantization?.type ?? 'int8_l2norm';
119
+ if (quantizationType !== 'int8_l2norm') {
120
+ throw new Error(`Unsupported semantic quantization type: ${quantizationType}`);
121
+ }
122
+ const dims = embeddings[0]?.length ?? 0;
123
+ if (!dims)
124
+ throw new Error('semantic.embeddings must contain vectors with non-zero dimensions.');
125
+ const vecs = new Int8Array(embeddings.length * dims);
126
+ const scales = new Uint16Array(embeddings.length);
127
+ for (let i = 0; i < embeddings.length; i++) {
128
+ const embedding = embeddings[i];
129
+ if (!(embedding instanceof Float32Array)) {
130
+ throw new Error(`semantic.embeddings[${i}] must be a Float32Array.`);
131
+ }
132
+ if (embedding.length !== dims) {
133
+ throw new Error(`semantic.embeddings[${i}] dims mismatch: expected ${dims}, got ${embedding.length}.`);
134
+ }
135
+ const { q, scale } = quantizeEmbeddingInt8L2Norm(embedding);
136
+ vecs.set(q, i * dims);
137
+ scales[i] = encodeScaleF16(scale);
138
+ }
139
+ const vecByteOffset = 0;
140
+ const vecByteLength = vecs.byteLength;
141
+ const scalesByteOffset = vecByteLength;
142
+ const scalesByteLength = scales.byteLength;
143
+ const semBlob = new Uint8Array(vecByteLength + scalesByteLength);
144
+ semBlob.set(new Uint8Array(vecs.buffer, vecs.byteOffset, vecByteLength), vecByteOffset);
145
+ semBlob.set(new Uint8Array(scales.buffer, scales.byteOffset, scalesByteLength), scalesByteOffset);
146
+ const semJson = {
147
+ version: 1,
148
+ modelId: semantic.modelId,
149
+ dims,
150
+ encoding: 'int8_l2norm',
151
+ perVectorScale: true,
152
+ blocks: {
153
+ vectors: { byteOffset: vecByteOffset, length: vecs.length },
154
+ scales: {
155
+ byteOffset: scalesByteOffset,
156
+ length: scales.length,
157
+ encoding: 'float16',
158
+ },
159
+ },
160
+ };
161
+ return { semJson, semBlob };
162
+ }
163
+ function validateDocs(docs) {
164
+ if (!Array.isArray(docs)) {
165
+ throw new Error('buildPack expects an array of docs: [{ text, id?, heading?, namespace? }, ...]');
166
+ }
167
+ return docs.map((doc, i) => {
168
+ if (!doc || typeof doc !== 'object') {
169
+ throw new Error(`Invalid doc at index ${i}: expected an object with a string "text" field.`);
170
+ }
171
+ if (typeof doc.text !== 'string' || !doc.text.trim()) {
172
+ throw new Error(`Invalid doc at index ${i}: "text" must be a non-empty string.`);
173
+ }
174
+ if (doc.id !== undefined && typeof doc.id !== 'string') {
175
+ throw new Error(`Invalid doc at index ${i}: "id" must be a string when provided.`);
176
+ }
177
+ if (doc.heading !== undefined && typeof doc.heading !== 'string') {
178
+ throw new Error(`Invalid doc at index ${i}: "heading" must be a string when provided.`);
179
+ }
180
+ if (doc.namespace !== undefined && typeof doc.namespace !== 'string') {
181
+ throw new Error(`Invalid doc at index ${i}: "namespace" must be a string when provided.`);
182
+ }
183
+ return doc;
184
+ });
185
+ }
72
186
  /** Strip Markdown syntax with lightweight regexes (no deps). */
73
187
  function stripMd(md) {
74
188
  let text = md.replace(/```[\s\S]*?```/g, ' ');
package/dist/index.d.ts CHANGED
@@ -1,7 +1,11 @@
1
- export { mountPack } from './pack.js';
2
- export { query } from './query.js';
1
+ export { mountPack, hasSemantic } from './pack.js';
2
+ export { query, lexConfidence, validateQueryOptions, validateSemanticQueryOptions, } from './query.js';
3
3
  export { makeContextPatch } from './patch.js';
4
4
  export { buildPack } from './builder.js';
5
+ export { quantizeEmbeddingInt8L2Norm, encodeScaleF16, decodeScaleF16, } from './semantic.js';
6
+ export { listAgents, getAgent, resolveAgent, buildSystemPrompt, isToolAllowed, assertToolAllowed, validateAgentRegistry, validateAgentDefinition, } from './agent.js';
5
7
  export type { MountOptions, PackMeta, Pack } from './pack.js';
6
8
  export type { QueryOptions, Hit } from './query.js';
7
9
  export type { ContextPatch } from './patch.js';
10
+ export type { BuildInputDoc, BuildPackOptions } from './builder.js';
11
+ export type { AgentPromptTemplate, AgentToolPolicy, AgentRetrievalDefaults, AgentDefinitionV1, AgentRegistry, ResolveAgentInput, ResolvedAgent, } from './agent.js';
package/dist/index.js CHANGED
@@ -1,5 +1,7 @@
1
1
  // src/index.ts
2
- export { mountPack } from './pack.js';
3
- export { query } from './query.js';
2
+ export { mountPack, hasSemantic } from './pack.js';
3
+ export { query, lexConfidence, validateQueryOptions, validateSemanticQueryOptions, } from './query.js';
4
4
  export { makeContextPatch } from './patch.js';
5
5
  export { buildPack } from './builder.js';
6
+ export { quantizeEmbeddingInt8L2Norm, encodeScaleF16, decodeScaleF16, } from './semantic.js';
7
+ export { listAgents, getAgent, resolveAgent, buildSystemPrompt, isToolAllowed, assertToolAllowed, validateAgentRegistry, validateAgentDefinition, } from './agent.js';
package/dist/indexer.d.ts CHANGED
@@ -13,8 +13,9 @@ export type IndexBuildResult = {
13
13
  * sequences of blockId and positions for that term, with zeros as delimiters.
14
14
  * The structure looks like:
15
15
  *
16
- * [termId, blockId, pos, pos, 0, blockId, pos, 0, 0, termId, ...]
16
+ * [termId, blockId+1, pos, pos, 0, blockId+1, pos, 0, 0, termId, ...]
17
17
  *
18
+ * Block IDs are stored as bid+1 so that 0 can remain a sentinel delimiter.
18
19
  * Each block section ends with a 0, and each term section ends with a 0. The
19
20
  * entire array can be streamed sequentially without needing to know the sizes
20
21
  * of individual lists ahead of time.
package/dist/indexer.js CHANGED
@@ -14,8 +14,9 @@ import { tokenize } from "./tokenize.js";
14
14
  * sequences of blockId and positions for that term, with zeros as delimiters.
15
15
  * The structure looks like:
16
16
  *
17
- * [termId, blockId, pos, pos, 0, blockId, pos, 0, 0, termId, ...]
17
+ * [termId, blockId+1, pos, pos, 0, blockId+1, pos, 0, 0, termId, ...]
18
18
  *
19
+ * Block IDs are stored as bid+1 so that 0 can remain a sentinel delimiter.
19
20
  * Each block section ends with a 0, and each term section ends with a 0. The
20
21
  * entire array can be streamed sequentially without needing to know the sizes
21
22
  * of individual lists ahead of time.
@@ -60,7 +61,7 @@ export function buildIndex(blocks) {
60
61
  for (const [tid, blockMap] of termBlockPositions) {
61
62
  postings.push(tid);
62
63
  for (const [bid, positions] of blockMap) {
63
- postings.push(bid, ...positions, 0);
64
+ postings.push(bid + 1, ...positions, 0);
64
65
  }
65
66
  postings.push(0); // end of term
66
67
  }
package/dist/pack.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { AgentRegistry } from './agent.js';
1
2
  export type MountOptions = {
2
3
  src: string | ArrayBufferLike | Uint8Array;
3
4
  };
@@ -9,6 +10,7 @@ export type PackMeta = {
9
10
  terms: number;
10
11
  avgBlockLen?: number;
11
12
  };
13
+ agents?: AgentRegistry;
12
14
  };
13
15
  export type Pack = {
14
16
  meta: PackMeta;
@@ -17,5 +19,17 @@ export type Pack = {
17
19
  blocks: string[];
18
20
  headings?: (string | null)[];
19
21
  docIds?: (string | null)[];
22
+ namespaces?: (string | null)[];
23
+ blockTokenLens?: number[];
24
+ semantic?: {
25
+ version: 1;
26
+ modelId: string;
27
+ dims: number;
28
+ encoding: 'int8_l2norm';
29
+ perVectorScale: boolean;
30
+ vecs: Int8Array;
31
+ scales?: Uint16Array;
32
+ };
20
33
  };
34
+ export declare function hasSemantic(pack: Pack): boolean;
21
35
  export declare function mountPack(opts: MountOptions): Promise<Pack>;
package/dist/pack.js CHANGED
@@ -1,12 +1,16 @@
1
1
  /*
2
2
  * pack.ts
3
3
  *
4
- * Mount `.knolo` packs across Node, browsers, and RN/Expo. Now tolerant of:
5
- * - blocks as string[] (v1) or object[] with { text, heading?, docId? } (v2)
4
+ * Mount `.knolo` packs across Node, browsers, and RN/Expo. Tolerant of:
5
+ * - blocks as string[] (v1) or object[] with { text, heading?, docId?, namespace?, len? }
6
6
  * - meta.stats.avgBlockLen (optional)
7
7
  * Includes RN/Expo-safe TextDecoder via ponyfill.
8
8
  */
9
9
  import { getTextDecoder } from './utils/utf8.js';
10
+ import { validateAgentRegistry } from './agent.js';
11
+ export function hasSemantic(pack) {
12
+ return Boolean(pack.semantic && pack.semantic.dims > 0 && pack.semantic.vecs.length > 0);
13
+ }
10
14
  export async function mountPack(opts) {
11
15
  const buf = await resolveToBuffer(opts.src);
12
16
  const dv = new DataView(buf);
@@ -18,6 +22,9 @@ export async function mountPack(opts) {
18
22
  const metaJson = dec.decode(new Uint8Array(buf, offset, metaLen));
19
23
  offset += metaLen;
20
24
  const meta = JSON.parse(metaJson);
25
+ if (meta.agents) {
26
+ validateAgentRegistry(meta.agents);
27
+ }
21
28
  // lexicon
22
29
  const lexLen = dv.getUint32(offset, true);
23
30
  offset += 4;
@@ -33,14 +40,17 @@ export async function mountPack(opts) {
33
40
  postings[i] = dv.getUint32(offset, true);
34
41
  offset += 4;
35
42
  }
36
- // blocks (v1: string[]; v2: {text, heading?, docId?}[])
43
+ // blocks (v1: string[]; v2/v3: {text, heading?, docId?, namespace?, len?}[])
37
44
  const blocksLen = dv.getUint32(offset, true);
38
45
  offset += 4;
39
46
  const blocksJson = dec.decode(new Uint8Array(buf, offset, blocksLen));
47
+ offset += blocksLen;
40
48
  const parsed = JSON.parse(blocksJson);
41
49
  let blocks = [];
42
50
  let headings;
43
51
  let docIds;
52
+ let namespaces;
53
+ let blockTokenLens;
44
54
  if (Array.isArray(parsed) && parsed.length && typeof parsed[0] === 'string') {
45
55
  // v1
46
56
  blocks = parsed;
@@ -49,26 +59,81 @@ export async function mountPack(opts) {
49
59
  blocks = [];
50
60
  headings = [];
51
61
  docIds = [];
62
+ namespaces = [];
63
+ blockTokenLens = [];
52
64
  for (const it of parsed) {
53
65
  if (it && typeof it === 'object') {
54
66
  blocks.push(String(it.text ?? ''));
55
67
  headings.push(it.heading ?? null);
56
68
  docIds.push(it.docId ?? null);
69
+ namespaces.push(it.namespace ?? null);
70
+ blockTokenLens.push(typeof it.len === 'number' ? it.len : 0);
57
71
  }
58
72
  else {
59
73
  blocks.push(String(it ?? ''));
60
74
  headings.push(null);
61
75
  docIds.push(null);
76
+ namespaces.push(null);
77
+ blockTokenLens.push(0);
62
78
  }
63
79
  }
64
80
  }
65
81
  else {
66
82
  blocks = [];
67
83
  }
68
- return { meta, lexicon, postings, blocks, headings, docIds };
84
+ let semantic;
85
+ if (offset < buf.byteLength) {
86
+ const semLen = dv.getUint32(offset, true);
87
+ offset += 4;
88
+ const semJson = dec.decode(new Uint8Array(buf, offset, semLen));
89
+ offset += semLen;
90
+ const sem = JSON.parse(semJson);
91
+ const semBlobLen = dv.getUint32(offset, true);
92
+ offset += 4;
93
+ const semBlob = new Uint8Array(buf, offset, semBlobLen);
94
+ semantic = parseSemanticSection(sem, semBlob);
95
+ }
96
+ return {
97
+ meta,
98
+ lexicon,
99
+ postings,
100
+ blocks,
101
+ headings,
102
+ docIds,
103
+ namespaces,
104
+ blockTokenLens,
105
+ semantic,
106
+ };
107
+ }
108
+ function parseSemanticSection(sem, blob) {
109
+ const vectors = sem?.blocks?.vectors;
110
+ const scales = sem?.blocks?.scales;
111
+ const vecs = new Int8Array(blob.buffer, blob.byteOffset + Number(vectors?.byteOffset ?? 0), Number(vectors?.length ?? 0));
112
+ let scaleView;
113
+ if (scales) {
114
+ const scaleLen = Number(scales.length ?? 0);
115
+ const scaleOffset = Number(scales.byteOffset ?? 0);
116
+ const dv = new DataView(blob.buffer, blob.byteOffset + scaleOffset, scaleLen * 2);
117
+ scaleView = new Uint16Array(scaleLen);
118
+ for (let i = 0; i < scaleLen; i++) {
119
+ scaleView[i] = dv.getUint16(i * 2, true);
120
+ }
121
+ }
122
+ return {
123
+ version: 1,
124
+ modelId: String(sem?.modelId ?? ''),
125
+ dims: Number(sem?.dims ?? 0),
126
+ encoding: 'int8_l2norm',
127
+ perVectorScale: Boolean(sem?.perVectorScale),
128
+ vecs,
129
+ scales: scaleView,
130
+ };
69
131
  }
70
132
  async function resolveToBuffer(src) {
71
133
  if (typeof src === 'string') {
134
+ if (isNodeRuntime() && isLikelyLocalPath(src)) {
135
+ return await readLocalFileAsBuffer(src);
136
+ }
72
137
  const res = await fetch(src);
73
138
  return await res.arrayBuffer();
74
139
  }
@@ -81,3 +146,30 @@ async function resolveToBuffer(src) {
81
146
  }
82
147
  return src;
83
148
  }
149
+ function isNodeRuntime() {
150
+ const p = globalThis
151
+ .process;
152
+ return !!p?.versions?.node;
153
+ }
154
+ function isLikelyLocalPath(value) {
155
+ if (value.startsWith('file://'))
156
+ return true;
157
+ if (value.startsWith('./') ||
158
+ value.startsWith('../') ||
159
+ value.startsWith('/') ||
160
+ value.startsWith('~'))
161
+ return true;
162
+ if (/^[A-Za-z]:[\\/]/.test(value))
163
+ return true; // Windows absolute path
164
+ if (/^[A-Za-z][A-Za-z\d+.-]*:/.test(value))
165
+ return false; // URL scheme
166
+ return true; // plain relative path like "knowledge.knolo"
167
+ }
168
+ async function readLocalFileAsBuffer(pathOrFileUrl) {
169
+ const { readFile } = await import('node:fs/promises');
170
+ const filePath = pathOrFileUrl.startsWith('file://')
171
+ ? decodeURIComponent(new URL(pathOrFileUrl).pathname)
172
+ : pathOrFileUrl;
173
+ const data = await readFile(filePath);
174
+ return data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
175
+ }
package/dist/patch.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Hit } from "./query.js";
1
+ import type { Hit } from './query.js';
2
2
  export type ContextPatch = {
3
3
  background: string[];
4
4
  snippets: Array<{
@@ -17,13 +17,6 @@ export type ContextPatch = {
17
17
  evidence?: number[];
18
18
  }>;
19
19
  };
20
- /** Assemble a context patch from an array of hits. The `budget` determines
21
- * how many snippets and how much text to include in each snippet. Currently
22
- * three budgets are supported:
23
- * - `mini`: ~512 token contexts
24
- * - `small`: ~1k token contexts
25
- * - `full`: ~2k token contexts
26
- */
27
20
  export declare function makeContextPatch(hits: Hit[], opts?: {
28
21
  budget?: 'mini' | 'small' | 'full';
29
22
  }): ContextPatch;
package/dist/patch.js CHANGED
@@ -1,17 +1,7 @@
1
1
  /*
2
2
  * patch.ts
3
3
  *
4
- * Provides the makeContextPatch function that assembles search results into
5
- * structured context patches for consumption by language models. A context
6
- * patch includes a background summary and selected snippets. Future versions
7
- * may include definitions, facts and other structured artifacts.
8
- */
9
- /** Assemble a context patch from an array of hits. The `budget` determines
10
- * how many snippets and how much text to include in each snippet. Currently
11
- * three budgets are supported:
12
- * - `mini`: ~512 token contexts
13
- * - `small`: ~1k token contexts
14
- * - `full`: ~2k token contexts
4
+ * Produces a compact, deterministic “context patch” from ranked hits.
15
5
  */
16
6
  export function makeContextPatch(hits, opts = {}) {
17
7
  const budget = opts.budget ?? 'small';
@@ -23,6 +13,7 @@ export function makeContextPatch(hits, opts = {}) {
23
13
  const limit = limits[budget];
24
14
  const snippets = hits.slice(0, limit.snippets).map((h) => ({
25
15
  text: truncate(h.text, limit.chars),
16
+ source: h.source,
26
17
  }));
27
18
  // Build background summary from first two snippets by extracting first sentence
28
19
  const background = snippets.slice(0, 2).map((s) => firstSentence(s.text));
@@ -33,18 +24,12 @@ export function makeContextPatch(hits, opts = {}) {
33
24
  facts: [],
34
25
  };
35
26
  }
36
- /** Extract the first sentence from a block of text. If no terminal punctuation
37
- * is found, returns the first N characters up to a reasonable length.
38
- */
39
27
  function firstSentence(text) {
40
28
  const m = text.match(/^(.{10,200}?[.!?])\s/);
41
29
  if (m)
42
30
  return m[1];
43
31
  return text.slice(0, 160);
44
32
  }
45
- /** Truncate text to a maximum length and append an ellipsis if it was
46
- * truncated.
47
- */
48
33
  function truncate(text, maxChars) {
49
34
  return text.length > maxChars ? text.slice(0, maxChars) + '…' : text;
50
35
  }
package/dist/query.d.ts CHANGED
@@ -1,12 +1,41 @@
1
1
  import type { Pack } from "./pack.js";
2
2
  export type QueryOptions = {
3
3
  topK?: number;
4
+ minScore?: number;
4
5
  requirePhrases?: string[];
6
+ namespace?: string | string[];
7
+ source?: string | string[];
8
+ queryExpansion?: {
9
+ enabled?: boolean;
10
+ docs?: number;
11
+ terms?: number;
12
+ weight?: number;
13
+ minTermLength?: number;
14
+ };
15
+ semantic?: {
16
+ enabled?: boolean;
17
+ mode?: "rerank";
18
+ topN?: number;
19
+ minLexConfidence?: number;
20
+ blend?: {
21
+ enabled?: boolean;
22
+ wLex?: number;
23
+ wSem?: number;
24
+ };
25
+ queryEmbedding?: Float32Array;
26
+ force?: boolean;
27
+ };
5
28
  };
29
+ export declare function validateQueryOptions(opts?: QueryOptions): void;
30
+ export declare function validateSemanticQueryOptions(options?: QueryOptions["semantic"]): void;
6
31
  export type Hit = {
7
32
  blockId: number;
8
33
  score: number;
9
34
  text: string;
10
35
  source?: string;
36
+ namespace?: string;
11
37
  };
12
38
  export declare function query(pack: Pack, q: string, opts?: QueryOptions): Hit[];
39
+ export declare function lexConfidence(hits: Array<{
40
+ score: number;
41
+ }>): number;