@totalreclaw/totalreclaw 3.3.1-rc.2 → 3.3.1-rc.21
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 +330 -0
- package/SKILL.md +50 -83
- package/api-client.ts +18 -11
- package/config.ts +117 -3
- package/crypto.ts +10 -2
- package/dist/api-client.js +226 -0
- package/dist/billing-cache.js +100 -0
- package/dist/claims-helper.js +606 -0
- package/dist/config.js +280 -0
- package/dist/consolidation.js +258 -0
- package/dist/contradiction-sync.js +1034 -0
- package/dist/crypto.js +138 -0
- package/dist/digest-sync.js +361 -0
- package/dist/download-ux.js +63 -0
- package/dist/embedding.js +86 -0
- package/dist/extractor.js +1225 -0
- package/dist/first-run.js +103 -0
- package/dist/fs-helpers.js +563 -0
- package/dist/gateway-url.js +197 -0
- package/dist/generate-mnemonic.js +13 -0
- package/dist/hot-cache-wrapper.js +101 -0
- package/dist/import-adapters/base-adapter.js +64 -0
- package/dist/import-adapters/chatgpt-adapter.js +238 -0
- package/dist/import-adapters/claude-adapter.js +114 -0
- package/dist/import-adapters/gemini-adapter.js +201 -0
- package/dist/import-adapters/index.js +26 -0
- package/dist/import-adapters/mcp-memory-adapter.js +219 -0
- package/dist/import-adapters/mem0-adapter.js +158 -0
- package/dist/import-adapters/types.js +1 -0
- package/dist/index.js +5348 -0
- package/dist/llm-client.js +686 -0
- package/dist/llm-profile-reader.js +346 -0
- package/dist/lsh.js +62 -0
- package/dist/onboarding-cli.js +750 -0
- package/dist/pair-cli.js +344 -0
- package/dist/pair-crypto.js +359 -0
- package/dist/pair-http.js +404 -0
- package/dist/pair-page.js +826 -0
- package/dist/pair-qr.js +107 -0
- package/dist/pair-remote-client.js +410 -0
- package/dist/pair-session-store.js +566 -0
- package/dist/pin.js +542 -0
- package/dist/qa-bug-report.js +301 -0
- package/dist/relay-headers.js +44 -0
- package/dist/reranker.js +442 -0
- package/dist/retype-setscope.js +348 -0
- package/dist/semantic-dedup.js +75 -0
- package/dist/subgraph-search.js +289 -0
- package/dist/subgraph-store.js +694 -0
- package/dist/tool-gating.js +58 -0
- package/download-ux.ts +91 -0
- package/embedding.ts +32 -9
- package/fs-helpers.ts +124 -0
- package/gateway-url.ts +57 -9
- package/index.ts +586 -357
- package/llm-client.ts +211 -23
- package/lsh.ts +7 -2
- package/onboarding-cli.ts +114 -1
- package/package.json +19 -5
- package/pair-cli.ts +76 -8
- package/pair-crypto.ts +34 -24
- package/pair-page.ts +28 -17
- package/pair-qr.ts +152 -0
- package/pair-remote-client.ts +540 -0
- package/qa-bug-report.ts +381 -0
- package/relay-headers.ts +50 -0
- package/reranker.ts +73 -0
- package/retype-setscope.ts +12 -0
- package/subgraph-search.ts +4 -3
- package/subgraph-store.ts +109 -16
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* llm-profile-reader — read OpenClaw's `auth-profiles.json` to harvest
|
|
3
|
+
* provider API keys when the plugin has no other source.
|
|
4
|
+
*
|
|
5
|
+
* Background
|
|
6
|
+
* ----------
|
|
7
|
+
* In 3.3.0-rc.6 and earlier, the plugin's `initLLMClient` only looked in:
|
|
8
|
+
* 1. `api.config.providers` / `openclawProviders` passed by the SDK
|
|
9
|
+
* 2. Env vars (`ZAI_API_KEY`, `OPENAI_API_KEY`, ...)
|
|
10
|
+
* 3. Plugin-config override `extraction.llm`
|
|
11
|
+
*
|
|
12
|
+
* Real-world OpenClaw installs store user API keys in
|
|
13
|
+
* `~/.openclaw/agents/<agent>/agent/auth-profiles.json`. None of the three
|
|
14
|
+
* paths above reach that file, so the plugin silently logged
|
|
15
|
+
* `No LLM available for auto-extraction` on every turn — auto-extraction
|
|
16
|
+
* was a no-op for virtually every real user. See user-findings 3.3.0-rc.6.
|
|
17
|
+
*
|
|
18
|
+
* 3.3.1 adds this file as the fourth resolution tier, sitting between
|
|
19
|
+
* "openclawProviders (SDK-passed)" and "env vars".
|
|
20
|
+
*
|
|
21
|
+
* Scope and scanner surface
|
|
22
|
+
* -------------------------
|
|
23
|
+
* This file does disk I/O. It MUST NOT contain the trigger substrings
|
|
24
|
+
* used by OpenClaw's scanner (see `skill/scripts/check-scanner.mjs`) —
|
|
25
|
+
* namely the outbound-request markers. All network work stays in
|
|
26
|
+
* `llm-client.ts` and friends; this file only reads local files.
|
|
27
|
+
*
|
|
28
|
+
* File format (auth-profiles.json, OpenClaw canonical shape)
|
|
29
|
+
* ----------------------------------------------------------
|
|
30
|
+
* {
|
|
31
|
+
* "profiles": {
|
|
32
|
+
* "openai:default": { "key": "sk-..." },
|
|
33
|
+
* "anthropic:default": { "key": "sk-ant-..." },
|
|
34
|
+
* "zai:default": { "key": "..." },
|
|
35
|
+
* ...
|
|
36
|
+
* }
|
|
37
|
+
* }
|
|
38
|
+
*
|
|
39
|
+
* We map the `<provider>:default` profile id to the canonical provider
|
|
40
|
+
* name the plugin uses elsewhere (openai, anthropic, zai, gemini, etc.).
|
|
41
|
+
* Non-default profile ids are ignored — a deliberate choice so users who
|
|
42
|
+
* have multiple profiles (`openai:work`, `openai:personal`) see the one
|
|
43
|
+
* they've explicitly flagged as `default`.
|
|
44
|
+
*
|
|
45
|
+
* File format (models.json, legacy OpenClaw shape) — 3.3.1-rc.2
|
|
46
|
+
* ------------------------------------------------------------
|
|
47
|
+
* {
|
|
48
|
+
* "providers": {
|
|
49
|
+
* "zai": { "apiKey": "..." },
|
|
50
|
+
* "openai": { "apiKey": "sk-..." },
|
|
51
|
+
* "anthropic": { "apiKey": "sk-ant-..." },
|
|
52
|
+
* ...
|
|
53
|
+
* }
|
|
54
|
+
* }
|
|
55
|
+
*
|
|
56
|
+
* 3.3.1-rc.1 QA found that some real OpenClaw installs (the VPS used for
|
|
57
|
+
* QA in particular) still have the pre-auth-profiles format — a single
|
|
58
|
+
* `models.json` at the same path with a `providers` map. Reading only
|
|
59
|
+
* auth-profiles.json silently no-op'd on those hosts. 3.3.1-rc.2 adds a
|
|
60
|
+
* 5th tier to the cascade: if auth-profiles.json is absent, fall back to
|
|
61
|
+
* the adjacent `models.json`.
|
|
62
|
+
*/
|
|
63
|
+
import fs from 'node:fs';
|
|
64
|
+
import path from 'node:path';
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
// Provider-name normalization
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
/**
|
|
69
|
+
* Map an auth-profile namespace (the part before `:` in a profile id like
|
|
70
|
+
* `openai:default`) to the plugin's canonical provider name.
|
|
71
|
+
*/
|
|
72
|
+
const PROFILE_NS_TO_PROVIDER = {
|
|
73
|
+
openai: 'openai',
|
|
74
|
+
anthropic: 'anthropic',
|
|
75
|
+
zai: 'zai',
|
|
76
|
+
'z.ai': 'zai',
|
|
77
|
+
google: 'gemini',
|
|
78
|
+
gemini: 'gemini',
|
|
79
|
+
mistral: 'mistral',
|
|
80
|
+
groq: 'groq',
|
|
81
|
+
deepseek: 'deepseek',
|
|
82
|
+
openrouter: 'openrouter',
|
|
83
|
+
xai: 'xai',
|
|
84
|
+
'x.ai': 'xai',
|
|
85
|
+
together: 'together',
|
|
86
|
+
cerebras: 'cerebras',
|
|
87
|
+
};
|
|
88
|
+
// ---------------------------------------------------------------------------
|
|
89
|
+
// File discovery
|
|
90
|
+
// ---------------------------------------------------------------------------
|
|
91
|
+
/**
|
|
92
|
+
* Default search root — `$HOME/.openclaw/agents`. Returns an empty
|
|
93
|
+
* string when HOME is unset (avoids path.join crash on bare envs).
|
|
94
|
+
*/
|
|
95
|
+
export function defaultAuthProfilesRoot(homeDir) {
|
|
96
|
+
if (!homeDir)
|
|
97
|
+
return '';
|
|
98
|
+
return path.join(homeDir, '.openclaw', 'agents');
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Walk `$HOME/.openclaw/agents/*` (one level deep), each subdirectory
|
|
102
|
+
* being an agent with potentially an `agent/auth-profiles.json`. Returns
|
|
103
|
+
* every auth-profiles.json path that exists on disk, in alphabetical
|
|
104
|
+
* order of the agent dir name (stable, so tests don't flake).
|
|
105
|
+
*
|
|
106
|
+
* Silently tolerates a missing root — returns [] instead of throwing.
|
|
107
|
+
*/
|
|
108
|
+
export function findAuthProfilesFiles(root) {
|
|
109
|
+
if (!root)
|
|
110
|
+
return [];
|
|
111
|
+
let entries;
|
|
112
|
+
try {
|
|
113
|
+
entries = fs.readdirSync(root, { withFileTypes: true });
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
118
|
+
const out = [];
|
|
119
|
+
for (const e of entries) {
|
|
120
|
+
if (!e.isDirectory())
|
|
121
|
+
continue;
|
|
122
|
+
if (e.name.startsWith('.'))
|
|
123
|
+
continue;
|
|
124
|
+
const candidate = path.join(root, e.name, 'agent', 'auth-profiles.json');
|
|
125
|
+
try {
|
|
126
|
+
if (fs.statSync(candidate).isFile())
|
|
127
|
+
out.push(candidate);
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
// missing or inaccessible — skip.
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
out.sort();
|
|
134
|
+
return out;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Parse one auth-profiles.json file into a list of (provider, apiKey)
|
|
138
|
+
* entries, keeping only `<ns>:default` profiles and only those whose `key`
|
|
139
|
+
* is a non-empty string. Unknown namespaces are skipped silently.
|
|
140
|
+
*/
|
|
141
|
+
export function parseAuthProfilesFile(filePath) {
|
|
142
|
+
let raw;
|
|
143
|
+
try {
|
|
144
|
+
raw = fs.readFileSync(filePath, 'utf-8');
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
return [];
|
|
148
|
+
}
|
|
149
|
+
let json;
|
|
150
|
+
try {
|
|
151
|
+
json = JSON.parse(raw);
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
return [];
|
|
155
|
+
}
|
|
156
|
+
if (typeof json !== 'object' || json === null)
|
|
157
|
+
return [];
|
|
158
|
+
const profilesField = json.profiles;
|
|
159
|
+
if (typeof profilesField !== 'object' || profilesField === null)
|
|
160
|
+
return [];
|
|
161
|
+
const profiles = profilesField;
|
|
162
|
+
const out = [];
|
|
163
|
+
for (const [profileId, entryRaw] of Object.entries(profiles)) {
|
|
164
|
+
const parts = profileId.split(':');
|
|
165
|
+
if (parts.length !== 2)
|
|
166
|
+
continue;
|
|
167
|
+
const [ns, suffix] = parts;
|
|
168
|
+
if (suffix !== 'default')
|
|
169
|
+
continue;
|
|
170
|
+
const nsKey = ns.toLowerCase();
|
|
171
|
+
const provider = PROFILE_NS_TO_PROVIDER[nsKey];
|
|
172
|
+
if (!provider)
|
|
173
|
+
continue;
|
|
174
|
+
if (typeof entryRaw !== 'object' || entryRaw === null)
|
|
175
|
+
continue;
|
|
176
|
+
const keyField = entryRaw.key;
|
|
177
|
+
if (typeof keyField !== 'string')
|
|
178
|
+
continue;
|
|
179
|
+
const trimmed = keyField.trim();
|
|
180
|
+
if (!trimmed)
|
|
181
|
+
continue;
|
|
182
|
+
out.push({
|
|
183
|
+
provider,
|
|
184
|
+
apiKey: trimmed,
|
|
185
|
+
sourcePath: filePath,
|
|
186
|
+
profileId,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
return out;
|
|
190
|
+
}
|
|
191
|
+
// ---------------------------------------------------------------------------
|
|
192
|
+
// Public aggregate
|
|
193
|
+
// ---------------------------------------------------------------------------
|
|
194
|
+
/**
|
|
195
|
+
* Harvest every non-empty provider key from every
|
|
196
|
+
* `~/.openclaw/agents/<agent>/agent/auth-profiles.json` on disk. Later files
|
|
197
|
+
* (alphabetical) win for duplicate provider names — intentional so a
|
|
198
|
+
* newly-added agent's keys shadow older ones. Callers that want the
|
|
199
|
+
* single "first match per provider" list should run through
|
|
200
|
+
* `dedupeByProvider` below.
|
|
201
|
+
*/
|
|
202
|
+
export function readAllAuthProfileKeys(options) {
|
|
203
|
+
const files = findAuthProfilesFiles(options.root);
|
|
204
|
+
const out = [];
|
|
205
|
+
for (const file of files) {
|
|
206
|
+
const keys = parseAuthProfilesFile(file);
|
|
207
|
+
out.push(...keys);
|
|
208
|
+
}
|
|
209
|
+
return out;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Reduce a list of AuthProfileKey entries to one-per-provider, picking
|
|
213
|
+
* the LAST one in list order (so later agent files override earlier ones
|
|
214
|
+
* for the same provider).
|
|
215
|
+
*/
|
|
216
|
+
export function dedupeByProvider(entries) {
|
|
217
|
+
const map = {};
|
|
218
|
+
for (const e of entries) {
|
|
219
|
+
map[e.provider] = e;
|
|
220
|
+
}
|
|
221
|
+
return map;
|
|
222
|
+
}
|
|
223
|
+
// ---------------------------------------------------------------------------
|
|
224
|
+
// 3.3.1-rc.2 — legacy models.json reader
|
|
225
|
+
// ---------------------------------------------------------------------------
|
|
226
|
+
/**
|
|
227
|
+
* Walk `$HOME/.openclaw/agents/*` and return every
|
|
228
|
+
* `agent/models.json` path that exists on disk. Mirrors findAuthProfilesFiles
|
|
229
|
+
* but targets the pre-auth-profiles filename.
|
|
230
|
+
*/
|
|
231
|
+
export function findModelsJsonFiles(root) {
|
|
232
|
+
if (!root)
|
|
233
|
+
return [];
|
|
234
|
+
let entries;
|
|
235
|
+
try {
|
|
236
|
+
entries = fs.readdirSync(root, { withFileTypes: true });
|
|
237
|
+
}
|
|
238
|
+
catch {
|
|
239
|
+
return [];
|
|
240
|
+
}
|
|
241
|
+
const out = [];
|
|
242
|
+
for (const e of entries) {
|
|
243
|
+
if (!e.isDirectory())
|
|
244
|
+
continue;
|
|
245
|
+
if (e.name.startsWith('.'))
|
|
246
|
+
continue;
|
|
247
|
+
const candidate = path.join(root, e.name, 'agent', 'models.json');
|
|
248
|
+
try {
|
|
249
|
+
if (fs.statSync(candidate).isFile())
|
|
250
|
+
out.push(candidate);
|
|
251
|
+
}
|
|
252
|
+
catch {
|
|
253
|
+
// missing — skip.
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
out.sort();
|
|
257
|
+
return out;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Parse a legacy `models.json` file into AuthProfileKey entries. Unknown
|
|
261
|
+
* provider namespaces (anything not in PROFILE_NS_TO_PROVIDER) are
|
|
262
|
+
* skipped silently. Accepts `apiKey`, `api_key`, or `key` as the key
|
|
263
|
+
* field — different OpenClaw versions used different names.
|
|
264
|
+
*/
|
|
265
|
+
export function parseModelsJsonFile(filePath) {
|
|
266
|
+
let raw;
|
|
267
|
+
try {
|
|
268
|
+
raw = fs.readFileSync(filePath, 'utf-8');
|
|
269
|
+
}
|
|
270
|
+
catch {
|
|
271
|
+
return [];
|
|
272
|
+
}
|
|
273
|
+
let json;
|
|
274
|
+
try {
|
|
275
|
+
json = JSON.parse(raw);
|
|
276
|
+
}
|
|
277
|
+
catch {
|
|
278
|
+
return [];
|
|
279
|
+
}
|
|
280
|
+
if (typeof json !== 'object' || json === null)
|
|
281
|
+
return [];
|
|
282
|
+
const providersField = json.providers;
|
|
283
|
+
if (typeof providersField !== 'object' || providersField === null)
|
|
284
|
+
return [];
|
|
285
|
+
const providers = providersField;
|
|
286
|
+
const out = [];
|
|
287
|
+
for (const [providerName, entryRaw] of Object.entries(providers)) {
|
|
288
|
+
const nsKey = providerName.toLowerCase();
|
|
289
|
+
const provider = PROFILE_NS_TO_PROVIDER[nsKey];
|
|
290
|
+
if (!provider)
|
|
291
|
+
continue;
|
|
292
|
+
if (typeof entryRaw !== 'object' || entryRaw === null)
|
|
293
|
+
continue;
|
|
294
|
+
const rec = entryRaw;
|
|
295
|
+
const rawKey = rec.apiKey ?? rec.api_key ?? rec.key;
|
|
296
|
+
if (typeof rawKey !== 'string')
|
|
297
|
+
continue;
|
|
298
|
+
const trimmed = rawKey.trim();
|
|
299
|
+
if (!trimmed)
|
|
300
|
+
continue;
|
|
301
|
+
out.push({
|
|
302
|
+
provider,
|
|
303
|
+
apiKey: trimmed,
|
|
304
|
+
sourcePath: filePath,
|
|
305
|
+
profileId: `${providerName}:models-json-legacy`,
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
return out;
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Read every models.json file under the agents root. Counterpart to
|
|
312
|
+
* `readAllAuthProfileKeys`.
|
|
313
|
+
*/
|
|
314
|
+
export function readAllModelsJsonKeys(options) {
|
|
315
|
+
const files = findModelsJsonFiles(options.root);
|
|
316
|
+
const out = [];
|
|
317
|
+
for (const file of files) {
|
|
318
|
+
const keys = parseModelsJsonFile(file);
|
|
319
|
+
out.push(...keys);
|
|
320
|
+
}
|
|
321
|
+
return out;
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* 3.3.1-rc.2 — combined reader. Reads auth-profiles.json first (if
|
|
325
|
+
* present), then merges in models.json entries for any provider NOT
|
|
326
|
+
* already covered by auth-profiles. The newer format wins on overlap.
|
|
327
|
+
*/
|
|
328
|
+
export function readAllProfileKeys(options) {
|
|
329
|
+
const primary = readAllAuthProfileKeys(options);
|
|
330
|
+
const primaryProviders = new Set(primary.map((e) => e.provider));
|
|
331
|
+
const legacy = readAllModelsJsonKeys(options);
|
|
332
|
+
const merged = [...primary];
|
|
333
|
+
for (const entry of legacy) {
|
|
334
|
+
if (!primaryProviders.has(entry.provider)) {
|
|
335
|
+
merged.push(entry);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
return merged;
|
|
339
|
+
}
|
|
340
|
+
// ---------------------------------------------------------------------------
|
|
341
|
+
// Test hook
|
|
342
|
+
// ---------------------------------------------------------------------------
|
|
343
|
+
/** Internal — exposed for tests. */
|
|
344
|
+
export const __internal = {
|
|
345
|
+
PROFILE_NS_TO_PROVIDER,
|
|
346
|
+
};
|
package/dist/lsh.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TotalReclaw Plugin - LSH Hasher
|
|
3
|
+
*
|
|
4
|
+
* Re-exports `WasmLshHasher` from `@totalreclaw/core` as `LSHHasher`
|
|
5
|
+
* for backward compatibility with existing plugin code.
|
|
6
|
+
*
|
|
7
|
+
* Default parameters: 32 bits per table, 20 tables.
|
|
8
|
+
*/
|
|
9
|
+
// Lazy-load WASM via createRequire. The shipped `dist/index.js` is ESM-only
|
|
10
|
+
// (`"type":"module"`) so the bare `require` global is undefined at runtime.
|
|
11
|
+
// See issue #124 for the bug this avoids; matches the pattern in
|
|
12
|
+
// claims-helper / consolidation / digest-sync / pin / retype-setscope.
|
|
13
|
+
import { createRequire } from 'node:module';
|
|
14
|
+
const requireWasm = createRequire(import.meta.url);
|
|
15
|
+
let _WasmLshHasher = null;
|
|
16
|
+
function getWasmLshHasher() {
|
|
17
|
+
if (!_WasmLshHasher)
|
|
18
|
+
_WasmLshHasher = requireWasm('@totalreclaw/core').WasmLshHasher;
|
|
19
|
+
return _WasmLshHasher;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Random Hyperplane LSH hasher.
|
|
23
|
+
*
|
|
24
|
+
* All state is deterministic from the seed -- no randomness at hash time.
|
|
25
|
+
* Construct once per session; call `hash()` for every store/search operation.
|
|
26
|
+
*/
|
|
27
|
+
export class LSHHasher {
|
|
28
|
+
inner;
|
|
29
|
+
/**
|
|
30
|
+
* Create a new LSH hasher.
|
|
31
|
+
*
|
|
32
|
+
* @param seed - 32-byte seed from `deriveLshSeed()` in crypto.ts.
|
|
33
|
+
* @param dims - Embedding dimensionality (e.g. 640 for Harrier).
|
|
34
|
+
* @param nTables - Number of independent hash tables (default 20).
|
|
35
|
+
* @param nBits - Number of bits per table (default 32).
|
|
36
|
+
*/
|
|
37
|
+
constructor(seed, dims, nTables = 20, nBits = 32) {
|
|
38
|
+
const seedHex = Buffer.from(seed).toString('hex');
|
|
39
|
+
this.inner = getWasmLshHasher().withParams(seedHex, dims, nTables, nBits);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Hash an embedding vector to an array of blind-hashed bucket IDs.
|
|
43
|
+
*
|
|
44
|
+
* @param embedding - The embedding vector (must have `dims` elements).
|
|
45
|
+
* @returns Array of `nTables` hex strings (one blind hash per table).
|
|
46
|
+
*/
|
|
47
|
+
hash(embedding) {
|
|
48
|
+
return this.inner.hash(new Float64Array(embedding));
|
|
49
|
+
}
|
|
50
|
+
/** Number of hash tables. */
|
|
51
|
+
get tables() {
|
|
52
|
+
return this.inner.tables;
|
|
53
|
+
}
|
|
54
|
+
/** Number of bits per table. */
|
|
55
|
+
get bits() {
|
|
56
|
+
return this.inner.bits;
|
|
57
|
+
}
|
|
58
|
+
/** Embedding dimensionality. */
|
|
59
|
+
get dimensions() {
|
|
60
|
+
return this.inner.dimensions;
|
|
61
|
+
}
|
|
62
|
+
}
|