@heripo/research-radar 2.3.7 → 3.1.0
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 +19 -10
- package/dist/index.cjs +63 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +36 -14
- package/dist/index.js +63 -37
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ An AI-powered newsletter service for Korean cultural heritage. Built on [`@llm-n
|
|
|
24
24
|
- Type-safe TypeScript with strict interfaces
|
|
25
25
|
- Provider pattern for swapping components (Crawling/Analysis/Content/Email)
|
|
26
26
|
- 66 crawling targets across heritage agencies, museums, academic societies
|
|
27
|
-
-
|
|
27
|
+
- Multi LLM providers: OpenAI GPT-5 (analysis) + selectable content generation (OpenAI / Anthropic / Google)
|
|
28
28
|
- Built-in retries, chain options, preview emails
|
|
29
29
|
|
|
30
30
|
**Links**: [Live service](https://heripo.com/research-radar/subscribe) • [Newsletter example](https://heripo.com/research-radar-newsletter-example.html) • [Core engine](https://github.com/heripo-lab/llm-newsletter-kit-core)
|
|
@@ -69,7 +69,7 @@ For academic publications:
|
|
|
69
69
|
npm install @heripo/research-radar @llm-newsletter-kit/core
|
|
70
70
|
```
|
|
71
71
|
|
|
72
|
-
**Requirements**: Node.js >= 24, OpenAI API key,
|
|
72
|
+
**Requirements**: Node.js >= 24, OpenAI API key, content generation API key (OpenAI / Anthropic / Google)
|
|
73
73
|
|
|
74
74
|
**Note**: `@llm-newsletter-kit/core` is a peer dependency and must be installed separately.
|
|
75
75
|
|
|
@@ -80,7 +80,11 @@ import { generateNewsletter } from '@heripo/research-radar';
|
|
|
80
80
|
|
|
81
81
|
const newsletterId = await generateNewsletter({
|
|
82
82
|
openAIApiKey: process.env.OPENAI_API_KEY,
|
|
83
|
-
|
|
83
|
+
contentGeneration: {
|
|
84
|
+
provider: 'anthropic', // 'openai' | 'anthropic' | 'google'
|
|
85
|
+
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
86
|
+
// model: 'claude-sonnet-4-6', // optional, uses sensible default
|
|
87
|
+
},
|
|
84
88
|
|
|
85
89
|
// Implement these repository interfaces (see src/types/dependencies.ts)
|
|
86
90
|
taskRepository: {
|
|
@@ -237,16 +241,21 @@ subscribeUrl: 'https://yourdomain.com/subscribe'
|
|
|
237
241
|
- Replace Korean heritage sites with your domain sources
|
|
238
242
|
- Implement parsers in `src/parsers/`
|
|
239
243
|
|
|
240
|
-
**4. Switch LLM provider** (optional):
|
|
244
|
+
**4. Switch content generation LLM provider** (optional):
|
|
241
245
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
+
Content generation supports **3 built-in providers** — just change `contentGeneration.provider`:
|
|
247
|
+
```typescript
|
|
248
|
+
contentGeneration: {
|
|
249
|
+
provider: 'google', // 'openai' | 'anthropic' | 'google'
|
|
250
|
+
apiKey: process.env.GOOGLE_API_KEY,
|
|
251
|
+
model: 'gemini-3.1-pro-preview', // optional, each provider has a default
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
Default models: openai=`gpt-5.1`, anthropic=`claude-sonnet-4-6`, google=`gemini-3.1-pro-preview`
|
|
246
255
|
|
|
247
|
-
Any [Vercel AI SDK provider](https://sdk.vercel.ai/providers) works.
|
|
256
|
+
Analysis provider (OpenAI) can be changed by modifying `src/providers/analysis.provider.ts`. Any [Vercel AI SDK provider](https://sdk.vercel.ai/providers) works.
|
|
248
257
|
|
|
249
|
-
**Search keywords**: `heripo`, `kimhongyeon`, `#D2691E`, `openai`, `gpt-5`, `
|
|
258
|
+
**Search keywords**: `heripo`, `kimhongyeon`, `#D2691E`, `openai`, `gpt-5`, `contentGeneration`
|
|
250
259
|
|
|
251
260
|
## Why Code-Based?
|
|
252
261
|
|
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var anthropic = require('@ai-sdk/anthropic');
|
|
3
4
|
var google = require('@ai-sdk/google');
|
|
4
5
|
var openai = require('@ai-sdk/openai');
|
|
5
6
|
var core = require('@llm-newsletter-kit/core');
|
|
@@ -1922,7 +1923,7 @@ const createNewsletterHtmlTemplate = (targets, options) => `<!DOCTYPE html>
|
|
|
1922
1923
|
-ms-text-size-adjust: 100%;
|
|
1923
1924
|
mso-table-lspace: 0pt;
|
|
1924
1925
|
mso-table-rspace: 0pt;
|
|
1925
|
-
max-width:
|
|
1926
|
+
max-width: 700px;
|
|
1926
1927
|
}
|
|
1927
1928
|
|
|
1928
1929
|
.content-cell {
|
|
@@ -2087,7 +2088,7 @@ const createNewsletterHtmlTemplate = (targets, options) => `<!DOCTYPE html>
|
|
|
2087
2088
|
background-color: #faf7f3;
|
|
2088
2089
|
}
|
|
2089
2090
|
|
|
2090
|
-
@media screen and (max-width:
|
|
2091
|
+
@media screen and (max-width: 700px) {
|
|
2091
2092
|
.container {
|
|
2092
2093
|
width: 100% !important;
|
|
2093
2094
|
max-width: 100% !important;
|
|
@@ -2246,6 +2247,18 @@ const createNewsletterHtmlTemplate = (targets, options) => `<!DOCTYPE html>
|
|
|
2246
2247
|
.dark-logo-inline {
|
|
2247
2248
|
display: inline-block !important;
|
|
2248
2249
|
}
|
|
2250
|
+
|
|
2251
|
+
.kras-newsletter .kras-header-title {
|
|
2252
|
+
color: #eeeeee !important;
|
|
2253
|
+
}
|
|
2254
|
+
|
|
2255
|
+
.kras-newsletter .kras-header-date {
|
|
2256
|
+
color: #bbbbbb !important;
|
|
2257
|
+
}
|
|
2258
|
+
|
|
2259
|
+
.kras-newsletter .kras-header-divider {
|
|
2260
|
+
border-top-color: #E59866 !important;
|
|
2261
|
+
}
|
|
2249
2262
|
}
|
|
2250
2263
|
</style>
|
|
2251
2264
|
</head>
|
|
@@ -2254,42 +2267,37 @@ const createNewsletterHtmlTemplate = (targets, options) => `<!DOCTYPE html>
|
|
|
2254
2267
|
<tr>
|
|
2255
2268
|
<td bgcolor="#f4f4f4" align="center" style="-webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; mso-table-lspace: 0pt; mso-table-rspace: 0pt; padding: 20px 0;" class="dark-mode-bg">
|
|
2256
2269
|
<!--[if (gte mso 9)|(IE)]>
|
|
2257
|
-
<table align="center" border="0" cellspacing="0" cellpadding="0" width="
|
|
2270
|
+
<table align="center" border="0" cellspacing="0" cellpadding="0" width="700">
|
|
2258
2271
|
<tr>
|
|
2259
|
-
<td align="center" valign="top" width="
|
|
2272
|
+
<td align="center" valign="top" width="700">
|
|
2260
2273
|
<![endif]-->
|
|
2261
|
-
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="-webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; mso-table-lspace: 0pt; mso-table-rspace: 0pt; max-width:
|
|
2274
|
+
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="-webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; mso-table-lspace: 0pt; mso-table-rspace: 0pt; max-width: 700px;" class="container" role="presentation">
|
|
2262
2275
|
<tr>
|
|
2263
|
-
<td bgcolor="#ffffff" align="left" class="content-cell dark-mode-content-bg" style="-webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; mso-table-lspace: 0pt; mso-table-rspace: 0pt; padding: 44px 44px 36px 44px; border-radius: 12px; box-shadow: 0 4px 18px rgba(0,0,0,0.07);">
|
|
2276
|
+
<td bgcolor="#ffffff" align="left" class="content-cell dark-mode-content-bg${options?.isKrasNewsletter ? ' kras-newsletter' : ''}" style="-webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; mso-table-lspace: 0pt; mso-table-rspace: 0pt; padding: 44px 44px 36px 44px; border-radius: 12px; box-shadow: 0 4px 18px rgba(0,0,0,0.07);">
|
|
2264
2277
|
${options?.isKrasNewsletter
|
|
2265
|
-
?
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
<
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
<td align="left" valign="baseline" class="header-dark-text" style="text-align: left; padding: 0 0 14px 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 24px; color: #333333; white-space: nowrap; border: none; width: 1px;" width="1">
|
|
2273
|
-
${options?.displayDate ?? ''}
|
|
2274
|
-
</td>
|
|
2275
|
-
</tr>
|
|
2276
|
-
</table>
|
|
2278
|
+
? `<!-- KRAS 50주년 헤더 -->
|
|
2279
|
+
<div style="text-align: center; margin-bottom: 28px;">
|
|
2280
|
+
<div style="width: 180px; min-height: 141px; display: inline-block; margin-bottom: 20px;"><img src="https://heripo.com/kras-50.png" width="180" alt="한국고고학회 50주년" style="-ms-interpolation-mode: bicubic; border: 0; height: auto; line-height: 100%; outline: none; text-decoration: none; display: block;" height="auto"></div>
|
|
2281
|
+
<div class="kras-header-title" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 30px; font-weight: bold; color: #111111; line-height: 1.2; margin-bottom: 8px;">한국고고학회 뉴스레터</div>
|
|
2282
|
+
<div class="kras-header-date" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; color: #666666; line-height: 1.5;">${options?.displayDate ?? ''}</div>
|
|
2283
|
+
</div>
|
|
2284
|
+
<hr class="kras-header-divider" style="border: 0; border-top: 2px solid #D2691E; margin: 0 0 32px 0;">
|
|
2277
2285
|
`
|
|
2278
2286
|
: `${heripoLogoHtml('12px')}
|
|
2279
2287
|
`}
|
|
2280
2288
|
|
|
2281
2289
|
${options?.krasNewsMarkdown
|
|
2282
|
-
?
|
|
2283
|
-
|
|
2290
|
+
? `<h2 style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 24px; font-weight: bold; line-height: 1.3; color: #D2691E; margin: 0 0 16px 0; letter-spacing: -0.2px; border-left: 5px solid #D2691E; padding-left: 12px; background: none;"><span style="display: inline-block; width: 26px; height: 26px; vertical-align: -4px; margin-right: 6px;"><img src="https://heripo.com/kras-symbol.png" width="26" height="26" alt="" style="border: 0; display: block;"></span>학회 소식</h2>` +
|
|
2291
|
+
safeMarkdown2Html(`${options.krasNewsMarkdown}
|
|
2284
2292
|
|
|
2285
2293
|
---
|
|
2286
2294
|
`, {
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2295
|
+
window: new jsdom.JSDOM('').window,
|
|
2296
|
+
linkTargetBlank: true,
|
|
2297
|
+
fixMalformedUrls: true,
|
|
2298
|
+
fixBoldSyntax: true,
|
|
2299
|
+
convertStrikethrough: true,
|
|
2300
|
+
}).replaceAll('%7B%7B%7BRESEND_UNSUBSCRIBE_URL%7D%7D%7D', '{{{RESEND_UNSUBSCRIBE_URL}}}')
|
|
2293
2301
|
: ''}
|
|
2294
2302
|
|
|
2295
2303
|
${options?.heripolabNewsMarkdown
|
|
@@ -2361,12 +2369,11 @@ ${poweredByFooterHtml()}
|
|
|
2361
2369
|
|
|
2362
2370
|
/**
|
|
2363
2371
|
* Content generation provider implementation
|
|
2364
|
-
* - LLM-based newsletter content generation
|
|
2372
|
+
* - LLM-based newsletter content generation
|
|
2365
2373
|
* - HTML template provisioning
|
|
2366
2374
|
* - Newsletter persistence
|
|
2367
2375
|
*/
|
|
2368
2376
|
class ContentGenerateProvider {
|
|
2369
|
-
google;
|
|
2370
2377
|
articleRepository;
|
|
2371
2378
|
newsletterRepository;
|
|
2372
2379
|
_issueOrder = null;
|
|
@@ -2375,11 +2382,10 @@ class ContentGenerateProvider {
|
|
|
2375
2382
|
htmlTemplate;
|
|
2376
2383
|
/** Newsletter brand name (defaults to config, can be overridden via constructor) */
|
|
2377
2384
|
newsletterBrandName;
|
|
2378
|
-
constructor(
|
|
2379
|
-
this.google = google;
|
|
2385
|
+
constructor(model, articleRepository, newsletterRepository, templateOptions, brandName) {
|
|
2380
2386
|
this.articleRepository = articleRepository;
|
|
2381
2387
|
this.newsletterRepository = newsletterRepository;
|
|
2382
|
-
this.model =
|
|
2388
|
+
this.model = model;
|
|
2383
2389
|
this.newsletterBrandName = brandName ?? newsletterConfig.brandName;
|
|
2384
2390
|
this.htmlTemplate = {
|
|
2385
2391
|
html: createNewsletterHtmlTemplate(createCrawlingTargetGroups().flatMap((group) => group.targets), templateOptions),
|
|
@@ -2575,7 +2581,10 @@ class TaskService {
|
|
|
2575
2581
|
* ```typescript
|
|
2576
2582
|
* const generator = createNewsletterGenerator({
|
|
2577
2583
|
* openAIApiKey: process.env.OPENAI_API_KEY,
|
|
2578
|
-
*
|
|
2584
|
+
* contentGeneration: {
|
|
2585
|
+
* provider: 'anthropic',
|
|
2586
|
+
* apiKey: process.env.ANTHROPIC_API_KEY,
|
|
2587
|
+
* },
|
|
2579
2588
|
* taskRepository: new PrismaTaskRepository(prisma),
|
|
2580
2589
|
* articleRepository: new PrismaArticleRepository(prisma),
|
|
2581
2590
|
* tagRepository: new PrismaTagRepository(prisma),
|
|
@@ -2591,13 +2600,26 @@ class TaskService {
|
|
|
2591
2600
|
* const newsletterId = await generator.generate();
|
|
2592
2601
|
* ```
|
|
2593
2602
|
*/
|
|
2603
|
+
function createContentGenerationModel(config) {
|
|
2604
|
+
switch (config.provider) {
|
|
2605
|
+
case 'openai': {
|
|
2606
|
+
const provider = openai.createOpenAI({ apiKey: config.apiKey });
|
|
2607
|
+
return provider(config.model ?? 'gpt-5.4');
|
|
2608
|
+
}
|
|
2609
|
+
case 'anthropic': {
|
|
2610
|
+
const provider = anthropic.createAnthropic({ apiKey: config.apiKey });
|
|
2611
|
+
return provider(config.model ?? 'claude-sonnet-4-6');
|
|
2612
|
+
}
|
|
2613
|
+
case 'google': {
|
|
2614
|
+
const provider = google.createGoogleGenerativeAI({ apiKey: config.apiKey });
|
|
2615
|
+
return provider(config.model ?? 'gemini-3.1-pro-preview');
|
|
2616
|
+
}
|
|
2617
|
+
}
|
|
2618
|
+
}
|
|
2594
2619
|
function createNewsletterGenerator(dependencies) {
|
|
2595
2620
|
const openai$1 = openai.createOpenAI({
|
|
2596
2621
|
apiKey: dependencies.openAIApiKey,
|
|
2597
2622
|
});
|
|
2598
|
-
const google$1 = google.createGoogleGenerativeAI({
|
|
2599
|
-
apiKey: dependencies.googleGenerativeAIApiKey,
|
|
2600
|
-
});
|
|
2601
2623
|
const dateService = new DateService(dependencies.publishDate);
|
|
2602
2624
|
const taskService = new TaskService(dependencies.taskRepository);
|
|
2603
2625
|
const crawlingProvider = new CrawlingProvider(dependencies.articleRepository, dependencies.customFetch);
|
|
@@ -2620,7 +2642,8 @@ function createNewsletterGenerator(dependencies) {
|
|
|
2620
2642
|
};
|
|
2621
2643
|
resolvedBrandName = '한국고고학회 뉴스레터';
|
|
2622
2644
|
}
|
|
2623
|
-
const
|
|
2645
|
+
const contentModel = createContentGenerationModel(dependencies.contentGeneration);
|
|
2646
|
+
const contentGenerateProvider = new ContentGenerateProvider(contentModel, dependencies.articleRepository, dependencies.newsletterRepository, templateOptions, resolvedBrandName);
|
|
2624
2647
|
return new core.GenerateNewsletter({
|
|
2625
2648
|
contentOptions: resolvedContentOptions,
|
|
2626
2649
|
dateService,
|
|
@@ -2650,7 +2673,10 @@ function createNewsletterGenerator(dependencies) {
|
|
|
2650
2673
|
* ```typescript
|
|
2651
2674
|
* const newsletterId = await generateNewsletter({
|
|
2652
2675
|
* openAIApiKey: process.env.OPENAI_API_KEY,
|
|
2653
|
-
*
|
|
2676
|
+
* contentGeneration: {
|
|
2677
|
+
* provider: 'anthropic',
|
|
2678
|
+
* apiKey: process.env.ANTHROPIC_API_KEY,
|
|
2679
|
+
* },
|
|
2654
2680
|
* taskRepository: new PrismaTaskRepository(prisma),
|
|
2655
2681
|
* articleRepository: new PrismaArticleRepository(prisma),
|
|
2656
2682
|
* tagRepository: new PrismaTagRepository(prisma),
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { UrlString, ParsedTarget, CrawlingTargetGroup, CrawlingTarget, UnscoredArticle, ArticleForUpdateByAnalysis, ArticleForGenerateContent, Newsletter, AppLogger, EmailService, EmailMessage, DateService as DateService$1, IsoDateString, TaskService as TaskService$1, AnalysisProvider as AnalysisProvider$1, ContentGenerateProvider as ContentGenerateProvider$1, HtmlTemplate, CrawlingProvider as CrawlingProvider$1, GenerateNewsletterConfig } from '@llm-newsletter-kit/core';
|
|
2
2
|
import { OpenAIProvider } from '@ai-sdk/openai';
|
|
3
|
-
import {
|
|
3
|
+
import { LanguageModel } from 'ai';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Repository interface for task management
|
|
@@ -151,12 +151,9 @@ interface NewsletterRepository {
|
|
|
151
151
|
}
|
|
152
152
|
|
|
153
153
|
/**
|
|
154
|
-
* Uses
|
|
155
|
-
* - OpenAI (gpt-5-mini, gpt-5.1): Article analysis (tag classification, image analysis, importance scoring)
|
|
156
|
-
* - Google Generative AI (gemini-3-pro-preview): Newsletter content generation
|
|
154
|
+
* Uses OpenAI for article analysis and a configurable provider (OpenAI / Anthropic / Google) for content generation.
|
|
157
155
|
*
|
|
158
|
-
*
|
|
159
|
-
* "⚠️ Fork하여 나만의 뉴스레터 만들기 > 4. LLM 프로바이더 변경"
|
|
156
|
+
* Content generation provider is selected via `contentGeneration.provider` in dependencies.
|
|
160
157
|
*/
|
|
161
158
|
|
|
162
159
|
/**
|
|
@@ -170,14 +167,37 @@ interface PreviewNewsletterOptions {
|
|
|
170
167
|
/** Email message configuration (subject, html, text are auto-generated) */
|
|
171
168
|
emailMessage: Omit<EmailMessage, 'subject' | 'html' | 'text'>;
|
|
172
169
|
}
|
|
170
|
+
/**
|
|
171
|
+
* Content generation LLM provider configuration.
|
|
172
|
+
* Choose one of the three supported providers and supply the API key.
|
|
173
|
+
* Each provider uses a sensible default model that can be overridden.
|
|
174
|
+
*
|
|
175
|
+
* Default models:
|
|
176
|
+
* - openai: `gpt-5.4`
|
|
177
|
+
* - anthropic: `claude-sonnet-4-6`
|
|
178
|
+
* - google: `gemini-3.1-pro-preview`
|
|
179
|
+
*/
|
|
180
|
+
type ContentGenerationConfig = {
|
|
181
|
+
provider: 'openai';
|
|
182
|
+
apiKey: string;
|
|
183
|
+
model?: string;
|
|
184
|
+
} | {
|
|
185
|
+
provider: 'anthropic';
|
|
186
|
+
apiKey: string;
|
|
187
|
+
model?: string;
|
|
188
|
+
} | {
|
|
189
|
+
provider: 'google';
|
|
190
|
+
apiKey: string;
|
|
191
|
+
model?: string;
|
|
192
|
+
};
|
|
173
193
|
/**
|
|
174
194
|
* Newsletter generator dependencies interface
|
|
175
195
|
*/
|
|
176
196
|
interface NewsletterGeneratorDependencies {
|
|
177
197
|
/** OpenAI API key (used for article analysis: tag classification, image analysis, importance scoring) */
|
|
178
198
|
openAIApiKey: string;
|
|
179
|
-
/**
|
|
180
|
-
|
|
199
|
+
/** Content generation LLM configuration (provider + API key + optional model) */
|
|
200
|
+
contentGeneration: ContentGenerationConfig;
|
|
181
201
|
/** Task management repository */
|
|
182
202
|
taskRepository: TaskRepository;
|
|
183
203
|
/** Article management repository */
|
|
@@ -212,7 +232,10 @@ interface NewsletterGeneratorDependencies {
|
|
|
212
232
|
* ```typescript
|
|
213
233
|
* const newsletterId = await generateNewsletter({
|
|
214
234
|
* openAIApiKey: process.env.OPENAI_API_KEY,
|
|
215
|
-
*
|
|
235
|
+
* contentGeneration: {
|
|
236
|
+
* provider: 'anthropic',
|
|
237
|
+
* apiKey: process.env.ANTHROPIC_API_KEY,
|
|
238
|
+
* },
|
|
216
239
|
* taskRepository: new PrismaTaskRepository(prisma),
|
|
217
240
|
* articleRepository: new PrismaArticleRepository(prisma),
|
|
218
241
|
* tagRepository: new PrismaTagRepository(prisma),
|
|
@@ -338,21 +361,20 @@ declare class AnalysisProvider implements AnalysisProvider$1 {
|
|
|
338
361
|
|
|
339
362
|
/**
|
|
340
363
|
* Content generation provider implementation
|
|
341
|
-
* - LLM-based newsletter content generation
|
|
364
|
+
* - LLM-based newsletter content generation
|
|
342
365
|
* - HTML template provisioning
|
|
343
366
|
* - Newsletter persistence
|
|
344
367
|
*/
|
|
345
368
|
declare class ContentGenerateProvider implements ContentGenerateProvider$1 {
|
|
346
|
-
private readonly google;
|
|
347
369
|
private readonly articleRepository;
|
|
348
370
|
private readonly newsletterRepository;
|
|
349
371
|
private _issueOrder;
|
|
350
|
-
model:
|
|
372
|
+
readonly model: LanguageModel;
|
|
351
373
|
/** HTML template with markers for title and content injection */
|
|
352
374
|
htmlTemplate: HtmlTemplate;
|
|
353
375
|
/** Newsletter brand name (defaults to config, can be overridden via constructor) */
|
|
354
376
|
newsletterBrandName: string;
|
|
355
|
-
constructor(
|
|
377
|
+
constructor(model: LanguageModel, articleRepository: ArticleRepository, newsletterRepository: NewsletterRepository, templateOptions?: NewsletterTemplateOptions, brandName?: string);
|
|
356
378
|
/** LLM temperature setting for content generation */
|
|
357
379
|
temperature: number;
|
|
358
380
|
/** Subscribe page URL */
|
|
@@ -460,4 +482,4 @@ declare const llmConfig: {
|
|
|
460
482
|
};
|
|
461
483
|
|
|
462
484
|
export { AnalysisProvider, ContentGenerateProvider, CrawlingProvider, DateService, TaskService, contentOptions, createCrawlingTargetGroups, generateNewsletter, generateWelcomeHTML, llmConfig, newsletterConfig };
|
|
463
|
-
export type { ArticleRepository, ContentOptions, NewsletterConfig, NewsletterGeneratorDependencies, NewsletterRepository, NewsletterTemplateOptions, PreviewNewsletterOptions, TagRepository, TaskRepository, WelcomeTemplateOptions };
|
|
485
|
+
export type { ArticleRepository, ContentGenerationConfig, ContentOptions, NewsletterConfig, NewsletterGeneratorDependencies, NewsletterRepository, NewsletterTemplateOptions, PreviewNewsletterOptions, TagRepository, TaskRepository, WelcomeTemplateOptions };
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createAnthropic } from '@ai-sdk/anthropic';
|
|
1
2
|
import { createGoogleGenerativeAI } from '@ai-sdk/google';
|
|
2
3
|
import { createOpenAI } from '@ai-sdk/openai';
|
|
3
4
|
import { DateType, GenerateNewsletter } from '@llm-newsletter-kit/core';
|
|
@@ -1901,7 +1902,7 @@ const createNewsletterHtmlTemplate = (targets, options) => `<!DOCTYPE html>
|
|
|
1901
1902
|
-ms-text-size-adjust: 100%;
|
|
1902
1903
|
mso-table-lspace: 0pt;
|
|
1903
1904
|
mso-table-rspace: 0pt;
|
|
1904
|
-
max-width:
|
|
1905
|
+
max-width: 700px;
|
|
1905
1906
|
}
|
|
1906
1907
|
|
|
1907
1908
|
.content-cell {
|
|
@@ -2066,7 +2067,7 @@ const createNewsletterHtmlTemplate = (targets, options) => `<!DOCTYPE html>
|
|
|
2066
2067
|
background-color: #faf7f3;
|
|
2067
2068
|
}
|
|
2068
2069
|
|
|
2069
|
-
@media screen and (max-width:
|
|
2070
|
+
@media screen and (max-width: 700px) {
|
|
2070
2071
|
.container {
|
|
2071
2072
|
width: 100% !important;
|
|
2072
2073
|
max-width: 100% !important;
|
|
@@ -2225,6 +2226,18 @@ const createNewsletterHtmlTemplate = (targets, options) => `<!DOCTYPE html>
|
|
|
2225
2226
|
.dark-logo-inline {
|
|
2226
2227
|
display: inline-block !important;
|
|
2227
2228
|
}
|
|
2229
|
+
|
|
2230
|
+
.kras-newsletter .kras-header-title {
|
|
2231
|
+
color: #eeeeee !important;
|
|
2232
|
+
}
|
|
2233
|
+
|
|
2234
|
+
.kras-newsletter .kras-header-date {
|
|
2235
|
+
color: #bbbbbb !important;
|
|
2236
|
+
}
|
|
2237
|
+
|
|
2238
|
+
.kras-newsletter .kras-header-divider {
|
|
2239
|
+
border-top-color: #E59866 !important;
|
|
2240
|
+
}
|
|
2228
2241
|
}
|
|
2229
2242
|
</style>
|
|
2230
2243
|
</head>
|
|
@@ -2233,42 +2246,37 @@ const createNewsletterHtmlTemplate = (targets, options) => `<!DOCTYPE html>
|
|
|
2233
2246
|
<tr>
|
|
2234
2247
|
<td bgcolor="#f4f4f4" align="center" style="-webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; mso-table-lspace: 0pt; mso-table-rspace: 0pt; padding: 20px 0;" class="dark-mode-bg">
|
|
2235
2248
|
<!--[if (gte mso 9)|(IE)]>
|
|
2236
|
-
<table align="center" border="0" cellspacing="0" cellpadding="0" width="
|
|
2249
|
+
<table align="center" border="0" cellspacing="0" cellpadding="0" width="700">
|
|
2237
2250
|
<tr>
|
|
2238
|
-
<td align="center" valign="top" width="
|
|
2251
|
+
<td align="center" valign="top" width="700">
|
|
2239
2252
|
<![endif]-->
|
|
2240
|
-
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="-webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; mso-table-lspace: 0pt; mso-table-rspace: 0pt; max-width:
|
|
2253
|
+
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="-webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; mso-table-lspace: 0pt; mso-table-rspace: 0pt; max-width: 700px;" class="container" role="presentation">
|
|
2241
2254
|
<tr>
|
|
2242
|
-
<td bgcolor="#ffffff" align="left" class="content-cell dark-mode-content-bg" style="-webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; mso-table-lspace: 0pt; mso-table-rspace: 0pt; padding: 44px 44px 36px 44px; border-radius: 12px; box-shadow: 0 4px 18px rgba(0,0,0,0.07);">
|
|
2255
|
+
<td bgcolor="#ffffff" align="left" class="content-cell dark-mode-content-bg${options?.isKrasNewsletter ? ' kras-newsletter' : ''}" style="-webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; mso-table-lspace: 0pt; mso-table-rspace: 0pt; padding: 44px 44px 36px 44px; border-radius: 12px; box-shadow: 0 4px 18px rgba(0,0,0,0.07);">
|
|
2243
2256
|
${options?.isKrasNewsletter
|
|
2244
|
-
?
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
<
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
<td align="left" valign="baseline" class="header-dark-text" style="text-align: left; padding: 0 0 14px 0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 24px; color: #333333; white-space: nowrap; border: none; width: 1px;" width="1">
|
|
2252
|
-
${options?.displayDate ?? ''}
|
|
2253
|
-
</td>
|
|
2254
|
-
</tr>
|
|
2255
|
-
</table>
|
|
2257
|
+
? `<!-- KRAS 50주년 헤더 -->
|
|
2258
|
+
<div style="text-align: center; margin-bottom: 28px;">
|
|
2259
|
+
<div style="width: 180px; min-height: 141px; display: inline-block; margin-bottom: 20px;"><img src="https://heripo.com/kras-50.png" width="180" alt="한국고고학회 50주년" style="-ms-interpolation-mode: bicubic; border: 0; height: auto; line-height: 100%; outline: none; text-decoration: none; display: block;" height="auto"></div>
|
|
2260
|
+
<div class="kras-header-title" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 30px; font-weight: bold; color: #111111; line-height: 1.2; margin-bottom: 8px;">한국고고학회 뉴스레터</div>
|
|
2261
|
+
<div class="kras-header-date" style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 16px; color: #666666; line-height: 1.5;">${options?.displayDate ?? ''}</div>
|
|
2262
|
+
</div>
|
|
2263
|
+
<hr class="kras-header-divider" style="border: 0; border-top: 2px solid #D2691E; margin: 0 0 32px 0;">
|
|
2256
2264
|
`
|
|
2257
2265
|
: `${heripoLogoHtml('12px')}
|
|
2258
2266
|
`}
|
|
2259
2267
|
|
|
2260
2268
|
${options?.krasNewsMarkdown
|
|
2261
|
-
?
|
|
2262
|
-
|
|
2269
|
+
? `<h2 style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 24px; font-weight: bold; line-height: 1.3; color: #D2691E; margin: 0 0 16px 0; letter-spacing: -0.2px; border-left: 5px solid #D2691E; padding-left: 12px; background: none;"><span style="display: inline-block; width: 26px; height: 26px; vertical-align: -4px; margin-right: 6px;"><img src="https://heripo.com/kras-symbol.png" width="26" height="26" alt="" style="border: 0; display: block;"></span>학회 소식</h2>` +
|
|
2270
|
+
safeMarkdown2Html(`${options.krasNewsMarkdown}
|
|
2263
2271
|
|
|
2264
2272
|
---
|
|
2265
2273
|
`, {
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2274
|
+
window: new JSDOM('').window,
|
|
2275
|
+
linkTargetBlank: true,
|
|
2276
|
+
fixMalformedUrls: true,
|
|
2277
|
+
fixBoldSyntax: true,
|
|
2278
|
+
convertStrikethrough: true,
|
|
2279
|
+
}).replaceAll('%7B%7B%7BRESEND_UNSUBSCRIBE_URL%7D%7D%7D', '{{{RESEND_UNSUBSCRIBE_URL}}}')
|
|
2272
2280
|
: ''}
|
|
2273
2281
|
|
|
2274
2282
|
${options?.heripolabNewsMarkdown
|
|
@@ -2340,12 +2348,11 @@ ${poweredByFooterHtml()}
|
|
|
2340
2348
|
|
|
2341
2349
|
/**
|
|
2342
2350
|
* Content generation provider implementation
|
|
2343
|
-
* - LLM-based newsletter content generation
|
|
2351
|
+
* - LLM-based newsletter content generation
|
|
2344
2352
|
* - HTML template provisioning
|
|
2345
2353
|
* - Newsletter persistence
|
|
2346
2354
|
*/
|
|
2347
2355
|
class ContentGenerateProvider {
|
|
2348
|
-
google;
|
|
2349
2356
|
articleRepository;
|
|
2350
2357
|
newsletterRepository;
|
|
2351
2358
|
_issueOrder = null;
|
|
@@ -2354,11 +2361,10 @@ class ContentGenerateProvider {
|
|
|
2354
2361
|
htmlTemplate;
|
|
2355
2362
|
/** Newsletter brand name (defaults to config, can be overridden via constructor) */
|
|
2356
2363
|
newsletterBrandName;
|
|
2357
|
-
constructor(
|
|
2358
|
-
this.google = google;
|
|
2364
|
+
constructor(model, articleRepository, newsletterRepository, templateOptions, brandName) {
|
|
2359
2365
|
this.articleRepository = articleRepository;
|
|
2360
2366
|
this.newsletterRepository = newsletterRepository;
|
|
2361
|
-
this.model =
|
|
2367
|
+
this.model = model;
|
|
2362
2368
|
this.newsletterBrandName = brandName ?? newsletterConfig.brandName;
|
|
2363
2369
|
this.htmlTemplate = {
|
|
2364
2370
|
html: createNewsletterHtmlTemplate(createCrawlingTargetGroups().flatMap((group) => group.targets), templateOptions),
|
|
@@ -2554,7 +2560,10 @@ class TaskService {
|
|
|
2554
2560
|
* ```typescript
|
|
2555
2561
|
* const generator = createNewsletterGenerator({
|
|
2556
2562
|
* openAIApiKey: process.env.OPENAI_API_KEY,
|
|
2557
|
-
*
|
|
2563
|
+
* contentGeneration: {
|
|
2564
|
+
* provider: 'anthropic',
|
|
2565
|
+
* apiKey: process.env.ANTHROPIC_API_KEY,
|
|
2566
|
+
* },
|
|
2558
2567
|
* taskRepository: new PrismaTaskRepository(prisma),
|
|
2559
2568
|
* articleRepository: new PrismaArticleRepository(prisma),
|
|
2560
2569
|
* tagRepository: new PrismaTagRepository(prisma),
|
|
@@ -2570,13 +2579,26 @@ class TaskService {
|
|
|
2570
2579
|
* const newsletterId = await generator.generate();
|
|
2571
2580
|
* ```
|
|
2572
2581
|
*/
|
|
2582
|
+
function createContentGenerationModel(config) {
|
|
2583
|
+
switch (config.provider) {
|
|
2584
|
+
case 'openai': {
|
|
2585
|
+
const provider = createOpenAI({ apiKey: config.apiKey });
|
|
2586
|
+
return provider(config.model ?? 'gpt-5.4');
|
|
2587
|
+
}
|
|
2588
|
+
case 'anthropic': {
|
|
2589
|
+
const provider = createAnthropic({ apiKey: config.apiKey });
|
|
2590
|
+
return provider(config.model ?? 'claude-sonnet-4-6');
|
|
2591
|
+
}
|
|
2592
|
+
case 'google': {
|
|
2593
|
+
const provider = createGoogleGenerativeAI({ apiKey: config.apiKey });
|
|
2594
|
+
return provider(config.model ?? 'gemini-3.1-pro-preview');
|
|
2595
|
+
}
|
|
2596
|
+
}
|
|
2597
|
+
}
|
|
2573
2598
|
function createNewsletterGenerator(dependencies) {
|
|
2574
2599
|
const openai = createOpenAI({
|
|
2575
2600
|
apiKey: dependencies.openAIApiKey,
|
|
2576
2601
|
});
|
|
2577
|
-
const google = createGoogleGenerativeAI({
|
|
2578
|
-
apiKey: dependencies.googleGenerativeAIApiKey,
|
|
2579
|
-
});
|
|
2580
2602
|
const dateService = new DateService(dependencies.publishDate);
|
|
2581
2603
|
const taskService = new TaskService(dependencies.taskRepository);
|
|
2582
2604
|
const crawlingProvider = new CrawlingProvider(dependencies.articleRepository, dependencies.customFetch);
|
|
@@ -2599,7 +2621,8 @@ function createNewsletterGenerator(dependencies) {
|
|
|
2599
2621
|
};
|
|
2600
2622
|
resolvedBrandName = '한국고고학회 뉴스레터';
|
|
2601
2623
|
}
|
|
2602
|
-
const
|
|
2624
|
+
const contentModel = createContentGenerationModel(dependencies.contentGeneration);
|
|
2625
|
+
const contentGenerateProvider = new ContentGenerateProvider(contentModel, dependencies.articleRepository, dependencies.newsletterRepository, templateOptions, resolvedBrandName);
|
|
2603
2626
|
return new GenerateNewsletter({
|
|
2604
2627
|
contentOptions: resolvedContentOptions,
|
|
2605
2628
|
dateService,
|
|
@@ -2629,7 +2652,10 @@ function createNewsletterGenerator(dependencies) {
|
|
|
2629
2652
|
* ```typescript
|
|
2630
2653
|
* const newsletterId = await generateNewsletter({
|
|
2631
2654
|
* openAIApiKey: process.env.OPENAI_API_KEY,
|
|
2632
|
-
*
|
|
2655
|
+
* contentGeneration: {
|
|
2656
|
+
* provider: 'anthropic',
|
|
2657
|
+
* apiKey: process.env.ANTHROPIC_API_KEY,
|
|
2658
|
+
* },
|
|
2633
2659
|
* taskRepository: new PrismaTaskRepository(prisma),
|
|
2634
2660
|
* articleRepository: new PrismaArticleRepository(prisma),
|
|
2635
2661
|
* tagRepository: new PrismaTagRepository(prisma),
|
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
|
+
"version": "3.1.0",
|
|
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.cjs",
|
|
8
8
|
"module": "dist/index.js",
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
"author": "kimhongyeon",
|
|
48
48
|
"license": "Apache-2.0",
|
|
49
49
|
"dependencies": {
|
|
50
|
+
"@ai-sdk/anthropic": "^3.0.58",
|
|
50
51
|
"@ai-sdk/google": "^3.0.43",
|
|
51
52
|
"@ai-sdk/openai": "^3.0.41",
|
|
52
53
|
"cheerio": "^1.2.0",
|
|
@@ -65,9 +66,9 @@
|
|
|
65
66
|
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
|
66
67
|
"@types/express": "^5.0.6",
|
|
67
68
|
"@types/jsdom": "^28.0.0",
|
|
68
|
-
"@types/node": "^25.3.
|
|
69
|
+
"@types/node": "^25.3.5",
|
|
69
70
|
"@types/turndown": "^5.0.6",
|
|
70
|
-
"eslint": "^10.0.
|
|
71
|
+
"eslint": "^10.0.3",
|
|
71
72
|
"eslint-plugin-unused-imports": "^4.4.1",
|
|
72
73
|
"express": "^5.2.1",
|
|
73
74
|
"prettier": "^3.8.1",
|