@heripo/research-radar 5.2.5 → 5.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -24,7 +24,7 @@ Previously reported service metrics were $0.2–1 per issue and 15% CTR. These a
24
24
  - Type-safe TypeScript with strict interfaces
25
25
  - Provider pattern for swapping components (Crawling/Analysis/Content/Email)
26
26
  - 74 active crawling targets across heritage agencies, museums, academic societies, filtered at runtime by robots.txt
27
- - Multi LLM providers: OpenAI GPT-5 (analysis) + selectable content generation (OpenAI / Anthropic / Google)
27
+ - OpenAI GPT-6 defaults for every LLM task, with per-task model overrides
28
28
  - Built-in retries, chain options, preview emails
29
29
 
30
30
  **Links**: [Live service](https://heripo.app/research-radar/subscribe) • [Newsletter example](https://heripo.app/research-radar-newsletter-example.html) • [Core engine](https://github.com/heripo-lab/llm-newsletter-kit-core)
@@ -72,7 +72,7 @@ npm install @heripo/research-radar '@llm-newsletter-kit/core@~3.0.6'
72
72
 
73
73
  **Requirements**: Node.js 24.15.0 or newer within 24.x, or >= 26.0.0, and an ESM application. Node.js 25 is not supported. The package exports `dist/index.js` and TypeScript declarations (`dist/index.d.ts`), with a JavaScript sourcemap. The core engine is a peer dependency; the current supported range is `~3.0.6`. The floor moved to 3.0.6 because the 나라장터 target runs its triage inside a crawl fetch that core times out, and before 3.0.6 core classified a timeout as non-retryable, so that target failed on the first attempt every run. 3.0.5 remains required for the self-verification retry cap the generation prompt relies on.
74
74
 
75
- Article analysis requires an OpenAI API key. Content generation requires a key for the selected provider (OpenAI / Anthropic / Google); OpenAI can use the same key for both. Keys are passed explicitly to the library; load environment variables in your application.
75
+ An OpenAI API key is always required and supplies all default models. The optional compatibility path for Anthropic or Google newsletter generation additionally requires that provider's key. Keys are passed explicitly to the library; load environment variables in your application.
76
76
 
77
77
  ## Quick Start
78
78
 
@@ -99,7 +99,6 @@ export async function runNewsletter(repositories: {
99
99
  const newsletterId = await generateNewsletter({
100
100
  ...repositories,
101
101
  openAIApiKey: apiKey,
102
- contentGeneration: { provider: 'openai', apiKey },
103
102
  logger: console,
104
103
  });
105
104
 
@@ -163,18 +162,33 @@ Uses the **Provider-Service pattern** from `@llm-newsletter-kit/core`. See [core
163
162
 
164
163
  ## Configuration and models
165
164
 
166
- Defaults below describe the checked-in code, not provider recommendations. This package uses AI SDK 7 and the OpenAI, Anthropic, and Google SDK adapters.
165
+ Defaults below describe the checked-in code, not provider recommendations. `openAIApiKey` is required and supplies every default model.
167
166
 
168
- | Stage | Provider | Default model |
169
- | ------------------ | --------- | ------------------------ |
170
- | Tag classification | OpenAI | `gpt-5.6-luna` |
171
- | Image analysis | OpenAI | `gpt-5.6-terra` |
172
- | Importance scoring | OpenAI | `gpt-5.6-terra` |
173
- | Content generation | OpenAI | `gpt-5.6-sol` |
174
- | Content generation | Anthropic | `claude-sonnet-4-6` |
175
- | Content generation | Google | `gemini-3.1-pro-preview` |
167
+ | Stage | Provider | Default model |
168
+ | --------------------- | -------- | ------------- |
169
+ | Heritage bid triage | OpenAI | `gpt-6-luna` |
170
+ | Tag classification | OpenAI | `gpt-6-luna` |
171
+ | Image analysis | OpenAI | `gpt-6-sol` |
172
+ | Importance scoring | OpenAI | `gpt-6-sol` |
173
+ | Newsletter generation | OpenAI | `gpt-6-sol` |
176
174
 
177
- Select content generation with `contentGeneration: { provider, apiKey, model? }`. `model` overrides that provider's default. Analysis models are configured in [analysis.provider.ts](./src/providers/analysis.provider.ts).
175
+ Override any task independently with `models`. Every value is an OpenAI model ID:
176
+
177
+ ```typescript
178
+ await generateNewsletter({
179
+ ...repositories,
180
+ openAIApiKey,
181
+ models: {
182
+ heritageBidTriage: 'gpt-6-luna',
183
+ classifyTags: 'gpt-6-luna',
184
+ analyzeImages: 'gpt-6-sol',
185
+ determineImportance: 'gpt-6-sol',
186
+ generateNewsletter: 'gpt-6-sol',
187
+ },
188
+ });
189
+ ```
190
+
191
+ The optional `contentGeneration: { provider, apiKey, model? }` compatibility path still supports Anthropic and Google for newsletter generation. `models.generateNewsletter` takes precedence when both are supplied.
178
192
 
179
193
  [src/config/index.ts](./src/config/index.ts) defines Korean output (`outputLanguage: '한국어'`), the cultural heritage domain (`expertField: ['문화유산']`), brand name, `subscribePageUrl`, LLM `maxRetries: 5`, chain `stopAfterAttempt: 3`, and generation `temperature: 0.3`. Publication settings are `minimumArticleCountForIssue: 5` and `priorityArticleScoreThreshold: 8`; the core engine evaluates them. In the locked core 3.0.6 implementation, the count check skips **5 or fewer** candidates unless at least one has importance score >= 8. An empty candidate list is always skipped.
180
194
 
@@ -379,7 +393,7 @@ export const newsletterConfig: NewsletterConfig = {
379
393
  - Replace Korean heritage sites with your domain sources
380
394
  - Implement parsers in `src/parsers/`
381
395
 
382
- **4. Switch content generation LLM provider** (optional):
396
+ **4. Switch content generation LLM provider** (legacy compatibility):
383
397
 
384
398
  Content generation supports **3 built-in providers** — just change `contentGeneration.provider`:
385
399
 
@@ -396,7 +410,7 @@ const contentGeneration: ContentGenerationConfig = {
396
410
  };
397
411
  ```
398
412
 
399
- Default models: openai=`gpt-5.6-sol`, anthropic=`claude-sonnet-4-6`, google=`gemini-3.1-pro-preview`
413
+ Compatibility defaults: openai=`gpt-6-sol`, anthropic=`claude-sonnet-4-6`, google=`gemini-3.1-pro-preview`
400
414
 
401
415
  To change analysis providers, update both `src/providers/analysis.provider.ts` (provider type and models) and `src/newsletter-generator.ts` (provider construction), using a compatible AI SDK provider. Also adapt domain-specific minimum-score rules in the analysis provider, output language and expert fields in config, package metadata, and the GitHub Actions runner/Slack settings for your fork.
402
416
 
package/dist/index.d.ts CHANGED
@@ -203,9 +203,11 @@ interface NewsletterRepository {
203
203
  }
204
204
 
205
205
  /**
206
- * Uses OpenAI for article analysis and a configurable provider (OpenAI / Anthropic / Google) for content generation.
206
+ * Uses the required OpenAI key for every default model. Each task accepts an
207
+ * OpenAI model-ID override, while the compatibility content-generation option
208
+ * can still select Anthropic or Google.
207
209
  *
208
- * Content generation provider is selected via `contentGeneration.provider` in dependencies.
210
+ * `models.generateNewsletter` takes precedence over `contentGeneration`.
209
211
  */
210
212
 
211
213
  /**
@@ -225,7 +227,7 @@ interface PreviewNewsletterOptions {
225
227
  * Each provider uses a sensible default model that can be overridden.
226
228
  *
227
229
  * Default models:
228
- * - openai: `gpt-5.6-sol`
230
+ * - openai: `gpt-6-sol`
229
231
  * - anthropic: `claude-sonnet-4-6`
230
232
  * - google: `gemini-3.1-pro-preview`
231
233
  */
@@ -242,14 +244,35 @@ type ContentGenerationConfig = {
242
244
  apiKey: string;
243
245
  model?: string;
244
246
  };
247
+ /**
248
+ * OpenAI model IDs used by each LLM task.
249
+ *
250
+ * Omitted values use the defaults from `llmConfig.models`. Content generation
251
+ * can still use a non-OpenAI provider through the legacy `contentGeneration`
252
+ * option; `models.generateNewsletter` takes precedence when both are supplied.
253
+ */
254
+ interface NewsletterModelConfig {
255
+ heritageBidTriage?: string;
256
+ classifyTags?: string;
257
+ analyzeImages?: string;
258
+ determineImportance?: string;
259
+ generateNewsletter?: string;
260
+ }
245
261
  /**
246
262
  * Newsletter generator dependencies interface
247
263
  */
248
264
  interface NewsletterGeneratorDependencies {
249
- /** OpenAI API key (used for article analysis: tag classification, image analysis, importance scoring) */
265
+ /** OpenAI API key used by every default model. */
250
266
  openAIApiKey: string;
251
- /** Content generation LLM configuration (provider + API key + optional model) */
252
- contentGeneration: ContentGenerationConfig;
267
+ /** OpenAI model overrides for every LLM task (optional). */
268
+ models?: NewsletterModelConfig;
269
+ /**
270
+ * Non-OpenAI content generation configuration (optional).
271
+ *
272
+ * Prefer `models.generateNewsletter`, which uses the required `openAIApiKey`.
273
+ * This remains available for Anthropic/Google compatibility.
274
+ */
275
+ contentGeneration?: ContentGenerationConfig;
253
276
  /** Task management repository */
254
277
  taskRepository: TaskRepository;
255
278
  /** Article management repository */
@@ -398,13 +421,17 @@ declare class TaskService implements TaskService$1<number> {
398
421
  end(): Promise<void>;
399
422
  }
400
423
 
424
+ interface AnalysisModels {
425
+ classifyTags: ReturnType<OpenAIProvider>;
426
+ analyzeImages: ReturnType<OpenAIProvider>;
427
+ determineImportance: ReturnType<OpenAIProvider>;
428
+ }
401
429
  /**
402
430
  * Analysis provider implementation
403
431
  * - LLM-based article analysis
404
432
  * - Tag classification, image analysis, importance scoring
405
433
  */
406
434
  declare class AnalysisProvider implements AnalysisProvider$1 {
407
- private readonly openai;
408
435
  private readonly articleRepository;
409
436
  private readonly tagRepository;
410
437
  classifyTagOptions: {
@@ -421,6 +448,7 @@ declare class AnalysisProvider implements AnalysisProvider$1 {
421
448
  }>;
422
449
  };
423
450
  constructor(openai: OpenAIProvider, articleRepository: ArticleRepository, tagRepository: TagRepository);
451
+ constructor(models: AnalysisModels, articleRepository: ArticleRepository, tagRepository: TagRepository);
424
452
  /**
425
453
  * Fetch articles that haven't been scored yet
426
454
  * @returns Unscored articles awaiting analysis
@@ -612,6 +640,13 @@ declare const robotsExemptOrigins: readonly string[];
612
640
  * LLM configuration
613
641
  */
614
642
  declare const llmConfig: {
643
+ models: {
644
+ heritageBidTriage: string;
645
+ classifyTags: string;
646
+ analyzeImages: string;
647
+ determineImportance: string;
648
+ generateNewsletter: string;
649
+ };
615
650
  maxRetries: number;
616
651
  chainStopAfterAttempt: number;
617
652
  generation: {
@@ -650,4 +685,4 @@ declare const llmConfig: {
650
685
  declare const researchRadarPromptProvider: PromptProvider;
651
686
 
652
687
  export { AnalysisProvider, ContentGenerateProvider, CrawlingProvider, DateService, TaskService, contentOptions, createCrawlingTargetGroups, generateNewsletter, generateWelcomeHTML, getSourceList, llmConfig, maximumImportanceScoreByDomain, newsletterConfig, researchRadarPromptProvider, robotsExemptOrigins };
653
- export type { ArticleRepository, ContentGenerationConfig, ContentOptions, ExcavationReport, ExcavationReportSource, NewsletterConfig, NewsletterGeneratorDependencies, NewsletterRepository, NewsletterTemplateOptions, PreviewNewsletterOptions, SourceGroup, SourceItem, TagRepository, TaskRepository, WelcomeTemplateOptions };
688
+ export type { ArticleRepository, ContentGenerationConfig, ContentOptions, ExcavationReport, ExcavationReportSource, NewsletterConfig, NewsletterGeneratorDependencies, NewsletterModelConfig, NewsletterRepository, NewsletterTemplateOptions, PreviewNewsletterOptions, SourceGroup, SourceItem, TagRepository, TaskRepository, WelcomeTemplateOptions };
package/dist/index.js CHANGED
@@ -3200,6 +3200,13 @@ const robotsExemptOrigins = [
3200
3200
  * LLM configuration
3201
3201
  */
3202
3202
  const llmConfig = {
3203
+ models: {
3204
+ heritageBidTriage: 'gpt-6-luna',
3205
+ classifyTags: 'gpt-6-luna',
3206
+ analyzeImages: 'gpt-6-sol',
3207
+ determineImportance: 'gpt-6-sol',
3208
+ generateNewsletter: 'gpt-6-sol',
3209
+ },
3203
3210
  maxRetries: 5,
3204
3211
  chainStopAfterAttempt: 3,
3205
3212
  generation: {
@@ -3310,46 +3317,86 @@ const DOMAIN_PRIORITY = `## 유산 영역 우선순위
3310
3317
  판단이 애매할 때 고고학 기사는 고려 중인 점수대의 위쪽을, 자연유산·무형유산 기사는 아래쪽을 택한다.
3311
3318
  영역은 tag1에 표기되어 있다. 단, 영역만으로 점수를 정하지는 않는다 — 사안 자체가 미미한 고고학 기사보다
3312
3319
  중대한 문화유산 기사가 높은 점수를 받는 것이 옳다.`;
3313
- const SUBJECT_OVER_INSTITUTION = `## 발주·공고 기관이 아니라 사업 내용으로 판단
3320
+ const SUBJECT_OVER_INSTITUTION = `## 입찰·조달 공고를 발주 내용으로 판단
3321
+
3322
+ **이 절은 입찰·조달 공고에만 적용한다.** 사업을 발주한다는 것을 알리는 글, 즉 업무구분·
3323
+ 추정가격·계약방법·입찰마감이 붙어 있는 공고가 대상이다. 보도자료, 공지사항, 학술행사
3324
+ 안내, 채용 공고에는 적용하지 않는다. 그런 기사는 아래 규칙을 거치지 않고 점수 기준으로
3325
+ 바로 평가한다.
3326
+
3327
+ 특히 **이미 끝난 일을 전하는 보도는 여기에 해당하지 않는다.** "○○ 복원 완료", "○○
3328
+ 보수 마치고 공개"처럼 수리·복원의 결과를 알리는 기사는 유산 소식이므로 사안의 무게대로
3329
+ 평가한다. 아래에서 1점을 주는 것은 그 일을 **하겠다고 발주하는 공고**이지, 그 일이
3330
+ 끝났다는 소식이 아니다.
3314
3331
 
3315
3332
  유산 기관이 낸 공고라도 **내용이 유산 업무가 아니면 낮게 준다.** 기관명은 단서일 뿐 근거가 아니다.
3316
3333
 
3334
+ **두 가지를 차례로 본다.**
3335
+
3336
+ 1. **대상이 유산인가.** 제목이나 본문에 유산이라는 근거가 없으면 유산 업무가 아니다.
3337
+ 조달 정보만 있고 무엇을 다루는지 알 수 없으면 유산으로 짐작하지 않는다.
3338
+ 2. **하는 일이 조사·연구·기록인가, 고치고 관리하는 시공인가.** 앞의 것은 독자에게
3339
+ 새로 알려줄 내용이 있고, 뒤의 것은 시공 발주다. 대상이 아무리 중요한 유산이어도
3340
+ 시공이면 1점이다.
3341
+
3317
3342
  **유산 업무 — 정상 채점**
3318
3343
 
3319
- - 발굴·시굴조사, 매장유산, 문화재 수리·보수정비, 보존처리, 기록화, 유적 정비
3320
- - 학술연구, 유물 조사·분석
3321
- - 전시·해설의 기획과 **콘텐츠 개발** — 무엇을 보여주고 어떻게 설명할지 정하는 일
3322
- (예: "달천철장 홍보관 무장애 관광 콘텐츠 개발 용역")
3344
+ - 발굴조사, 시굴조사, 표본·입회·지표조사, 매장유산 조사
3345
+ - 학술연구, 유물 조사·분석, 기록화, 정밀실측, 보고서·자료집 발간
3346
+ - **유물(동산유산) 보존처리** — 불상, 괘불도, 전적, 박물관 소장자료
3323
3347
  - **유산 정보를 다루는 데이터베이스·아카이브·디지털화의 구축과 유지보수**
3324
- (예: "B-헤리티지 웹 아카이빙 플랫폼 구축", "국외문화유산DB관리시스템 유지관리",
3325
- "AI 학습데이터 구축 기반 아카이브 자료 디지털화")
3348
+ - 전시·해설의 기획과 **콘텐츠 개발** — 무엇을 보여주고 어떻게 설명할지 정하는 일
3326
3349
  - **유산 교육 프로그램의 기획·운영** (예: "평창올림픽 유산교육 프로그램 기획·운영 대행용역")
3350
+ — 운영 대행이라는 형식 때문에 행사 운영으로 보지 않는다. 가르치는 내용이 있으면 교육이다.
3351
+ - **발굴과 직결된 유적 복원·정비** — 발굴조사 결과에 따른 유적 복원, 발굴지 복토,
3352
+ 고분군·폐사지 정비처럼 조사에서 이어지는 시공
3327
3353
 
3328
3354
  **유산 업무가 아닌 것 → 발주처가 국가유산청·국립박물관이어도 1점**
3329
3355
 
3356
+ - **건조물·구조물의 수리와 보수공사** — 지붕보수, 단청, 초가이엉잇기, 성벽·석축 보수,
3357
+ 담장·울타리 정비. 전통기법을 쓰더라도 시공이다.
3358
+ - **주변·부대시설 정비, 탐방로·진입로 정비, 조성 사업**
3359
+ - **수목·조경 관리와 병해충 방제** — 재선충 방제, 위험수목 정비, 경관림 복원, 식재
3360
+ - **종합정비계획·관리계획 수립**, 실시설계, **공사 감리**
3330
3361
  - 소방·전기·냉난방·창호·정보통신 같은 건물 설비와 그 유지보수, 청사 환경개선
3331
3362
  - **구조안전진단**, 건물 철거와 폐기물 처리
3332
3363
  - 기관 홈페이지, 사무용 전산 시스템
3333
- - 차량 임차, **운송·운반** (예: "국내 주요 화석산지 표본 헬기 운반")
3364
+ - 차량 임차, **운송·운반**
3334
3365
  - 홍보물·영상 제작
3335
3366
  - **전시를 물리적으로 만들고 옮기고 설치하는 일** — 전시설계, 전시물·실감영상 제작·설치,
3336
- 전시품 포장·운송 (예: "상설전시실3 실감영상 제작·설치", "특별전 전시품 포장, 운송 및 설치")
3367
+ 전시품 포장·운송
3337
3368
  - 축제·행사의 운영과 그 부대 용역(무대, 홍보, 교통·안전관리)
3369
+ - **대상이 유산이 아닌 조사·연구** — 자연환경·생태·수질·보호구역 실태조사, 생태지도 제작.
3370
+ 조사라는 형식이 아니라 무엇을 조사하는지가 기준이다.
3338
3371
 
3339
3372
  발주처가 유산 기관이라는 이유로 2-3점으로 올리지 않는다. 유산 업무가 아니면 1점이다.
3340
3373
 
3341
- **갈리는 지점 세 가지**
3342
-
3343
- 1. **전산**: 다루는 대상이 무엇인지로 가른다. 유산 자료를 담고 보여주고 보존하는
3344
- 시스템이면 유산 업무이고, 기관을 운영하기 위한 전산이면 아니다.
3345
- 2. **전시**: 무엇을 보여줄지 정하는 일은 유산 업무이고, 그것을 만들고 옮기고 설치하는
3346
- 일은 아니다.
3347
- 3. **프로그램**: 유산을 가르치고 해설하는 프로그램은 유산 업무이고, 축제·기념행사를
3374
+ **갈리는 지점**
3375
+
3376
+ 1. **시공이냐 조사냐**: "창원 웅천읍성 서쪽성벽 보수공사", "부평향교 동무 동재 지붕보수공사",
3377
+ "옥천 송림사 요사채 단청공사", "서산 해미읍성 진남문 주변 석축정비공사"는 모두 1점이다.
3378
+ 대상은 지정유산이지만 하는 일이 시공이다. 반대로 "구)경주역 폐철도부지 매장유산
3379
+ 시굴조사"는 같은 땅을 파도 조사이므로 정상 채점한다.
3380
+ 2. **복원의 예외**: 발굴조사에서 이어지는 복원·복토·유적 정비는 시공이어도 포함한다.
3381
+ 어디서 무엇이 나왔고 그래서 무엇을 되살리는지가 고고학 소식이기 때문이다.
3382
+ 건조물을 고쳐 쓰는 수리와는 다르다.
3383
+ 3. **수목·방제**: "양산 통도사 일원 재선충병 예방 지상방제", "여주 영릉과 영릉 소나무재선충병
3384
+ 예방나무주사", "남양주 홍릉과 유릉 위험수목 정비", "서울 헌릉과 인릉 역사경관림 복원 정비"는
3385
+ 모두 1점이다. 왕릉과 사찰의 나무를 돌보는 일이지 유산을 조사하는 일이 아니다.
3386
+ 4. **계획과 감리**: "안동 백운정 산불피해 복원 종합정비계획 수립", "경주 인왕동사지 석탑복원
3387
+ 공사 책임감리", "정선 고성리 산성 성곽 보수공사 실시설계"는 모두 1점이다. 시공을 준비하고
3388
+ 관리하는 일은 시공에 딸린다.
3389
+ 5. **대상 불명**: "2026년 사천시 보호구역 실태조사"는 무엇의 보호구역인지 본문에 없다.
3390
+ "독도 수중 생태지도(닭바위) 제작"은 해양 생태 조사다. 둘 다 1점이다. 조사라는 말에
3391
+ 끌려가지 말고, 유산을 조사한다는 근거가 실제로 있는지 본다.
3392
+ 6. **전산**: 유산 자료를 담고 보여주고 보존하는 시스템이면 유산 업무이고, 기관을 운영하기
3393
+ 위한 전산이면 아니다.
3394
+ 7. **전시**: 무엇을 보여줄지 정하는 일은 유산 업무이고, 만들고 옮기고 설치하는 일은 아니다.
3395
+ 8. **프로그램**: 유산을 가르치고 해설하는 프로그램은 유산 업무이고, 축제·기념행사를
3348
3396
  진행하는 일은 아니다.
3349
3397
 
3350
- 그 밖의 예: "창덕궁 미분무 소화설비 성능개선 공사"는 궁궐 소재지만 소방 설비 공사다.
3351
- "국립항공박물관 정보통신설비 유지보수"도 건물 설비다. 반대로 "팔만대장경 P-XRF 분석"은
3352
- 발주처가 사찰이어도 명백한 유산 조사이고, "안양암 아미타괘불도 보존처리",
3398
+ 반대쪽 예: "팔만대장경 P-XRF 분석"은 발주처가 사찰이어도 명백한 유산 조사이고,
3399
+ "안양암 아미타괘불도 보존처리", "국립농업박물관 소장 박물관자료 보존처리",
3353
3400
  "중요동산문화유산 기록화 3D 스캐닝", "소장유물 복제", "소장품도록 발간"은
3354
3401
  유물을 직접 다루므로 정상 채점한다.`;
3355
3402
  const EMPLOYMENT_FILTER = `## 채용 공고 판별
@@ -3401,7 +3448,9 @@ function temporalRule$1(minimumScore) {
3401
3448
  독자는 입찰에 참여하려고 이 소식을 보는 것이 아니라 **어디서 어떤 조사가 시작되는지** 알려고 본다.
3402
3449
  "○○ 유적 정밀발굴조사 용역 발주"는 그 자체로 고고학계 소식이다.
3403
3450
  이런 공고는 수의계약으로 나와 마감이 공고 다음 날인 경우가 많아, 마감으로 거르면 정작 알려야 할 조사가 사라진다.
3404
- 문화재 수리·보수정비·보존처리 발주도 같게 본다. 다만 입찰 참여 조건이나 마감을 강조해 쓰지는 않는다.
3451
+ 유물 보존처리와 발굴 연계 복원 발주도 같게 본다. 다만 입찰 참여 조건이나 마감을 강조해
3452
+ 쓰지는 않는다. **수리·보수정비 발주는 여기에 넣지 않는다.** 위 발주 판정에서 이미 1점이므로
3453
+ 이 예외로 되살리면 앞뒤가 어긋난다.
3405
3454
  - 마감·일정 언급이 전혀 없으면 이 규칙은 적용하지 않는다.
3406
3455
  `;
3407
3456
  }
@@ -3789,26 +3838,44 @@ const researchRadarPromptProvider = {
3789
3838
  * - Tag classification, image analysis, importance scoring
3790
3839
  */
3791
3840
  class AnalysisProvider {
3792
- openai;
3793
3841
  articleRepository;
3794
3842
  tagRepository;
3795
3843
  classifyTagOptions;
3796
3844
  analyzeImagesOptions;
3797
3845
  determineScoreOptions;
3798
- constructor(openai, articleRepository, tagRepository) {
3799
- this.openai = openai;
3846
+ constructor(modelsOrOpenAI, articleRepository, tagRepository) {
3800
3847
  this.articleRepository = articleRepository;
3801
3848
  this.tagRepository = tagRepository;
3849
+ const models = typeof modelsOrOpenAI === 'function'
3850
+ ? {
3851
+ classifyTags: modelsOrOpenAI(llmConfig.models.classifyTags),
3852
+ analyzeImages: modelsOrOpenAI(llmConfig.models.analyzeImages),
3853
+ determineImportance: modelsOrOpenAI(llmConfig.models.determineImportance),
3854
+ }
3855
+ : modelsOrOpenAI;
3802
3856
  this.classifyTagOptions = {
3803
- model: this.openai('gpt-5.6-luna'),
3857
+ model: models.classifyTags,
3804
3858
  };
3805
3859
  this.analyzeImagesOptions = {
3806
- model: this.openai('gpt-5.6-terra'),
3860
+ model: models.analyzeImages,
3807
3861
  };
3808
3862
  this.determineScoreOptions = {
3809
- model: this.openai('gpt-5.6-terra'),
3863
+ model: models.determineImportance,
3810
3864
  minimumImportanceScoreRules: [
3811
- // Korean Archaeological Society news: minimum score 6
3865
+ // Archaeology-only boards: minimum score 6. Everything they carry
3866
+ // concerns the field, so the floor keeps routine-looking posts from
3867
+ // being scored as if they came from a general board. That includes the
3868
+ // association's 사업공고 board (`bussopen`), which despite the name is
3869
+ // not a tender board: across everything collected from it, not one post
3870
+ // is an 입찰 공고. It announces 지원사업, 조사기관 모집 and 선정결과,
3871
+ // 발굴현장 안전컨설팅 and 교육 — programme news for the field.
3872
+ //
3873
+ // `ipcopen` is the actual tender board and deliberately carries no
3874
+ // floor. Tenders are judged by what the work is: a 지표조사 발주 is
3875
+ // archaeology news, while the 전기·통신·소방 공사 and 감리용역 posted
3876
+ // alongside it are construction. A floor of 6 cannot express the 1 that
3877
+ // policy calls for, so it pinned exactly the notices the importance
3878
+ // prompt is meant to drop.
3812
3879
  {
3813
3880
  targetUrl: 'https://www.kras.or.kr/?r=kras&m=bbs&bid=notice',
3814
3881
  minScore: 6,
@@ -3853,10 +3920,6 @@ class AnalysisProvider {
3853
3920
  targetUrl: 'https://www.kaah.kr/bussopen',
3854
3921
  minScore: 6,
3855
3922
  },
3856
- {
3857
- targetUrl: 'https://www.kaah.kr/ipcopen',
3858
- minScore: 6,
3859
- },
3860
3923
  {
3861
3924
  targetUrl: 'https://www.seamuse.go.kr/resources/academiccultural/list/1',
3862
3925
  minScore: 6,
@@ -5054,7 +5117,7 @@ function createContentGenerationModel(config) {
5054
5117
  switch (config.provider) {
5055
5118
  case 'openai': {
5056
5119
  const provider = createOpenAI({ apiKey: config.apiKey });
5057
- return provider(config.model ?? 'gpt-5.6-sol');
5120
+ return provider(config.model ?? 'gpt-6-sol');
5058
5121
  }
5059
5122
  case 'anthropic': {
5060
5123
  const provider = createAnthropic({ apiKey: config.apiKey });
@@ -5070,6 +5133,10 @@ function createNewsletterGenerator(dependencies) {
5070
5133
  const openai = createOpenAI({
5071
5134
  apiKey: dependencies.openAIApiKey,
5072
5135
  });
5136
+ const modelIds = {
5137
+ ...llmConfig.models,
5138
+ ...dependencies.models,
5139
+ };
5073
5140
  const dateService = new DateService(dependencies.publishDate);
5074
5141
  const taskService = new TaskService(dependencies.taskRepository);
5075
5142
  const crawlingProvider = new CrawlingProvider(dependencies.articleRepository, dependencies.customFetch, dependencies.excavationReportSource, dependencies.logger, dependencies.publicDataApiKey,
@@ -5077,7 +5144,7 @@ function createNewsletterGenerator(dependencies) {
5077
5144
  // reach per-article scoring is decided here, 100 titles per request. The
5078
5145
  // deterministic filter stays behind it as the fallback.
5079
5146
  createHeritageBidTriage({
5080
- model: openai('gpt-5.6-luna'),
5147
+ model: openai(modelIds.heritageBidTriage),
5081
5148
  onFallback: (reason, batchSize) => {
5082
5149
  dependencies.logger?.info({
5083
5150
  event: 'crawl.g2b.triage.fallback',
@@ -5085,7 +5152,11 @@ function createNewsletterGenerator(dependencies) {
5085
5152
  });
5086
5153
  },
5087
5154
  }));
5088
- const analysisProvider = new AnalysisProvider(openai, dependencies.articleRepository, dependencies.tagRepository);
5155
+ const analysisProvider = new AnalysisProvider({
5156
+ classifyTags: openai(modelIds.classifyTags),
5157
+ analyzeImages: openai(modelIds.analyzeImages),
5158
+ determineImportance: openai(modelIds.determineImportance),
5159
+ }, dependencies.articleRepository, dependencies.tagRepository);
5089
5160
  // Inject display date from DateService into template options
5090
5161
  const templateOptions = dependencies.templateOptions
5091
5162
  ? {
@@ -5104,7 +5175,11 @@ function createNewsletterGenerator(dependencies) {
5104
5175
  };
5105
5176
  resolvedBrandName = '한국고고학회 뉴스레터';
5106
5177
  }
5107
- const contentModel = createContentGenerationModel(dependencies.contentGeneration);
5178
+ const contentModel = dependencies.models?.generateNewsletter
5179
+ ? openai(modelIds.generateNewsletter)
5180
+ : dependencies.contentGeneration
5181
+ ? createContentGenerationModel(dependencies.contentGeneration)
5182
+ : openai(modelIds.generateNewsletter);
5108
5183
  const contentGenerateProvider = new ContentGenerateProvider(contentModel, dependencies.articleRepository, dependencies.newsletterRepository, templateOptions, resolvedBrandName);
5109
5184
  return new GenerateNewsletter({
5110
5185
  contentOptions: resolvedContentOptions,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@heripo/research-radar",
3
3
  "private": false,
4
4
  "type": "module",
5
- "version": "5.2.5",
5
+ "version": "5.3.1",
6
6
  "description": "AI-driven intelligence for Korean cultural heritage. This package serves as both a ready-to-use newsletter service and a practical implementation example for the LLM-Newsletter-Kit.",
7
7
  "main": "dist/index.js",
8
8
  "types": "dist/index.d.ts",