@llangtop/pwiki-core 0.3.3 → 0.4.1

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.
Files changed (141) hide show
  1. package/dist/WikiEngine.d.ts +25 -37
  2. package/dist/WikiEngine.d.ts.map +1 -1
  3. package/dist/WikiEngine.js +157 -298
  4. package/dist/WikiEngine.js.map +1 -1
  5. package/dist/ast-chunker.d.ts +23 -0
  6. package/dist/ast-chunker.d.ts.map +1 -0
  7. package/dist/ast-chunker.js +434 -0
  8. package/dist/ast-chunker.js.map +1 -0
  9. package/dist/content-cache.d.ts +13 -0
  10. package/dist/content-cache.d.ts.map +1 -0
  11. package/dist/content-cache.js +33 -0
  12. package/dist/content-cache.js.map +1 -0
  13. package/dist/embedder.d.ts +38 -0
  14. package/dist/embedder.d.ts.map +1 -0
  15. package/dist/embedder.js +267 -0
  16. package/dist/embedder.js.map +1 -0
  17. package/dist/file-manifest.d.ts +46 -0
  18. package/dist/file-manifest.d.ts.map +1 -0
  19. package/dist/file-manifest.js +121 -0
  20. package/dist/file-manifest.js.map +1 -0
  21. package/dist/index.d.ts +18 -8
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +25 -7
  24. package/dist/index.js.map +1 -1
  25. package/dist/indexer-compile.d.ts +20 -0
  26. package/dist/indexer-compile.d.ts.map +1 -0
  27. package/dist/indexer-compile.js +198 -0
  28. package/dist/indexer-compile.js.map +1 -0
  29. package/dist/indexer-embed.d.ts +21 -0
  30. package/dist/indexer-embed.d.ts.map +1 -0
  31. package/dist/indexer-embed.js +248 -0
  32. package/dist/indexer-embed.js.map +1 -0
  33. package/dist/indexer-scan.d.ts +4 -0
  34. package/dist/indexer-scan.d.ts.map +1 -0
  35. package/dist/indexer-scan.js +51 -0
  36. package/dist/indexer-scan.js.map +1 -0
  37. package/dist/indexer.d.ts +4 -0
  38. package/dist/indexer.d.ts.map +1 -0
  39. package/dist/indexer.js +7 -0
  40. package/dist/indexer.js.map +1 -0
  41. package/dist/model-registry.d.ts +32 -0
  42. package/dist/model-registry.d.ts.map +1 -0
  43. package/dist/model-registry.js +82 -0
  44. package/dist/model-registry.js.map +1 -0
  45. package/dist/parser.d.ts +9 -0
  46. package/dist/parser.d.ts.map +1 -0
  47. package/dist/parser.js +54 -0
  48. package/dist/parser.js.map +1 -0
  49. package/dist/preprocessor.d.ts +36 -0
  50. package/dist/preprocessor.d.ts.map +1 -0
  51. package/dist/preprocessor.js +209 -0
  52. package/dist/preprocessor.js.map +1 -0
  53. package/dist/search.d.ts +6 -0
  54. package/dist/search.d.ts.map +1 -0
  55. package/dist/search.js +81 -0
  56. package/dist/search.js.map +1 -0
  57. package/dist/semantic-compiler.d.ts +44 -0
  58. package/dist/semantic-compiler.d.ts.map +1 -0
  59. package/dist/semantic-compiler.js +376 -0
  60. package/dist/semantic-compiler.js.map +1 -0
  61. package/dist/semantic-search.d.ts +11 -0
  62. package/dist/semantic-search.d.ts.map +1 -0
  63. package/dist/semantic-search.js +217 -0
  64. package/dist/semantic-search.js.map +1 -0
  65. package/dist/store-settings.d.ts +32 -0
  66. package/dist/store-settings.d.ts.map +1 -0
  67. package/dist/store-settings.js +138 -0
  68. package/dist/store-settings.js.map +1 -0
  69. package/dist/store-vectors.d.ts +13 -0
  70. package/dist/store-vectors.d.ts.map +1 -0
  71. package/dist/store-vectors.js +101 -0
  72. package/dist/store-vectors.js.map +1 -0
  73. package/dist/store.d.ts +11 -0
  74. package/dist/store.d.ts.map +1 -0
  75. package/dist/store.js +28 -0
  76. package/dist/store.js.map +1 -0
  77. package/dist/types.d.ts +75 -92
  78. package/dist/types.d.ts.map +1 -1
  79. package/dist/types.js +1 -1
  80. package/dist/types.js.map +1 -1
  81. package/dist/wiki-paths.d.ts +3 -0
  82. package/dist/wiki-paths.d.ts.map +1 -0
  83. package/dist/wiki-paths.js +13 -0
  84. package/dist/wiki-paths.js.map +1 -0
  85. package/package.json +38 -36
  86. package/dist/compile/compiler.d.ts +0 -39
  87. package/dist/compile/compiler.d.ts.map +0 -1
  88. package/dist/compile/compiler.js +0 -227
  89. package/dist/compile/compiler.js.map +0 -1
  90. package/dist/compile/index.d.ts +0 -3
  91. package/dist/compile/index.d.ts.map +0 -1
  92. package/dist/compile/index.js +0 -2
  93. package/dist/compile/index.js.map +0 -1
  94. package/dist/embed/WikiEmbedder.d.ts +0 -28
  95. package/dist/embed/WikiEmbedder.d.ts.map +0 -1
  96. package/dist/embed/WikiEmbedder.js +0 -147
  97. package/dist/embed/WikiEmbedder.js.map +0 -1
  98. package/dist/embed/index.d.ts +0 -2
  99. package/dist/embed/index.d.ts.map +0 -1
  100. package/dist/embed/index.js +0 -2
  101. package/dist/embed/index.js.map +0 -1
  102. package/dist/llm/WikiLLM.d.ts +0 -24
  103. package/dist/llm/WikiLLM.d.ts.map +0 -1
  104. package/dist/llm/WikiLLM.js +0 -46
  105. package/dist/llm/WikiLLM.js.map +0 -1
  106. package/dist/llm/index.d.ts +0 -3
  107. package/dist/llm/index.d.ts.map +0 -1
  108. package/dist/llm/index.js +0 -2
  109. package/dist/llm/index.js.map +0 -1
  110. package/dist/models.d.ts +0 -5
  111. package/dist/models.d.ts.map +0 -1
  112. package/dist/models.js +0 -54
  113. package/dist/models.js.map +0 -1
  114. package/dist/search/WikiSearch.d.ts +0 -14
  115. package/dist/search/WikiSearch.d.ts.map +0 -1
  116. package/dist/search/WikiSearch.js +0 -223
  117. package/dist/search/WikiSearch.js.map +0 -1
  118. package/dist/search/index.d.ts +0 -2
  119. package/dist/search/index.d.ts.map +0 -1
  120. package/dist/search/index.js +0 -2
  121. package/dist/search/index.js.map +0 -1
  122. package/dist/store/WikiStore.d.ts +0 -47
  123. package/dist/store/WikiStore.d.ts.map +0 -1
  124. package/dist/store/WikiStore.js +0 -301
  125. package/dist/store/WikiStore.js.map +0 -1
  126. package/dist/store/index.d.ts +0 -2
  127. package/dist/store/index.d.ts.map +0 -1
  128. package/dist/store/index.js +0 -2
  129. package/dist/store/index.js.map +0 -1
  130. package/dist/util/fs.d.ts +0 -7
  131. package/dist/util/fs.d.ts.map +0 -1
  132. package/dist/util/fs.js +0 -36
  133. package/dist/util/fs.js.map +0 -1
  134. package/dist/util/index.d.ts +0 -3
  135. package/dist/util/index.d.ts.map +0 -1
  136. package/dist/util/index.js +0 -3
  137. package/dist/util/index.js.map +0 -1
  138. package/dist/util/paths.d.ts +0 -17
  139. package/dist/util/paths.d.ts.map +0 -1
  140. package/dist/util/paths.js +0 -31
  141. package/dist/util/paths.js.map +0 -1
@@ -1,34 +1,25 @@
1
- import { WikiStore } from "./store/index.js";
2
- import { WikiSearch } from "./search/index.js";
3
- import type { EngineConfig, SearchHit, SearchMode, FileEntry, RawChunk, WikiStatus, ModelInfo } from "./types.js";
1
+ import type { ModelInfo } from "./model-registry.js";
2
+ import type { SearchMode, SearchHit, FileEntry } from "./types.js";
3
+ export interface EngineConfig {
4
+ basePath?: string;
5
+ modelId?: string;
6
+ }
4
7
  export declare class WikiEngine {
5
- readonly store: WikiStore;
6
- readonly search: WikiSearch;
7
- private embedder;
8
- private llm;
9
8
  constructor(config?: EngineConfig);
10
9
  get sources(): string[];
11
10
  addSource(absPath: string): boolean;
12
11
  removeSource(target: string): string | null;
12
+ loadSource(absPath: string): Promise<number>;
13
13
  load(): Promise<{
14
14
  files: number;
15
15
  sources: number;
16
16
  }>;
17
- loadSource(absPath: string): Promise<number>;
18
17
  search_(query: string, mode?: SearchMode): Promise<SearchHit[]>;
19
- /** 创建 .md 条目 */
20
18
  createEntry(sourceDir: string, relPath: string, title: string, tags?: string[], content?: string): string;
21
- /** 读取条目全文 */
22
19
  readEntry(relPath: string): {
23
20
  entry: FileEntry;
24
21
  content: string;
25
22
  } | null;
26
- /** 重命名(修改 frontmatter title) */
27
- renameEntry(relPath: string, newTitle: string): boolean;
28
- /** 移动文件 */
29
- moveEntry(relPath: string, newRelPath: string): boolean;
30
- /** 修改内容 */
31
- modifyEntry(relPath: string, content: string): boolean;
32
23
  get semanticEnabled(): boolean;
33
24
  enableSemantic(modelId?: string): Promise<{
34
25
  ok: boolean;
@@ -39,43 +30,40 @@ export declare class WikiEngine {
39
30
  embedded: number;
40
31
  skipped: number;
41
32
  }>;
42
- /** 获取待编译的原始块 */
43
- getRawChunks(sourceDir?: string, uncompiledOnly?: boolean): RawChunk[];
44
- /** 获取编译 prompt(调用方自行调 LLM) */
45
- getCompilePrompt(relPath: string): {
46
- system: string;
47
- user: string;
48
- } | null;
49
- /** 存储编译结果 */
50
- storeCompiled(relPath: string, result: {
51
- topic: string;
52
- normalizedText: string;
53
- concepts: string[];
54
- aliases: string[];
55
- }): void;
56
- /** 编译状态查询 */
57
- compileStatus(sourceDir?: string): {
58
- total: number;
33
+ downloadModel(): Promise<{
34
+ ok: boolean;
35
+ msg: string;
36
+ }>;
37
+ status(): {
38
+ configPath: string;
39
+ sources: string[];
40
+ files: number;
41
+ lastScan: string;
42
+ semantic: boolean;
43
+ embeddings: number;
44
+ model: string;
45
+ modelDim: number;
59
46
  compiled: number;
60
- uncompiled: string[];
61
47
  };
62
48
  get llmInfo(): {
63
49
  apiBase: string;
64
50
  model: string;
65
51
  hasKey: boolean;
66
52
  };
67
- /** 编译单个文件(调 LLM + 存储结果) */
68
53
  compileFile(relPath: string): Promise<{
69
54
  ok: boolean;
70
55
  msg: string;
71
56
  }>;
72
- /** 批量编译 */
73
57
  compileAll(sourceDir?: string, limit?: number): Promise<{
74
58
  compiled: number;
75
59
  failed: number;
76
60
  msgs: string[];
77
61
  }>;
78
- status(): WikiStatus;
62
+ compileStatus(_sourceDir?: string): {
63
+ total: number;
64
+ compiled: number;
65
+ uncompiled: string[];
66
+ };
79
67
  listModels(): ModelInfo[];
80
68
  }
81
69
  //# sourceMappingURL=WikiEngine.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"WikiEngine.d.ts","sourceRoot":"","sources":["../src/WikiEngine.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"WikiEngine.d.ts","sourceRoot":"","sources":["../src/WikiEngine.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAInE,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,UAAU;gBACT,MAAM,GAAE,YAAiB;IAMrC,IAAI,OAAO,IAAI,MAAM,EAAE,CAA+B;IAEtD,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAInC,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAIrC,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAY5C,IAAI,IAAI,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAUnD,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,GAAE,UAAsB,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAShF,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,GAAE,MAAM,EAAO,EAAE,OAAO,SAAK,GAAG,MAAM;IA0BzG,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG;QAAE,KAAK,EAAE,SAAS,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IASxE,IAAI,eAAe,IAAI,OAAO,CAAuC;IAE/D,cAAc,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IAY7E,eAAe,IAAI,IAAI;IAKjB,kBAAkB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAUtF,aAAa,IAAI,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IAK5D,MAAM;;;;;;;;;;;IAiBN,IAAI,OAAO;;;;MASV;IAEK,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IA2CnE,UAAU,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IAe/G,aAAa,CAAC,UAAU,CAAC,EAAE,MAAM;;;;;IAYjC,UAAU,IAAI,SAAS,EAAE;CAG1B"}
@@ -1,371 +1,230 @@
1
- // WikiEngine.ts — 协调层:组装 Store / Search / Embedder / Compiler
2
- import { existsSync, renameSync, mkdirSync, writeFileSync } from "node:fs";
3
- import { resolve, dirname } from "node:path";
4
- import { createHash } from "node:crypto";
5
- import { WikiStore } from "./store/index.js";
6
- import { WikiSearch } from "./search/index.js";
7
- import { WikiEmbedder } from "./embed/index.js";
8
- import { defaultModel, findModel } from "./models.js";
9
- import { chunkByHeadings, preprocess, buildEmbeddingText, COMPILE_SYSTEM_PROMPT, buildCompilePrompt, parseCompileResult, getCompileStats, markCompiled } from "./compile/index.js";
10
- import { WikiLLM } from "./llm/index.js";
11
- function defaultBasePath() {
12
- const home = process.env.HOME || process.env.USERPROFILE || process.cwd();
13
- return `${home}/.Pwiki`;
14
- }
1
+ // WikiEngine.ts — Adapter: extensions/lib API WikiEngine class (CLI-compatible)
2
+ import { resolve } from "node:path";
3
+ import { existsSync, mkdirSync, writeFileSync, readFileSync } from "node:fs";
4
+ import { dirname } from "node:path";
5
+ import { setWikiHome, getWikiHome } from "./wiki-paths.js";
6
+ import * as store from "./store-settings.js";
7
+ import * as embedder from "./embedder.js";
8
+ import { keywordSearch } from "./search.js";
9
+ import { semanticSearch, hybridSearch } from "./semantic-search.js";
10
+ import { scanDir } from "./indexer-scan.js";
11
+ import { generateEmbeddings as doGenerateEmbeddings } from "./indexer-embed.js";
12
+ import { getRawChunks, storeCompiledChunks } from "./indexer-compile.js";
13
+ import { setContent, getContent } from "./content-cache.js";
14
+ import { findModel, getCurrentModel, selectModel, getBuiltinModels } from "./model-registry.js";
15
+ import { stats } from "./store.js";
16
+ import { getFileState } from "./file-manifest.js";
15
17
  export class WikiEngine {
16
- store;
17
- search;
18
- embedder;
19
- llm;
20
18
  constructor(config = {}) {
21
- const basePath = config.basePath ?? defaultBasePath();
22
- const modelId = config.modelId ?? defaultModel().id;
23
- const model = findModel(modelId) ?? defaultModel();
24
- this.store = new WikiStore(basePath);
25
- this.embedder = new WikiEmbedder(model, this.store.paths.modelsDir);
26
- this.search = new WikiSearch(this.store, (text) => this.embedder.embed(text));
27
- this.llm = new WikiLLM({ apiBase: this.store.config.llmApiBase, apiKey: this.store.config.llmApiKey, model: this.store.config.llmModel });
28
- // 如果 config.json 已有 semantic=true,自动准备 embedder
29
- if (this.store.config.semantic) {
30
- this.embedder.init().catch(() => { });
31
- }
19
+ if (config.basePath)
20
+ setWikiHome(config.basePath);
21
+ if (config.modelId)
22
+ selectModel(config.modelId);
32
23
  }
33
- // ==========================================================================
34
- // 数据源
35
- // ==========================================================================
36
- get sources() { return this.store.config.sources; }
24
+ // ---- Sources ----
25
+ get sources() { return store.getSources(); }
37
26
  addSource(absPath) {
38
- return this.store.addSource(absPath);
27
+ return store.addSource(absPath);
39
28
  }
40
29
  removeSource(target) {
41
- return this.store.removeSource(target);
30
+ return store.removeSource(target);
31
+ }
32
+ async loadSource(absPath) {
33
+ const entries = await scanDir(absPath);
34
+ store.mergeIndex(entries);
35
+ for (const e of entries) {
36
+ try {
37
+ const content = readFileSync(resolve(e.sourceDir, e.relPath), "utf-8");
38
+ setContent(e.relPath, content);
39
+ }
40
+ catch { }
41
+ }
42
+ return entries.length;
42
43
  }
43
- // ==========================================================================
44
- // 扫描 + 索引
45
- // ==========================================================================
46
44
  async load() {
47
- const sources = this.store.config.sources;
45
+ const sources = store.getSources();
48
46
  let totalFiles = 0;
49
47
  for (const src of sources) {
50
- const entries = await this.store.scanDir(src);
51
- this.store.setEntries(entries);
52
- totalFiles += entries.length;
48
+ totalFiles += await this.loadSource(src);
53
49
  }
54
50
  return { files: totalFiles, sources: sources.length };
55
51
  }
56
- async loadSource(absPath) {
57
- const entries = await this.store.scanDir(absPath);
58
- this.store.setEntries(entries);
59
- return entries.length;
60
- }
61
- // ==========================================================================
62
- // 搜索
63
- // ==========================================================================
52
+ // ---- Search ----
64
53
  async search_(query, mode = "keyword") {
65
- return this.search.search(query, mode);
54
+ switch (mode) {
55
+ case "keyword": return keywordSearch(query);
56
+ case "semantic": return semanticSearch(query);
57
+ case "hybrid": return hybridSearch(query);
58
+ }
66
59
  }
67
- // ==========================================================================
68
- // 条目 CRUD
69
- // ==========================================================================
70
- /** 创建 .md 条目 */
60
+ // ---- CRUD ----
71
61
  createEntry(sourceDir, relPath, title, tags = [], content = "") {
72
- const absPath = resolve(sourceDir, relPath);
73
- if (existsSync(absPath))
74
- return `已存在: ${absPath}`;
75
- // 确保 .md 后缀
76
- const finalPath = relPath.endsWith(".md") ? relPath : `${relPath}.md`;
62
+ const finalPath = relPath.endsWith(".md") ? relPath : relPath + ".md";
77
63
  const finalAbs = resolve(sourceDir, finalPath);
64
+ if (existsSync(finalAbs))
65
+ return "exists: " + finalAbs;
78
66
  const fm = [
79
67
  "---",
80
- `title: ${title}`,
81
- `tags: [${tags.join(", ")}]`,
82
- `created: ${new Date().toISOString()}`,
68
+ "title: " + title,
69
+ "tags: [" + tags.join(", ") + "]",
70
+ "created: " + new Date().toISOString(),
83
71
  "---", "",
84
72
  ].join("\n");
85
73
  mkdirSync(dirname(finalAbs), { recursive: true });
86
74
  writeFileSync(finalAbs, fm + content, "utf-8");
87
- // 更新索引
88
75
  const entry = {
89
76
  title, tags, sourceDir,
90
77
  relPath: finalPath.replace(/\\/g, "/"),
91
78
  mtime: new Date().toISOString(),
92
79
  };
93
- this.store.setEntries([entry]);
94
- this.store.setContent(entry.relPath, fm + content);
80
+ store.mergeIndex([entry]);
81
+ setContent(entry.relPath, fm + content);
95
82
  return entry.relPath;
96
83
  }
97
- /** 读取条目全文 */
98
84
  readEntry(relPath) {
99
- const entry = this.store.getEntry(relPath);
85
+ const entry = store.getEntry(relPath);
100
86
  if (!entry)
101
87
  return null;
102
- const content = this.store.getContent(relPath);
88
+ const content = getContent(relPath);
103
89
  if (!content)
104
90
  return null;
105
91
  return { entry, content };
106
92
  }
107
- /** 重命名(修改 frontmatter title) */
108
- renameEntry(relPath, newTitle) {
109
- const entry = this.store.getEntry(relPath);
110
- const content = this.store.getContent(relPath);
111
- if (!entry || !content)
112
- return false;
113
- const updated = content.replace(/^(title:\s*).+$/m, `$1${newTitle}`);
114
- // 写文件
115
- const absPath = resolve(entry.sourceDir, relPath);
116
- writeFileSync(absPath, updated, "utf-8");
117
- // 更新索引
118
- entry.title = newTitle;
119
- entry.mtime = new Date().toISOString();
120
- this.store.setEntries([entry]);
121
- this.store.setContent(relPath, updated);
122
- return true;
123
- }
124
- /** 移动文件 */
125
- moveEntry(relPath, newRelPath) {
126
- const entry = this.store.getEntry(relPath);
127
- if (!entry)
128
- return false;
129
- const oldAbs = resolve(entry.sourceDir, relPath);
130
- const newAbs = resolve(entry.sourceDir, newRelPath);
131
- if (existsSync(newAbs))
132
- return false;
133
- mkdirSync(dirname(newAbs), { recursive: true });
134
- renameSync(oldAbs, newAbs);
135
- const newEntry = { ...entry, relPath: newRelPath, mtime: new Date().toISOString() };
136
- this.store.removeEntry(relPath);
137
- this.store.setEntries([newEntry]);
138
- // 迁移向量
139
- const vec = this.store.getEmbedding(relPath);
140
- if (vec) {
141
- this.store.setSingleEmbedding(newRelPath, vec);
142
- this.store.removeEmbeddingsByPath(relPath);
143
- this.store.flushVectors();
144
- }
145
- return true;
146
- }
147
- /** 修改内容 */
148
- modifyEntry(relPath, content) {
149
- const entry = this.store.getEntry(relPath);
150
- if (!entry)
151
- return false;
152
- const absPath = resolve(entry.sourceDir, relPath);
153
- writeFileSync(absPath, content, "utf-8");
154
- entry.mtime = new Date().toISOString();
155
- this.store.setEntries([entry]);
156
- this.store.setContent(relPath, content);
157
- return true;
158
- }
159
- // ==========================================================================
160
- // 语义搜索控制
161
- // ==========================================================================
162
- get semanticEnabled() { return this.store.config.semantic; }
93
+ // ---- Semantic ----
94
+ get semanticEnabled() { return store.getSemanticEnabled(); }
163
95
  async enableSemantic(modelId) {
164
96
  if (modelId) {
165
97
  const m = findModel(modelId);
166
98
  if (!m)
167
- return { ok: false, msg: `未知模型: ${modelId}` };
168
- this.embedder.switchModel(m);
169
- this.store.saveConfig({ model: m.id });
99
+ return { ok: false, msg: "Unknown model: " + modelId };
100
+ selectModel(modelId);
170
101
  }
171
- const ok = await this.embedder.init();
102
+ const ok = await embedder.initialize();
172
103
  if (!ok)
173
- return { ok: false, msg: `初始化失败: ${this.embedder.error}` };
174
- this.store.saveConfig({ semantic: true });
175
- return { ok: true, msg: `语义搜索已开启。模型: ${this.embedder.modelInfo.name}。来源: ${this.embedder.source}` };
104
+ return { ok: false, msg: "Init failed: " + (embedder.getInitError() || "unknown") };
105
+ store.setSemanticEnabled(true);
106
+ return { ok: true, msg: "Semantic search ON. Model: " + embedder.getModelName() + ". Source: " + embedder.getModelSource() };
176
107
  }
177
108
  disableSemantic() {
178
- this.store.saveConfig({ semantic: false });
109
+ store.setSemanticEnabled(false);
179
110
  }
180
- // ==========================================================================
181
- // 向量生成
182
- // ==========================================================================
111
+ // ---- Embeddings ----
183
112
  async generateEmbeddings(sourceDir) {
184
- if (!this.embedder.ready)
185
- await this.embedder.init();
186
- if (!this.embedder.ready)
187
- throw new Error(`Embedder 未就绪: ${this.embedder.error}`);
188
- const entries = Object.values(this.store.entries).filter(e => !sourceDir || e.sourceDir === sourceDir);
189
- let embedded = 0, skipped = 0;
190
- for (const entry of entries) {
191
- const content = this.store.getContent(entry.relPath);
192
- if (!content) {
193
- skipped++;
194
- continue;
195
- }
196
- const mft = this.store.getManifestEntry(entry.relPath);
197
- const md5 = md5Hash(content);
198
- if (mft?.embedded && mft.md5 === md5) {
199
- skipped++;
200
- continue;
201
- }
202
- const chunks = chunkByHeadings(content, entry.relPath);
203
- if (chunks.length <= 1) {
204
- // 单块文件:直接嵌入全文
205
- const vec = await this.embedder.embed(content.slice(0, 800));
206
- this.store.setSingleEmbedding(entry.relPath, vec);
207
- }
208
- else {
209
- // 多块文件:逐块嵌入
210
- for (const chunk of chunks) {
211
- const pre = preprocess(chunk.text, chunk.heading, chunk.level);
212
- const embedText = buildEmbeddingText(chunk.text, pre);
213
- const vec = await this.embedder.embed(embedText);
214
- this.store.setSingleEmbedding(chunk.key, vec);
215
- this.store.setChunkMeta(chunk.key, {
216
- heading: chunk.heading, level: chunk.level,
217
- ...pre,
218
- });
219
- }
220
- }
221
- // 更新 manifest
222
- this.store.setManifestEntry(entry.relPath, {
223
- md5, size: content.length, chunks: chunks.length,
224
- compiled: mft?.compiled ?? false,
225
- embedded: true,
226
- updatedAt: new Date().toISOString(),
227
- });
228
- embedded++;
229
- }
230
- this.store.flushVectors();
231
- this.store.vectors.model = this.embedder.modelInfo.hfRepo;
232
- this.store.vectors.dim = this.embedder.modelInfo.dim;
233
- this.store.flushVectors();
234
- return { embedded, skipped };
113
+ if (!embedder.isAvailable())
114
+ await embedder.initialize();
115
+ const entries = Object.values(store.getIndex()).filter(e => !sourceDir || e.sourceDir === sourceDir);
116
+ const count = await doGenerateEmbeddings(sourceDir || "", entries);
117
+ return { embedded: count, skipped: 0 };
235
118
  }
236
- // ==========================================================================
237
- // LLM 编译
238
- // ==========================================================================
239
- /** 获取待编译的原始块 */
240
- getRawChunks(sourceDir, uncompiledOnly = true) {
241
- const entries = Object.values(this.store.entries).filter(e => !sourceDir || e.sourceDir === sourceDir);
242
- const result = [];
243
- for (const entry of entries) {
244
- if (uncompiledOnly) {
245
- const mft = this.store.getManifestEntry(entry.relPath);
246
- if (mft?.compiled)
247
- continue;
248
- }
249
- const content = this.store.getContent(entry.relPath);
250
- if (!content)
251
- continue;
252
- const chunks = chunkByHeadings(content, entry.relPath);
253
- for (const chunk of chunks) {
254
- const key = chunk.key;
255
- result.push({
256
- key, relPath: entry.relPath,
257
- heading: chunk.heading,
258
- rawText: chunk.text,
259
- compiled: !!this.store.getChunkMeta(key)?.topic,
260
- });
261
- }
262
- }
263
- return result;
119
+ // ---- Model download ----
120
+ async downloadModel() {
121
+ return embedder.downloadModel();
264
122
  }
265
- /** 获取编译 prompt(调用方自行调 LLM) */
266
- getCompilePrompt(relPath) {
267
- const content = this.store.getContent(relPath);
268
- if (!content)
269
- return null;
270
- const chunks = chunkByHeadings(content, relPath);
271
- if (chunks.length === 0)
272
- return null;
273
- // 合并为单文件 prompt
274
- const texts = chunks.map(c => `### ${c.heading}\n${c.text}`).join("\n\n");
123
+ // ---- Status ----
124
+ status() {
125
+ const s = stats();
126
+ const model = getCurrentModel();
275
127
  return {
276
- system: COMPILE_SYSTEM_PROMPT,
277
- user: buildCompilePrompt(relPath, texts, chunks[0].heading),
128
+ configPath: getWikiHome(),
129
+ sources: store.getSources(),
130
+ files: s.files,
131
+ lastScan: s.lastScan || "",
132
+ semantic: store.getSemanticEnabled(),
133
+ embeddings: s.embeddings || 0,
134
+ model: model?.id || "unknown",
135
+ modelDim: model?.dim || 0,
136
+ compiled: 0,
278
137
  };
279
138
  }
280
- /** 存储编译结果 */
281
- storeCompiled(relPath, result) {
282
- const content = this.store.getContent(relPath);
283
- const md5 = content ? md5Hash(content) : "";
284
- // 存储 compiled record
285
- const record = {
286
- path: relPath,
287
- compiledAt: new Date().toISOString(),
288
- sourceMD5: md5,
289
- model: "llm",
290
- ...result,
291
- embeddingText: buildEmbeddingText(result.normalizedText, { topic: result.topic, concepts: result.concepts, aliases: result.aliases }),
292
- };
293
- this.store.setCompiled(relPath, record);
294
- // 存储 LLM 块元数据
295
- this.store.setChunkMeta(`${relPath}###llm`, {
296
- heading: "(file-level)",
297
- level: 0,
298
- topic: result.topic,
299
- summary: result.normalizedText.slice(0, 200),
300
- concepts: result.concepts,
301
- aliases: result.aliases,
302
- });
303
- markCompiled(this.store, relPath);
139
+ // ---- LLM ----
140
+ get llmInfo() {
141
+ const apiKey = process.env.DEEPSEEK_API_KEY || process.env.OPENAI_API_KEY || "";
142
+ const apiBase = process.env.DEEPSEEK_API_KEY
143
+ ? "https://api.deepseek.com/v1"
144
+ : process.env.OPENAI_API_KEY
145
+ ? "https://api.openai.com/v1"
146
+ : "";
147
+ const model = process.env.LLM_MODEL || "deepseek-chat";
148
+ return { apiBase, model, hasKey: !!apiKey };
304
149
  }
305
- /** 编译状态查询 */
306
- compileStatus(sourceDir) {
307
- return getCompileStats(this.store);
308
- }
309
- // ==========================================================================
310
- // 状态
311
- // ==========================================================================
312
- // ==========================================================================
313
- // LLM 编译(内置调用)
314
- // ==========================================================================
315
- get llmInfo() { return this.llm.info; }
316
- /** 编译单个文件(调 LLM + 存储结果) */
317
150
  async compileFile(relPath) {
318
- const content = this.store.getContent(relPath);
151
+ const content = getContent(relPath);
319
152
  if (!content)
320
- return { ok: false, msg: `Not found: ${relPath}` };
321
- const chunks = chunkByHeadings(content, relPath);
322
- if (chunks.length === 0)
323
- return { ok: false, msg: `No chunks: ${relPath}` };
324
- const texts = chunks.map(c => `### ${c.heading}\n${c.text}`).join("\n\n");
325
- const userPrompt = buildCompilePrompt(relPath, texts, chunks[0].heading);
153
+ return { ok: false, msg: "Not found: " + relPath };
154
+ const chunks = await getRawChunks(undefined, false);
155
+ const fileChunks = chunks.filter(c => c.relPath === relPath);
156
+ if (!fileChunks.length)
157
+ return { ok: false, msg: "No chunks: " + relPath };
158
+ const { COMPILE_SYSTEM_PROMPT, buildCompilePrompt, parseCompiledResult } = await import("./semantic-compiler.js");
159
+ const prompt = buildCompilePrompt(fileChunks);
326
160
  try {
327
- const raw = await this.llm.chatJson(COMPILE_SYSTEM_PROMPT, userPrompt);
328
- const result = parseCompileResult(raw);
329
- if (!result)
330
- return { ok: false, msg: `Invalid JSON: ${raw.slice(0, 200)}` };
331
- this.storeCompiled(relPath, result);
332
- return { ok: true, msg: `Compiled: ${relPath} -> "${result.topic}"` };
161
+ const apiKey = process.env.DEEPSEEK_API_KEY || process.env.OPENAI_API_KEY;
162
+ if (!apiKey)
163
+ return { ok: false, msg: "No API key. Set DEEPSEEK_API_KEY or OPENAI_API_KEY" };
164
+ const apiBase = process.env.DEEPSEEK_API_KEY
165
+ ? "https://api.deepseek.com/v1"
166
+ : "https://api.openai.com/v1";
167
+ const model = process.env.LLM_MODEL || "deepseek-chat";
168
+ const res = await fetch(apiBase + "/chat/completions", {
169
+ method: "POST",
170
+ headers: { "Content-Type": "application/json", "Authorization": "Bearer " + apiKey },
171
+ body: JSON.stringify({
172
+ model,
173
+ messages: [
174
+ { role: "system", content: COMPILE_SYSTEM_PROMPT },
175
+ { role: "user", content: prompt },
176
+ ],
177
+ response_format: { type: "json_object" },
178
+ temperature: 0.1,
179
+ }),
180
+ });
181
+ const data = await res.json();
182
+ const raw = data?.choices?.[0]?.message?.content || "";
183
+ const compiled = parseCompiledResult(raw);
184
+ if (!compiled)
185
+ return { ok: false, msg: "Invalid JSON: " + raw.slice(0, 200) };
186
+ await storeCompiledChunks(compiled);
187
+ const topic = compiled[0]?.topic || "unknown";
188
+ return { ok: true, msg: "Compiled: " + relPath + ' -> "' + topic + '"' };
333
189
  }
334
190
  catch (e) {
335
- return { ok: false, msg: `LLM error: ${e.message}` };
191
+ return { ok: false, msg: "LLM error: " + e.message };
336
192
  }
337
193
  }
338
- /** 批量编译 */
339
194
  async compileAll(sourceDir, limit = 10) {
340
- const stats = getCompileStats(this.store);
341
- const targets = sourceDir
342
- ? stats.uncompiled.filter(p => this.store.getEntry(p)?.sourceDir === sourceDir)
343
- : stats.uncompiled;
344
- const batch = targets.slice(0, limit);
345
- let compiled = 0, failed = 0;
195
+ let count = 0, failed = 0;
346
196
  const msgs = [];
347
- for (const path of batch) {
348
- const r = await this.compileFile(path);
197
+ const entries = Object.values(store.getIndex()).filter(e => !sourceDir || e.sourceDir === sourceDir);
198
+ for (const entry of entries) {
199
+ if (count >= limit)
200
+ break;
201
+ const r = await this.compileFile(entry.relPath);
349
202
  if (r.ok) {
350
- compiled++;
351
- msgs.push(` ${r.msg}`);
203
+ count++;
204
+ msgs.push(" " + r.msg);
352
205
  }
353
206
  else {
354
207
  failed++;
355
- msgs.push(` FAIL: ${r.msg}`);
208
+ msgs.push(" FAIL: " + r.msg);
356
209
  }
357
210
  }
358
- return { compiled, failed, msgs };
359
- }
360
- status() {
361
- return this.store.status();
211
+ return { compiled: count, failed, msgs };
212
+ }
213
+ compileStatus(_sourceDir) {
214
+ const all = Object.keys(store.getIndex());
215
+ const compiled = [];
216
+ const uncompiled = [];
217
+ for (const relPath of all) {
218
+ const state = getFileState(relPath);
219
+ if (state?.llmCompiled)
220
+ compiled.push(relPath);
221
+ else
222
+ uncompiled.push(relPath);
223
+ }
224
+ return { total: all.length, compiled: compiled.length, uncompiled };
362
225
  }
363
226
  listModels() {
364
- const { MODELS } = require("./models.js");
365
- return MODELS;
227
+ return getBuiltinModels();
366
228
  }
367
229
  }
368
- function md5Hash(content) {
369
- return createHash("md5").update(content).digest("hex");
370
- }
371
230
  //# sourceMappingURL=WikiEngine.js.map