kakaotalk-chat-analyzer 0.19.4 → 0.19.5

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 (46) hide show
  1. package/README.md +5 -1
  2. package/data/ml-models/manifest.json +25 -0
  3. package/dist/src/ml/model-ids.d.ts +15 -0
  4. package/dist/src/ml/model-ids.js +28 -0
  5. package/dist/src/ml/model-ids.js.map +1 -0
  6. package/dist/src/ml/registry.d.ts +0 -2
  7. package/dist/src/ml/registry.js +10 -13
  8. package/dist/src/ml/registry.js.map +1 -1
  9. package/dist/src/ml/tasks/index.js +15 -0
  10. package/dist/src/ml/tasks/index.js.map +1 -1
  11. package/dist/src/ml-bundle-cache.d.ts +14 -0
  12. package/dist/src/ml-bundle-cache.js +167 -0
  13. package/dist/src/ml-bundle-cache.js.map +1 -0
  14. package/dist/src/ml-bundle-ids.d.ts +6 -0
  15. package/dist/src/ml-bundle-ids.js +7 -0
  16. package/dist/src/ml-bundle-ids.js.map +1 -0
  17. package/dist/src/ml-bundle-install.d.ts +2 -0
  18. package/dist/src/ml-bundle-install.js +57 -0
  19. package/dist/src/ml-bundle-install.js.map +1 -0
  20. package/dist/src/ml-bundled-models.d.ts +2 -9
  21. package/dist/src/ml-bundled-models.js +29 -47
  22. package/dist/src/ml-bundled-models.js.map +1 -1
  23. package/dist/src/semantic-keywords.js +7 -4
  24. package/dist/src/semantic-keywords.js.map +1 -1
  25. package/dist/src/semantic-policy.d.ts +5 -7
  26. package/dist/src/semantic-policy.js +12 -12
  27. package/dist/src/semantic-policy.js.map +1 -1
  28. package/dist/src/sentiment-analyze.js +2 -0
  29. package/dist/src/sentiment-analyze.js.map +1 -1
  30. package/dist/src/sentiment-hub-registry.d.ts +2 -2
  31. package/dist/src/sentiment-hub-registry.js +4 -9
  32. package/dist/src/sentiment-hub-registry.js.map +1 -1
  33. package/dist/src/sentiment-policy.d.ts +4 -8
  34. package/dist/src/sentiment-policy.js +12 -20
  35. package/dist/src/sentiment-policy.js.map +1 -1
  36. package/dist/src/toxicity-analyze.js +18 -7
  37. package/dist/src/toxicity-analyze.js.map +1 -1
  38. package/dist/src/toxicity-policy.js +1 -2
  39. package/dist/src/toxicity-policy.js.map +1 -1
  40. package/dist/src/version.d.ts +2 -2
  41. package/dist/src/version.js +1 -1
  42. package/package.json +8 -4
  43. package/scripts/ensure-ml-bundles.mjs +52 -0
  44. package/scripts/ml-bundle-install.mjs +62 -0
  45. package/scripts/ml-bundle-lib.mjs +65 -0
  46. package/data/ml-models/kca-koelectra-korean-sentiment/README.md +0 -24
package/README.md CHANGED
@@ -205,7 +205,9 @@ kca llm pull 4B # 수동 size (없으면 분석 시 자동
205
205
  KCA_LLM_BACKEND=ollama kca ./chat.csv --preset custom
206
206
  ```
207
207
 
208
- 환경 변수: `KCA_PRESET`, `KCA_SEMANTIC_MODEL`, `KCA_SENTIMENT_MODEL`, `KCA_LLM`(기본 on, `0`만 끔), `KCA_LLM_MODEL`(0.8B|2B|4B|9B), `KCA_LLM_BACKEND`, `KCA_OLLAMA_MODEL`, `KCA_LLM_MOCK`, `KCA_ONNX_GPU`, `KCA_EMBED_BATCH`, `KCA_SENTIMENT_BATCH`, `KCA_KIWI_WORKERS`, `KCA_NO_KIWI_WORKERS`, `KCA_PROFILE_PHASES`, `KCA_BENCH_CSV`, `KCA_KEYWORD_SUMMARY_TOP`, `KCA_SHOP_SEARCH_TOP`.
208
+ 환경 변수: `KCA_PRESET`, `KCA_SEMANTIC_MODEL`, `KCA_SENTIMENT_MODEL`, `KCA_LLM`(기본 on, `0`만 끔), `KCA_LLM_MODEL`(0.8B|2B|4B|9B), `KCA_LLM_BACKEND`, `KCA_OLLAMA_MODEL`, `KCA_LLM_MOCK`, `KCA_ONNX_GPU`, `KCA_EMBED_BATCH`, `KCA_SENTIMENT_BATCH`, `KCA_KIWI_WORKERS`, `KCA_NO_KIWI_WORKERS`, `KCA_PROFILE_PHASES`, `KCA_BENCH_CSV`, `KCA_KEYWORD_SUMMARY_TOP`, `KCA_SHOP_SEARCH_TOP`, `KCA_NO_ML_AUTO_INSTALL`, `KCA_SKIP_ML_POSTINSTALL`.
209
+
210
+ **ML ONNX:** `npm install`·첫 분석 시 `kakaotalk-chat-analyzer-models`(감정 NSMC·임베딩) 자동 설치 시도. 없으면 Hub `daekeun-ml/koelectra-small-v3-nsmc` 등으로 폴백. 독성 ONNX는 필요 시 GitHub Release zip lazy(`KCA_NO_TOXICITY_DOWNLOAD=1`로 끔).
209
211
 
210
212
  **속도(품질 유지):** 대용량 CSV는 Kiwi worker pool(`KCA_KIWI_WORKERS`, RAM≥8GB 기본 2–4)·시맨틱/감정을 키워드 패스와 겹쳐 실행. `KCA_PROFILE_PHASES=1`로 단계별 ms. quality에서 GPU 가속: `onnxruntime-node` 설치 후 `KCA_ONNX_GPU=metal`(macOS)·`cuda`(Linux)·`dml`(Windows).
211
213
 
@@ -249,6 +251,8 @@ cd kakaotalk-chat-analyzer && npm install && npm run build && npm test
249
251
 
250
252
  | 버전 | 요약 |
251
253
  |------|------|
254
+ | **0.19.5** | KoELECTRA NSMC·embed ONNX npm 번들 자동 설치·독성 lazy zip·Hub 폴백 정리 |
255
+ | **0.19.4** | Qwen3.5 RAM greedy 자동 선택·모든 preset LLM on |
252
256
  | **0.18.2** | 주제 맵 3레인(graph·키워드·임베딩) 병합·테마 6~12·LLM `topicProposals` |
253
257
  | **0.18.1** | 키워드 빈도/특이어 dual-view·샵검색 통계·dyad 셀 숫자·LLM 인사이트 필드 |
254
258
  | **0.18.0** | preset(speed/balanced/quality)·5분 예산 skip·LLM 서사·KLUE 감정·dual-lane 툴팁·CI Playwright |
@@ -0,0 +1,25 @@
1
+ {
2
+ "schema": "kca-ml-models/1",
3
+ "version": "0.2.0",
4
+ "bundled": false,
5
+ "models": [
6
+ {
7
+ "id": "kca-koelectra-small-v3-nsmc",
8
+ "task": "text-classification",
9
+ "source": "daekeun-ml/koelectra-small-v3-nsmc",
10
+ "bundled": true
11
+ },
12
+ {
13
+ "id": "kca-koelectra-small-v3-embed",
14
+ "task": "feature-extraction",
15
+ "source": "monologg/koelectra-small-v3-discriminator",
16
+ "bundled": true
17
+ },
18
+ {
19
+ "id": "kca-kcelectra-base-toxicity",
20
+ "task": "text-classification",
21
+ "source": "monologg/koelectra-base-v3-discriminator",
22
+ "bundled": true
23
+ }
24
+ ]
25
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * kca 한국어 인코더 플레인 — Hub 원본 id (번들은 ml-bundled-models).
3
+ * 구 Xenova·dragonkue·cringepnh 모델은 사용하지 않습니다.
4
+ */
5
+ /** NSMC 이진 감정 — 기본 감정 모델 */
6
+ export declare const HUB_KOELECTRA_NSMC = "daekeun-ml/koelectra-small-v3-nsmc";
7
+ /** feature-extraction — quality 시맨틱·임베딩 */
8
+ export declare const HUB_KOELECTRA_EMBED = "monologg/koelectra-small-v3-discriminator";
9
+ /** KorSTS 유사도 — balanced 시맨틱 */
10
+ export declare const HUB_KOELECTRA_KORSTS = "daekeun-ml/koelectra-small-v3-korsts";
11
+ /** 독성·공격 톤 (base) */
12
+ export declare const HUB_KCELECTRA_TOXICITY = "monologg/koelectra-base-v3-discriminator";
13
+ /** 익명 Hub 401 또는 정책상 금지 — `KCA_*_MODEL` 로만 시도 */
14
+ export declare const DEPRECATED_SENTIMENT_HUB_IDS: readonly ["Xenova/bert-base-multilingual-uncased-sentiment", "Xenova/distilbert-base-multilingual-cased-sentiment", "Xenova/klue-roberta-small-sentiment", "Xenova/klue-roberta-base", "Xenova/twitter-xlm-roberta-base-sentiment", "smilegate-ai/kor_unified_sentiment", "cringepnh/koelectra-korean-sentiment"];
15
+ export declare const DEPRECATED_SEMANTIC_HUB_IDS: readonly ["Xenova/multilingual-e5-small", "dragonkue/multilingual-e5-small-ko-v2", "Xenova/paraphrase-multilingual-MiniLM-L12-v2"];
@@ -0,0 +1,28 @@
1
+ /**
2
+ * kca 한국어 인코더 플레인 — Hub 원본 id (번들은 ml-bundled-models).
3
+ * 구 Xenova·dragonkue·cringepnh 모델은 사용하지 않습니다.
4
+ */
5
+ /** NSMC 이진 감정 — 기본 감정 모델 */
6
+ export const HUB_KOELECTRA_NSMC = "daekeun-ml/koelectra-small-v3-nsmc";
7
+ /** feature-extraction — quality 시맨틱·임베딩 */
8
+ export const HUB_KOELECTRA_EMBED = "monologg/koelectra-small-v3-discriminator";
9
+ /** KorSTS 유사도 — balanced 시맨틱 */
10
+ export const HUB_KOELECTRA_KORSTS = "daekeun-ml/koelectra-small-v3-korsts";
11
+ /** 독성·공격 톤 (base) */
12
+ export const HUB_KCELECTRA_TOXICITY = "monologg/koelectra-base-v3-discriminator";
13
+ /** 익명 Hub 401 또는 정책상 금지 — `KCA_*_MODEL` 로만 시도 */
14
+ export const DEPRECATED_SENTIMENT_HUB_IDS = [
15
+ "Xenova/bert-base-multilingual-uncased-sentiment",
16
+ "Xenova/distilbert-base-multilingual-cased-sentiment",
17
+ "Xenova/klue-roberta-small-sentiment",
18
+ "Xenova/klue-roberta-base",
19
+ "Xenova/twitter-xlm-roberta-base-sentiment",
20
+ "smilegate-ai/kor_unified_sentiment",
21
+ "cringepnh/koelectra-korean-sentiment",
22
+ ];
23
+ export const DEPRECATED_SEMANTIC_HUB_IDS = [
24
+ "Xenova/multilingual-e5-small",
25
+ "dragonkue/multilingual-e5-small-ko-v2",
26
+ "Xenova/paraphrase-multilingual-MiniLM-L12-v2",
27
+ ];
28
+ //# sourceMappingURL=model-ids.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-ids.js","sourceRoot":"","sources":["../../../src/ml/model-ids.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,4BAA4B;AAC5B,MAAM,CAAC,MAAM,kBAAkB,GAAG,oCAAoC,CAAC;AAEvE,2CAA2C;AAC3C,MAAM,CAAC,MAAM,mBAAmB,GAAG,2CAA2C,CAAC;AAE/E,gCAAgC;AAChC,MAAM,CAAC,MAAM,oBAAoB,GAAG,sCAAsC,CAAC;AAE3E,qBAAqB;AACrB,MAAM,CAAC,MAAM,sBAAsB,GAAG,0CAA0C,CAAC;AAEjF,iDAAiD;AACjD,MAAM,CAAC,MAAM,4BAA4B,GAAG;IAC1C,iDAAiD;IACjD,qDAAqD;IACrD,qCAAqC;IACrC,0BAA0B;IAC1B,2CAA2C;IAC3C,oCAAoC;IACpC,sCAAsC;CAC9B,CAAC;AAEX,MAAM,CAAC,MAAM,2BAA2B,GAAG;IACzC,8BAA8B;IAC9B,uCAAuC;IACvC,8CAA8C;CACtC,CAAC"}
@@ -7,8 +7,6 @@ export interface MlModelSpec {
7
7
  }
8
8
  export declare const ML_MODEL_REGISTRY: Record<MlTask, MlModelSpec>;
9
9
  export declare function isBundledModelReady(task: MlTask): boolean;
10
- /** quality preset sentiment: 번들 id (레거시 디렉터리 포함) */
11
10
  export declare function resolveSentimentBundledId(): string;
12
- /** quality preset embedding: 번들 KoELECTRA 또는 E5 Hub */
13
11
  export declare function resolveEmbeddingModelId(preset: string | undefined): string;
14
12
  export declare function resolveToxicityModelId(): string;
@@ -1,23 +1,22 @@
1
1
  import { BUNDLED_EMBED_MODEL_ID, BUNDLED_SENTIMENT_MODEL_ID, BUNDLED_TOXICITY_MODEL_ID, isBundledEmbedModelReady, isBundledSentimentModelReady, isBundledToxicityModelReady, resolveBundledSentimentModelId, } from "../ml-bundled-models.js";
2
- import { DEFAULT_SENTIMENT_MODEL } from "../sentiment-policy.js";
3
- import { DEFAULT_KOREAN_SEMANTIC_MODEL, QUALITY_KOREAN_SEMANTIC_MODEL, } from "../semantic-policy.js";
2
+ import { HUB_KCELECTRA_TOXICITY, HUB_KOELECTRA_EMBED, HUB_KOELECTRA_KORSTS, HUB_KOELECTRA_NSMC, } from "./model-ids.js";
4
3
  export const ML_MODEL_REGISTRY = {
5
4
  sentiment: {
6
5
  task: "sentiment",
7
6
  bundledId: BUNDLED_SENTIMENT_MODEL_ID,
8
- hubFallback: DEFAULT_SENTIMENT_MODEL,
7
+ hubFallback: HUB_KOELECTRA_NSMC,
9
8
  hubTask: "text-classification",
10
9
  },
11
10
  embedding: {
12
11
  task: "embedding",
13
12
  bundledId: BUNDLED_EMBED_MODEL_ID,
14
- hubFallback: QUALITY_KOREAN_SEMANTIC_MODEL,
13
+ hubFallback: HUB_KOELECTRA_EMBED,
15
14
  hubTask: "feature-extraction",
16
15
  },
17
16
  toxicity: {
18
17
  task: "toxicity",
19
18
  bundledId: BUNDLED_TOXICITY_MODEL_ID,
20
- hubFallback: "",
19
+ hubFallback: HUB_KCELECTRA_TOXICITY,
21
20
  hubTask: "text-classification",
22
21
  },
23
22
  };
@@ -28,27 +27,25 @@ export function isBundledModelReady(task) {
28
27
  return isBundledEmbedModelReady();
29
28
  return isBundledToxicityModelReady();
30
29
  }
31
- /** quality preset sentiment: 번들 id (레거시 디렉터리 포함) */
32
30
  export function resolveSentimentBundledId() {
33
31
  return resolveBundledSentimentModelId();
34
32
  }
35
- /** quality preset embedding: 번들 KoELECTRA 또는 E5 Hub */
36
33
  export function resolveEmbeddingModelId(preset) {
37
34
  const env = process.env.KCA_SEMANTIC_MODEL?.trim();
38
35
  if (env)
39
36
  return env;
40
- if (preset === "quality" && isBundledEmbedModelReady())
37
+ if (isBundledEmbedModelReady())
41
38
  return BUNDLED_EMBED_MODEL_ID;
42
39
  if (preset === "quality")
43
- return QUALITY_KOREAN_SEMANTIC_MODEL;
44
- return DEFAULT_KOREAN_SEMANTIC_MODEL;
40
+ return HUB_KOELECTRA_EMBED;
41
+ return HUB_KOELECTRA_KORSTS;
45
42
  }
46
43
  export function resolveToxicityModelId() {
47
44
  const env = process.env.KCA_TOXICITY_MODEL?.trim();
48
45
  if (env)
49
46
  return env;
50
- if (isBundledToxicityModelReady())
51
- return BUNDLED_TOXICITY_MODEL_ID;
52
- return "";
47
+ if (process.env.KCA_TOXICITY_HUB_ONLY === "1")
48
+ return HUB_KCELECTRA_TOXICITY;
49
+ return BUNDLED_TOXICITY_MODEL_ID;
53
50
  }
54
51
  //# sourceMappingURL=registry.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/ml/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,0BAA0B,EAC1B,yBAAyB,EACzB,wBAAwB,EACxB,4BAA4B,EAC5B,2BAA2B,EAC3B,8BAA8B,GAC/B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EACL,6BAA6B,EAC7B,6BAA6B,GAC9B,MAAM,uBAAuB,CAAC;AAW/B,MAAM,CAAC,MAAM,iBAAiB,GAAgC;IAC5D,SAAS,EAAE;QACT,IAAI,EAAE,WAAW;QACjB,SAAS,EAAE,0BAA0B;QACrC,WAAW,EAAE,uBAAuB;QACpC,OAAO,EAAE,qBAAqB;KAC/B;IACD,SAAS,EAAE;QACT,IAAI,EAAE,WAAW;QACjB,SAAS,EAAE,sBAAsB;QACjC,WAAW,EAAE,6BAA6B;QAC1C,OAAO,EAAE,oBAAoB;KAC9B;IACD,QAAQ,EAAE;QACR,IAAI,EAAE,UAAU;QAChB,SAAS,EAAE,yBAAyB;QACpC,WAAW,EAAE,EAAE;QACf,OAAO,EAAE,qBAAqB;KAC/B;CACF,CAAC;AAEF,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,IAAI,IAAI,KAAK,WAAW;QAAE,OAAO,4BAA4B,EAAE,CAAC;IAChE,IAAI,IAAI,KAAK,WAAW;QAAE,OAAO,wBAAwB,EAAE,CAAC;IAC5D,OAAO,2BAA2B,EAAE,CAAC;AACvC,CAAC;AAED,oDAAoD;AACpD,MAAM,UAAU,yBAAyB;IACvC,OAAO,8BAA8B,EAAE,CAAC;AAC1C,CAAC;AAED,uDAAuD;AACvD,MAAM,UAAU,uBAAuB,CAAC,MAA0B;IAChE,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,IAAI,EAAE,CAAC;IACnD,IAAI,GAAG;QAAE,OAAO,GAAG,CAAC;IACpB,IAAI,MAAM,KAAK,SAAS,IAAI,wBAAwB,EAAE;QAAE,OAAO,sBAAsB,CAAC;IACtF,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,6BAA6B,CAAC;IAC/D,OAAO,6BAA6B,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,sBAAsB;IACpC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,IAAI,EAAE,CAAC;IACnD,IAAI,GAAG;QAAE,OAAO,GAAG,CAAC;IACpB,IAAI,2BAA2B,EAAE;QAAE,OAAO,yBAAyB,CAAC;IACpE,OAAO,EAAE,CAAC;AACZ,CAAC"}
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/ml/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,0BAA0B,EAC1B,yBAAyB,EACzB,wBAAwB,EACxB,4BAA4B,EAC5B,2BAA2B,EAC3B,8BAA8B,GAC/B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,sBAAsB,EACtB,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,gBAAgB,CAAC;AAWxB,MAAM,CAAC,MAAM,iBAAiB,GAAgC;IAC5D,SAAS,EAAE;QACT,IAAI,EAAE,WAAW;QACjB,SAAS,EAAE,0BAA0B;QACrC,WAAW,EAAE,kBAAkB;QAC/B,OAAO,EAAE,qBAAqB;KAC/B;IACD,SAAS,EAAE;QACT,IAAI,EAAE,WAAW;QACjB,SAAS,EAAE,sBAAsB;QACjC,WAAW,EAAE,mBAAmB;QAChC,OAAO,EAAE,oBAAoB;KAC9B;IACD,QAAQ,EAAE;QACR,IAAI,EAAE,UAAU;QAChB,SAAS,EAAE,yBAAyB;QACpC,WAAW,EAAE,sBAAsB;QACnC,OAAO,EAAE,qBAAqB;KAC/B;CACF,CAAC;AAEF,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,IAAI,IAAI,KAAK,WAAW;QAAE,OAAO,4BAA4B,EAAE,CAAC;IAChE,IAAI,IAAI,KAAK,WAAW;QAAE,OAAO,wBAAwB,EAAE,CAAC;IAC5D,OAAO,2BAA2B,EAAE,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,yBAAyB;IACvC,OAAO,8BAA8B,EAAE,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,MAA0B;IAChE,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,IAAI,EAAE,CAAC;IACnD,IAAI,GAAG;QAAE,OAAO,GAAG,CAAC;IACpB,IAAI,wBAAwB,EAAE;QAAE,OAAO,sBAAsB,CAAC;IAC9D,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,mBAAmB,CAAC;IACrD,OAAO,oBAAoB,CAAC;AAC9B,CAAC;AAED,MAAM,UAAU,sBAAsB;IACpC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,IAAI,EAAE,CAAC;IACnD,IAAI,GAAG;QAAE,OAAO,GAAG,CAAC;IACpB,IAAI,OAAO,CAAC,GAAG,CAAC,qBAAqB,KAAK,GAAG;QAAE,OAAO,sBAAsB,CAAC;IAC7E,OAAO,yBAAyB,CAAC;AACnC,CAAC"}
@@ -1,8 +1,23 @@
1
+ import { ensureCoreMlBundles } from "../../ml-bundle-install.js";
2
+ import { ensureToxicityBundle } from "../../ml-bundle-cache.js";
1
3
  import { preloadSentimentPipeline } from "./sentiment.js";
2
4
  import { preloadSemanticPipeline } from "./embedding.js";
3
5
  import { preloadToxicityPipeline } from "../../toxicity-analyze.js";
6
+ import { isBundledToxicityModelReady } from "../../ml-bundled-models.js";
4
7
  /** transformers 파이프라인 병렬 워밍업(실패는 stderr 만) */
5
8
  export async function preloadUtteranceMlTasks(opts) {
9
+ if (opts.sentiment || opts.semantic || opts.toxicity) {
10
+ await ensureCoreMlBundles().catch((error) => {
11
+ const msg = error instanceof Error ? error.message : String(error);
12
+ process.stderr.write(`[kca] ML 번들 준비 건너뜀: ${msg}\n`);
13
+ });
14
+ }
15
+ if (opts.toxicity && !process.env.KCA_TOXICITY_MODEL?.trim() && !isBundledToxicityModelReady()) {
16
+ await ensureToxicityBundle().catch((error) => {
17
+ const msg = error instanceof Error ? error.message : String(error);
18
+ process.stderr.write(`[kca] 독성 번들 준비 건너뜀: ${msg}\n`);
19
+ });
20
+ }
6
21
  const warmups = [];
7
22
  if (opts.sentiment) {
8
23
  warmups.push(preloadSentimentPipeline(opts.buildOptions, opts.messageCount).catch((error) => {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/ml/tasks/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AACzD,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAUpE,8CAA8C;AAC9C,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,IAAoC;IAChF,MAAM,OAAO,GAAuB,EAAE,CAAC;IACvC,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;QACnB,OAAO,CAAC,IAAI,CACV,wBAAwB,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;YAC7E,MAAM,GAAG,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACnE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,qBAAqB,GAAG,IAAI,CAAC,CAAC;QACrD,CAAC,CAAC,CACH,CAAC;IACJ,CAAC;IACD,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClB,OAAO,CAAC,IAAI,CACV,uBAAuB,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;YAC5E,MAAM,GAAG,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACnE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,sBAAsB,GAAG,IAAI,CAAC,CAAC;QACtD,CAAC,CAAC,CACH,CAAC;IACJ,CAAC;IACD,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClB,OAAO,CAAC,IAAI,CACV,uBAAuB,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;YACxC,MAAM,GAAG,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACnE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,qBAAqB,GAAG,IAAI,CAAC,CAAC;QACrD,CAAC,CAAC,CACH,CAAC;IACJ,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,OAAO;QAAE,MAAM,IAAI,CAAC;AACzC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/ml/tasks/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AACzD,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,EAAE,2BAA2B,EAAE,MAAM,4BAA4B,CAAC;AAUzE,8CAA8C;AAC9C,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,IAAoC;IAChF,IAAI,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QACrD,MAAM,mBAAmB,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;YAC1C,MAAM,GAAG,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACnE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,uBAAuB,GAAG,IAAI,CAAC,CAAC;QACvD,CAAC,CAAC,CAAC;IACL,CAAC;IACD,IAAI,IAAI,CAAC,QAAQ,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,IAAI,EAAE,IAAI,CAAC,2BAA2B,EAAE,EAAE,CAAC;QAC/F,MAAM,oBAAoB,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;YAC3C,MAAM,GAAG,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACnE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,uBAAuB,GAAG,IAAI,CAAC,CAAC;QACvD,CAAC,CAAC,CAAC;IACL,CAAC;IAED,MAAM,OAAO,GAAuB,EAAE,CAAC;IACvC,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;QACnB,OAAO,CAAC,IAAI,CACV,wBAAwB,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;YAC7E,MAAM,GAAG,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACnE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,qBAAqB,GAAG,IAAI,CAAC,CAAC;QACrD,CAAC,CAAC,CACH,CAAC;IACJ,CAAC;IACD,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClB,OAAO,CAAC,IAAI,CACV,uBAAuB,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;YAC5E,MAAM,GAAG,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACnE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,sBAAsB,GAAG,IAAI,CAAC,CAAC;QACtD,CAAC,CAAC,CACH,CAAC;IACJ,CAAC;IACD,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClB,OAAO,CAAC,IAAI,CACV,uBAAuB,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;YACxC,MAAM,GAAG,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACnE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,qBAAqB,GAAG,IAAI,CAAC,CAAC;QACrD,CAAC,CAAC,CACH,CAAC;IACJ,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,OAAO;QAAE,MAAM,IAAI,CAAC;AACzC,CAAC"}
@@ -0,0 +1,14 @@
1
+ export declare const TOXICITY_ONNX_ASSET = "kca-kcelectra-base-toxicity-onnx.zip";
2
+ export declare function mlBundleCacheDir(): string;
3
+ export declare function readModelsPackageVersion(): string;
4
+ export declare function toxicityReleaseTag(): string;
5
+ export declare function toxicityReleaseAssetUrl(): string;
6
+ export declare function isSentimentBundleReady(): boolean;
7
+ export declare function isEmbedBundleReady(): boolean;
8
+ export declare function isToxicityBundleReady(): boolean;
9
+ export declare function isCoreBundleReady(): boolean;
10
+ /** npm models 패키지 → monorepo data → user cache */
11
+ export declare function listMlModelRoots(): string[];
12
+ export declare function resolveMlModelRootFor(modelId: string): string | undefined;
13
+ /** GitHub Release 에서 독성 ONNX zip 다운로드 → cache */
14
+ export declare function ensureToxicityBundle(): Promise<boolean>;
@@ -0,0 +1,167 @@
1
+ import { createWriteStream, cpSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { dirname, join } from "node:path";
4
+ import { pipeline } from "node:stream/promises";
5
+ import { Readable } from "node:stream";
6
+ import { createRequire } from "node:module";
7
+ import { spawnSync } from "node:child_process";
8
+ import { fileURLToPath } from "node:url";
9
+ import { BUNDLED_EMBED_MODEL_ID, BUNDLED_SENTIMENT_MODEL_ID, BUNDLED_TOXICITY_MODEL_ID, } from "./ml-bundle-ids.js";
10
+ export const TOXICITY_ONNX_ASSET = "kca-kcelectra-base-toxicity-onnx.zip";
11
+ const DEFAULT_REPO = "claudianus/kakaotalk-chat-analyzer";
12
+ export function mlBundleCacheDir() {
13
+ const env = process.env.KCA_ML_CACHE?.trim();
14
+ if (env)
15
+ return join(env, "ml-models");
16
+ return join(homedir(), ".cache", "kakaotalk-chat-analyzer", "ml-models");
17
+ }
18
+ export function readModelsPackageVersion() {
19
+ try {
20
+ const req = createRequire(import.meta.url);
21
+ const pkg = req.resolve("kakaotalk-chat-analyzer-models/package.json");
22
+ return JSON.parse(readFileSync(pkg, "utf8")).version;
23
+ }
24
+ catch {
25
+ return process.env.KCA_ML_MODELS_VERSION?.trim() || "0.2.0";
26
+ }
27
+ }
28
+ export function toxicityReleaseTag() {
29
+ const env = process.env.KCA_ML_MODELS_RELEASE?.trim();
30
+ if (env)
31
+ return env;
32
+ return `ml-models-v${readModelsPackageVersion()}`;
33
+ }
34
+ export function toxicityReleaseAssetUrl() {
35
+ const repo = process.env.KCA_ML_MODELS_REPO?.trim() || DEFAULT_REPO;
36
+ const tag = toxicityReleaseTag();
37
+ return `https://github.com/${repo}/releases/download/${tag}/${TOXICITY_ONNX_ASSET}`;
38
+ }
39
+ function modelOnnxReady(root, modelId) {
40
+ return (existsSync(join(root, modelId, "config.json")) &&
41
+ existsSync(join(root, modelId, "onnx", "model.onnx")));
42
+ }
43
+ export function isSentimentBundleReady() {
44
+ return listMlModelRoots().some((r) => modelOnnxReady(r, BUNDLED_SENTIMENT_MODEL_ID));
45
+ }
46
+ export function isEmbedBundleReady() {
47
+ return listMlModelRoots().some((r) => modelOnnxReady(r, BUNDLED_EMBED_MODEL_ID));
48
+ }
49
+ export function isToxicityBundleReady() {
50
+ return listMlModelRoots().some((r) => modelOnnxReady(r, BUNDLED_TOXICITY_MODEL_ID));
51
+ }
52
+ export function isCoreBundleReady() {
53
+ return isSentimentBundleReady() && isEmbedBundleReady();
54
+ }
55
+ /** npm models 패키지 → monorepo data → user cache */
56
+ export function listMlModelRoots() {
57
+ const roots = [];
58
+ try {
59
+ const req = createRequire(import.meta.url);
60
+ const pkg = req.resolve("kakaotalk-chat-analyzer-models/package.json");
61
+ const dir = join(dirname(pkg), "data", "ml-models");
62
+ if (existsSync(dir))
63
+ roots.push(dir);
64
+ }
65
+ catch {
66
+ /* not installed */
67
+ }
68
+ const pkgData = join(fileURLToPath(new URL("..", import.meta.url)), "..", "data", "ml-models");
69
+ if (existsSync(pkgData))
70
+ roots.push(pkgData);
71
+ const cache = mlBundleCacheDir();
72
+ if (existsSync(cache))
73
+ roots.push(cache);
74
+ return [...new Set(roots)];
75
+ }
76
+ export function resolveMlModelRootFor(modelId) {
77
+ for (const root of listMlModelRoots()) {
78
+ if (modelOnnxReady(root, modelId))
79
+ return root;
80
+ }
81
+ return undefined;
82
+ }
83
+ function extractZip(zipPath, destDir) {
84
+ mkdirSync(destDir, { recursive: true });
85
+ const tar = spawnSync("tar", ["-xf", zipPath, "-C", destDir], { encoding: "utf8" });
86
+ if (tar.status === 0)
87
+ return true;
88
+ const unzip = spawnSync("unzip", ["-q", "-o", zipPath, "-d", destDir], { encoding: "utf8" });
89
+ return unzip.status === 0;
90
+ }
91
+ let toxicityDownloadPromise = null;
92
+ /** GitHub Release 에서 독성 ONNX zip 다운로드 → cache */
93
+ export async function ensureToxicityBundle() {
94
+ if (isToxicityBundleReady())
95
+ return true;
96
+ if (process.env.KCA_NO_TOXICITY_DOWNLOAD === "1")
97
+ return false;
98
+ if (!toxicityDownloadPromise) {
99
+ toxicityDownloadPromise = downloadToxicityBundle().finally(() => {
100
+ if (!isToxicityBundleReady())
101
+ toxicityDownloadPromise = null;
102
+ });
103
+ }
104
+ return toxicityDownloadPromise;
105
+ }
106
+ async function downloadToxicityBundle() {
107
+ const url = toxicityReleaseAssetUrl();
108
+ const cache = mlBundleCacheDir();
109
+ const dlDir = join(cache, "..", "downloads");
110
+ mkdirSync(dlDir, { recursive: true });
111
+ const zipPath = join(dlDir, TOXICITY_ONNX_ASSET);
112
+ const tmpExtract = join(dlDir, "_toxicity_extract");
113
+ try {
114
+ process.stderr.write(`[kca] 독성 ML 번들 다운로드 중… ${url}\n`);
115
+ const res = await fetch(url, { redirect: "follow" });
116
+ if (!res.ok || !res.body) {
117
+ process.stderr.write(`[kca] 독성 번들 다운로드 실패 (${res.status}): ${url}\n`);
118
+ return false;
119
+ }
120
+ await pipeline(Readable.fromWeb(res.body), createWriteStream(zipPath));
121
+ if (existsSync(tmpExtract))
122
+ rmSync(tmpExtract, { recursive: true, force: true });
123
+ mkdirSync(tmpExtract, { recursive: true });
124
+ if (!extractZip(zipPath, tmpExtract)) {
125
+ process.stderr.write("[kca] 독성 번들 압축 해제 실패 (tar/unzip 필요)\n");
126
+ return false;
127
+ }
128
+ const nested = join(tmpExtract, BUNDLED_TOXICITY_MODEL_ID);
129
+ const src = existsSync(join(nested, "config.json"))
130
+ ? nested
131
+ : findModelDir(tmpExtract, BUNDLED_TOXICITY_MODEL_ID);
132
+ if (!src) {
133
+ process.stderr.write("[kca] 독성 번들 zip 구조가 예상과 다릅니다\n");
134
+ return false;
135
+ }
136
+ const dest = join(cache, BUNDLED_TOXICITY_MODEL_ID);
137
+ mkdirSync(cache, { recursive: true });
138
+ if (existsSync(dest))
139
+ rmSync(dest, { recursive: true, force: true });
140
+ cpSync(src, dest, { recursive: true });
141
+ process.stderr.write(`[kca] 독성 ML 번들 설치됨: ${dest}\n`);
142
+ return isToxicityBundleReady();
143
+ }
144
+ catch (error) {
145
+ const msg = error instanceof Error ? error.message : String(error);
146
+ process.stderr.write(`[kca] 독성 번들 다운로드 오류: ${msg}\n`);
147
+ return false;
148
+ }
149
+ finally {
150
+ if (existsSync(tmpExtract))
151
+ rmSync(tmpExtract, { recursive: true, force: true });
152
+ }
153
+ }
154
+ function findModelDir(root, modelId) {
155
+ const direct = join(root, modelId);
156
+ if (existsSync(join(direct, "config.json")))
157
+ return direct;
158
+ for (const name of readdirSync(root, { withFileTypes: true })) {
159
+ if (!name.isDirectory())
160
+ continue;
161
+ const p = join(root, name.name);
162
+ if (existsSync(join(p, "config.json")))
163
+ return p;
164
+ }
165
+ return undefined;
166
+ }
167
+ //# sourceMappingURL=ml-bundle-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ml-bundle-cache.js","sourceRoot":"","sources":["../../src/ml-bundle-cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /** NSMC KoELECTRA-Small */
2
+ export declare const BUNDLED_SENTIMENT_MODEL_ID = "kca-koelectra-small-v3-nsmc";
3
+ /** KorSTS / semantic embedding */
4
+ export declare const BUNDLED_EMBED_MODEL_ID = "kca-koelectra-small-v3-embed";
5
+ /** KcELECTRA-base toxicity */
6
+ export declare const BUNDLED_TOXICITY_MODEL_ID = "kca-kcelectra-base-toxicity";
@@ -0,0 +1,7 @@
1
+ /** NSMC KoELECTRA-Small */
2
+ export const BUNDLED_SENTIMENT_MODEL_ID = "kca-koelectra-small-v3-nsmc";
3
+ /** KorSTS / semantic embedding */
4
+ export const BUNDLED_EMBED_MODEL_ID = "kca-koelectra-small-v3-embed";
5
+ /** KcELECTRA-base toxicity */
6
+ export const BUNDLED_TOXICITY_MODEL_ID = "kca-kcelectra-base-toxicity";
7
+ //# sourceMappingURL=ml-bundle-ids.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ml-bundle-ids.js","sourceRoot":"","sources":["../../src/ml-bundle-ids.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAC3B,MAAM,CAAC,MAAM,0BAA0B,GAAG,6BAA6B,CAAC;AAExE,kCAAkC;AAClC,MAAM,CAAC,MAAM,sBAAsB,GAAG,8BAA8B,CAAC;AAErE,8BAA8B;AAC9B,MAAM,CAAC,MAAM,yBAAyB,GAAG,6BAA6B,CAAC"}
@@ -0,0 +1,2 @@
1
+ /** 감정·임베딩 ONNX — npm models 패키지 자동 설치 */
2
+ export declare function ensureCoreMlBundles(): Promise<boolean>;
@@ -0,0 +1,57 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { spawnSync } from "node:child_process";
3
+ import { createRequire } from "node:module";
4
+ import { dirname, join } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import { isCoreBundleReady, readModelsPackageVersion } from "./ml-bundle-cache.js";
7
+ let coreInstallPromise = null;
8
+ function kcaPackageRoot() {
9
+ try {
10
+ const req = createRequire(import.meta.url);
11
+ return dirname(req.resolve("kakaotalk-chat-analyzer/package.json"));
12
+ }
13
+ catch {
14
+ try {
15
+ return dirname(fileURLToPath(new URL("../../package.json", import.meta.url)));
16
+ }
17
+ catch {
18
+ return process.cwd();
19
+ }
20
+ }
21
+ }
22
+ function pinnedModelsVersion() {
23
+ try {
24
+ const root = kcaPackageRoot();
25
+ const pkg = JSON.parse(readFileSync(join(root, "package.json"), "utf8"));
26
+ const dep = pkg.dependencies?.["kakaotalk-chat-analyzer-models"] ??
27
+ pkg.optionalDependencies?.["kakaotalk-chat-analyzer-models"];
28
+ if (typeof dep === "string" && dep.length > 0 && !dep.startsWith("file:"))
29
+ return dep;
30
+ }
31
+ catch {
32
+ /* ignore */
33
+ }
34
+ return readModelsPackageVersion();
35
+ }
36
+ function runNpmInstallModels() {
37
+ const ver = pinnedModelsVersion();
38
+ const cwd = kcaPackageRoot();
39
+ process.stderr.write(`[kca] ML 번들 npm 설치 시도: kakaotalk-chat-analyzer-models@${ver}\n`);
40
+ const run = spawnSync("npm", ["install", `kakaotalk-chat-analyzer-models@${ver}`, "--no-save", "--no-audit", "--no-fund"], { cwd, stdio: "inherit", env: process.env });
41
+ return run.status === 0;
42
+ }
43
+ /** 감정·임베딩 ONNX — npm models 패키지 자동 설치 */
44
+ export async function ensureCoreMlBundles() {
45
+ if (isCoreBundleReady())
46
+ return true;
47
+ if (process.env.KCA_NO_ML_AUTO_INSTALL === "1")
48
+ return false;
49
+ if (!coreInstallPromise) {
50
+ coreInstallPromise = (async () => {
51
+ runNpmInstallModels();
52
+ return isCoreBundleReady();
53
+ })();
54
+ }
55
+ return coreInstallPromise;
56
+ }
57
+ //# sourceMappingURL=ml-bundle-install.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ml-bundle-install.js","sourceRoot":"","sources":["../../src/ml-bundle-install.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAEnF,IAAI,kBAAkB,GAA4B,IAAI,CAAC;AAEvD,SAAS,cAAc;IACrB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC3C,OAAO,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,sCAAsC,CAAC,CAAC,CAAC;IACtE,CAAC;IAAC,MAAM,CAAC;QACP,IAAI,CAAC;YACH,OAAO,OAAO,CAAC,aAAa,CAAC,IAAI,GAAG,CAAC,oBAAoB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAChF,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,OAAO,CAAC,GAAG,EAAE,CAAC;QACvB,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,mBAAmB;IAC1B,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,cAAc,EAAE,CAAC;QAC9B,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAGtE,CAAC;QACF,MAAM,GAAG,GACP,GAAG,CAAC,YAAY,EAAE,CAAC,gCAAgC,CAAC;YACpD,GAAG,CAAC,oBAAoB,EAAE,CAAC,gCAAgC,CAAC,CAAC;QAC/D,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC;YAAE,OAAO,GAAG,CAAC;IACxF,CAAC;IAAC,MAAM,CAAC;QACP,YAAY;IACd,CAAC;IACD,OAAO,wBAAwB,EAAE,CAAC;AACpC,CAAC;AAED,SAAS,mBAAmB;IAC1B,MAAM,GAAG,GAAG,mBAAmB,EAAE,CAAC;IAClC,MAAM,GAAG,GAAG,cAAc,EAAE,CAAC;IAC7B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,yDAAyD,GAAG,IAAI,CAAC,CAAC;IACvF,MAAM,GAAG,GAAG,SAAS,CACnB,KAAK,EACL,CAAC,SAAS,EAAE,kCAAkC,GAAG,EAAE,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,CAAC,EAC5F,EAAE,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAC5C,CAAC;IACF,OAAO,GAAG,CAAC,MAAM,KAAK,CAAC,CAAC;AAC1B,CAAC;AAED,yCAAyC;AACzC,MAAM,CAAC,KAAK,UAAU,mBAAmB;IACvC,IAAI,iBAAiB,EAAE;QAAE,OAAO,IAAI,CAAC;IACrC,IAAI,OAAO,CAAC,GAAG,CAAC,sBAAsB,KAAK,GAAG;QAAE,OAAO,KAAK,CAAC;IAC7D,IAAI,CAAC,kBAAkB,EAAE,CAAC;QACxB,kBAAkB,GAAG,CAAC,KAAK,IAAI,EAAE;YAC/B,mBAAmB,EAAE,CAAC;YACtB,OAAO,iBAAiB,EAAE,CAAC;QAC7B,CAAC,CAAC,EAAE,CAAC;IACP,CAAC;IACD,OAAO,kBAAkB,CAAC;AAC5B,CAAC"}
@@ -1,12 +1,5 @@
1
- /** NSMC KoELECTRA-Small (quality sentiment) */
2
- export declare const BUNDLED_SENTIMENT_MODEL_ID = "kca-koelectra-small-v3-nsmc";
3
- /** 이전 번들 디렉터리(호환) */
4
- export declare const LEGACY_BUNDLED_SENTIMENT_MODEL_ID = "kca-koelectra-korean-sentiment";
5
- /** KorSTS / semantic quality embedding */
6
- export declare const BUNDLED_EMBED_MODEL_ID = "kca-koelectra-small-v3-embed";
7
- /** KcELECTRA-base toxicity (optional, large) */
8
- export declare const BUNDLED_TOXICITY_MODEL_ID = "kca-kcelectra-base-toxicity";
9
- /** `data/ml-models/` — optional npm models 패키지 우선 */
1
+ export { BUNDLED_EMBED_MODEL_ID, BUNDLED_SENTIMENT_MODEL_ID, BUNDLED_TOXICITY_MODEL_ID, } from "./ml-bundle-ids.js";
2
+ /** transformers `env.localModelPath` 코어 번들(NSMC+embed)이 함께 있는 루트 우선 */
10
3
  export declare function bundledMlModelsDir(): string;
11
4
  export declare function bundledModelDir(modelId: string): string;
12
5
  export declare function bundledSentimentModelDir(): string;
@@ -1,61 +1,48 @@
1
1
  import { existsSync } from "node:fs";
2
- import { createRequire } from "node:module";
3
- import { dirname, join } from "node:path";
2
+ import { join, dirname } from "node:path";
4
3
  import { fileURLToPath } from "node:url";
5
- /** NSMC KoELECTRA-Small (quality sentiment) */
6
- export const BUNDLED_SENTIMENT_MODEL_ID = "kca-koelectra-small-v3-nsmc";
7
- /** 이전 번들 디렉터리(호환) */
8
- export const LEGACY_BUNDLED_SENTIMENT_MODEL_ID = "kca-koelectra-korean-sentiment";
9
- /** KorSTS / semantic quality embedding */
10
- export const BUNDLED_EMBED_MODEL_ID = "kca-koelectra-small-v3-embed";
11
- /** KcELECTRA-base toxicity (optional, large) */
12
- export const BUNDLED_TOXICITY_MODEL_ID = "kca-kcelectra-base-toxicity";
13
- const PKG_DATA = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "data");
14
- function tryModelsPackageMlDir() {
15
- try {
16
- const req = createRequire(import.meta.url);
17
- const pkgJson = req.resolve("kakaotalk-chat-analyzer-models/package.json");
18
- const dir = join(dirname(pkgJson), "data", "ml-models");
19
- if (existsSync(dir))
20
- return dir;
4
+ import { BUNDLED_EMBED_MODEL_ID, BUNDLED_SENTIMENT_MODEL_ID, BUNDLED_TOXICITY_MODEL_ID, } from "./ml-bundle-ids.js";
5
+ import { isEmbedBundleReady, isSentimentBundleReady, isToxicityBundleReady, listMlModelRoots, resolveMlModelRootFor, } from "./ml-bundle-cache.js";
6
+ const PKG_DATA_ML = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "data", "ml-models");
7
+ export { BUNDLED_EMBED_MODEL_ID, BUNDLED_SENTIMENT_MODEL_ID, BUNDLED_TOXICITY_MODEL_ID, } from "./ml-bundle-ids.js";
8
+ /** transformers `env.localModelPath` 코어 번들(NSMC+embed)이 함께 있는 루트 우선 */
9
+ export function bundledMlModelsDir() {
10
+ for (const root of listMlModelRoots()) {
11
+ const sent = join(root, BUNDLED_SENTIMENT_MODEL_ID, "onnx", "model.onnx");
12
+ const embed = join(root, BUNDLED_EMBED_MODEL_ID, "onnx", "model.onnx");
13
+ if (existsSync(sent) && existsSync(embed))
14
+ return root;
21
15
  }
22
- catch {
23
- /* optional package not installed */
16
+ for (const modelId of [
17
+ BUNDLED_SENTIMENT_MODEL_ID,
18
+ BUNDLED_EMBED_MODEL_ID,
19
+ BUNDLED_TOXICITY_MODEL_ID,
20
+ ]) {
21
+ const root = resolveMlModelRootFor(modelId);
22
+ if (root)
23
+ return root;
24
24
  }
25
- return undefined;
26
- }
27
- /** `data/ml-models/` — optional npm models 패키지 우선 */
28
- export function bundledMlModelsDir() {
29
- const fromPkg = tryModelsPackageMlDir();
30
- if (fromPkg)
31
- return fromPkg;
32
- return join(PKG_DATA, "ml-models");
25
+ const roots = listMlModelRoots();
26
+ return roots[0] ?? PKG_DATA_ML;
33
27
  }
34
28
  export function bundledModelDir(modelId) {
35
- return join(bundledMlModelsDir(), modelId);
29
+ const root = resolveMlModelRootFor(modelId) ?? bundledMlModelsDir();
30
+ return join(root, modelId);
36
31
  }
37
32
  export function bundledSentimentModelDir() {
38
- return bundledModelDir(resolveBundledSentimentModelId());
39
- }
40
- function modelConfigExists(modelId) {
41
- return existsSync(join(bundledMlModelsDir(), modelId, "config.json"));
33
+ return bundledModelDir(BUNDLED_SENTIMENT_MODEL_ID);
42
34
  }
43
35
  export function resolveBundledSentimentModelId() {
44
- if (modelConfigExists(BUNDLED_SENTIMENT_MODEL_ID))
45
- return BUNDLED_SENTIMENT_MODEL_ID;
46
- if (modelConfigExists(LEGACY_BUNDLED_SENTIMENT_MODEL_ID))
47
- return LEGACY_BUNDLED_SENTIMENT_MODEL_ID;
48
36
  return BUNDLED_SENTIMENT_MODEL_ID;
49
37
  }
50
38
  export function isBundledSentimentModelReady() {
51
- return (modelConfigExists(BUNDLED_SENTIMENT_MODEL_ID) ||
52
- modelConfigExists(LEGACY_BUNDLED_SENTIMENT_MODEL_ID));
39
+ return isSentimentBundleReady();
53
40
  }
54
41
  export function isBundledEmbedModelReady() {
55
- return modelConfigExists(BUNDLED_EMBED_MODEL_ID);
42
+ return isEmbedBundleReady();
56
43
  }
57
44
  export function isBundledToxicityModelReady() {
58
- return modelConfigExists(BUNDLED_TOXICITY_MODEL_ID);
45
+ return isToxicityBundleReady();
59
46
  }
60
47
  /** 번들 ONNX가 있으면 transformers `env.localModelPath` 로 쓸 루트 */
61
48
  export function bundledMlModelsRoot() {
@@ -67,12 +54,7 @@ export function bundledMlModelsRoot() {
67
54
  return undefined;
68
55
  }
69
56
  export function isLocalBundledSentimentModel(modelId) {
70
- if (modelId === BUNDLED_SENTIMENT_MODEL_ID)
71
- return modelConfigExists(BUNDLED_SENTIMENT_MODEL_ID);
72
- if (modelId === LEGACY_BUNDLED_SENTIMENT_MODEL_ID) {
73
- return modelConfigExists(LEGACY_BUNDLED_SENTIMENT_MODEL_ID);
74
- }
75
- return false;
57
+ return modelId === BUNDLED_SENTIMENT_MODEL_ID && isBundledSentimentModelReady();
76
58
  }
77
59
  export function isLocalBundledEmbedModel(modelId) {
78
60
  return modelId === BUNDLED_EMBED_MODEL_ID && isBundledEmbedModelReady();