dsh-memu-memory 1.1.1 → 1.1.3
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/AGENTS.md +8 -5
- package/README.md +22 -5
- package/README.zh.md +21 -4
- package/docs/contributing.md +5 -3
- package/docs/env-and-commands.md +13 -0
- package/docs/host-api.md +35 -3
- package/docs/memory-semantics.md +41 -1
- package/docs/troubleshooting.md +395 -350
- package/lib/activity-log.js +190 -0
- package/lib/backup.js +1103 -0
- package/lib/binding-diagnostics.js +630 -0
- package/lib/client.js +703 -50
- package/lib/curator.js +81 -37
- package/lib/cycle.js +3 -0
- package/lib/embedding-catalog.js +763 -0
- package/lib/embedding-download.js +407 -0
- package/lib/embedding-runtime.js +710 -0
- package/lib/engine.js +2 -2
- package/lib/index.js +61 -4
- package/lib/inject.js +195 -49
- package/lib/local-embedding-entry.js +145 -0
- package/lib/local-embedding-manager.js +249 -0
- package/lib/local-embedding-protocol.js +173 -0
- package/lib/local-embedding-worker.js +652 -0
- package/lib/local-vector-service.js +283 -0
- package/lib/memory.js +385 -45
- package/lib/memu-config.js +10 -22
- package/lib/memu.js +53 -19
- package/lib/paths.js +24 -0
- package/lib/preset-scope.js +121 -0
- package/lib/private.js +139 -13
- package/lib/rpc.js +45 -4
- package/lib/state.js +109 -161
- package/lib/tools.js +9 -5
- package/lib/vector-migration.js +878 -0
- package/lib/vector-profile.js +941 -0
- package/package.json +3 -2
- package/scripts/acceptance-local.mjs +5 -4
- package/scripts/chain-matrix.mjs +527 -0
- package/scripts/check-all.mjs +19 -0
- package/scripts/check-doc-drift.mjs +353 -0
- package/scripts/diagnose-bindings.mjs +176 -0
- package/scripts/local-runtime-install.mjs +142 -0
- package/scripts/local-runtime-verify.mjs +56 -0
- package/scripts/local-vector-verify.mjs +93 -0
- package/scripts/recall-bench.mjs +813 -0
- package/scripts/repetition-ab-scenarios.mjs +93 -0
- package/scripts/repetition-detector-snapshot.mjs +314 -0
- package/scripts/repetition-eval-live.mjs +360 -0
- package/scripts/repetition-eval.mjs +702 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import crypto from 'node:crypto'
|
|
4
|
+
import { performance } from 'node:perf_hooks'
|
|
5
|
+
import { getEmbeddingModel } from '../lib/embedding-catalog.js'
|
|
6
|
+
import { startEmbeddingWorker } from '../lib/local-embedding-worker.js'
|
|
7
|
+
|
|
8
|
+
const id = 'bge-small-zh-v1.5-int8'
|
|
9
|
+
const root = path.resolve('.tmp-local-vector-20261030')
|
|
10
|
+
const modelDir = path.join(root, 'model-root', id)
|
|
11
|
+
const runtimeDir = path.join(root, 'runtime-root')
|
|
12
|
+
const manifest = getEmbeddingModel(id)
|
|
13
|
+
const token = crypto.randomBytes(32).toString('hex')
|
|
14
|
+
const begin = performance.now()
|
|
15
|
+
let worker
|
|
16
|
+
const norm = a => Math.sqrt(a.reduce((n, x) => n + x * x, 0))
|
|
17
|
+
const cosine = (a, b) => a.reduce((n, x, i) => n + x * b[i], 0) / norm(a) / norm(b)
|
|
18
|
+
try {
|
|
19
|
+
worker = await startEmbeddingWorker({ modelDir, runtimeDir, manifest, token })
|
|
20
|
+
const readyMs = performance.now() - begin
|
|
21
|
+
const texts = ['一个人在海边散步,海风很轻。', '海边的微风吹着散步的人。', '数据库迁移必须保持事务一致性。']
|
|
22
|
+
async function request(input, encoding_format = 'float') {
|
|
23
|
+
const start = performance.now()
|
|
24
|
+
const response = await fetch(worker.url, { method: 'POST', headers: { authorization: 'Bearer ' + token, 'content-type': 'application/json' }, body: JSON.stringify({ model: worker.model, input, encoding_format }) })
|
|
25
|
+
const result = await response.json()
|
|
26
|
+
if (!response.ok) throw new Error(JSON.stringify(result))
|
|
27
|
+
return { result, ms: performance.now() - start }
|
|
28
|
+
}
|
|
29
|
+
const batch = await request(texts)
|
|
30
|
+
const single = await request(texts[0])
|
|
31
|
+
const single2 = await request(texts[0])
|
|
32
|
+
const base64 = await request(texts[0], 'base64')
|
|
33
|
+
const vectors = batch.result.data.map(row => row.embedding)
|
|
34
|
+
const buf = Buffer.from(base64.result.data[0].embedding, 'base64')
|
|
35
|
+
const decoded = Array.from({ length: buf.length / 4 }, (_, i) => buf.readFloatLE(i * 4))
|
|
36
|
+
if (vectors.some(v => v.length !== 512 || v.some(x => !Number.isFinite(x)) || Math.abs(norm(v) - 1) > 0.00001)) throw new Error('invalid real inference vector')
|
|
37
|
+
const batchSingleCosine = cosine(vectors[0], single.result.data[0].embedding)
|
|
38
|
+
const repeatedSingleCosine = cosine(single.result.data[0].embedding, single2.result.data[0].embedding)
|
|
39
|
+
console.log(JSON.stringify({ batchSingleCosine, repeatedSingleCosine, norms: vectors.map(norm) }))
|
|
40
|
+
if (batchSingleCosine < 0.999) throw new Error('batch padding changed embedding')
|
|
41
|
+
if (cosine(decoded, single.result.data[0].embedding) < 0.999999) throw new Error('base64 encoding mismatch')
|
|
42
|
+
const metadata = ['onnxruntime-node', 'onnxruntime-common', '@huggingface/tokenizers'].map(name => {
|
|
43
|
+
const file = path.join(runtimeDir, 'node_modules', name, 'package.json')
|
|
44
|
+
const bytes = fs.readFileSync(file)
|
|
45
|
+
const pkg = JSON.parse(bytes)
|
|
46
|
+
return { name, version: pkg.version, license: pkg.license, bytes: bytes.length, sha256: crypto.createHash('sha256').update(bytes).digest('hex') }
|
|
47
|
+
})
|
|
48
|
+
const report = { ok: true, model: worker.model, dimension: worker.dimension, readyMs, batchMs: batch.ms, singleMs: single.ms, norms: vectors.map(norm), relatedCosine: cosine(vectors[0], vectors[1]), unrelatedCosine: cosine(vectors[0], vectors[2]), batchSingleCosine, encodingCosine: cosine(decoded, single.result.data[0].embedding), metadata }
|
|
49
|
+
fs.writeFileSync(path.join(root, 'runtime-smoke.json'), JSON.stringify(report, null, 2) + '\n')
|
|
50
|
+
console.log(JSON.stringify(report, null, 2))
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.error(error)
|
|
53
|
+
process.exitCode = 1
|
|
54
|
+
} finally {
|
|
55
|
+
if (worker) await worker.close()
|
|
56
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// 本地向量目录/下载器的可复现核验脚本(§14.4)。
|
|
3
|
+
//
|
|
4
|
+
// node scripts/local-vector-verify.mjs # 只读:校验目录、manifest、来源与已安装制品
|
|
5
|
+
// node scripts/local-vector-verify.mjs --download # 显式联网下载(等同用户确认),默认落到临时目录
|
|
6
|
+
// node scripts/local-vector-verify.mjs --download --target <dir> --id <modelId>
|
|
7
|
+
//
|
|
8
|
+
// 边界:不读不写真实记忆库、不写 active profile;默认目标目录是仓库里的 .tmp-* 临时目录。
|
|
9
|
+
|
|
10
|
+
import path from 'node:path'
|
|
11
|
+
import { fileURLToPath } from 'node:url'
|
|
12
|
+
import {
|
|
13
|
+
getEmbeddingCatalog,
|
|
14
|
+
getEmbeddingModel,
|
|
15
|
+
resolveEmbeddingSources,
|
|
16
|
+
validateEmbeddingManifest,
|
|
17
|
+
} from '../lib/embedding-catalog.js'
|
|
18
|
+
import { createEmbeddingDownloadManager } from '../lib/embedding-download.js'
|
|
19
|
+
|
|
20
|
+
const HERE = path.dirname(fileURLToPath(import.meta.url))
|
|
21
|
+
const REPO = path.resolve(HERE, '..')
|
|
22
|
+
|
|
23
|
+
const argv = process.argv.slice(2)
|
|
24
|
+
const flag = name => argv.includes('--' + name)
|
|
25
|
+
const option = name => {
|
|
26
|
+
const index = argv.indexOf('--' + name)
|
|
27
|
+
return index >= 0 ? argv[index + 1] : undefined
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const download = flag('download')
|
|
31
|
+
const target = path.resolve(option('target') || path.join(REPO, '.tmp-local-vector-20261030', 'model-root'))
|
|
32
|
+
const only = option('id')
|
|
33
|
+
|
|
34
|
+
let failures = 0
|
|
35
|
+
const log = line => console.log(line)
|
|
36
|
+
const bad = line => { failures += 1; console.error('FAIL ' + line) }
|
|
37
|
+
|
|
38
|
+
log('== 目录校验(manifest schema / 固定 revision / 逐文件 hash 声明)')
|
|
39
|
+
for (const entry of getEmbeddingCatalog()) {
|
|
40
|
+
if (only && entry.id !== only) continue
|
|
41
|
+
const report = validateEmbeddingManifest(entry)
|
|
42
|
+
const source = resolveEmbeddingSources(entry)
|
|
43
|
+
log('-- ' + entry.id + ' [' + entry.availability + '] dim=' + entry.dimension + ' maxTokens=' + entry.maxTokens
|
|
44
|
+
+ ' bytes=' + entry.totalBytes + ' fingerprint=' + entry.profileFingerprint.slice(0, 12))
|
|
45
|
+
if (report.errors.length) for (const error of report.errors) bad(entry.id + ' ' + error.code + ': ' + error.message)
|
|
46
|
+
else log(' manifest ok, ' + report.warnings.length + ' warning(s)')
|
|
47
|
+
for (const warning of report.warnings) log(' warn ' + warning.code + ': ' + warning.message)
|
|
48
|
+
if (entry.availability === 'available') {
|
|
49
|
+
if (!source.primary) bad(entry.id + ' has no usable pinned source')
|
|
50
|
+
else {
|
|
51
|
+
log(' source=' + source.primary.provider + ' repo=' + source.primary.repo + ' revision=' + source.primary.revision)
|
|
52
|
+
for (const file of source.primary.files) log(' file ' + file.path + ' ' + file.bytes + 'B ' + file.sha256.slice(0, 16) + '…')
|
|
53
|
+
}
|
|
54
|
+
for (const skipped of source.skipped) log(' skipped ' + skipped.provider + ': ' + skipped.reason)
|
|
55
|
+
} else {
|
|
56
|
+
log(' pending, 无可下载制品:' + entry.pendingReasons.join(' / '))
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (download) {
|
|
61
|
+
const entry = getEmbeddingModel(only || 'bge-small-zh-v1.5-int8')
|
|
62
|
+
if (!entry) { bad('unknown model id: ' + only); process.exit(1) }
|
|
63
|
+
log('')
|
|
64
|
+
log('== 显式下载 ' + entry.id + ' → ' + target)
|
|
65
|
+
const manager = createEmbeddingDownloadManager({ root: target })
|
|
66
|
+
try {
|
|
67
|
+
const status = await manager.download(entry.id, { confirmed: true })
|
|
68
|
+
log('installed=' + status.installed + ' state=' + status.state + ' revision=' + status.revision + ' provider=' + status.provider)
|
|
69
|
+
log('checks: ' + status.checks.join(', '))
|
|
70
|
+
const deep = manager.getStatus(entry.id, { deep: true })
|
|
71
|
+
log('deep hash re-verify: ' + deep.verified)
|
|
72
|
+
if (deep.verified !== true) bad('installed artifact does not re-verify')
|
|
73
|
+
} catch (error) {
|
|
74
|
+
bad('download failed: ' + (error.code || '') + ' ' + error.message)
|
|
75
|
+
}
|
|
76
|
+
} else {
|
|
77
|
+
log('')
|
|
78
|
+
log('== 已安装状态(只读,deep=重新计算每个文件的 sha256)')
|
|
79
|
+
const manager = createEmbeddingDownloadManager({ root: target })
|
|
80
|
+
for (const entry of getEmbeddingCatalog()) {
|
|
81
|
+
if (only && entry.id !== only) continue
|
|
82
|
+
const status = manager.getStatus(entry.id, { deep: true })
|
|
83
|
+
log('-- ' + entry.id + ': state=' + status.state + ' installed=' + status.installed + ' active=' + status.active
|
|
84
|
+
+ (status.verified === null ? '' : ' deep=' + status.verified)
|
|
85
|
+
+ (status.error ? ' error=' + status.error.code : ''))
|
|
86
|
+
if (status.installed && status.verified === false) bad(entry.id + ' installed files no longer match their hashes')
|
|
87
|
+
}
|
|
88
|
+
log('(加 --download 才会联网;本脚本从不写 active profile)')
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
log('')
|
|
92
|
+
log(failures === 0 ? 'RESULT ok' : 'RESULT ' + failures + ' failure(s)')
|
|
93
|
+
process.exit(failures === 0 ? 0 : 1)
|