@customize-agent/knowledge 4.0.20 → 4.0.22

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.
@@ -54,5 +54,6 @@ export declare class TextChunker {
54
54
  private kindForCategory;
55
55
  private extractSectionTitle;
56
56
  private takeOverlap;
57
+ private hasLongUnbrokenSegment;
57
58
  private estimateTokens;
58
59
  }
@@ -295,7 +295,7 @@ export class TextChunker {
295
295
  return chunks.flatMap(chunk => this.estimateTokens(chunk) > maxTokens ? this.splitByWindow(chunk, maxTokens, overlapTokens) : [chunk]);
296
296
  }
297
297
  splitByWindow(text, maxTokens, overlapTokens = 0) {
298
- if (!/[\s\n\r\t。?!;;.!?,、]/u.test(text) && text.length > Math.max(2000, maxTokens * 3)) {
298
+ if (this.hasLongUnbrokenSegment(text) && text.length > Math.max(2000, maxTokens * 3)) {
299
299
  const maxChars = Math.max(1, maxTokens);
300
300
  const overlapChars = Math.max(0, Math.min(Math.floor(maxChars / 2), overlapTokens));
301
301
  const step = Math.max(1, maxChars - overlapChars);
@@ -452,7 +452,7 @@ export class TextChunker {
452
452
  startChar: candidate.startChar,
453
453
  endChar: candidate.endChar,
454
454
  splitStrategy: 'recursive_parent_child_v2',
455
- parentText: candidate.parentText, // <=== 原始父块内容
455
+ parentText: candidate.childIndex === 0 ? candidate.parentText : undefined,
456
456
  },
457
457
  };
458
458
  }
@@ -481,9 +481,12 @@ export class TextChunker {
481
481
  const chars = overlapTokens * 4;
482
482
  return text.slice(Math.max(0, text.length - chars));
483
483
  }
484
+ hasLongUnbrokenSegment(text) {
485
+ return /[^\s\n\r\t。?!;;.!?,、]{2001,}/u.test(text);
486
+ }
484
487
  estimateTokens(text) {
485
- if (!/[\s\n\r\t。?!;;.!?,、]/u.test(text) && text.length > 2000)
486
- return Math.max(1, Math.ceil(text.length / 3));
488
+ if (this.hasLongUnbrokenSegment(text))
489
+ return text.length;
487
490
  return Math.max(1, this.tokenizer.countTokens(text));
488
491
  }
489
492
  }
@@ -11,6 +11,13 @@ import { fileURLToPath } from 'node:url';
11
11
  import { resolveAndImport, resolvePackage } from './module-resolver.js';
12
12
  // ─── 路径工具 ───────────────────────────────────────────────────
13
13
  const knowledgeDir = path.dirname(fileURLToPath(import.meta.url));
14
+ const OCR_NATIVE_NOISE_PATTERNS = [/^Image too small to scale!!/u, /^Line cannot be recognized!!$/u];
15
+ const OCR_NOISE_SUPPRESSION_KEY = Symbol.for('customize-agent.ocr-noise-suppression');
16
+ function ocrNoiseSuppressionState() {
17
+ const globalState = globalThis;
18
+ globalState[OCR_NOISE_SUPPRESSION_KEY] ??= { depth: 0 };
19
+ return globalState[OCR_NOISE_SUPPRESSION_KEY];
20
+ }
14
21
  function tessdataDir() {
15
22
  if (process.env.TESSDATA_PREFIX)
16
23
  return process.env.TESSDATA_PREFIX;
@@ -19,6 +26,44 @@ function tessdataDir() {
19
26
  return pkg;
20
27
  return pkg;
21
28
  }
29
+ function isNativeOcrNoise(chunk) {
30
+ const text = Buffer.isBuffer(chunk) ? chunk.toString('utf8') : chunk instanceof Uint8Array ? Buffer.from(chunk).toString('utf8') : typeof chunk === 'string' ? chunk : '';
31
+ if (!text)
32
+ return false;
33
+ const lines = text.split(/\r?\n/u).map(line => line.trim()).filter(Boolean);
34
+ return lines.length > 0 && lines.every(line => OCR_NATIVE_NOISE_PATTERNS.some(pattern => pattern.test(line)));
35
+ }
36
+ async function suppressNativeOcrNoise(operation) {
37
+ const state = ocrNoiseSuppressionState();
38
+ if (state.depth === 0) {
39
+ state.stdout = process.stdout.write;
40
+ state.stderr = process.stderr.write;
41
+ const filter = (original) => function write(chunk, ...args) {
42
+ if (isNativeOcrNoise(chunk)) {
43
+ const callback = args.find((arg) => typeof arg === 'function');
44
+ if (callback)
45
+ process.nextTick(callback);
46
+ return true;
47
+ }
48
+ return original.call(this, chunk, ...args);
49
+ };
50
+ process.stdout.write = filter(state.stdout);
51
+ process.stderr.write = filter(state.stderr);
52
+ }
53
+ state.depth += 1;
54
+ try {
55
+ return await operation();
56
+ }
57
+ finally {
58
+ state.depth -= 1;
59
+ if (state.depth === 0 && state.stdout && state.stderr) {
60
+ process.stdout.write = state.stdout;
61
+ process.stderr.write = state.stderr;
62
+ state.stdout = undefined;
63
+ state.stderr = undefined;
64
+ }
65
+ }
66
+ }
22
67
  // ─── Tesseract.js Provider(主 OCR 引擎,跨平台) ──────────────
23
68
  export class TesseractJsProvider {
24
69
  id = 'tesseract.js';
@@ -71,7 +116,7 @@ export class TesseractJsProvider {
71
116
  }
72
117
  try {
73
118
  const worker = await this.getWorker();
74
- const result = await worker.recognize(pngPath);
119
+ const result = await suppressNativeOcrNoise(() => worker.recognize(pngPath));
75
120
  const text = (result.data.text ?? '').trim();
76
121
  const lines = (result.data.lines ?? []);
77
122
  const regions = lines
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@customize-agent/knowledge",
3
- "version": "4.0.20",
3
+ "version": "4.0.22",
4
4
  "description": "Local knowledge base infrastructure for customize-agent",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",