chatlab-cli 0.34.2 → 0.34.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/dist/{chat-command-6IOH2QRF.mjs → chat-command-QH7DXCAJ.mjs} +8 -8
- package/dist/{chunk-JX6MFS6F.mjs → chunk-2Q3HHVTS.mjs} +15 -15
- package/dist/{chunk-INBJ7SND.mjs → chunk-2WYXD5ZP.mjs} +117 -416
- package/dist/chunk-2WYXD5ZP.mjs.map +1 -0
- package/dist/{chunk-U6MVJSXX.mjs → chunk-3X26JJIP.mjs} +15 -9
- package/dist/chunk-3X26JJIP.mjs.map +1 -0
- package/dist/{chunk-UMBEHBIZ.mjs → chunk-6U2QZO3N.mjs} +2 -2
- package/dist/{chunk-52P5ZRBD.mjs → chunk-AWGXYFIC.mjs} +2 -2
- package/dist/{chunk-4LW6443F.mjs → chunk-AWXHYTD5.mjs} +795 -876
- package/dist/chunk-AWXHYTD5.mjs.map +1 -0
- package/dist/{chunk-4RLZFGO5.mjs → chunk-C2PD3BS7.mjs} +2 -2
- package/dist/{chunk-H6D4DZOC.mjs → chunk-JYBPJWE2.mjs} +7 -5
- package/dist/{chunk-H6D4DZOC.mjs.map → chunk-JYBPJWE2.mjs.map} +1 -1
- package/dist/{chunk-652KEAX3.mjs → chunk-SWUTRDGZ.mjs} +2 -2
- package/dist/{chunk-72RWABYJ.mjs → chunk-WJVE5Q4Y.mjs} +3 -3
- package/dist/chunk-WJVE5Q4Y.mjs.map +1 -0
- package/dist/{chunk-2XCNTG4O.mjs → chunk-XIWVJAXH.mjs} +670 -54
- package/dist/chunk-XIWVJAXH.mjs.map +1 -0
- package/dist/{chunk-MENKFEPC.mjs → chunk-ZVGDOSGG.mjs} +29 -41
- package/dist/chunk-ZVGDOSGG.mjs.map +1 -0
- package/dist/cli.mjs +9 -9
- package/dist/contacts-worker.mjs +2 -2
- package/dist/global-insight-worker.mjs +1 -1
- package/dist/{http-HSK46XEU.mjs → http-D2F4QTQW.mjs} +12 -12
- package/dist/{import-DF2WD2HE.mjs → import-H7X2JU2O.mjs} +7 -7
- package/dist/index.mjs +12 -12
- package/dist/{mcp-A2RND7KN.mjs → mcp-UURXRGDF.mjs} +8 -8
- package/dist/people-relationships-worker.mjs +2 -2
- package/dist/semantic-index-worker.mjs +1 -1
- package/dist/{src-SHFHW2QB.mjs → src-MHCO4HYY.mjs} +22 -38
- package/dist/{stream-import-GZ2QDE3Z.mjs → stream-import-BHXACUCA.mjs} +7 -7
- package/dist/{worker-runtime-OWN56GG5.mjs → worker-runtime-JOUCP2L4.mjs} +5 -5
- package/dist-cli-web/assets/ActionToolsPanel.vue_vue_type_script_setup_true_lang-kN3uxTW8.js +110 -0
- package/dist-cli-web/assets/{BufferResource-BdKXeRZS.js → BufferResource-Dak23r3B.js} +1 -1
- package/dist-cli-web/assets/{CanvasRenderer-DDRdIyZ8.js → CanvasRenderer-Cmjtvski.js} +1 -1
- package/dist-cli-web/assets/DataDirCleanupNotice-B5tyDzK-.js +1 -0
- package/dist-cli-web/assets/{EChartWordcloud-CqknPGui.js → EChartWordcloud-Bd3L3G8I.js} +1 -1
- package/dist-cli-web/assets/{Filter-B2eEAPxI.js → Filter-CHKZVYB3.js} +1 -1
- package/dist-cli-web/assets/LoadingState.vue_vue_type_script_setup_true_lang-CKf_gC1k.js +1 -0
- package/dist-cli-web/assets/{MessageExportModal-CZge6qub.js → MessageExportModal-ODYCcSnk.js} +1 -1
- package/dist-cli-web/assets/{PageHeader-CxKbcLDc.js → PageHeader-Bn7Jn8JW.js} +1 -1
- package/dist-cli-web/assets/{RelationshipGalaxyCanvas-CwQ63TPi.js → RelationshipGalaxyCanvas-mJhEPzsY.js} +3 -3
- package/dist-cli-web/assets/{RelationshipGalaxyThreeCanvas-BLqISQe5.js → RelationshipGalaxyThreeCanvas-Bf76yzmQ.js} +1 -1
- package/dist-cli-web/assets/{RenderTargetSystem-eC_VrDAr.js → RenderTargetSystem-1rM7OF-M.js} +1 -1
- package/dist-cli-web/assets/{ThemeCard.vue_vue_type_script_setup_true_lang-DVmpDz05.js → ThemeCard.vue_vue_type_script_setup_true_lang-CADZpPia.js} +1 -1
- package/dist-cli-web/assets/TimeSelect.vue_vue_type_script_setup_true_lang-I3FYjhfs.js +1 -0
- package/dist-cli-web/assets/{WebGLRenderer-CRp_HC84.js → WebGLRenderer-Y0jQtBvK.js} +1 -1
- package/dist-cli-web/assets/{WebGPURenderer-WIUnEhpi.js → WebGPURenderer-BcUvCPhd.js} +1 -1
- package/dist-cli-web/assets/{annual-summary-time-range-R3pLPEuN.js → annual-summary-time-range-BIADd8qE.js} +1 -1
- package/dist-cli-web/assets/{base-CLwWn3Rs.js → base-BpxxC5Z5.js} +1 -1
- package/dist-cli-web/assets/{browserAll-B29Ox2in.js → browserAll-BsR1UaCC.js} +1 -1
- package/dist-cli-web/assets/{cli-web-CVLb5Pkq.js → cli-web-BiWUOncc.js} +1 -1
- package/dist-cli-web/assets/{dateFormat-T7kMS5IP.js → dateFormat-B6JnNCxp.js} +1 -1
- package/dist-cli-web/assets/{fetch-DvrVGAzR.js → fetch-BAJ_VjWE.js} +1 -1
- package/dist-cli-web/assets/{fetch-BLi6Jsjg.js → fetch-BNcHe2-O.js} +1 -1
- package/dist-cli-web/assets/{fetch-D8hJZa8o.js → fetch-CPloHijF.js} +1 -1
- package/dist-cli-web/assets/{fetch-D9sn99zX.js → fetch-Clj_RVUD.js} +1 -1
- package/dist-cli-web/assets/{fetch-CYaGyohV.js → fetch-D7vgaLpy.js} +1 -1
- package/dist-cli-web/assets/{fetch-DsHf-61a.js → fetch-DGvqctIb.js} +1 -1
- package/dist-cli-web/assets/fetch-DSxgaVBO.js +1 -0
- package/dist-cli-web/assets/{fetch-BKBlpBl4.js → fetch-DtnhbqTN.js} +1 -1
- package/dist-cli-web/assets/{fetch-BBgZQob_.js → fetch-bdY0RMM3.js} +1 -1
- package/dist-cli-web/assets/{fetch-L6PKD8-N.js → fetch-bhHY8rO7.js} +1 -1
- package/dist-cli-web/assets/index-B6Gq11lw.css +1 -0
- package/dist-cli-web/assets/index-BJ2lywiG.js +1 -0
- package/dist-cli-web/assets/{index-C4TuoOLM.js → index-BT3_fSP6.js} +2 -2
- package/dist-cli-web/assets/{index-CkgUsaeX.js → index-BVZhP1J5.js} +1 -1
- package/dist-cli-web/assets/index-Br8r4pu8.js +1 -0
- package/dist-cli-web/assets/index-Byok4VaA.css +1 -0
- package/dist-cli-web/assets/index-CKnnxOci.js +1 -0
- package/dist-cli-web/assets/index-CXnYfvTv.js +137 -0
- package/dist-cli-web/assets/index-CsFLLk3T.css +1 -0
- package/dist-cli-web/assets/index-CzoOQ7zg.js +1 -0
- package/dist-cli-web/assets/{index-C6Mk-Xqt.js → index-D-dTTUGj.js} +1 -1
- package/dist-cli-web/assets/index-DEbvEKzi.js +2 -0
- package/dist-cli-web/assets/{index-0qcENyvH.js → index-DMYFpctK.js} +1 -1
- package/dist-cli-web/assets/index-KO7uPd8W.js +64 -0
- package/dist-cli-web/assets/index-lkmJ6dI7.js +1 -0
- package/dist-cli-web/assets/{index-D_0qeKKc.js → index-y7xPDpsW.js} +5 -5
- package/dist-cli-web/assets/{people-page-header-CCNJOrmK.js → people-page-header-gjTyODWs.js} +1 -1
- package/dist-cli-web/assets/{webworkerAll-B2qvP2qR.js → webworkerAll-CfZWlukP.js} +1 -1
- package/dist-cli-web/index.html +2 -2
- package/package.json +1 -1
- package/dist/chunk-2XCNTG4O.mjs.map +0 -1
- package/dist/chunk-4LW6443F.mjs.map +0 -1
- package/dist/chunk-72RWABYJ.mjs.map +0 -1
- package/dist/chunk-INBJ7SND.mjs.map +0 -1
- package/dist/chunk-MENKFEPC.mjs.map +0 -1
- package/dist/chunk-U6MVJSXX.mjs.map +0 -1
- package/dist-cli-web/assets/ActionToolsPanel.vue_vue_type_script_setup_true_lang-B06LYYS9.js +0 -110
- package/dist-cli-web/assets/DataDirCleanupNotice-CP8P1lsL.js +0 -1
- package/dist-cli-web/assets/LoadingState.vue_vue_type_script_setup_true_lang-BFaXTz1c.js +0 -1
- package/dist-cli-web/assets/TimeSelect.vue_vue_type_script_setup_true_lang-CCH1UQGc.js +0 -1
- package/dist-cli-web/assets/fetch-Cm2WR2Cv.js +0 -1
- package/dist-cli-web/assets/index-AkF_KqVI.js +0 -1
- package/dist-cli-web/assets/index-BVOFyB9E.js +0 -137
- package/dist-cli-web/assets/index-BkdOH41D.js +0 -1
- package/dist-cli-web/assets/index-CuAy1Xmt.js +0 -64
- package/dist-cli-web/assets/index-DeR0Vukh.js +0 -1
- package/dist-cli-web/assets/index-DrjoMv9s.css +0 -1
- package/dist-cli-web/assets/index-Dx-XvfIK.js +0 -2
- package/dist-cli-web/assets/index-Jhwca9kg.css +0 -1
- package/dist-cli-web/assets/index-_DrviTnP.js +0 -1
- package/dist-cli-web/assets/index-vtF05fh7.css +0 -1
- package/dist-cli-web/assets/index-xN0sjOLK.js +0 -1
- /package/dist/{chat-command-6IOH2QRF.mjs.map → chat-command-QH7DXCAJ.mjs.map} +0 -0
- /package/dist/{chunk-JX6MFS6F.mjs.map → chunk-2Q3HHVTS.mjs.map} +0 -0
- /package/dist/{chunk-UMBEHBIZ.mjs.map → chunk-6U2QZO3N.mjs.map} +0 -0
- /package/dist/{chunk-52P5ZRBD.mjs.map → chunk-AWGXYFIC.mjs.map} +0 -0
- /package/dist/{chunk-4RLZFGO5.mjs.map → chunk-C2PD3BS7.mjs.map} +0 -0
- /package/dist/{chunk-652KEAX3.mjs.map → chunk-SWUTRDGZ.mjs.map} +0 -0
- /package/dist/{http-HSK46XEU.mjs.map → http-D2F4QTQW.mjs.map} +0 -0
- /package/dist/{import-DF2WD2HE.mjs.map → import-H7X2JU2O.mjs.map} +0 -0
- /package/dist/{mcp-A2RND7KN.mjs.map → mcp-UURXRGDF.mjs.map} +0 -0
- /package/dist/{src-SHFHW2QB.mjs.map → src-MHCO4HYY.mjs.map} +0 -0
- /package/dist/{stream-import-GZ2QDE3Z.mjs.map → stream-import-BHXACUCA.mjs.map} +0 -0
- /package/dist/{worker-runtime-OWN56GG5.mjs.map → worker-runtime-JOUCP2L4.mjs.map} +0 -0
|
@@ -40,46 +40,49 @@ import {
|
|
|
40
40
|
composeChunkId,
|
|
41
41
|
computeChunkerConfigHash,
|
|
42
42
|
computeDbPathHash,
|
|
43
|
-
createChatDbFtsSearcher,
|
|
44
43
|
createChatDbMessageRangeReader,
|
|
45
44
|
createChatDbMessageSource,
|
|
46
45
|
createChatLabTempDir,
|
|
47
46
|
createEmbedder,
|
|
48
47
|
createLocalEmbeddingRuntimeManager,
|
|
48
|
+
createMessageDedupState,
|
|
49
49
|
createSemanticIndexService,
|
|
50
50
|
defaultSemanticIndexConfig,
|
|
51
51
|
deriveParentId,
|
|
52
52
|
estimateTokens,
|
|
53
|
-
extractFtsKeywords,
|
|
54
53
|
formatEvidenceMessages,
|
|
54
|
+
generateFallbackMessageKey,
|
|
55
55
|
getChatLabTempScopeDir,
|
|
56
56
|
getLocalProfileByModelId,
|
|
57
57
|
getLocalProfilesForLocale,
|
|
58
|
-
hybridSearch,
|
|
59
58
|
isKeylessSemanticIndexApiBaseUrl,
|
|
60
59
|
isSemanticIndexConfigured,
|
|
61
60
|
isSemanticVoid,
|
|
62
61
|
parseParentBounds,
|
|
63
62
|
persistSemanticIndexConfig,
|
|
64
63
|
rebaseChatLabDemoDocuments,
|
|
65
|
-
|
|
64
|
+
registerMessageAndCheckDuplicate,
|
|
66
65
|
removeChatLabTempDir,
|
|
67
66
|
resolveModelId,
|
|
68
67
|
resolveSemanticIndexApiKeySet,
|
|
69
68
|
runWarmup,
|
|
69
|
+
semanticSearch,
|
|
70
70
|
vecTableName,
|
|
71
71
|
vecTableSchema
|
|
72
|
-
} from "./chunk-
|
|
72
|
+
} from "./chunk-AWXHYTD5.mjs";
|
|
73
73
|
import {
|
|
74
74
|
CHATLAB_FORMAT_VERSION,
|
|
75
75
|
MEANINGFUL_POS_TAGS,
|
|
76
|
+
POS_TAG_DEFINITIONS,
|
|
76
77
|
StaticPathProvider,
|
|
77
78
|
appLogger,
|
|
79
|
+
buildExcludeKeywordsConditions,
|
|
80
|
+
cleanText,
|
|
78
81
|
detectConflictsInMessages,
|
|
79
|
-
generateMessageKey,
|
|
80
82
|
getCollidingPlatformIds,
|
|
81
|
-
|
|
83
|
+
getCollidingPlatformIdsFromMessages,
|
|
82
84
|
isStopword,
|
|
85
|
+
isValidWord,
|
|
83
86
|
loadConfig,
|
|
84
87
|
mergeMembers2 as mergeMembers,
|
|
85
88
|
migrateFromElectronIfNeeded,
|
|
@@ -90,7 +93,7 @@ import {
|
|
|
90
93
|
withFileLock,
|
|
91
94
|
writeConfigField,
|
|
92
95
|
writeJsonFileAtomically
|
|
93
|
-
} from "./chunk-
|
|
96
|
+
} from "./chunk-2WYXD5ZP.mjs";
|
|
94
97
|
import {
|
|
95
98
|
__export
|
|
96
99
|
} from "./chunk-VSOWNKPA.mjs";
|
|
@@ -556,6 +559,220 @@ function expandHome(filePath) {
|
|
|
556
559
|
return filePath;
|
|
557
560
|
}
|
|
558
561
|
|
|
562
|
+
// ../../packages/node-runtime/src/nlp/segmenter.ts
|
|
563
|
+
import * as fs3 from "fs";
|
|
564
|
+
import * as path3 from "path";
|
|
565
|
+
import { createRequire } from "module";
|
|
566
|
+
var _nlpDir = null;
|
|
567
|
+
var jiebaInstances = /* @__PURE__ */ new Map();
|
|
568
|
+
var require2 = createRequire(import.meta.url);
|
|
569
|
+
function initNlpDir(nlpDir) {
|
|
570
|
+
_nlpDir = nlpDir;
|
|
571
|
+
}
|
|
572
|
+
function getNlpDir() {
|
|
573
|
+
return _nlpDir;
|
|
574
|
+
}
|
|
575
|
+
function existsDictOnDisk(dictId) {
|
|
576
|
+
if (!_nlpDir) return false;
|
|
577
|
+
return fs3.existsSync(path3.join(_nlpDir, `${dictId}.dict`));
|
|
578
|
+
}
|
|
579
|
+
function tryLoadDictFromDisk(dictId) {
|
|
580
|
+
if (!_nlpDir) return null;
|
|
581
|
+
const dictPath = path3.join(_nlpDir, `${dictId}.dict`);
|
|
582
|
+
if (!fs3.existsSync(dictPath)) return null;
|
|
583
|
+
try {
|
|
584
|
+
return fs3.readFileSync(dictPath);
|
|
585
|
+
} catch {
|
|
586
|
+
return null;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
function getJieba(dictType = "default") {
|
|
590
|
+
const effectiveType = dictType === "default" ? "zh-CN" : dictType;
|
|
591
|
+
const cached = jiebaInstances.get(effectiveType);
|
|
592
|
+
if (cached) {
|
|
593
|
+
if (cached.fromDiskDict === existsDictOnDisk(effectiveType)) return cached.instance;
|
|
594
|
+
jiebaInstances.delete(effectiveType);
|
|
595
|
+
console.error(`[NLP] jieba cache invalidated (dict ${cached.fromDiskDict ? "removed" : "added"}): ${effectiveType}`);
|
|
596
|
+
}
|
|
597
|
+
try {
|
|
598
|
+
const { Jieba } = require2("@node-rs/jieba");
|
|
599
|
+
let instance;
|
|
600
|
+
const diskDict = tryLoadDictFromDisk(effectiveType);
|
|
601
|
+
if (diskDict) {
|
|
602
|
+
instance = Jieba.withDict(diskDict);
|
|
603
|
+
console.error(`[NLP] jieba dict loaded: ${effectiveType} (${diskDict.length} bytes)`);
|
|
604
|
+
} else {
|
|
605
|
+
instance = new Jieba();
|
|
606
|
+
console.warn(`[NLP] jieba dict missing: ${effectiveType}, fallback to built-in tokenizer`);
|
|
607
|
+
}
|
|
608
|
+
jiebaInstances.set(effectiveType, { instance, fromDiskDict: !!diskDict });
|
|
609
|
+
return instance;
|
|
610
|
+
} catch (error) {
|
|
611
|
+
console.error(`[NLP] Failed to load jieba module (dict=${effectiveType}):`, error);
|
|
612
|
+
throw new Error(`jieba \u6A21\u5757\u52A0\u8F7D\u5931\u8D25 (${effectiveType})`);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
function clearJiebaInstance(dictType) {
|
|
616
|
+
jiebaInstances.delete(dictType);
|
|
617
|
+
console.error(`[NLP] jieba instance cleared: ${dictType}`);
|
|
618
|
+
}
|
|
619
|
+
function segmentChinese(text, options = {}) {
|
|
620
|
+
const { posFilterMode = "meaningful", customPosTags, dictType = "default" } = options;
|
|
621
|
+
const cleaned = cleanText(text);
|
|
622
|
+
if (!cleaned) return [];
|
|
623
|
+
try {
|
|
624
|
+
const jieba = getJieba(dictType);
|
|
625
|
+
if (posFilterMode === "all") return jieba.cut(cleaned, false);
|
|
626
|
+
const tagged = jieba.tag(cleaned);
|
|
627
|
+
const allowedTags = posFilterMode === "custom" && customPosTags ? new Set(customPosTags) : MEANINGFUL_POS_TAGS;
|
|
628
|
+
return tagged.filter((item) => allowedTags.has(item.tag)).map((item) => item.word);
|
|
629
|
+
} catch (error) {
|
|
630
|
+
console.error("[NLP] Chinese segmentation failed:", error);
|
|
631
|
+
try {
|
|
632
|
+
return getJieba("default").cut(cleanText(text), false);
|
|
633
|
+
} catch {
|
|
634
|
+
return cleaned.split("");
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
function segmentEnglish(text) {
|
|
639
|
+
const cleaned = cleanText(text);
|
|
640
|
+
if (!cleaned) return [];
|
|
641
|
+
try {
|
|
642
|
+
const segmenter = new Intl.Segmenter("en", { granularity: "word" });
|
|
643
|
+
return [...segmenter.segment(cleaned)].filter((s) => s.isWordLike).map((s) => s.segment.toLowerCase());
|
|
644
|
+
} catch {
|
|
645
|
+
return cleaned.toLowerCase().split(/\s+/).filter((w) => w.length > 0);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
function segmentJapanese(text) {
|
|
649
|
+
const cleaned = cleanText(text);
|
|
650
|
+
if (!cleaned) return [];
|
|
651
|
+
try {
|
|
652
|
+
const segmenter = new Intl.Segmenter("ja", { granularity: "word" });
|
|
653
|
+
return [...segmenter.segment(cleaned)].filter((s) => s.isWordLike).map((s) => s.segment);
|
|
654
|
+
} catch {
|
|
655
|
+
return cleaned.split("").filter((ch) => ch.trim().length > 0);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
function segment(text, locale, options = {}) {
|
|
659
|
+
const {
|
|
660
|
+
minLength,
|
|
661
|
+
posFilterMode = "meaningful",
|
|
662
|
+
customPosTags,
|
|
663
|
+
enableStopwords = true,
|
|
664
|
+
dictType = "default"
|
|
665
|
+
} = options;
|
|
666
|
+
const isChinese = locale.startsWith("zh");
|
|
667
|
+
const isJapanese = locale === "ja-JP";
|
|
668
|
+
const defaultMinLength = isChinese || isJapanese ? 2 : 3;
|
|
669
|
+
const effectiveMinLength = minLength ?? defaultMinLength;
|
|
670
|
+
let words;
|
|
671
|
+
if (isChinese) {
|
|
672
|
+
words = segmentChinese(text, { posFilterMode, customPosTags, dictType });
|
|
673
|
+
} else if (isJapanese) {
|
|
674
|
+
words = segmentJapanese(text);
|
|
675
|
+
} else {
|
|
676
|
+
words = segmentEnglish(text);
|
|
677
|
+
}
|
|
678
|
+
return words.filter((word) => isValidWord(word, locale, effectiveMinLength, enableStopwords, isStopword));
|
|
679
|
+
}
|
|
680
|
+
function batchSegmentWithFrequency(texts, locale, options = {}) {
|
|
681
|
+
const {
|
|
682
|
+
minLength,
|
|
683
|
+
minCount = 2,
|
|
684
|
+
topN = 100,
|
|
685
|
+
posFilterMode,
|
|
686
|
+
customPosTags,
|
|
687
|
+
enableStopwords,
|
|
688
|
+
dictType,
|
|
689
|
+
excludeWords
|
|
690
|
+
} = options;
|
|
691
|
+
const wordFrequency = /* @__PURE__ */ new Map();
|
|
692
|
+
const excludeSet = excludeWords?.length ? new Set(excludeWords.map((w) => w.toLowerCase())) : null;
|
|
693
|
+
for (const text of texts) {
|
|
694
|
+
const words = segment(text, locale, { minLength, posFilterMode, customPosTags, enableStopwords, dictType });
|
|
695
|
+
for (const word of words) {
|
|
696
|
+
if (excludeSet && excludeSet.has(word.toLowerCase())) continue;
|
|
697
|
+
wordFrequency.set(word, (wordFrequency.get(word) || 0) + 1);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
const filtered = /* @__PURE__ */ new Map();
|
|
701
|
+
let totalWords = 0;
|
|
702
|
+
for (const [word, count] of wordFrequency) {
|
|
703
|
+
if (count >= minCount) {
|
|
704
|
+
filtered.set(word, count);
|
|
705
|
+
totalWords += count;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
const sorted = [...filtered.entries()].sort((a, b) => b[1] - a[1]).slice(0, topN);
|
|
709
|
+
return { words: new Map(sorted), uniqueWords: filtered.size, totalWords };
|
|
710
|
+
}
|
|
711
|
+
function batchSegmentChineseWithStats(texts, locale, options = {}) {
|
|
712
|
+
const {
|
|
713
|
+
minLength,
|
|
714
|
+
minCount = 2,
|
|
715
|
+
topN = 100,
|
|
716
|
+
posFilterMode = "meaningful",
|
|
717
|
+
customPosTags,
|
|
718
|
+
enableStopwords = true,
|
|
719
|
+
dictType = "default",
|
|
720
|
+
excludeWords
|
|
721
|
+
} = options;
|
|
722
|
+
const effectiveMinLength = minLength ?? 2;
|
|
723
|
+
const allowedTags = posFilterMode === "custom" && customPosTags ? new Set(customPosTags) : MEANINGFUL_POS_TAGS;
|
|
724
|
+
const excludeSet = excludeWords?.length ? new Set(excludeWords.map((w) => w.toLowerCase())) : null;
|
|
725
|
+
const wordFrequency = /* @__PURE__ */ new Map();
|
|
726
|
+
const posTagStats = /* @__PURE__ */ new Map();
|
|
727
|
+
try {
|
|
728
|
+
const jieba = getJieba(dictType);
|
|
729
|
+
for (const text of texts) {
|
|
730
|
+
const cleaned = cleanText(text);
|
|
731
|
+
if (!cleaned) continue;
|
|
732
|
+
for (const { tag, word } of jieba.tag(cleaned)) {
|
|
733
|
+
if (!isValidWord(word, locale, effectiveMinLength, enableStopwords, isStopword)) continue;
|
|
734
|
+
posTagStats.set(tag, (posTagStats.get(tag) || 0) + 1);
|
|
735
|
+
if (!allowedTags.has(tag)) continue;
|
|
736
|
+
if (excludeSet && excludeSet.has(word.toLowerCase())) continue;
|
|
737
|
+
wordFrequency.set(word, (wordFrequency.get(word) || 0) + 1);
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
} catch (error) {
|
|
741
|
+
console.error("[NLP] Chinese single-pass segmentation failed:", error);
|
|
742
|
+
}
|
|
743
|
+
const filtered = /* @__PURE__ */ new Map();
|
|
744
|
+
let totalWords = 0;
|
|
745
|
+
for (const [word, count] of wordFrequency) {
|
|
746
|
+
if (count >= minCount) {
|
|
747
|
+
filtered.set(word, count);
|
|
748
|
+
totalWords += count;
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
const sorted = [...filtered.entries()].sort((a, b) => b[1] - a[1]).slice(0, topN);
|
|
752
|
+
return { words: new Map(sorted), uniqueWords: filtered.size, totalWords, posTagStats };
|
|
753
|
+
}
|
|
754
|
+
function collectPosTagStats(texts, minWordLength = 2, enableStopwords = true, dictType = "default") {
|
|
755
|
+
const posStats = /* @__PURE__ */ new Map();
|
|
756
|
+
try {
|
|
757
|
+
const jieba = getJieba(dictType);
|
|
758
|
+
for (const text of texts) {
|
|
759
|
+
const cleaned = cleanText(text);
|
|
760
|
+
if (!cleaned) continue;
|
|
761
|
+
const tagged = jieba.tag(cleaned);
|
|
762
|
+
for (const item of tagged) {
|
|
763
|
+
if (!isValidWord(item.word, "zh-CN", minWordLength, enableStopwords, isStopword)) continue;
|
|
764
|
+
posStats.set(item.tag, (posStats.get(item.tag) || 0) + 1);
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
} catch (error) {
|
|
768
|
+
console.error("[NLP] Failed to collect POS stats:", error);
|
|
769
|
+
}
|
|
770
|
+
return posStats;
|
|
771
|
+
}
|
|
772
|
+
function getPosTagDefinitions() {
|
|
773
|
+
return POS_TAG_DEFINITIONS;
|
|
774
|
+
}
|
|
775
|
+
|
|
559
776
|
// ../../packages/node-runtime/src/jieba-nlp-provider.ts
|
|
560
777
|
function createJiebaNlpProvider(dictType = "default") {
|
|
561
778
|
return {
|
|
@@ -567,9 +784,233 @@ function createJiebaNlpProvider(dictType = "default") {
|
|
|
567
784
|
};
|
|
568
785
|
}
|
|
569
786
|
|
|
787
|
+
// ../../packages/node-runtime/src/nlp/word-frequency.ts
|
|
788
|
+
function buildMessageQuery(timeFilter, memberId, excludeKeywords) {
|
|
789
|
+
const conditions = [];
|
|
790
|
+
const params = [];
|
|
791
|
+
if (timeFilter?.startTs !== void 0) {
|
|
792
|
+
conditions.push("msg.ts >= ?");
|
|
793
|
+
params.push(timeFilter.startTs);
|
|
794
|
+
}
|
|
795
|
+
if (timeFilter?.endTs !== void 0) {
|
|
796
|
+
conditions.push("msg.ts <= ?");
|
|
797
|
+
params.push(timeFilter.endTs);
|
|
798
|
+
}
|
|
799
|
+
if (memberId !== void 0 && memberId !== null) {
|
|
800
|
+
conditions.push("msg.sender_id = ?");
|
|
801
|
+
params.push(memberId);
|
|
802
|
+
}
|
|
803
|
+
if (excludeKeywords && excludeKeywords.length > 0) {
|
|
804
|
+
const exclude = buildExcludeKeywordsConditions(excludeKeywords);
|
|
805
|
+
conditions.push(...exclude.conditions);
|
|
806
|
+
params.push(...exclude.params);
|
|
807
|
+
}
|
|
808
|
+
conditions.push("COALESCE(m.account_name, '') != '\u7CFB\u7EDF\u6D88\u606F'");
|
|
809
|
+
conditions.push("msg.type = 0");
|
|
810
|
+
conditions.push("msg.content IS NOT NULL");
|
|
811
|
+
conditions.push("TRIM(msg.content) != ''");
|
|
812
|
+
return {
|
|
813
|
+
clause: ` WHERE ${conditions.join(" AND ")}`,
|
|
814
|
+
params
|
|
815
|
+
};
|
|
816
|
+
}
|
|
817
|
+
function computeWordFrequency(db, params) {
|
|
818
|
+
const {
|
|
819
|
+
locale,
|
|
820
|
+
timeFilter,
|
|
821
|
+
memberId,
|
|
822
|
+
topN = 100,
|
|
823
|
+
minWordLength,
|
|
824
|
+
minCount = 2,
|
|
825
|
+
posFilterMode = "meaningful",
|
|
826
|
+
customPosTags,
|
|
827
|
+
enableStopwords = true,
|
|
828
|
+
dictType = "default",
|
|
829
|
+
excludeWords,
|
|
830
|
+
excludeKeywords
|
|
831
|
+
} = params;
|
|
832
|
+
const { clause, params: filterParams } = buildMessageQuery(timeFilter, memberId, excludeKeywords);
|
|
833
|
+
const messages = db.prepare(`SELECT msg.content FROM message msg JOIN member m ON msg.sender_id = m.id${clause}`).all(...filterParams);
|
|
834
|
+
if (messages.length === 0) {
|
|
835
|
+
return { words: [], totalWords: 0, totalMessages: 0, uniqueWords: 0 };
|
|
836
|
+
}
|
|
837
|
+
const texts = messages.map((m) => m.content);
|
|
838
|
+
const segmentOptions = {
|
|
839
|
+
minLength: minWordLength,
|
|
840
|
+
minCount,
|
|
841
|
+
topN,
|
|
842
|
+
posFilterMode,
|
|
843
|
+
customPosTags,
|
|
844
|
+
enableStopwords,
|
|
845
|
+
dictType,
|
|
846
|
+
excludeWords
|
|
847
|
+
};
|
|
848
|
+
let posTagStats;
|
|
849
|
+
let result;
|
|
850
|
+
if (locale.startsWith("zh") && posFilterMode !== "all") {
|
|
851
|
+
const combined = batchSegmentChineseWithStats(texts, locale, segmentOptions);
|
|
852
|
+
result = { words: combined.words, uniqueWords: combined.uniqueWords, totalWords: combined.totalWords };
|
|
853
|
+
posTagStats = [...combined.posTagStats.entries()].map(([tag, count]) => ({ tag, count }));
|
|
854
|
+
} else {
|
|
855
|
+
if (locale.startsWith("zh")) {
|
|
856
|
+
const posStatsMap = collectPosTagStats(texts, minWordLength ?? 2, enableStopwords, dictType);
|
|
857
|
+
posTagStats = [...posStatsMap.entries()].map(([tag, count]) => ({ tag, count }));
|
|
858
|
+
}
|
|
859
|
+
result = batchSegmentWithFrequency(texts, locale, segmentOptions);
|
|
860
|
+
}
|
|
861
|
+
let topNTotalWords = 0;
|
|
862
|
+
for (const count of result.words.values()) topNTotalWords += count;
|
|
863
|
+
const words = [...result.words.entries()].map(([word, count]) => ({
|
|
864
|
+
word,
|
|
865
|
+
count,
|
|
866
|
+
percentage: topNTotalWords > 0 ? Math.round(count / topNTotalWords * 1e4) / 100 : 0
|
|
867
|
+
}));
|
|
868
|
+
return {
|
|
869
|
+
words,
|
|
870
|
+
totalWords: result.totalWords,
|
|
871
|
+
totalMessages: messages.length,
|
|
872
|
+
uniqueWords: result.uniqueWords,
|
|
873
|
+
posTagStats
|
|
874
|
+
};
|
|
875
|
+
}
|
|
876
|
+
function segmentText(text, locale, minLength) {
|
|
877
|
+
return segment(text, locale, { minLength });
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
// ../../packages/node-runtime/src/nlp/dict-manager.ts
|
|
881
|
+
import * as fs4 from "fs";
|
|
882
|
+
import * as path4 from "path";
|
|
883
|
+
import { createHash } from "crypto";
|
|
884
|
+
var DICT_DOWNLOAD_URL_BASE = "https://chatlab.fun/assets/nlp";
|
|
885
|
+
var DICT_SHA256 = {
|
|
886
|
+
"zh-CN": "139519822fe8ab9e10d9d07e68ea0451045380aedaf54ecc51e2a28c6b42a13f",
|
|
887
|
+
"zh-TW": "a63ec7e388f16f1b486dcd948a9f1a3b492be5d9b6bdab786a95e59966786dfd"
|
|
888
|
+
};
|
|
889
|
+
var AVAILABLE_DICTS = [
|
|
890
|
+
{ id: "zh-CN", label: "\u7B80\u4F53\u4E2D\u6587", locale: "zh-CN" },
|
|
891
|
+
{ id: "zh-TW", label: "\u7E41\u9AD4\u4E2D\u6587", locale: "zh-TW" }
|
|
892
|
+
];
|
|
893
|
+
function getDictFilePath(nlpDir, dictId) {
|
|
894
|
+
return path4.join(nlpDir, `${dictId}.dict`);
|
|
895
|
+
}
|
|
896
|
+
function isDictDownloaded(nlpDir, dictId) {
|
|
897
|
+
return fs4.existsSync(getDictFilePath(nlpDir, dictId));
|
|
898
|
+
}
|
|
899
|
+
function getDictList(nlpDir) {
|
|
900
|
+
return AVAILABLE_DICTS.map((d) => {
|
|
901
|
+
const filePath = getDictFilePath(nlpDir, d.id);
|
|
902
|
+
const downloaded = fs4.existsSync(filePath);
|
|
903
|
+
let fileSize;
|
|
904
|
+
if (downloaded) {
|
|
905
|
+
try {
|
|
906
|
+
fileSize = fs4.statSync(filePath).size;
|
|
907
|
+
} catch {
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
return { ...d, downloaded, fileSize };
|
|
911
|
+
});
|
|
912
|
+
}
|
|
913
|
+
function loadDictBuffer(nlpDir, dictId) {
|
|
914
|
+
const filePath = getDictFilePath(nlpDir, dictId);
|
|
915
|
+
if (!fs4.existsSync(filePath)) return null;
|
|
916
|
+
try {
|
|
917
|
+
return fs4.readFileSync(filePath);
|
|
918
|
+
} catch (error) {
|
|
919
|
+
console.error(`[NLP DictManager] Failed to read dict file: ${filePath}`, error);
|
|
920
|
+
return null;
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
async function downloadDict(nlpDir, dictId, onProgress) {
|
|
924
|
+
if (!AVAILABLE_DICTS.find((d) => d.id === dictId)) {
|
|
925
|
+
return { success: false, error: `Unknown dict: ${dictId}` };
|
|
926
|
+
}
|
|
927
|
+
fs4.mkdirSync(nlpDir, { recursive: true });
|
|
928
|
+
const url = `${DICT_DOWNLOAD_URL_BASE}/${dictId}.dict`;
|
|
929
|
+
const filePath = getDictFilePath(nlpDir, dictId);
|
|
930
|
+
const tmpPath = filePath + ".tmp";
|
|
931
|
+
try {
|
|
932
|
+
const response = await fetch(url, { signal: AbortSignal.timeout(12e4) });
|
|
933
|
+
if (!response.ok) throw new Error(`Download failed: HTTP ${response.status}`);
|
|
934
|
+
const total = Number(response.headers.get("content-length") || 0);
|
|
935
|
+
let buffer;
|
|
936
|
+
if (!response.body) {
|
|
937
|
+
buffer = Buffer.from(await response.arrayBuffer());
|
|
938
|
+
} else {
|
|
939
|
+
const reader = response.body.getReader();
|
|
940
|
+
const chunks = [];
|
|
941
|
+
let loaded = 0;
|
|
942
|
+
while (true) {
|
|
943
|
+
const { done, value } = await reader.read();
|
|
944
|
+
if (done) break;
|
|
945
|
+
const chunk = Buffer.from(value);
|
|
946
|
+
chunks.push(chunk);
|
|
947
|
+
loaded += chunk.length;
|
|
948
|
+
if (total > 0 && onProgress) onProgress(Math.round(loaded / total * 100));
|
|
949
|
+
}
|
|
950
|
+
buffer = Buffer.concat(chunks);
|
|
951
|
+
}
|
|
952
|
+
const MIN_DICT_SIZE = 1e6;
|
|
953
|
+
if (buffer.length < MIN_DICT_SIZE) {
|
|
954
|
+
return { success: false, error: `Downloaded file is invalid (${buffer.length} bytes)` };
|
|
955
|
+
}
|
|
956
|
+
const head = buffer.subarray(0, 50).toString("utf-8").trim();
|
|
957
|
+
if (head.startsWith("<!") || head.startsWith("<html")) {
|
|
958
|
+
return { success: false, error: "Downloaded file is HTML, not a dictionary file" };
|
|
959
|
+
}
|
|
960
|
+
const expectedSha256 = DICT_SHA256[dictId];
|
|
961
|
+
if (!expectedSha256) {
|
|
962
|
+
return { success: false, error: `Missing SHA256 checksum for dict: ${dictId}` };
|
|
963
|
+
}
|
|
964
|
+
const actualSha256 = createHash("sha256").update(buffer).digest("hex");
|
|
965
|
+
if (actualSha256 !== expectedSha256) {
|
|
966
|
+
return { success: false, error: "Dictionary integrity check failed (SHA256 mismatch)" };
|
|
967
|
+
}
|
|
968
|
+
fs4.writeFileSync(tmpPath, buffer);
|
|
969
|
+
if (fs4.existsSync(filePath)) fs4.unlinkSync(filePath);
|
|
970
|
+
fs4.renameSync(tmpPath, filePath);
|
|
971
|
+
clearJiebaInstance(dictId);
|
|
972
|
+
console.log(`[NLP DictManager] Dict downloaded: ${dictId} (${fs4.statSync(filePath).size} bytes)`);
|
|
973
|
+
return { success: true };
|
|
974
|
+
} catch (error) {
|
|
975
|
+
if (fs4.existsSync(tmpPath)) {
|
|
976
|
+
try {
|
|
977
|
+
fs4.unlinkSync(tmpPath);
|
|
978
|
+
} catch {
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
982
|
+
console.error(`[NLP DictManager] Download failed for ${dictId}:`, msg);
|
|
983
|
+
return { success: false, error: msg };
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
function deleteDict(nlpDir, dictId) {
|
|
987
|
+
const filePath = getDictFilePath(nlpDir, dictId);
|
|
988
|
+
if (!fs4.existsSync(filePath)) return { success: true };
|
|
989
|
+
try {
|
|
990
|
+
fs4.unlinkSync(filePath);
|
|
991
|
+
clearJiebaInstance(dictId);
|
|
992
|
+
console.log(`[NLP DictManager] Dict deleted: ${dictId}`);
|
|
993
|
+
return { success: true };
|
|
994
|
+
} catch (error) {
|
|
995
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
996
|
+
console.error(`[NLP DictManager] Delete failed for ${dictId}:`, msg);
|
|
997
|
+
return { success: false, error: msg };
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
async function ensureDefaultDict(nlpDir) {
|
|
1001
|
+
if (isDictDownloaded(nlpDir, "zh-CN")) return;
|
|
1002
|
+
console.log("[NLP DictManager] zh-CN dict not found, starting background download...");
|
|
1003
|
+
const result = await downloadDict(nlpDir, "zh-CN");
|
|
1004
|
+
if (result.success) {
|
|
1005
|
+
console.log("[NLP DictManager] zh-CN dict auto-downloaded successfully");
|
|
1006
|
+
} else {
|
|
1007
|
+
console.warn("[NLP DictManager] zh-CN dict auto-download failed:", result.error);
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
|
|
570
1011
|
// ../../packages/node-runtime/src/preferences.ts
|
|
571
|
-
import * as
|
|
572
|
-
import * as
|
|
1012
|
+
import * as fs5 from "fs";
|
|
1013
|
+
import * as path5 from "path";
|
|
573
1014
|
var DEFAULTS = {
|
|
574
1015
|
pinnedSessionIds: [],
|
|
575
1016
|
assistantUpgradeSkippedVersions: {},
|
|
@@ -616,13 +1057,13 @@ var PreferencesManager = class {
|
|
|
616
1057
|
filePath;
|
|
617
1058
|
cache = null;
|
|
618
1059
|
constructor(systemDir) {
|
|
619
|
-
this.filePath =
|
|
1060
|
+
this.filePath = path5.join(systemDir, "preferences.json");
|
|
620
1061
|
}
|
|
621
1062
|
load() {
|
|
622
1063
|
if (this.cache) return this.cache;
|
|
623
1064
|
try {
|
|
624
|
-
if (
|
|
625
|
-
const raw =
|
|
1065
|
+
if (fs5.existsSync(this.filePath)) {
|
|
1066
|
+
const raw = fs5.readFileSync(this.filePath, "utf-8");
|
|
626
1067
|
const parsed = JSON.parse(raw);
|
|
627
1068
|
const migrated = this.migrateLegacyDesensitizeRules(parsed, raw);
|
|
628
1069
|
this.cache = this.mergeDefaults(migrated.preferences);
|
|
@@ -748,21 +1189,21 @@ var PreferencesManager = class {
|
|
|
748
1189
|
};
|
|
749
1190
|
}
|
|
750
1191
|
backupLegacyPreferences(rawContent) {
|
|
751
|
-
const backupDir =
|
|
752
|
-
if (!
|
|
753
|
-
|
|
1192
|
+
const backupDir = path5.join(path5.dirname(this.filePath), "backups");
|
|
1193
|
+
if (!fs5.existsSync(backupDir)) {
|
|
1194
|
+
fs5.mkdirSync(backupDir, { recursive: true });
|
|
754
1195
|
}
|
|
755
1196
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
756
|
-
const backupPath =
|
|
757
|
-
|
|
1197
|
+
const backupPath = path5.join(backupDir, `preferences-pre-desensitize-groups-${timestamp}.json`);
|
|
1198
|
+
fs5.writeFileSync(backupPath, rawContent, "utf-8");
|
|
758
1199
|
console.info(`[Preferences] Backed up legacy desensitize rules to ${backupPath}`);
|
|
759
1200
|
}
|
|
760
1201
|
writePreferences(preferences) {
|
|
761
|
-
const dir =
|
|
762
|
-
if (!
|
|
763
|
-
|
|
1202
|
+
const dir = path5.dirname(this.filePath);
|
|
1203
|
+
if (!fs5.existsSync(dir)) {
|
|
1204
|
+
fs5.mkdirSync(dir, { recursive: true });
|
|
764
1205
|
}
|
|
765
|
-
|
|
1206
|
+
fs5.writeFileSync(this.filePath, JSON.stringify(preferences, null, 2), "utf-8");
|
|
766
1207
|
}
|
|
767
1208
|
/**
|
|
768
1209
|
* Deep merge: arrays are replaced (not concatenated), objects are merged.
|
|
@@ -782,26 +1223,146 @@ var PreferencesManager = class {
|
|
|
782
1223
|
};
|
|
783
1224
|
|
|
784
1225
|
// ../../packages/node-runtime/src/merger/index.ts
|
|
1226
|
+
function createMessageScope(meta, members, sourceIndex) {
|
|
1227
|
+
const platform = meta?.platform || "unknown";
|
|
1228
|
+
const type = meta?.type || "unknown";
|
|
1229
|
+
if (meta?.groupId) return JSON.stringify([platform, type, "group", meta.groupId]);
|
|
1230
|
+
if (type === "group") return JSON.stringify([platform, type, "source", sourceIndex]);
|
|
1231
|
+
const memberIds = [...new Set(members.map((member) => member.platformId))].sort();
|
|
1232
|
+
if (memberIds.length > 0) {
|
|
1233
|
+
if (type === "private") {
|
|
1234
|
+
if (memberIds.length >= 2) return JSON.stringify([platform, type, "members", memberIds]);
|
|
1235
|
+
} else {
|
|
1236
|
+
return JSON.stringify([platform, type, "name-members", meta?.name || "", memberIds]);
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
return JSON.stringify([platform, type, "source", sourceIndex]);
|
|
1240
|
+
}
|
|
1241
|
+
function createMessageBridgeFamily(meta) {
|
|
1242
|
+
if (!meta || meta.groupId || meta.type !== "group" && meta.type !== "private") return void 0;
|
|
1243
|
+
return JSON.stringify([meta.platform || "unknown", meta.type]);
|
|
1244
|
+
}
|
|
1245
|
+
function createMessageIdNormalizer(scopes) {
|
|
1246
|
+
const uniqueScopes = [...new Set(scopes)];
|
|
1247
|
+
const scopeIndexes = new Map(uniqueScopes.map((scope, index) => [scope, index]));
|
|
1248
|
+
const namespaceMessageIds = uniqueScopes.length > 1;
|
|
1249
|
+
return (id, scope) => {
|
|
1250
|
+
if (!id || !namespaceMessageIds) return id;
|
|
1251
|
+
return `__chatlab_message_scope__${scopeIndexes.get(scope) ?? 0}__${encodeURIComponent(id)}`;
|
|
1252
|
+
};
|
|
1253
|
+
}
|
|
1254
|
+
function createMessageScopeResolver(contexts) {
|
|
1255
|
+
const contextBySource = /* @__PURE__ */ new Map();
|
|
1256
|
+
const parents = /* @__PURE__ */ new Map();
|
|
1257
|
+
const firstSourceBySignature = /* @__PURE__ */ new Map();
|
|
1258
|
+
for (const context of contexts) {
|
|
1259
|
+
contextBySource.set(context.sourceIndex, context);
|
|
1260
|
+
parents.set(context.sourceIndex, context.sourceIndex);
|
|
1261
|
+
}
|
|
1262
|
+
const find = (sourceIndex) => {
|
|
1263
|
+
const parent = parents.get(sourceIndex) ?? sourceIndex;
|
|
1264
|
+
if (parent === sourceIndex) return sourceIndex;
|
|
1265
|
+
const root = find(parent);
|
|
1266
|
+
parents.set(sourceIndex, root);
|
|
1267
|
+
return root;
|
|
1268
|
+
};
|
|
1269
|
+
const union = (firstSourceIndex, secondSourceIndex) => {
|
|
1270
|
+
const firstRoot = find(firstSourceIndex);
|
|
1271
|
+
const secondRoot = find(secondSourceIndex);
|
|
1272
|
+
if (firstRoot === secondRoot) return;
|
|
1273
|
+
const root = Math.min(firstRoot, secondRoot);
|
|
1274
|
+
parents.set(firstRoot === root ? secondRoot : firstRoot, root);
|
|
1275
|
+
};
|
|
1276
|
+
return {
|
|
1277
|
+
addMessage(message, context) {
|
|
1278
|
+
if (!context.messageBridgeFamily || !message.platformMessageId) return;
|
|
1279
|
+
const fallbackKey = generateFallbackMessageKey({
|
|
1280
|
+
timestamp: message.timestamp,
|
|
1281
|
+
senderPlatformId: message.senderPlatformId,
|
|
1282
|
+
type: message.type,
|
|
1283
|
+
content: message.content ?? null,
|
|
1284
|
+
replyToMessageId: message.replyToMessageId
|
|
1285
|
+
});
|
|
1286
|
+
const signature = JSON.stringify([context.messageBridgeFamily, message.platformMessageId, fallbackKey]);
|
|
1287
|
+
const firstSourceIndex = firstSourceBySignature.get(signature);
|
|
1288
|
+
if (firstSourceIndex === void 0) {
|
|
1289
|
+
firstSourceBySignature.set(signature, context.sourceIndex);
|
|
1290
|
+
return;
|
|
1291
|
+
}
|
|
1292
|
+
union(firstSourceIndex, context.sourceIndex);
|
|
1293
|
+
},
|
|
1294
|
+
complete() {
|
|
1295
|
+
firstSourceBySignature.clear();
|
|
1296
|
+
},
|
|
1297
|
+
resolve(sourceIndex) {
|
|
1298
|
+
const rootContext = contextBySource.get(find(sourceIndex));
|
|
1299
|
+
return rootContext?.messageScope ?? JSON.stringify(["unknown", "source", sourceIndex]);
|
|
1300
|
+
}
|
|
1301
|
+
};
|
|
1302
|
+
}
|
|
785
1303
|
function checkConflictsFromSources(dataSources) {
|
|
786
|
-
const
|
|
787
|
-
for (const { source, filename } of dataSources) {
|
|
1304
|
+
const preparedSources = dataSources.map(({ source, filename }, sourceIndex) => {
|
|
788
1305
|
const meta = source.getMeta();
|
|
789
|
-
const
|
|
1306
|
+
const members = source.getMembers();
|
|
1307
|
+
return {
|
|
1308
|
+
source,
|
|
1309
|
+
filename,
|
|
1310
|
+
sourceIndex,
|
|
1311
|
+
platform: meta?.platform || "unknown",
|
|
1312
|
+
messageScope: createMessageScope(meta, members, sourceIndex),
|
|
1313
|
+
messageBridgeFamily: createMessageBridgeFamily(meta)
|
|
1314
|
+
};
|
|
1315
|
+
});
|
|
1316
|
+
const allMessages = [];
|
|
1317
|
+
for (const { source, filename, sourceIndex, platform, messageScope, messageBridgeFamily } of preparedSources) {
|
|
790
1318
|
source.streamMessages(1e4, (messages) => {
|
|
791
1319
|
for (const msg of messages) {
|
|
792
|
-
allMessages.push({ msg, source: filename, platform });
|
|
1320
|
+
allMessages.push({ msg, source: filename, sourceIndex, platform, messageScope, messageBridgeFamily });
|
|
793
1321
|
}
|
|
794
1322
|
});
|
|
795
1323
|
}
|
|
796
|
-
|
|
1324
|
+
const result = detectConflictsInMessages(allMessages);
|
|
1325
|
+
const collidingIds = getCollidingPlatformIdsFromMessages(allMessages);
|
|
1326
|
+
const scopeResolver = createMessageScopeResolver(preparedSources);
|
|
1327
|
+
for (const message of allMessages) scopeResolver.addMessage(message.msg, message);
|
|
1328
|
+
scopeResolver.complete();
|
|
1329
|
+
const normalizeMessageId = createMessageIdNormalizer(
|
|
1330
|
+
preparedSources.map(({ sourceIndex }) => scopeResolver.resolve(sourceIndex))
|
|
1331
|
+
);
|
|
1332
|
+
const dedupState = createMessageDedupState();
|
|
1333
|
+
let totalMessages = 0;
|
|
1334
|
+
for (const { msg, sourceIndex, platform } of allMessages) {
|
|
1335
|
+
const messageScope = scopeResolver.resolve(sourceIndex);
|
|
1336
|
+
const senderPlatformId = normalizePlatformId(msg.senderPlatformId, platform, collidingIds);
|
|
1337
|
+
const duplicate = registerMessageAndCheckDuplicate(
|
|
1338
|
+
{
|
|
1339
|
+
platformMessageId: normalizeMessageId(msg.platformMessageId, messageScope),
|
|
1340
|
+
timestamp: msg.timestamp,
|
|
1341
|
+
senderPlatformId,
|
|
1342
|
+
type: msg.type,
|
|
1343
|
+
content: msg.content ?? null,
|
|
1344
|
+
replyToMessageId: normalizeMessageId(msg.replyToMessageId, messageScope)
|
|
1345
|
+
},
|
|
1346
|
+
dedupState
|
|
1347
|
+
);
|
|
1348
|
+
if (!duplicate) totalMessages++;
|
|
1349
|
+
}
|
|
1350
|
+
return { ...result, totalMessages };
|
|
797
1351
|
}
|
|
798
1352
|
function buildMergedOutput(dataSources, outputName) {
|
|
799
|
-
const metas = dataSources.map(({ source, filename }) =>
|
|
800
|
-
meta
|
|
801
|
-
members
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
1353
|
+
const metas = dataSources.map(({ source, filename }, sourceIndex) => {
|
|
1354
|
+
const meta = source.getMeta();
|
|
1355
|
+
const members = source.getMembers();
|
|
1356
|
+
return {
|
|
1357
|
+
meta,
|
|
1358
|
+
members,
|
|
1359
|
+
filename,
|
|
1360
|
+
source,
|
|
1361
|
+
sourceIndex,
|
|
1362
|
+
messageScope: createMessageScope(meta, members, sourceIndex),
|
|
1363
|
+
messageBridgeFamily: createMessageBridgeFamily(meta)
|
|
1364
|
+
};
|
|
1365
|
+
});
|
|
805
1366
|
const collidingIds = getCollidingPlatformIds(
|
|
806
1367
|
metas.map(({ meta, members }) => ({
|
|
807
1368
|
platform: meta?.platform || "unknown",
|
|
@@ -815,24 +1376,63 @@ function buildMergedOutput(dataSources, outputName) {
|
|
|
815
1376
|
})),
|
|
816
1377
|
collidingIds
|
|
817
1378
|
);
|
|
818
|
-
const
|
|
1379
|
+
const uniquePlatforms = [...new Set(metas.map(({ meta }) => meta?.platform || "unknown"))];
|
|
1380
|
+
const scopeResolver = createMessageScopeResolver(metas);
|
|
1381
|
+
for (const context of metas) {
|
|
1382
|
+
if (!context.messageBridgeFamily) continue;
|
|
1383
|
+
context.source.streamMessages(1e4, (messages) => {
|
|
1384
|
+
for (const message of messages) scopeResolver.addMessage(message, context);
|
|
1385
|
+
});
|
|
1386
|
+
}
|
|
1387
|
+
scopeResolver.complete();
|
|
1388
|
+
const normalizeMessageId = createMessageIdNormalizer(
|
|
1389
|
+
metas.map(({ sourceIndex }) => scopeResolver.resolve(sourceIndex))
|
|
1390
|
+
);
|
|
1391
|
+
const dedupState = createMessageDedupState();
|
|
1392
|
+
const fallbackOnlyMessageIndexes = /* @__PURE__ */ new Map();
|
|
819
1393
|
const mergedMessages = [];
|
|
820
|
-
for (const { source, meta } of metas) {
|
|
1394
|
+
for (const { source, meta, sourceIndex } of metas) {
|
|
821
1395
|
const platform2 = meta?.platform || "unknown";
|
|
1396
|
+
const messageScope = scopeResolver.resolve(sourceIndex);
|
|
822
1397
|
source.streamMessages(1e4, (messages) => {
|
|
823
1398
|
for (const msg of messages) {
|
|
824
1399
|
const nid = normalizePlatformId(msg.senderPlatformId, platform2, collidingIds);
|
|
825
|
-
const
|
|
826
|
-
|
|
827
|
-
|
|
1400
|
+
const platformMessageId = normalizeMessageId(msg.platformMessageId, messageScope);
|
|
1401
|
+
const replyToMessageId = normalizeMessageId(msg.replyToMessageId, messageScope);
|
|
1402
|
+
const dedupMessage = {
|
|
1403
|
+
platformMessageId,
|
|
1404
|
+
timestamp: msg.timestamp,
|
|
1405
|
+
senderPlatformId: nid,
|
|
1406
|
+
type: msg.type,
|
|
1407
|
+
content: msg.content ?? null,
|
|
1408
|
+
replyToMessageId
|
|
1409
|
+
};
|
|
1410
|
+
const fallbackKey = generateFallbackMessageKey(dedupMessage);
|
|
1411
|
+
const shouldBackfillId = Boolean(
|
|
1412
|
+
platformMessageId && !dedupState.platformMessageIds.has(platformMessageId) && dedupState.fallbackOnlyKeys.has(fallbackKey)
|
|
1413
|
+
);
|
|
1414
|
+
if (registerMessageAndCheckDuplicate(dedupMessage, dedupState)) {
|
|
1415
|
+
if (shouldBackfillId) {
|
|
1416
|
+
const retainedIndex2 = fallbackOnlyMessageIndexes.get(fallbackKey);
|
|
1417
|
+
if (retainedIndex2 !== void 0) {
|
|
1418
|
+
mergedMessages[retainedIndex2].platformMessageId = platformMessageId;
|
|
1419
|
+
fallbackOnlyMessageIndexes.delete(fallbackKey);
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
continue;
|
|
1423
|
+
}
|
|
1424
|
+
const retainedIndex = mergedMessages.length;
|
|
828
1425
|
mergedMessages.push({
|
|
1426
|
+
platformMessageId,
|
|
829
1427
|
sender: nid,
|
|
830
1428
|
accountName: msg.senderAccountName,
|
|
831
1429
|
groupNickname: msg.senderGroupNickname,
|
|
832
1430
|
timestamp: msg.timestamp,
|
|
833
1431
|
type: msg.type,
|
|
834
|
-
content: msg.content
|
|
1432
|
+
content: msg.content,
|
|
1433
|
+
replyToMessageId
|
|
835
1434
|
});
|
|
1435
|
+
if (!platformMessageId) fallbackOnlyMessageIndexes.set(fallbackKey, retainedIndex);
|
|
836
1436
|
}
|
|
837
1437
|
});
|
|
838
1438
|
}
|
|
@@ -842,11 +1442,13 @@ function buildMergedOutput(dataSources, outputName) {
|
|
|
842
1442
|
platform: source.getMeta()?.platform || "unknown",
|
|
843
1443
|
messageCount: source.getMessageCount()
|
|
844
1444
|
}));
|
|
845
|
-
const uniquePlatforms = [...new Set(metas.map(({ meta }) => meta?.platform || "unknown"))];
|
|
846
1445
|
const platform = uniquePlatforms.length === 1 ? uniquePlatforms[0] : "mixed";
|
|
847
1446
|
const groupIds = new Set(metas.map(({ meta }) => meta?.groupId).filter(Boolean));
|
|
848
1447
|
const groupId = groupIds.size === 1 ? metas.find(({ meta }) => meta?.groupId)?.meta?.groupId : void 0;
|
|
849
1448
|
const groupAvatar = groupId ? metas.filter(({ meta }) => meta?.groupId === groupId).pop()?.meta?.groupAvatar : void 0;
|
|
1449
|
+
const ownerIds = new Set(metas.map(({ meta }) => meta?.ownerId).filter((id) => Boolean(id)));
|
|
1450
|
+
const ownerCandidate = uniquePlatforms.length === 1 && ownerIds.size === 1 ? [...ownerIds][0] : void 0;
|
|
1451
|
+
const ownerId = ownerCandidate && memberMap.has(ownerCandidate) ? ownerCandidate : void 0;
|
|
850
1452
|
const chatLabData = {
|
|
851
1453
|
chatlab: {
|
|
852
1454
|
version: CHATLAB_FORMAT_VERSION,
|
|
@@ -860,7 +1462,8 @@ function buildMergedOutput(dataSources, outputName) {
|
|
|
860
1462
|
type: metas[0]?.meta?.type || "group",
|
|
861
1463
|
sources,
|
|
862
1464
|
groupId,
|
|
863
|
-
groupAvatar
|
|
1465
|
+
groupAvatar,
|
|
1466
|
+
ownerId
|
|
864
1467
|
},
|
|
865
1468
|
members: Array.from(memberMap.values()),
|
|
866
1469
|
messages: mergedMessages
|
|
@@ -882,7 +1485,7 @@ function* serializeChatLabToJsonl(data) {
|
|
|
882
1485
|
}
|
|
883
1486
|
|
|
884
1487
|
// ../../packages/node-runtime/src/services/analytics.ts
|
|
885
|
-
import * as
|
|
1488
|
+
import * as path6 from "path";
|
|
886
1489
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
887
1490
|
var DEFAULT_DATA = {
|
|
888
1491
|
lastReportDate: null,
|
|
@@ -994,7 +1597,7 @@ var AnalyticsService = class {
|
|
|
994
1597
|
// Daily-active deduplication is intentionally process-local unless concurrent runtimes become supported.
|
|
995
1598
|
dailyActivePromise = null;
|
|
996
1599
|
constructor(systemDir, options) {
|
|
997
|
-
this.dataPath =
|
|
1600
|
+
this.dataPath = path6.join(systemDir, "analytics.json");
|
|
998
1601
|
this.options = {
|
|
999
1602
|
...options,
|
|
1000
1603
|
umami: options.umami ?? getUmamiConfigFromEnv()
|
|
@@ -1156,8 +1759,8 @@ var AnalyticsService = class {
|
|
|
1156
1759
|
};
|
|
1157
1760
|
|
|
1158
1761
|
// ../../packages/node-runtime/src/services/demo-import.ts
|
|
1159
|
-
import * as
|
|
1160
|
-
import * as
|
|
1762
|
+
import * as fs6 from "fs";
|
|
1763
|
+
import * as path7 from "path";
|
|
1161
1764
|
var DEMO_BASE_URL = "https://chatlab.fun/assets/demo";
|
|
1162
1765
|
var DEMO_FILES = ["demo-group.json", "demo-private-B-wukong.json"];
|
|
1163
1766
|
async function importDemoSessions(options) {
|
|
@@ -1189,8 +1792,8 @@ async function importDemoSessions(options) {
|
|
|
1189
1792
|
}
|
|
1190
1793
|
const rebased = rebaseChatLabDemoDocuments(downloaded, options.now?.() ?? /* @__PURE__ */ new Date(), options.targetTimeZone);
|
|
1191
1794
|
const localPaths = rebased.documents.map((document, index) => {
|
|
1192
|
-
const localPath =
|
|
1193
|
-
|
|
1795
|
+
const localPath = path7.join(createdTempDir, DEMO_FILES[index]);
|
|
1796
|
+
fs6.writeFileSync(localPath, document, "utf8");
|
|
1194
1797
|
return localPath;
|
|
1195
1798
|
});
|
|
1196
1799
|
appLogger.info("demo-import", "Demo timeline prepared", {
|
|
@@ -1274,7 +1877,6 @@ __export(semantic_index_exports, {
|
|
|
1274
1877
|
composeChunkId: () => composeChunkId,
|
|
1275
1878
|
computeChunkerConfigHash: () => computeChunkerConfigHash,
|
|
1276
1879
|
computeDbPathHash: () => computeDbPathHash,
|
|
1277
|
-
createChatDbFtsSearcher: () => createChatDbFtsSearcher,
|
|
1278
1880
|
createChatDbMessageRangeReader: () => createChatDbMessageRangeReader,
|
|
1279
1881
|
createChatDbMessageSource: () => createChatDbMessageSource,
|
|
1280
1882
|
createEmbedder: () => createEmbedder,
|
|
@@ -1287,24 +1889,22 @@ __export(semantic_index_exports, {
|
|
|
1287
1889
|
deriveParentId: () => deriveParentId,
|
|
1288
1890
|
embedding: () => embedding_exports,
|
|
1289
1891
|
estimateTokens: () => estimateTokens,
|
|
1290
|
-
extractFtsKeywords: () => extractFtsKeywords,
|
|
1291
1892
|
formatEvidenceMessages: () => formatEvidenceMessages,
|
|
1292
|
-
hybridSearch: () => hybridSearch,
|
|
1293
1893
|
isSemanticIndexConfigured: () => isSemanticIndexConfigured,
|
|
1294
1894
|
isSemanticVoid: () => isSemanticVoid,
|
|
1295
1895
|
parseParentBounds: () => parseParentBounds,
|
|
1296
1896
|
persistSemanticIndexConfig: () => persistSemanticIndexConfig,
|
|
1297
|
-
reciprocalRankFusion: () => reciprocalRankFusion,
|
|
1298
1897
|
resolveModelId: () => resolveModelId,
|
|
1299
1898
|
resolveSemanticIndexApiKeySet: () => resolveSemanticIndexApiKeySet,
|
|
1300
1899
|
runWarmup: () => runWarmup,
|
|
1900
|
+
semanticSearch: () => semanticSearch,
|
|
1301
1901
|
snapshotPathProvider: () => snapshotPathProvider,
|
|
1302
1902
|
vecTableName: () => vecTableName,
|
|
1303
1903
|
vecTableSchema: () => vecTableSchema
|
|
1304
1904
|
});
|
|
1305
1905
|
|
|
1306
1906
|
// ../../packages/node-runtime/src/semantic-index/worker-client.ts
|
|
1307
|
-
import
|
|
1907
|
+
import path8 from "path";
|
|
1308
1908
|
|
|
1309
1909
|
// ../../packages/node-runtime/src/semantic-index/worker-thread-transport.ts
|
|
1310
1910
|
import { Worker } from "worker_threads";
|
|
@@ -1686,7 +2286,7 @@ function createSemanticIndexWorkerClient(options) {
|
|
|
1686
2286
|
}
|
|
1687
2287
|
function createSemanticIndexWorkerRuntimeClient(options) {
|
|
1688
2288
|
const configStore = new SemanticIndexConfigStore(
|
|
1689
|
-
|
|
2289
|
+
path8.join(options.pathProvider.getAiDataDir(), SEMANTIC_INDEX_CONFIG_FILE)
|
|
1690
2290
|
);
|
|
1691
2291
|
return createSemanticIndexWorkerClient({
|
|
1692
2292
|
configStore,
|
|
@@ -1741,7 +2341,23 @@ export {
|
|
|
1741
2341
|
NodePathProvider,
|
|
1742
2342
|
getDefaultNodeUserDataDir,
|
|
1743
2343
|
getSystemLogsDir,
|
|
2344
|
+
initNlpDir,
|
|
2345
|
+
getNlpDir,
|
|
2346
|
+
getJieba,
|
|
2347
|
+
clearJiebaInstance,
|
|
2348
|
+
segment,
|
|
2349
|
+
batchSegmentWithFrequency,
|
|
2350
|
+
collectPosTagStats,
|
|
2351
|
+
getPosTagDefinitions,
|
|
1744
2352
|
createJiebaNlpProvider,
|
|
2353
|
+
computeWordFrequency,
|
|
2354
|
+
segmentText,
|
|
2355
|
+
isDictDownloaded,
|
|
2356
|
+
getDictList,
|
|
2357
|
+
loadDictBuffer,
|
|
2358
|
+
downloadDict,
|
|
2359
|
+
deleteDict,
|
|
2360
|
+
ensureDefaultDict,
|
|
1745
2361
|
PreferencesManager,
|
|
1746
2362
|
checkConflictsFromSources,
|
|
1747
2363
|
buildMergedOutput,
|
|
@@ -1751,4 +2367,4 @@ export {
|
|
|
1751
2367
|
createSemanticIndexWorkerRuntimeClient,
|
|
1752
2368
|
semantic_index_exports
|
|
1753
2369
|
};
|
|
1754
|
-
//# sourceMappingURL=chunk-
|
|
2370
|
+
//# sourceMappingURL=chunk-XIWVJAXH.mjs.map
|