@heripo/research-radar 2.3.6 → 3.0.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 +30 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +36 -14
- package/dist/index.js +30 -11
- package/dist/index.js.map +1 -1
- package/package.json +9 -8
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');
|
|
@@ -2361,12 +2362,11 @@ ${poweredByFooterHtml()}
|
|
|
2361
2362
|
|
|
2362
2363
|
/**
|
|
2363
2364
|
* Content generation provider implementation
|
|
2364
|
-
* - LLM-based newsletter content generation
|
|
2365
|
+
* - LLM-based newsletter content generation
|
|
2365
2366
|
* - HTML template provisioning
|
|
2366
2367
|
* - Newsletter persistence
|
|
2367
2368
|
*/
|
|
2368
2369
|
class ContentGenerateProvider {
|
|
2369
|
-
google;
|
|
2370
2370
|
articleRepository;
|
|
2371
2371
|
newsletterRepository;
|
|
2372
2372
|
_issueOrder = null;
|
|
@@ -2375,11 +2375,10 @@ class ContentGenerateProvider {
|
|
|
2375
2375
|
htmlTemplate;
|
|
2376
2376
|
/** Newsletter brand name (defaults to config, can be overridden via constructor) */
|
|
2377
2377
|
newsletterBrandName;
|
|
2378
|
-
constructor(
|
|
2379
|
-
this.google = google;
|
|
2378
|
+
constructor(model, articleRepository, newsletterRepository, templateOptions, brandName) {
|
|
2380
2379
|
this.articleRepository = articleRepository;
|
|
2381
2380
|
this.newsletterRepository = newsletterRepository;
|
|
2382
|
-
this.model =
|
|
2381
|
+
this.model = model;
|
|
2383
2382
|
this.newsletterBrandName = brandName ?? newsletterConfig.brandName;
|
|
2384
2383
|
this.htmlTemplate = {
|
|
2385
2384
|
html: createNewsletterHtmlTemplate(createCrawlingTargetGroups().flatMap((group) => group.targets), templateOptions),
|
|
@@ -2575,7 +2574,10 @@ class TaskService {
|
|
|
2575
2574
|
* ```typescript
|
|
2576
2575
|
* const generator = createNewsletterGenerator({
|
|
2577
2576
|
* openAIApiKey: process.env.OPENAI_API_KEY,
|
|
2578
|
-
*
|
|
2577
|
+
* contentGeneration: {
|
|
2578
|
+
* provider: 'anthropic',
|
|
2579
|
+
* apiKey: process.env.ANTHROPIC_API_KEY,
|
|
2580
|
+
* },
|
|
2579
2581
|
* taskRepository: new PrismaTaskRepository(prisma),
|
|
2580
2582
|
* articleRepository: new PrismaArticleRepository(prisma),
|
|
2581
2583
|
* tagRepository: new PrismaTagRepository(prisma),
|
|
@@ -2591,13 +2593,26 @@ class TaskService {
|
|
|
2591
2593
|
* const newsletterId = await generator.generate();
|
|
2592
2594
|
* ```
|
|
2593
2595
|
*/
|
|
2596
|
+
function createContentGenerationModel(config) {
|
|
2597
|
+
switch (config.provider) {
|
|
2598
|
+
case 'openai': {
|
|
2599
|
+
const provider = openai.createOpenAI({ apiKey: config.apiKey });
|
|
2600
|
+
return provider(config.model ?? 'gpt-5.4');
|
|
2601
|
+
}
|
|
2602
|
+
case 'anthropic': {
|
|
2603
|
+
const provider = anthropic.createAnthropic({ apiKey: config.apiKey });
|
|
2604
|
+
return provider(config.model ?? 'claude-sonnet-4-6');
|
|
2605
|
+
}
|
|
2606
|
+
case 'google': {
|
|
2607
|
+
const provider = google.createGoogleGenerativeAI({ apiKey: config.apiKey });
|
|
2608
|
+
return provider(config.model ?? 'gemini-3.1-pro-preview');
|
|
2609
|
+
}
|
|
2610
|
+
}
|
|
2611
|
+
}
|
|
2594
2612
|
function createNewsletterGenerator(dependencies) {
|
|
2595
2613
|
const openai$1 = openai.createOpenAI({
|
|
2596
2614
|
apiKey: dependencies.openAIApiKey,
|
|
2597
2615
|
});
|
|
2598
|
-
const google$1 = google.createGoogleGenerativeAI({
|
|
2599
|
-
apiKey: dependencies.googleGenerativeAIApiKey,
|
|
2600
|
-
});
|
|
2601
2616
|
const dateService = new DateService(dependencies.publishDate);
|
|
2602
2617
|
const taskService = new TaskService(dependencies.taskRepository);
|
|
2603
2618
|
const crawlingProvider = new CrawlingProvider(dependencies.articleRepository, dependencies.customFetch);
|
|
@@ -2620,7 +2635,8 @@ function createNewsletterGenerator(dependencies) {
|
|
|
2620
2635
|
};
|
|
2621
2636
|
resolvedBrandName = '한국고고학회 뉴스레터';
|
|
2622
2637
|
}
|
|
2623
|
-
const
|
|
2638
|
+
const contentModel = createContentGenerationModel(dependencies.contentGeneration);
|
|
2639
|
+
const contentGenerateProvider = new ContentGenerateProvider(contentModel, dependencies.articleRepository, dependencies.newsletterRepository, templateOptions, resolvedBrandName);
|
|
2624
2640
|
return new core.GenerateNewsletter({
|
|
2625
2641
|
contentOptions: resolvedContentOptions,
|
|
2626
2642
|
dateService,
|
|
@@ -2650,7 +2666,10 @@ function createNewsletterGenerator(dependencies) {
|
|
|
2650
2666
|
* ```typescript
|
|
2651
2667
|
* const newsletterId = await generateNewsletter({
|
|
2652
2668
|
* openAIApiKey: process.env.OPENAI_API_KEY,
|
|
2653
|
-
*
|
|
2669
|
+
* contentGeneration: {
|
|
2670
|
+
* provider: 'anthropic',
|
|
2671
|
+
* apiKey: process.env.ANTHROPIC_API_KEY,
|
|
2672
|
+
* },
|
|
2654
2673
|
* taskRepository: new PrismaTaskRepository(prisma),
|
|
2655
2674
|
* articleRepository: new PrismaArticleRepository(prisma),
|
|
2656
2675
|
* 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';
|
|
@@ -2340,12 +2341,11 @@ ${poweredByFooterHtml()}
|
|
|
2340
2341
|
|
|
2341
2342
|
/**
|
|
2342
2343
|
* Content generation provider implementation
|
|
2343
|
-
* - LLM-based newsletter content generation
|
|
2344
|
+
* - LLM-based newsletter content generation
|
|
2344
2345
|
* - HTML template provisioning
|
|
2345
2346
|
* - Newsletter persistence
|
|
2346
2347
|
*/
|
|
2347
2348
|
class ContentGenerateProvider {
|
|
2348
|
-
google;
|
|
2349
2349
|
articleRepository;
|
|
2350
2350
|
newsletterRepository;
|
|
2351
2351
|
_issueOrder = null;
|
|
@@ -2354,11 +2354,10 @@ class ContentGenerateProvider {
|
|
|
2354
2354
|
htmlTemplate;
|
|
2355
2355
|
/** Newsletter brand name (defaults to config, can be overridden via constructor) */
|
|
2356
2356
|
newsletterBrandName;
|
|
2357
|
-
constructor(
|
|
2358
|
-
this.google = google;
|
|
2357
|
+
constructor(model, articleRepository, newsletterRepository, templateOptions, brandName) {
|
|
2359
2358
|
this.articleRepository = articleRepository;
|
|
2360
2359
|
this.newsletterRepository = newsletterRepository;
|
|
2361
|
-
this.model =
|
|
2360
|
+
this.model = model;
|
|
2362
2361
|
this.newsletterBrandName = brandName ?? newsletterConfig.brandName;
|
|
2363
2362
|
this.htmlTemplate = {
|
|
2364
2363
|
html: createNewsletterHtmlTemplate(createCrawlingTargetGroups().flatMap((group) => group.targets), templateOptions),
|
|
@@ -2554,7 +2553,10 @@ class TaskService {
|
|
|
2554
2553
|
* ```typescript
|
|
2555
2554
|
* const generator = createNewsletterGenerator({
|
|
2556
2555
|
* openAIApiKey: process.env.OPENAI_API_KEY,
|
|
2557
|
-
*
|
|
2556
|
+
* contentGeneration: {
|
|
2557
|
+
* provider: 'anthropic',
|
|
2558
|
+
* apiKey: process.env.ANTHROPIC_API_KEY,
|
|
2559
|
+
* },
|
|
2558
2560
|
* taskRepository: new PrismaTaskRepository(prisma),
|
|
2559
2561
|
* articleRepository: new PrismaArticleRepository(prisma),
|
|
2560
2562
|
* tagRepository: new PrismaTagRepository(prisma),
|
|
@@ -2570,13 +2572,26 @@ class TaskService {
|
|
|
2570
2572
|
* const newsletterId = await generator.generate();
|
|
2571
2573
|
* ```
|
|
2572
2574
|
*/
|
|
2575
|
+
function createContentGenerationModel(config) {
|
|
2576
|
+
switch (config.provider) {
|
|
2577
|
+
case 'openai': {
|
|
2578
|
+
const provider = createOpenAI({ apiKey: config.apiKey });
|
|
2579
|
+
return provider(config.model ?? 'gpt-5.4');
|
|
2580
|
+
}
|
|
2581
|
+
case 'anthropic': {
|
|
2582
|
+
const provider = createAnthropic({ apiKey: config.apiKey });
|
|
2583
|
+
return provider(config.model ?? 'claude-sonnet-4-6');
|
|
2584
|
+
}
|
|
2585
|
+
case 'google': {
|
|
2586
|
+
const provider = createGoogleGenerativeAI({ apiKey: config.apiKey });
|
|
2587
|
+
return provider(config.model ?? 'gemini-3.1-pro-preview');
|
|
2588
|
+
}
|
|
2589
|
+
}
|
|
2590
|
+
}
|
|
2573
2591
|
function createNewsletterGenerator(dependencies) {
|
|
2574
2592
|
const openai = createOpenAI({
|
|
2575
2593
|
apiKey: dependencies.openAIApiKey,
|
|
2576
2594
|
});
|
|
2577
|
-
const google = createGoogleGenerativeAI({
|
|
2578
|
-
apiKey: dependencies.googleGenerativeAIApiKey,
|
|
2579
|
-
});
|
|
2580
2595
|
const dateService = new DateService(dependencies.publishDate);
|
|
2581
2596
|
const taskService = new TaskService(dependencies.taskRepository);
|
|
2582
2597
|
const crawlingProvider = new CrawlingProvider(dependencies.articleRepository, dependencies.customFetch);
|
|
@@ -2599,7 +2614,8 @@ function createNewsletterGenerator(dependencies) {
|
|
|
2599
2614
|
};
|
|
2600
2615
|
resolvedBrandName = '한국고고학회 뉴스레터';
|
|
2601
2616
|
}
|
|
2602
|
-
const
|
|
2617
|
+
const contentModel = createContentGenerationModel(dependencies.contentGeneration);
|
|
2618
|
+
const contentGenerateProvider = new ContentGenerateProvider(contentModel, dependencies.articleRepository, dependencies.newsletterRepository, templateOptions, resolvedBrandName);
|
|
2603
2619
|
return new GenerateNewsletter({
|
|
2604
2620
|
contentOptions: resolvedContentOptions,
|
|
2605
2621
|
dateService,
|
|
@@ -2629,7 +2645,10 @@ function createNewsletterGenerator(dependencies) {
|
|
|
2629
2645
|
* ```typescript
|
|
2630
2646
|
* const newsletterId = await generateNewsletter({
|
|
2631
2647
|
* openAIApiKey: process.env.OPENAI_API_KEY,
|
|
2632
|
-
*
|
|
2648
|
+
* contentGeneration: {
|
|
2649
|
+
* provider: 'anthropic',
|
|
2650
|
+
* apiKey: process.env.ANTHROPIC_API_KEY,
|
|
2651
|
+
* },
|
|
2633
2652
|
* taskRepository: new PrismaTaskRepository(prisma),
|
|
2634
2653
|
* articleRepository: new PrismaArticleRepository(prisma),
|
|
2635
2654
|
* 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.0.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,13 +47,14 @@
|
|
|
47
47
|
"author": "kimhongyeon",
|
|
48
48
|
"license": "Apache-2.0",
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@ai-sdk/
|
|
51
|
-
"@ai-sdk/
|
|
50
|
+
"@ai-sdk/anthropic": "^3.0.58",
|
|
51
|
+
"@ai-sdk/google": "^3.0.43",
|
|
52
|
+
"@ai-sdk/openai": "^3.0.41",
|
|
52
53
|
"cheerio": "^1.2.0",
|
|
53
|
-
"dompurify": "^3.3.
|
|
54
|
+
"dompurify": "^3.3.2",
|
|
54
55
|
"jsdom": "^28.1.0",
|
|
55
56
|
"juice": "^11.1.1",
|
|
56
|
-
"safe-markdown2html": "^1.0.
|
|
57
|
+
"safe-markdown2html": "^1.0.1",
|
|
57
58
|
"turndown": "^7.2.2"
|
|
58
59
|
},
|
|
59
60
|
"peerDependencies": {
|
|
@@ -61,13 +62,13 @@
|
|
|
61
62
|
},
|
|
62
63
|
"devDependencies": {
|
|
63
64
|
"@eslint/js": "^10.0.1",
|
|
64
|
-
"@llm-newsletter-kit/core": "^1.3.
|
|
65
|
+
"@llm-newsletter-kit/core": "^1.3.5",
|
|
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",
|