dsh-data-domain-knowledge 0.1.0-alpha.12 → 0.1.0-alpha.13
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 +15 -7
- package/lib/client.js +64 -7
- package/lib/types/environment/install.d.ts +2 -1
- package/lib/types/environment/install.d.ts.map +1 -1
- package/lib/types/environment/install.js +20 -11
- package/lib/types/environment/install.js.map +1 -1
- package/lib/types/environment/manifest.d.ts +203 -12
- package/lib/types/environment/manifest.d.ts.map +1 -1
- package/lib/types/environment/manifest.js +23 -21
- package/lib/types/environment/manifest.js.map +1 -1
- package/lib/types/environment/model-catalog.d.ts +191 -0
- package/lib/types/environment/model-catalog.d.ts.map +1 -0
- package/lib/types/environment/model-catalog.js +91 -0
- package/lib/types/environment/model-catalog.js.map +1 -0
- package/lib/types/environment/selection.d.ts +9 -0
- package/lib/types/environment/selection.d.ts.map +1 -0
- package/lib/types/environment/selection.js +29 -0
- package/lib/types/environment/selection.js.map +1 -0
- package/lib/types/environment/service.d.ts +1 -1
- package/lib/types/environment/service.d.ts.map +1 -1
- package/lib/types/environment/service.js +54 -33
- package/lib/types/environment/service.js.map +1 -1
- package/lib/types/environment/types.d.ts +7 -1
- package/lib/types/environment/types.d.ts.map +1 -1
- package/lib/types/images.d.ts +26 -0
- package/lib/types/images.d.ts.map +1 -0
- package/lib/types/images.js +196 -0
- package/lib/types/images.js.map +1 -0
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +25 -3
- package/lib/types/index.js.map +1 -1
- package/lib/types/layout.d.ts +2 -0
- package/lib/types/layout.d.ts.map +1 -1
- package/lib/types/layout.js +2 -0
- package/lib/types/layout.js.map +1 -1
- package/lib/types/runtime.d.ts +9 -3
- package/lib/types/runtime.d.ts.map +1 -1
- package/lib/types/runtime.js +73 -16
- package/lib/types/runtime.js.map +1 -1
- package/lib/types/store/migrate.d.ts.map +1 -1
- package/lib/types/store/migrate.js +5 -4
- package/lib/types/store/migrate.js.map +1 -1
- package/lib/types/types.d.ts +1 -1
- package/lib/types/types.d.ts.map +1 -1
- package/lib/types/types.js +3 -4
- package/lib/types/types.js.map +1 -1
- package/package.json +1 -1
- package/python/prepare-models.py +1 -1
- package/scripts/setup-embedding.sh +11 -2
- package/src/client/EnvironmentSetup.tsx +54 -8
- package/src/environment/install.ts +22 -10
- package/src/environment/manifest.ts +25 -22
- package/src/environment/model-catalog.ts +105 -0
- package/src/environment/selection.ts +29 -0
- package/src/environment/service.ts +46 -26
- package/src/environment/types.ts +5 -1
- package/src/images.ts +203 -0
- package/src/index.ts +30 -4
- package/src/layout.ts +2 -0
- package/src/runtime.ts +68 -16
- package/src/store/migrate.ts +4 -4
- package/src/types.ts +4 -5
package/src/runtime.ts
CHANGED
|
@@ -44,7 +44,7 @@ import {
|
|
|
44
44
|
RELATED_METADATA_KEY, type DeadLink, type RelatedEntry,
|
|
45
45
|
} from './wiki-finalize.js'
|
|
46
46
|
import { applyRerank, applyThreshold, evidenceExcerpts, graphBoost, matchOf, noteIndexText, reviewWeight, rrfFuse, tokenSet, type RouteRanking } from './recall-rank.js'
|
|
47
|
-
import { ASSETS_DIR, KNOWLEDGE_DB_FILE, SOURCE_RAW_DIR, defaultAgentRoot, knowledgeBaseRoot } from './layout.js'
|
|
47
|
+
import { ASSETS_DIR, KNOWLEDGE_DB_FILE, PUBLISHED_IMAGE_MARKER, SOURCE_RAW_DIR, defaultAgentRoot, knowledgeBaseRoot } from './layout.js'
|
|
48
48
|
import { KnowledgeDb, type CardKnowledgeRow, type Journal as StoreJournal, type SourceOriginal } from './store/knowledge-db.js'
|
|
49
49
|
import { exportKnowledgeTree as exportTree, migrateKnowledgeTree } from './store/migrate.js'
|
|
50
50
|
import { COMPILE_SNAPSHOT, type CompileSnapshot } from './compiler/snapshot.js'
|
|
@@ -59,6 +59,8 @@ import { unlinkIfExists, writeBufferAtomic, writeTextAtomic } from './fs-atomic.
|
|
|
59
59
|
import { envelopeFailure, envelopeSuccess } from 'dsh-data-contracts/envelope'
|
|
60
60
|
import type { LineageContext, LineageRef } from 'dsh-data-contracts/refs'
|
|
61
61
|
import { analyzeQuery, relationCandidates, type QueryAnalysis } from './query-analysis.js'
|
|
62
|
+
import { readModelSelection, selectedEmbedding } from './environment/selection.js'
|
|
63
|
+
import { rerankModel as rerankModelOption } from './environment/model-catalog.js'
|
|
62
64
|
|
|
63
65
|
// **知识树只有三段**(v1.1 起):`01_source_raw` / `02_wiki` / `03_compiled`。
|
|
64
66
|
//
|
|
@@ -1719,8 +1721,8 @@ export interface WikiSchemaSnapshot {
|
|
|
1719
1721
|
/**
|
|
1720
1722
|
* 解析三个「家当」路径(0029)。
|
|
1721
1723
|
*
|
|
1722
|
-
*
|
|
1723
|
-
*
|
|
1724
|
+
* 资源目录内的隔离解释器优先于 DSH_PYTHON;自定义配置路径仍由构造函数处理。
|
|
1725
|
+
* 未准备资源时回退到 DSH_PYTHON、`python3` 或 HF 模型名。
|
|
1724
1726
|
*
|
|
1725
1727
|
* `modelId` 把本地模型的**身份**与它的位置解耦,避免同一 Agent 根目录下的
|
|
1726
1728
|
* resource 目录变化导致向量清单误判为配置不匹配。
|
|
@@ -1729,6 +1731,7 @@ function resolveResources(resourceRoot: string | undefined): {
|
|
|
1729
1731
|
python: string
|
|
1730
1732
|
embeddingModel: string
|
|
1731
1733
|
rerankModel: string
|
|
1734
|
+
embeddingDimension?: number
|
|
1732
1735
|
modelId: (path: string) => string
|
|
1733
1736
|
} {
|
|
1734
1737
|
const root = resourceRoot ? resolve(resourceRoot) : undefined
|
|
@@ -1739,14 +1742,20 @@ function resolveResources(resourceRoot: string | undefined): {
|
|
|
1739
1742
|
const python = firstExisting([
|
|
1740
1743
|
root && process.platform === 'win32' ? join(root, 'python', 'Scripts', 'python.exe') : undefined,
|
|
1741
1744
|
root ? join(root, 'python', 'bin', 'python') : undefined,
|
|
1742
|
-
], 'python3')
|
|
1745
|
+
], process.env.DSH_PYTHON || 'python3')
|
|
1746
|
+
const selected = selectedEmbedding(root)
|
|
1747
|
+
const selection = root ? readModelSelection(root) : undefined
|
|
1748
|
+
const selectedRerank = root && selection ? join(root, 'models', rerankModelOption(selection.rerank).directory) : undefined
|
|
1743
1749
|
const embeddingModel = firstExisting([
|
|
1750
|
+
selected?.path,
|
|
1744
1751
|
root ? join(root, 'models', PREFERRED_LOCAL_EMBEDDING_MODEL_DIR) : undefined,
|
|
1745
1752
|
root ? join(root, 'models', LOCAL_EMBEDDING_MODEL_DIR) : undefined,
|
|
1746
1753
|
], FALLBACK_EMBEDDING_MODEL)
|
|
1747
1754
|
const rerankModel = firstExisting([
|
|
1755
|
+
selectedRerank,
|
|
1748
1756
|
root ? join(root, 'models', LOCAL_RERANK_MODEL_DIR) : undefined,
|
|
1749
1757
|
], FALLBACK_RERANK_MODEL)
|
|
1758
|
+
const embeddingDimension = selected?.path && embeddingModel === selected.path ? selected.dimension : undefined
|
|
1750
1759
|
const modelId = (path: string): string => {
|
|
1751
1760
|
for (const base of [root]) {
|
|
1752
1761
|
if (!base || !path.startsWith(`${base}${sep}`)) continue
|
|
@@ -1755,7 +1764,14 @@ function resolveResources(resourceRoot: string | undefined): {
|
|
|
1755
1764
|
}
|
|
1756
1765
|
return path
|
|
1757
1766
|
}
|
|
1758
|
-
return { python, embeddingModel, rerankModel, modelId }
|
|
1767
|
+
return { python, embeddingModel, rerankModel, embeddingDimension, modelId }
|
|
1768
|
+
}
|
|
1769
|
+
|
|
1770
|
+
/** An inherited DSH_PYTHON value selects the base interpreter, not the dependency environment. */
|
|
1771
|
+
function resolveConfiguredPython(configured: string | undefined, resourcePython: string): string {
|
|
1772
|
+
const selected = configured?.trim()
|
|
1773
|
+
const hostPython = process.env.DSH_PYTHON?.trim()
|
|
1774
|
+
return selected && selected !== hostPython ? selected : resourcePython
|
|
1759
1775
|
}
|
|
1760
1776
|
|
|
1761
1777
|
export interface KnowledgeRuntimeOptions {
|
|
@@ -1773,7 +1789,7 @@ export interface KnowledgeRuntimeOptions {
|
|
|
1773
1789
|
* 0029:家当目录(`<agentRoot>/resource`)。
|
|
1774
1790
|
*
|
|
1775
1791
|
* 配置里那三个路径字段(`python` / `embeddingModel` / `rerankModel`)为空时,
|
|
1776
|
-
*
|
|
1792
|
+
* 它们从这里解析。省略时使用 DSH Python、系统命令或模型名作为兜底 —— 独立运行时与单元
|
|
1777
1793
|
* 测试不必先造一个根路径。
|
|
1778
1794
|
*/
|
|
1779
1795
|
resourceRoot?: string
|
|
@@ -2161,7 +2177,8 @@ export class KnowledgeRuntime {
|
|
|
2161
2177
|
this.modelSeamProvider = options.modelSeam
|
|
2162
2178
|
this.resourceRoot = options.resourceRoot
|
|
2163
2179
|
const resources = resolveResources(options.resourceRoot)
|
|
2164
|
-
this.
|
|
2180
|
+
if (!this.config.embeddingModel?.trim() && resources.embeddingDimension) this.config.embeddingDimension = resources.embeddingDimension
|
|
2181
|
+
this.pythonPath = resolveConfiguredPython(config.python, resources.python)
|
|
2165
2182
|
this.embeddingModelPath = config.embeddingModel?.trim() || resources.embeddingModel
|
|
2166
2183
|
this.rerankModelPath = config.rerankModel?.trim() || resources.rerankModel
|
|
2167
2184
|
this.embeddingModelId = resources.modelId(this.embeddingModelPath)
|
|
@@ -2201,6 +2218,10 @@ export class KnowledgeRuntime {
|
|
|
2201
2218
|
updateConfig(patch: Partial<RuntimeKnowledgeConfig>): void {
|
|
2202
2219
|
const previous = this.config
|
|
2203
2220
|
this.config = { ...previous, ...patch }
|
|
2221
|
+
const resources = resolveResources(this.resourceRoot)
|
|
2222
|
+
// A managed model preset owns its output dimension. Custom model paths keep
|
|
2223
|
+
// the advanced dimension setting from the plugin config.
|
|
2224
|
+
if (!this.config.embeddingModel?.trim() && resources.embeddingDimension) this.config.embeddingDimension = resources.embeddingDimension
|
|
2204
2225
|
|
|
2205
2226
|
const modelChanged =
|
|
2206
2227
|
previous.embeddingProvider !== this.config.embeddingProvider ||
|
|
@@ -2227,8 +2248,7 @@ export class KnowledgeRuntime {
|
|
|
2227
2248
|
this.rerankWorker = null
|
|
2228
2249
|
this.rerankUnavailable = undefined
|
|
2229
2250
|
}
|
|
2230
|
-
|
|
2231
|
-
if (patch.python !== undefined) this.pythonPath = patch.python.trim() || resources.python
|
|
2251
|
+
this.pythonPath = resolveConfiguredPython(this.config.python, resources.python)
|
|
2232
2252
|
if (patch.embeddingModel !== undefined) this.embeddingModelPath = patch.embeddingModel.trim() || resources.embeddingModel
|
|
2233
2253
|
if (patch.rerankModel !== undefined) this.rerankModelPath = patch.rerankModel.trim() || resources.rerankModel
|
|
2234
2254
|
this.embeddingModelId = resources.modelId(this.embeddingModelPath)
|
|
@@ -7454,7 +7474,7 @@ export class KnowledgeRuntime {
|
|
|
7454
7474
|
const cardOrphans = cards.filter(card => card.sourceRefs.every(ref => !live.has(ref))).length
|
|
7455
7475
|
const events = (await this.loadEvents()).filter(event => event.records.some(record => !live.has(record.sourceId))).length
|
|
7456
7476
|
/**
|
|
7457
|
-
*
|
|
7477
|
+
* 未被引用的来源图片也进这张账(2026-10-05)。`images.publish` 写入的独立图片会被识别并保留。
|
|
7458
7478
|
*
|
|
7459
7479
|
* 少了它,「来源与卡片都清完了、只剩一堆没人引用的图」这种状态在界面上**一个字都没有**
|
|
7460
7480
|
* —— 清理孤儿的入口因此不出现,而用户唯一能看到的读数(「收录 N 个文件」)又把它算成
|
|
@@ -7462,7 +7482,7 @@ export class KnowledgeRuntime {
|
|
|
7462
7482
|
* 「按一下能清掉几个」是同几个。
|
|
7463
7483
|
*/
|
|
7464
7484
|
const names = await this.assetBucketNames()
|
|
7465
|
-
const assets = names.length === 0 ? 0 : this.unreferencedBuckets(names, sources, cards).length
|
|
7485
|
+
const assets = names.length === 0 ? 0 : (await this.unreferencedBuckets(names, sources, cards)).length
|
|
7466
7486
|
return { knowledge, cards: cardOrphans, events, assets }
|
|
7467
7487
|
}
|
|
7468
7488
|
|
|
@@ -7490,8 +7510,9 @@ export class KnowledgeRuntime {
|
|
|
7490
7510
|
}
|
|
7491
7511
|
|
|
7492
7512
|
/**
|
|
7493
|
-
* 还有没有人在引用这个桶 ——
|
|
7494
|
-
*
|
|
7513
|
+
* 还有没有人在引用这个桶 —— 判据是来源正文、来源 front matter 的 `metadata.assets`、
|
|
7514
|
+
* 卡片正文出现过 `_assets/<bucket>/`,或者它是 `images.publish` 写入的独立图床图片。
|
|
7515
|
+
* 独立图片没有来源/卡片引用是正常状态,必须保留。
|
|
7495
7516
|
*
|
|
7496
7517
|
* 为什么三处都要看:桶名是**内容哈希的前 12 位**(`convert/ingest.ts`),不是来源 id。
|
|
7497
7518
|
* 同一份内容以两个分类导入会共用同一个桶;而卡片正文会把图片引用**复制进去**
|
|
@@ -7499,7 +7520,7 @@ export class KnowledgeRuntime {
|
|
|
7499
7520
|
*
|
|
7500
7521
|
* 不看 `03_compiled/`:那是派生物,下一次 `rebuild` 会把指向已删图片的引用一起扔掉。
|
|
7501
7522
|
*/
|
|
7502
|
-
private unreferencedBuckets(names: string[], sources: SourceRaw[], cards: KnowledgeCard[]): string[] {
|
|
7523
|
+
private async unreferencedBuckets(names: string[], sources: SourceRaw[], cards: KnowledgeCard[]): Promise<string[]> {
|
|
7503
7524
|
const referenced = new Set<string>()
|
|
7504
7525
|
/**
|
|
7505
7526
|
* **两种引用形式都算**:`assets/<桶>/`(新写的)与 `_assets/<桶>/`(存量)。
|
|
@@ -7518,7 +7539,38 @@ export class KnowledgeRuntime {
|
|
|
7518
7539
|
if (Array.isArray(source.metadata.assets)) for (const item of source.metadata.assets) if (typeof item === 'string') collect(item)
|
|
7519
7540
|
}
|
|
7520
7541
|
for (const card of cards) collect(card.content)
|
|
7521
|
-
|
|
7542
|
+
const unreferenced: string[] = []
|
|
7543
|
+
for (const name of names) {
|
|
7544
|
+
if (referenced.has(name)) continue
|
|
7545
|
+
if (await this.isPublishedImageBucket(name)) continue
|
|
7546
|
+
unreferenced.push(name)
|
|
7547
|
+
}
|
|
7548
|
+
return unreferenced
|
|
7549
|
+
}
|
|
7550
|
+
|
|
7551
|
+
/**
|
|
7552
|
+
* `images.publish` 的图片不需要被来源或卡片引用。新写入的桶有明确标记;同时兼容
|
|
7553
|
+
* 标记加入前已发布的图片:它们以图片自身的 SHA-256 前 12 位作为桶名。
|
|
7554
|
+
*/
|
|
7555
|
+
private async isPublishedImageBucket(name: string): Promise<boolean> {
|
|
7556
|
+
for (const root of this.assetRoots()) {
|
|
7557
|
+
if (existsSync(join(root, name, PUBLISHED_IMAGE_MARKER))) return true
|
|
7558
|
+
}
|
|
7559
|
+
|
|
7560
|
+
// 旧版本 images.publish 只会写入知识库根目录的 assets/,因此只在这里做哈希识别。
|
|
7561
|
+
if (!/^[a-f0-9]{12}$/i.test(name)) return false
|
|
7562
|
+
const dir = join(this.rootDir, ASSETS_DIR, name)
|
|
7563
|
+
try {
|
|
7564
|
+
for (const entry of await readdir(dir, { withFileTypes: true })) {
|
|
7565
|
+
if (!entry.isFile() || !/\.(?:png|jpe?g|gif|webp|avif|bmp|svg)$/i.test(entry.name)) continue
|
|
7566
|
+
const path = join(dir, entry.name)
|
|
7567
|
+
const details = await stat(path)
|
|
7568
|
+
if (details.size > 20 * 1024 * 1024) continue
|
|
7569
|
+
const bytes = await readFile(path)
|
|
7570
|
+
if (createHash('sha256').update(bytes).digest('hex').slice(0, 12) === name) return true
|
|
7571
|
+
}
|
|
7572
|
+
} catch { /* 桶目录可能在扫描期间被删除 */ }
|
|
7573
|
+
return false
|
|
7522
7574
|
}
|
|
7523
7575
|
|
|
7524
7576
|
/**
|
|
@@ -7588,7 +7640,7 @@ export class KnowledgeRuntime {
|
|
|
7588
7640
|
if (names.length === 0) return { buckets: 0, files: 0, problems: [] }
|
|
7589
7641
|
const problems: string[] = []
|
|
7590
7642
|
const cards = this.cards.length > 0 ? this.cards : await this.loadCards()
|
|
7591
|
-
const doomed = this.unreferencedBuckets(names, await this.loadSources(), cards)
|
|
7643
|
+
const doomed = await this.unreferencedBuckets(names, await this.loadSources(), cards)
|
|
7592
7644
|
let buckets = 0
|
|
7593
7645
|
let files = 0
|
|
7594
7646
|
for (const name of doomed) {
|
package/src/store/migrate.ts
CHANGED
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
import { existsSync } from 'node:fs'
|
|
36
36
|
import { cp, mkdir, readFile, readdir, rename, rm, writeFile } from 'node:fs/promises'
|
|
37
37
|
import { basename, dirname, join, relative } from 'node:path'
|
|
38
|
-
import { ASSETS_DIR, SOURCE_RAW_DIR } from '../layout.js'
|
|
38
|
+
import { ASSETS_DIR, PUBLISHED_IMAGE_MARKER, SOURCE_RAW_DIR } from '../layout.js'
|
|
39
39
|
import type { KnowledgeCard, SourceRaw } from '../types.js'
|
|
40
40
|
import type { KnowledgeDb } from './knowledge-db.js'
|
|
41
41
|
|
|
@@ -300,7 +300,7 @@ export async function migrateKnowledgeTree(input: MigrationInput): Promise<Migra
|
|
|
300
300
|
const to = join(kbRoot, ASSETS_DIR, bucket)
|
|
301
301
|
report.assetBuckets += 1
|
|
302
302
|
if (dryRun) {
|
|
303
|
-
report.assetFiles += (await listFiles(from, '')).length
|
|
303
|
+
report.assetFiles += (await listFiles(from, '')).filter(path => basename(path) !== PUBLISHED_IMAGE_MARKER).length
|
|
304
304
|
continue
|
|
305
305
|
}
|
|
306
306
|
try {
|
|
@@ -316,7 +316,7 @@ export async function migrateKnowledgeTree(input: MigrationInput): Promise<Migra
|
|
|
316
316
|
*/
|
|
317
317
|
if (!existsSync(to)) await rename(from, to)
|
|
318
318
|
else await mergeAssetBucket(from, to)
|
|
319
|
-
report.assetFiles += (await listFiles(to, '')).length
|
|
319
|
+
report.assetFiles += (await listFiles(to, '')).filter(path => basename(path) !== PUBLISHED_IMAGE_MARKER).length
|
|
320
320
|
} catch (cause) {
|
|
321
321
|
problems.push(`搬不动图片桶 ${bucket}:${cause instanceof Error ? cause.message : String(cause)}`)
|
|
322
322
|
}
|
|
@@ -565,7 +565,7 @@ async function copyContentTree(from: string, to: string, overwrite: boolean): Pr
|
|
|
565
565
|
if (!overwrite && existsSync(target)) continue
|
|
566
566
|
await mkdir(dirname(target), { recursive: true })
|
|
567
567
|
await cp(source, target, { force: true })
|
|
568
|
-
copied += 1
|
|
568
|
+
if (basename(source) !== PUBLISHED_IMAGE_MARKER) copied += 1
|
|
569
569
|
}
|
|
570
570
|
return copied
|
|
571
571
|
}
|
package/src/types.ts
CHANGED
|
@@ -130,7 +130,7 @@ export interface KnowledgeConfig {
|
|
|
130
130
|
* 组织(表达层)。两步都开着才是完整的「Knowledge Compiler + Wiki Materializer」。
|
|
131
131
|
*/
|
|
132
132
|
cardOrganize: boolean
|
|
133
|
-
/** Python 解释器。空串 =
|
|
133
|
+
/** Python 解释器。空串 = 自动(隔离依赖环境,再退回 `DSH_PYTHON` / `python3`)。 */
|
|
134
134
|
python: string
|
|
135
135
|
embeddingProvider: EmbeddingProvider
|
|
136
136
|
embeddingBaseUrl: string
|
|
@@ -231,10 +231,9 @@ export const DEFAULT_KNOWLEDGE_CONFIG: RuntimeKnowledgeConfig = {
|
|
|
231
231
|
// 卡片组织(模型)默认**开**:Knowledge 只是事实层,Wiki 卡片正文必须经过这一步
|
|
232
232
|
// 物化成可读的表达。只有显式写 `false` / `0` 才关闭兼容路径。
|
|
233
233
|
cardOrganize: !['false', '0'].includes((process.env.DSH_KNOWLEDGE_CARD_ORGANIZE ?? '').trim().toLowerCase()),
|
|
234
|
-
// 0029
|
|
235
|
-
//
|
|
236
|
-
|
|
237
|
-
python: process.env.DSH_PYTHON || '',
|
|
234
|
+
// 0029:空串 = 自动。优先用 `<agentRoot>/resource/python/` 的隔离依赖环境;
|
|
235
|
+
// DSH_PYTHON 只作为创建该环境的基础解释器和未准备时的运行时回退。
|
|
236
|
+
python: '',
|
|
238
237
|
embeddingProvider: 'wemm-python',
|
|
239
238
|
embeddingBaseUrl: process.env.DSH_EMBEDDING_BASE_URL || 'http://127.0.0.1:11434',
|
|
240
239
|
embeddingModel: process.env.DSH_EMBEDDING_MODEL || process.env.DSH_EMBEDDING_MODEL_PATH || '',
|