@sunub/obsidian-mcp-server 0.3.28 → 0.3.30

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 (62) hide show
  1. package/README.md +214 -72
  2. package/build/cli/ui/MainContent.d.ts +1 -2
  3. package/build/cli/ui/MainContent.js.map +1 -1
  4. package/build/index.js +28 -10
  5. package/build/index.js.map +1 -1
  6. package/build/server.d.ts +2 -1
  7. package/build/server.js +7 -1
  8. package/build/server.js.map +1 -1
  9. package/build/tools/index.d.ts +1 -1
  10. package/build/tools/organize_attachments/utils.d.ts +1 -1
  11. package/build/tools/organize_attachments/utils.js +1 -1
  12. package/build/tools/organize_attachments/utils.js.map +1 -1
  13. package/build/tools/vault/index.d.ts +1 -1
  14. package/build/tools/vault/params.d.ts +4 -4
  15. package/build/tools/vault/params.js +3 -3
  16. package/build/tools/vault/params.js.map +1 -1
  17. package/build/tools/vault/types/collect_context.d.ts +15 -15
  18. package/build/tools/vault/types/read_specific.d.ts +46 -46
  19. package/build/tools/vault/types/read_specific.js +4 -4
  20. package/build/tools/vault/types/read_specific.js.map +1 -1
  21. package/build/tools/vault/utils/actions/collect_context.js +15 -10
  22. package/build/tools/vault/utils/actions/collect_context.js.map +1 -1
  23. package/build/tools/vault/utils/actions/read.js +48 -12
  24. package/build/tools/vault/utils/actions/read.js.map +1 -1
  25. package/build/tools/vault/utils/actions/search.js +7 -3
  26. package/build/tools/vault/utils/actions/search.js.map +1 -1
  27. package/build/tools/vault/utils/shared.d.ts +1 -1
  28. package/build/tools/vault/utils/shared.js +9 -1
  29. package/build/tools/vault/utils/shared.js.map +1 -1
  30. package/build/utils/BackgroundTaskRegistry.d.ts +7 -0
  31. package/build/utils/BackgroundTaskRegistry.js +39 -0
  32. package/build/utils/BackgroundTaskRegistry.js.map +1 -0
  33. package/build/utils/Embedder.d.ts +4 -3
  34. package/build/utils/Embedder.js +20 -8
  35. package/build/utils/Embedder.js.map +1 -1
  36. package/build/utils/Indexer.js +36 -14
  37. package/build/utils/Indexer.js.map +1 -1
  38. package/build/utils/LocalModelManager.d.ts +45 -0
  39. package/build/utils/LocalModelManager.js +196 -0
  40. package/build/utils/LocalModelManager.js.map +1 -0
  41. package/build/utils/LocalReranker.d.ts +4 -3
  42. package/build/utils/LocalReranker.js +20 -8
  43. package/build/utils/LocalReranker.js.map +1 -1
  44. package/build/utils/RAGIndexer.d.ts +8 -3
  45. package/build/utils/RAGIndexer.js +61 -34
  46. package/build/utils/RAGIndexer.js.map +1 -1
  47. package/build/utils/ServerLifecycle.d.ts +23 -0
  48. package/build/utils/ServerLifecycle.js +50 -0
  49. package/build/utils/ServerLifecycle.js.map +1 -0
  50. package/build/utils/VaultManger/VaultManager.d.ts +2 -1
  51. package/build/utils/VaultManger/VaultManager.js +87 -60
  52. package/build/utils/VaultManger/VaultManager.js.map +1 -1
  53. package/build/utils/VaultWatcher.d.ts +2 -1
  54. package/build/utils/VaultWatcher.js +3 -6
  55. package/build/utils/VaultWatcher.js.map +1 -1
  56. package/build/utils/abort.d.ts +5 -0
  57. package/build/utils/abort.js +51 -0
  58. package/build/utils/abort.js.map +1 -0
  59. package/build/utils/worker/WorkerPool.d.ts +7 -2
  60. package/build/utils/worker/WorkerPool.js +22 -7
  61. package/build/utils/worker/WorkerPool.js.map +1 -1
  62. package/package.json +3 -1
@@ -1,17 +1,18 @@
1
- declare class Reranker {
2
- private modelName;
1
+ export declare class Reranker {
2
+ readonly modelName = "Xenova/bge-reranker-base";
3
3
  private tokenizer;
4
4
  private model;
5
5
  private initPromise;
6
6
  constructor();
7
7
  checkModelPresence(): Promise<boolean>;
8
+ hasLocalModelFiles(): boolean;
8
9
  get isReady(): boolean;
9
10
  private loadModel;
10
11
  init(): Promise<void>;
12
+ dispose(): Promise<void>;
11
13
  rerank(query: string, documents: string[]): Promise<{
12
14
  document: string;
13
15
  score: number;
14
16
  }[]>;
15
17
  }
16
18
  export declare const localReranker: Reranker;
17
- export {};
@@ -1,3 +1,5 @@
1
+ import { existsSync } from "node:fs";
2
+ import { join } from "node:path";
1
3
  import { AutoModelForSequenceClassification, AutoTokenizer, env, } from "@huggingface/transformers";
2
4
  import z from "zod";
3
5
  import { MODELS_DIR } from "./constants.js";
@@ -5,7 +7,7 @@ const rerankedResultsSchema = z.array(z.object({
5
7
  document: z.string(),
6
8
  score: z.number(),
7
9
  }));
8
- class Reranker {
10
+ export class Reranker {
9
11
  modelName = "Xenova/bge-reranker-base";
10
12
  tokenizer = null;
11
13
  model = null;
@@ -16,13 +18,10 @@ class Reranker {
16
18
  env.cacheDir = MODELS_DIR;
17
19
  }
18
20
  async checkModelPresence() {
19
- try {
20
- await this.init();
21
- return true;
22
- }
23
- catch (_e) {
24
- return false;
25
- }
21
+ return this.hasLocalModelFiles();
22
+ }
23
+ hasLocalModelFiles() {
24
+ return existsSync(join(MODELS_DIR, this.modelName));
26
25
  }
27
26
  get isReady() {
28
27
  return this.tokenizer !== null && this.model !== null;
@@ -41,6 +40,19 @@ class Reranker {
41
40
  }
42
41
  return this.initPromise;
43
42
  }
43
+ async dispose() {
44
+ if (this.model) {
45
+ try {
46
+ await this.model.dispose();
47
+ }
48
+ catch (e) {
49
+ console.error("Failed to dispose reranker model:", e);
50
+ }
51
+ }
52
+ this.tokenizer = null;
53
+ this.model = null;
54
+ this.initPromise = null;
55
+ }
44
56
  async rerank(query, documents) {
45
57
  if (!this.isReady) {
46
58
  await this.init();
@@ -1 +1 @@
1
- {"version":3,"file":"LocalReranker.js","sourceRoot":"","sources":["../../src/utils/LocalReranker.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"LocalReranker.js","sourceRoot":"","sources":["../../src/utils/LocalReranker.ts"],"names":[],"mappings":"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"}
@@ -24,12 +24,17 @@ export declare class RAGIndexer {
24
24
  startIndexing(total: number): void;
25
25
  stopIndexing(): void;
26
26
  private buildFileRecords;
27
- processFile(filePath: string, content?: string): Promise<void>;
28
- processFileInMemory(filePath: string, content?: string): Promise<{
27
+ processFile(filePath: string, content?: string, options?: {
28
+ signal?: AbortSignal;
29
+ }): Promise<void>;
30
+ processFileInMemory(filePath: string, content?: string, options?: {
31
+ signal?: AbortSignal;
32
+ }): Promise<{
29
33
  records: VectorRecord[];
30
34
  mtime: string;
31
35
  } | null>;
32
- indexAll(vaultPath: string): Promise<void>;
36
+ indexAll(vaultPath: string, signal?: AbortSignal): Promise<void>;
37
+ shutdown(): Promise<void>;
33
38
  deleteFile(filePath: string): Promise<void>;
34
39
  }
35
40
  export declare const ragIndexer: RAGIndexer;
@@ -1,8 +1,10 @@
1
1
  import fs from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import ora, {} from "ora";
4
+ import { throwIfAborted } from "./abort.js";
4
5
  import { DirectoryWalker } from "./DirectoryWalker.js";
5
6
  import { localEmbedder } from "./Embedder.js";
7
+ import { localModelManager } from "./LocalModelManager.js";
6
8
  import { Semaphore } from "./semaphore.js";
7
9
  import { vectorDB } from "./VectorDB.js";
8
10
  import { WorkerPool } from "./worker/WorkerPool.js";
@@ -54,46 +56,59 @@ export class RAGIndexer {
54
56
  stopIndexing() {
55
57
  this._isIndexing = false;
56
58
  }
57
- async buildFileRecords(filePath, content) {
59
+ async buildFileRecords(filePath, content, signal) {
60
+ throwIfAborted(signal);
58
61
  const fileStat = await fs.stat(filePath);
59
62
  const fileContent = content ?? (await fs.readFile(filePath, "utf-8"));
63
+ throwIfAborted(signal);
60
64
  const chunkMetadatas = await this.chukingWorkerPool.runTask({
61
65
  filePath,
62
66
  fileContent,
63
67
  birthTime: fileStat.birthtime.toISOString(),
64
68
  });
65
- await localEmbedder.init();
66
- const records = [];
67
- for (const metadata of chunkMetadatas) {
68
- const combined = [
69
- metadata.context ? metadata.context : "",
70
- metadata.content,
71
- ]
72
- .filter(Boolean)
73
- .join("\n\n");
74
- const MAX_EMBED_TOKENS = 500;
75
- const combinedTokenCount = localEmbedder.getTokenCount(combined);
76
- const safeText = combinedTokenCount > MAX_EMBED_TOKENS
77
- ? combined.slice(0, Math.floor(combined.length * (MAX_EMBED_TOKENS / combinedTokenCount)))
78
- : combined;
79
- await this.embeddingSemaphore.acquire();
80
- let vector;
81
- try {
82
- vector = await localEmbedder.embed(`search_document: ${safeText}`);
69
+ return await localModelManager.withEmbedder(3000, async (isReady) => {
70
+ throwIfAborted(signal);
71
+ if (!isReady) {
72
+ return null;
83
73
  }
84
- finally {
85
- this.embeddingSemaphore.release();
74
+ const records = [];
75
+ for (const metadata of chunkMetadatas) {
76
+ throwIfAborted(signal);
77
+ const combined = [
78
+ metadata.context ? metadata.context : "",
79
+ metadata.content,
80
+ ]
81
+ .filter(Boolean)
82
+ .join("\n\n");
83
+ const MAX_EMBED_TOKENS = 500;
84
+ const combinedTokenCount = localEmbedder.getTokenCount(combined);
85
+ const safeText = combinedTokenCount > MAX_EMBED_TOKENS
86
+ ? combined.slice(0, Math.floor(combined.length * (MAX_EMBED_TOKENS / combinedTokenCount)))
87
+ : combined;
88
+ await this.embeddingSemaphore.acquire();
89
+ let vector;
90
+ try {
91
+ throwIfAborted(signal);
92
+ vector = await localEmbedder.embed(`search_document: ${safeText}`);
93
+ }
94
+ finally {
95
+ this.embeddingSemaphore.release();
96
+ }
97
+ records.push({
98
+ ...metadata,
99
+ vector,
100
+ });
86
101
  }
87
- records.push({
88
- ...metadata,
89
- vector,
90
- });
91
- }
92
- return { records, mtime: fileStat.mtime.toISOString() };
102
+ return { records, mtime: fileStat.mtime.toISOString() };
103
+ });
93
104
  }
94
- async processFile(filePath, content) {
105
+ async processFile(filePath, content, options = {}) {
95
106
  try {
96
- const { records, mtime } = await this.buildFileRecords(filePath, content);
107
+ const result = await this.buildFileRecords(filePath, content, options.signal);
108
+ if (!result) {
109
+ return;
110
+ }
111
+ const { records, mtime } = result;
97
112
  if (records.length > 0) {
98
113
  await vectorDB.upsertChunks(records);
99
114
  }
@@ -107,9 +122,9 @@ export class RAGIndexer {
107
122
  this.updateProgress(filePath);
108
123
  }
109
124
  }
110
- async processFileInMemory(filePath, content) {
125
+ async processFileInMemory(filePath, content, options = {}) {
111
126
  try {
112
- return await this.buildFileRecords(filePath, content);
127
+ return await this.buildFileRecords(filePath, content, options.signal);
113
128
  }
114
129
  catch (error) {
115
130
  console.error(`\nError processing file for RAG: ${filePath}`, error);
@@ -119,10 +134,11 @@ export class RAGIndexer {
119
134
  this.updateProgress(filePath);
120
135
  }
121
136
  }
122
- async indexAll(vaultPath) {
137
+ async indexAll(vaultPath, signal) {
123
138
  this._isIndexing = true;
124
139
  this.chukingWorkerPool.init();
125
140
  try {
141
+ throwIfAborted(signal);
126
142
  const walker = new DirectoryWalker();
127
143
  const filePaths = await walker.walk(vaultPath, this.ioSemaphore);
128
144
  const markdownFiles = filePaths.filter((f) => f.endsWith(".md") || f.endsWith(".mdx"));
@@ -132,11 +148,14 @@ export class RAGIndexer {
132
148
  let currentMeta = [];
133
149
  const fileSemaphore = new Semaphore(8);
134
150
  for (let i = 0; i < markdownFiles.length; i += BATCH_SIZE) {
151
+ throwIfAborted(signal);
135
152
  const batchFiles = markdownFiles.slice(i, i + BATCH_SIZE);
136
153
  await Promise.all(batchFiles.map(async (filePath) => {
137
154
  await fileSemaphore.acquire();
138
155
  try {
139
- const result = await this.processFileInMemory(filePath);
156
+ const result = await this.processFileInMemory(filePath, undefined, {
157
+ signal,
158
+ });
140
159
  if (result && result.records.length > 0) {
141
160
  currentBatch.push(...result.records);
142
161
  currentMeta.push({ filePath, mtime: result.mtime });
@@ -158,8 +177,16 @@ export class RAGIndexer {
158
177
  }
159
178
  finally {
160
179
  this._isIndexing = false;
161
- this.chukingWorkerPool.terminateAll();
180
+ await this.chukingWorkerPool.terminateAll();
181
+ }
182
+ }
183
+ async shutdown() {
184
+ this._isIndexing = false;
185
+ if (this.spinner) {
186
+ this.spinner.stop();
187
+ this.spinner = null;
162
188
  }
189
+ await this.chukingWorkerPool.terminateAll();
163
190
  }
164
191
  async deleteFile(filePath) {
165
192
  await vectorDB.deleteByFilePath(filePath);
@@ -1 +1 @@
1
- {"version":3,"file":"RAGIndexer.js","sourceRoot":"","sources":["../../src/utils/RAGIndexer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"RAGIndexer.js","sourceRoot":"","sources":["../../src/utils/RAGIndexer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ type CleanupTask = () => Promise<void> | void;
2
+ type ToolCallModelManager = {
3
+ beginToolCall: () => void;
4
+ endToolCall: () => Promise<void>;
5
+ };
6
+ export interface ServerLifecycleOptions {
7
+ modelManager?: ToolCallModelManager;
8
+ }
9
+ export declare class ServerLifecycle {
10
+ private readonly controller;
11
+ private readonly backgroundTasks;
12
+ private readonly modelManager;
13
+ private readonly cleanups;
14
+ private shutdownPromise;
15
+ constructor(options?: ServerLifecycleOptions);
16
+ get signal(): AbortSignal;
17
+ registerCleanup(name: string, task: CleanupTask): void;
18
+ runBackgroundTask<T>(name: string, task: (signal: AbortSignal) => Promise<T>): Promise<T | undefined>;
19
+ runToolCall<T>(_name: string, task: () => Promise<T>): Promise<T>;
20
+ shutdown(reason: string, timeoutMs?: number): Promise<void>;
21
+ private executeShutdown;
22
+ }
23
+ export {};
@@ -0,0 +1,50 @@
1
+ import { createAbortError, withTimeout } from "./abort.js";
2
+ import { BackgroundTaskRegistry } from "./BackgroundTaskRegistry.js";
3
+ import { localModelManager } from "./LocalModelManager.js";
4
+ export class ServerLifecycle {
5
+ controller = new AbortController();
6
+ backgroundTasks = new BackgroundTaskRegistry();
7
+ modelManager;
8
+ cleanups = [];
9
+ shutdownPromise = null;
10
+ constructor(options = {}) {
11
+ this.modelManager = options.modelManager ?? localModelManager;
12
+ }
13
+ get signal() {
14
+ return this.controller.signal;
15
+ }
16
+ registerCleanup(name, task) {
17
+ this.cleanups.push({ name, task });
18
+ }
19
+ runBackgroundTask(name, task) {
20
+ return this.backgroundTasks.run(name, task);
21
+ }
22
+ async runToolCall(_name, task) {
23
+ this.modelManager.beginToolCall();
24
+ try {
25
+ return await task();
26
+ }
27
+ finally {
28
+ await this.modelManager.endToolCall();
29
+ }
30
+ }
31
+ shutdown(reason, timeoutMs = 3000) {
32
+ if (this.shutdownPromise) {
33
+ return this.shutdownPromise;
34
+ }
35
+ this.shutdownPromise = this.executeShutdown(reason, timeoutMs);
36
+ return this.shutdownPromise;
37
+ }
38
+ async executeShutdown(reason, timeoutMs) {
39
+ if (!this.controller.signal.aborted) {
40
+ this.controller.abort(createAbortError(reason));
41
+ }
42
+ await this.backgroundTasks.abortAll(reason, timeoutMs);
43
+ const cleanups = [...this.cleanups].reverse();
44
+ this.cleanups.length = 0;
45
+ for (const { task } of cleanups) {
46
+ await withTimeout(Promise.resolve(task()), timeoutMs).catch(() => undefined);
47
+ }
48
+ }
49
+ }
50
+ //# sourceMappingURL=ServerLifecycle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ServerLifecycle.js","sourceRoot":"","sources":["../../src/utils/ServerLifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC3D,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAiB3D,MAAM,OAAO,eAAe;IACV,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACnC,eAAe,GAAG,IAAI,sBAAsB,EAAE,CAAC;IAC/C,YAAY,CAAuB;IACnC,QAAQ,GAAwB,EAAE,CAAC;IAC5C,eAAe,GAAyB,IAAI,CAAC;IAErD,YAAY,UAAkC,EAAE;QAC/C,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,iBAAiB,CAAC;IAC/D,CAAC;IAED,IAAI,MAAM;QACT,OAAO,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC;IAC/B,CAAC;IAED,eAAe,CAAC,IAAY,EAAE,IAAiB;QAC9C,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IACpC,CAAC;IAED,iBAAiB,CAChB,IAAY,EACZ,IAAyC;QAEzC,OAAO,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,WAAW,CAAI,KAAa,EAAE,IAAsB;QACzD,IAAI,CAAC,YAAY,CAAC,aAAa,EAAE,CAAC;QAClC,IAAI,CAAC;YACJ,OAAO,MAAM,IAAI,EAAE,CAAC;QACrB,CAAC;gBAAS,CAAC;YACV,MAAM,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE,CAAC;QACvC,CAAC;IACF,CAAC;IAED,QAAQ,CAAC,MAAc,EAAE,SAAS,GAAG,IAAI;QACxC,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YAC1B,OAAO,IAAI,CAAC,eAAe,CAAC;QAC7B,CAAC;QAED,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QAC/D,OAAO,IAAI,CAAC,eAAe,CAAC;IAC7B,CAAC;IAEO,KAAK,CAAC,eAAe,CAC5B,MAAc,EACd,SAAiB;QAEjB,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACrC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC;QACjD,CAAC;QAED,MAAM,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QAEvD,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE,CAAC;QAC9C,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;QACzB,KAAK,MAAM,EAAE,IAAI,EAAE,IAAI,QAAQ,EAAE,CAAC;YACjC,MAAM,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,EAAE,SAAS,CAAC,CAAC,KAAK,CAC1D,GAAG,EAAE,CAAC,SAAS,CACf,CAAC;QACH,CAAC;IACF,CAAC;CACD"}
@@ -25,9 +25,10 @@ export declare class VaultManager {
25
25
  diagnostic_message?: string;
26
26
  }>;
27
27
  private safeSemanticSearch;
28
- syncMissingRagIndices(): Promise<void>;
28
+ syncMissingRagIndices(signal?: AbortSignal): Promise<void>;
29
29
  private executeSync;
30
30
  upsertDocument(filePath: string): Promise<void>;
31
+ shutdown(): Promise<void>;
31
32
  removeDocument(filePath: string): Promise<void>;
32
33
  getAllDocuments(): Promise<DocumentIndex[]>;
33
34
  searchDocuments(keyword: string): Promise<DocumentIndex[]>;
@@ -4,9 +4,11 @@ import { mkdir, readFile, writeFile } from "node:fs/promises";
4
4
  import { basename, dirname, extname, isAbsolute, join, relative, resolve, } from "node:path";
5
5
  import matter from "gray-matter";
6
6
  import { debugLogger } from "../../shared/index.js";
7
+ import { throwIfAborted } from "../abort.js";
7
8
  import { DirectoryWalker } from "../DirectoryWalker.js";
8
9
  import { localEmbedder } from "../Embedder.js";
9
10
  import { Indexer } from "../Indexer.js";
11
+ import { localModelManager } from "../LocalModelManager.js";
10
12
  import { localReranker } from "../LocalReranker.js";
11
13
  import { ragIndexer } from "../RAGIndexer.js";
12
14
  import { Semaphore } from "../semaphore.js";
@@ -89,6 +91,9 @@ export class VaultManager {
89
91
  this.indexer.search(query),
90
92
  this.safeSemanticSearch(query, limit * 3),
91
93
  ]);
94
+ const semanticFallbackMessage = semanticResults.length === 0
95
+ ? "💡 [검색 품질 안내] 로컬 semantic 모델이 아직 준비 중이거나 사용할 수 없어 이번 검색은 키워드 중심으로 반환했습니다. 잠시 후 다시 검색하면 하이브리드 검색이 적용될 수 있습니다."
96
+ : undefined;
92
97
  // 3. RRF (Reciprocal Rank Fusion) 스코어 계산
93
98
  const rrfScores = new Map();
94
99
  const RRF_K = 60;
@@ -143,7 +148,7 @@ export class VaultManager {
143
148
  const dist = chunk["_distance"];
144
149
  return typeof dist === "number" && dist < 0.2;
145
150
  }));
146
- if (hasHighlyRelevantMatch) {
151
+ if (hasHighlyRelevantMatch || semanticResults.length === 0) {
147
152
  debugLogger.debug(`[RAG] Highly relevant semantic match found. Skipping rerank in hybridSearch.`);
148
153
  }
149
154
  else {
@@ -154,20 +159,24 @@ export class VaultManager {
154
159
  item.document.frontmatter.summary ||
155
160
  item.document.filePath);
156
161
  });
157
- const reranked = await localReranker.rerank(query, rerankDocs);
158
- finalResults = reranked
159
- .map((r) => {
160
- const original = fusedResults.find((f) => {
161
- const content = f.matchedChunks[0]?.content ||
162
- f.document.frontmatter.summary ||
163
- f.document.filePath;
164
- return content === r.document;
165
- });
166
- if (!original)
167
- return null;
168
- return { ...original, finalScore: r.score };
169
- })
170
- .filter((r) => r !== null);
162
+ await localModelManager.withReranker(3000, async (rerankerReady) => {
163
+ if (rerankerReady) {
164
+ const reranked = await localReranker.rerank(query, rerankDocs);
165
+ finalResults = reranked
166
+ .map((r) => {
167
+ const original = fusedResults.find((f) => {
168
+ const content = f.matchedChunks[0]?.content ||
169
+ f.document.frontmatter.summary ||
170
+ f.document.filePath;
171
+ return content === r.document;
172
+ });
173
+ if (!original)
174
+ return null;
175
+ return { ...original, finalScore: r.score };
176
+ })
177
+ .filter((r) => r !== null);
178
+ }
179
+ });
171
180
  }
172
181
  catch (error) {
173
182
  console.error("[VaultManager] Reranking failed:", error);
@@ -179,6 +188,11 @@ export class VaultManager {
179
188
  if (ragStatus.isIndexing) {
180
189
  diagnostic_message = `⏳ [인덱싱 진행 중] 백그라운드에서 의미 기반 검색 인덱싱이 진행 중입니다 (${ragStatus.progress}% - ${ragStatus.processed}/${ragStatus.total}). 최근 변경된 파일은 검색 결과에 즉시 반영되지 않을 수 있습니다.`;
181
190
  }
191
+ if (semanticFallbackMessage) {
192
+ diagnostic_message = diagnostic_message
193
+ ? `${diagnostic_message}\n\n${semanticFallbackMessage}`
194
+ : semanticFallbackMessage;
195
+ }
182
196
  return {
183
197
  results: finalResults.slice(0, limit),
184
198
  diagnostic_message,
@@ -190,42 +204,44 @@ export class VaultManager {
190
204
  }
191
205
  await this.dbSemaphore.acquire();
192
206
  try {
193
- const isLocalReady = await localEmbedder.checkModelPresence();
194
- if (!isLocalReady) {
195
- await localEmbedder.init();
196
- }
197
- const queryVector = await localEmbedder.embed(`search_query: ${query}`);
198
- const recallLimit = Math.max(limit * 3, 15);
199
- const initialResults = await vectorDB.search(queryVector, recallLimit);
200
- if (initialResults.length === 0)
201
- return [];
202
- const documents = initialResults.map((r) => r.content);
203
- // 1차 검색 결과 중 코사인 거리가 극도로 가까운(유사도 0.80 이상) 강력한 매칭인 경우 리랭킹 건너뜀
204
- const SKIP_RERANK_DISTANCE_THRESHOLD = 0.2;
205
- const firstDistance = initialResults[0]["_distance"];
206
- if (typeof firstDistance === "number" &&
207
- firstDistance < SKIP_RERANK_DISTANCE_THRESHOLD) {
208
- debugLogger.debug(`[RAG] First semantic match distance is ${firstDistance.toFixed(4)}. Skipping rerank in safeSemanticSearch.`);
209
- return initialResults.slice(0, limit);
210
- }
211
- const isRerankerReady = await localReranker.checkModelPresence();
212
- if (!isRerankerReady) {
213
- await localReranker.init();
214
- }
215
- const reranked = await localReranker.rerank(query, documents);
216
- const normalizeForComparison = (text) => text
217
- .replace(/\r\n|\r|\n/g, " ")
218
- .replace(/\s+/g, " ")
219
- .trim();
220
- const finalResults = reranked
221
- .slice(0, limit)
222
- .map((r) => {
223
- const targetNorm = normalizeForComparison(r.document);
224
- const originalIndex = documents.findIndex((doc) => normalizeForComparison(doc) === targetNorm);
225
- return initialResults[originalIndex];
226
- })
227
- .filter((r) => r !== undefined);
228
- return finalResults;
207
+ return await localModelManager.withEmbedder(3000, async (isLocalReady) => {
208
+ if (!isLocalReady) {
209
+ return [];
210
+ }
211
+ const queryVector = await localEmbedder.embed(`search_query: ${query}`);
212
+ const recallLimit = Math.max(limit * 3, 15);
213
+ const initialResults = await vectorDB.search(queryVector, recallLimit);
214
+ if (initialResults.length === 0)
215
+ return [];
216
+ const documents = initialResults.map((r) => r.content);
217
+ // 1차 검색 결과 중 코사인 거리가 극도로 가까운(유사도 0.80 이상) 강력한 매칭인 경우 리랭킹 건너뜀
218
+ const SKIP_RERANK_DISTANCE_THRESHOLD = 0.2;
219
+ const firstDistance = initialResults[0]["_distance"];
220
+ if (typeof firstDistance === "number" &&
221
+ firstDistance < SKIP_RERANK_DISTANCE_THRESHOLD) {
222
+ debugLogger.debug(`[RAG] First semantic match distance is ${firstDistance.toFixed(4)}. Skipping rerank in safeSemanticSearch.`);
223
+ return initialResults.slice(0, limit);
224
+ }
225
+ return await localModelManager.withReranker(3000, async (isRerankerReady) => {
226
+ if (!isRerankerReady) {
227
+ return initialResults.slice(0, limit);
228
+ }
229
+ const reranked = await localReranker.rerank(query, documents);
230
+ const normalizeForComparison = (text) => text
231
+ .replace(/\r\n|\r|\n/g, " ")
232
+ .replace(/\s+/g, " ")
233
+ .trim();
234
+ const finalResults = reranked
235
+ .slice(0, limit)
236
+ .map((r) => {
237
+ const targetNorm = normalizeForComparison(r.document);
238
+ const originalIndex = documents.findIndex((doc) => normalizeForComparison(doc) === targetNorm);
239
+ return initialResults[originalIndex];
240
+ })
241
+ .filter((r) => r !== undefined);
242
+ return finalResults;
243
+ });
244
+ });
229
245
  }
230
246
  catch (_error) {
231
247
  return [];
@@ -234,26 +250,29 @@ export class VaultManager {
234
250
  this.dbSemaphore.release();
235
251
  }
236
252
  }
237
- async syncMissingRagIndices() {
253
+ async syncMissingRagIndices(signal) {
254
+ throwIfAborted(signal);
238
255
  // 로컬 모델이 있는 경우 우선적으로 사용
239
256
  if (this.isLocalAIReady) {
240
- await this.executeSync();
257
+ await this.executeSync(signal);
241
258
  return;
242
259
  }
243
260
  const isLocalReady = await localEmbedder.checkModelPresence();
244
261
  if (isLocalReady) {
245
- await this.executeSync();
262
+ await this.executeSync(signal);
246
263
  }
247
264
  else {
248
265
  // 아무것도 없으면 조용히 종료 (로그는 이미 index.ts에서 나옴)
249
266
  return;
250
267
  }
251
268
  }
252
- async executeSync() {
269
+ async executeSync(signal) {
270
+ throwIfAborted(signal);
253
271
  const allDocs = await this.getAllDocuments();
254
272
  const forceReindex = await vectorDB.checkAndMigrateIfNeeded();
255
273
  const filesToProcess = [];
256
274
  for (const doc of allDocs) {
275
+ throwIfAborted(signal);
257
276
  if (forceReindex) {
258
277
  filesToProcess.push(doc.filePath);
259
278
  }
@@ -277,7 +296,8 @@ export class VaultManager {
277
296
  await this.dbSemaphore.acquire();
278
297
  try {
279
298
  for (const filePath of filesToProcess) {
280
- await ragIndexer.processFile(filePath);
299
+ throwIfAborted(signal);
300
+ await ragIndexer.processFile(filePath, undefined, { signal });
281
301
  }
282
302
  await vectorDB.createVectorIndex();
283
303
  }
@@ -289,11 +309,10 @@ export class VaultManager {
289
309
  }
290
310
  async upsertDocument(filePath) {
291
311
  await this.indexer.upsertFile(filePath, this.ioSemaphore);
292
- if (!this.isLocalAIReady) {
312
+ if (!this.isLocalAIReady || !localModelManager.isEmbedderReady) {
293
313
  return;
294
314
  }
295
- const isLocalReady = await localEmbedder.checkModelPresence();
296
- if (isLocalReady) {
315
+ if (localModelManager.isEmbedderReady) {
297
316
  await this.dbSemaphore.acquire();
298
317
  try {
299
318
  await ragIndexer.processFile(filePath);
@@ -309,6 +328,9 @@ export class VaultManager {
309
328
  console.error(`[VaultManager] Embedding server is down. Skipping RAG index for: ${filePath}`);
310
329
  }
311
330
  }
331
+ async shutdown() {
332
+ await ragIndexer.shutdown();
333
+ }
312
334
  async removeDocument(filePath) {
313
335
  this.indexer.removeFileEntries(filePath);
314
336
  await this.dbSemaphore.acquire();
@@ -449,9 +471,14 @@ export class VaultManager {
449
471
  filename.replace(/\.mdx?$/, ""),
450
472
  basename(filename, extname(filename)),
451
473
  ];
474
+ const normalizeForMatch = (str) => str.toLowerCase().replace(/[\s_-]/g, "");
452
475
  for (const term of searchTerms) {
476
+ const normalizedTerm = normalizeForMatch(term);
453
477
  const searchResults = this.indexer.search(term);
454
- const foundDoc = searchResults.find((doc) => doc.filePath.includes(term));
478
+ const foundDoc = searchResults.find((doc) => {
479
+ const fileBase = basename(doc.filePath, extname(doc.filePath));
480
+ return normalizeForMatch(fileBase) === normalizedTerm;
481
+ });
455
482
  if (foundDoc) {
456
483
  return foundDoc.filePath;
457
484
  }