@ttkit/daily-hot-api 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/.env.example +24 -0
  2. package/README.md +1 -0
  3. package/dist/app.module.js +2 -0
  4. package/dist/app.module.js.map +1 -1
  5. package/dist/config/validation.schema.d.ts +51 -0
  6. package/dist/config/validation.schema.js +69 -0
  7. package/dist/config/validation.schema.js.map +1 -1
  8. package/dist/daily-brief/clients/ai-analysis.client.d.ts +15 -6
  9. package/dist/daily-brief/clients/ai-analysis.client.js +103 -15
  10. package/dist/daily-brief/clients/ai-analysis.client.js.map +1 -1
  11. package/dist/daily-brief/daily-brief.module.js +1 -1
  12. package/dist/daily-brief/daily-brief.module.js.map +1 -1
  13. package/dist/daily-brief/daily-brief.service.js +1 -1
  14. package/dist/daily-brief/daily-brief.service.js.map +1 -1
  15. package/dist/database/database.module.js +15 -2
  16. package/dist/database/database.module.js.map +1 -1
  17. package/dist/database/repositories/daily-brief.repository.js +11 -1
  18. package/dist/database/repositories/daily-brief.repository.js.map +1 -1
  19. package/dist/database/repositories/hot-item.repository.d.ts +1 -1
  20. package/dist/database/repositories/hot-item.repository.js +7 -4
  21. package/dist/database/repositories/hot-item.repository.js.map +1 -1
  22. package/dist/database/repositories/news-brief.repository.d.ts +81 -0
  23. package/dist/database/repositories/news-brief.repository.js +208 -0
  24. package/dist/database/repositories/news-brief.repository.js.map +1 -0
  25. package/dist/database/schemas/news-brief.schema.d.ts +48 -0
  26. package/dist/database/schemas/news-brief.schema.js +130 -0
  27. package/dist/database/schemas/news-brief.schema.js.map +1 -0
  28. package/dist/news-brief/categories/news-category.registry.d.ts +5 -0
  29. package/dist/news-brief/categories/news-category.registry.js +106 -0
  30. package/dist/news-brief/categories/news-category.registry.js.map +1 -0
  31. package/dist/news-brief/clients/news-brief-ai.client.d.ts +45 -0
  32. package/dist/news-brief/clients/news-brief-ai.client.js +314 -0
  33. package/dist/news-brief/clients/news-brief-ai.client.js.map +1 -0
  34. package/dist/news-brief/clients/news-brief-search.service.d.ts +12 -0
  35. package/dist/news-brief/clients/news-brief-search.service.js +57 -0
  36. package/dist/news-brief/clients/news-brief-search.service.js.map +1 -0
  37. package/dist/news-brief/dto/generate-news-brief.dto.d.ts +7 -0
  38. package/dist/news-brief/dto/generate-news-brief.dto.js +45 -0
  39. package/dist/news-brief/dto/generate-news-brief.dto.js.map +1 -0
  40. package/dist/news-brief/dto/list-news-briefs-query.dto.d.ts +7 -0
  41. package/dist/news-brief/dto/list-news-briefs-query.dto.js +59 -0
  42. package/dist/news-brief/dto/list-news-briefs-query.dto.js.map +1 -0
  43. package/dist/news-brief/dto/news-brief-date-param.dto.d.ts +3 -0
  44. package/dist/news-brief/dto/news-brief-date-param.dto.js +22 -0
  45. package/dist/news-brief/dto/news-brief-date-param.dto.js.map +1 -0
  46. package/dist/news-brief/interfaces/news-brief.interface.d.ts +202 -0
  47. package/dist/news-brief/interfaces/news-brief.interface.js +3 -0
  48. package/dist/news-brief/interfaces/news-brief.interface.js.map +1 -0
  49. package/dist/news-brief/news-brief.config.d.ts +33 -0
  50. package/dist/news-brief/news-brief.config.js +118 -0
  51. package/dist/news-brief/news-brief.config.js.map +1 -0
  52. package/dist/news-brief/news-brief.controller.d.ts +56 -0
  53. package/dist/news-brief/news-brief.controller.js +185 -0
  54. package/dist/news-brief/news-brief.controller.js.map +1 -0
  55. package/dist/news-brief/news-brief.module.d.ts +2 -0
  56. package/dist/news-brief/news-brief.module.js +47 -0
  57. package/dist/news-brief/news-brief.module.js.map +1 -0
  58. package/dist/news-brief/news-brief.scheduler.d.ts +34 -0
  59. package/dist/news-brief/news-brief.scheduler.js +143 -0
  60. package/dist/news-brief/news-brief.scheduler.js.map +1 -0
  61. package/dist/news-brief/news-brief.service.d.ts +60 -0
  62. package/dist/news-brief/news-brief.service.js +676 -0
  63. package/dist/news-brief/news-brief.service.js.map +1 -0
  64. package/package.json +1 -1
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NEWS_CATEGORY_PROFILES = void 0;
4
+ exports.listNewsCategoryProfiles = listNewsCategoryProfiles;
5
+ exports.getNewsCategoryProfile = getNewsCategoryProfile;
6
+ exports.resolveNewsCategories = resolveNewsCategories;
7
+ exports.NEWS_CATEGORY_PROFILES = [
8
+ {
9
+ code: 'finance',
10
+ name: '财经',
11
+ description: '货币政策、市场与公司事件、产业经济、宏观数据',
12
+ focus: '关注货币政策与监管动向、市场与公司重大事件、宏观数据、产业经济;只做新闻整理,不做个股推荐或产业链映射。',
13
+ sources: [
14
+ 'cls',
15
+ 'yicai',
16
+ 'wallstreet',
17
+ 'jin10',
18
+ 'tonghuashun',
19
+ 'eastmoney',
20
+ 'gelonghui',
21
+ 'caixin',
22
+ ],
23
+ maxItems: 8,
24
+ search: true,
25
+ },
26
+ {
27
+ code: 'general',
28
+ name: '综合新闻',
29
+ description: '社会、时政、国际、民生、公共事件',
30
+ focus: '关注社会民生、时政、国际、公共安全与突发事件;优先选取影响面大、可核实的事实性新闻。',
31
+ sources: [
32
+ 'thepaper',
33
+ 'qq-news',
34
+ 'netease-news',
35
+ 'sina-news',
36
+ 'toutiao',
37
+ 'baidu',
38
+ 'weibo',
39
+ 'zhihu',
40
+ ],
41
+ maxItems: 8,
42
+ search: true,
43
+ },
44
+ {
45
+ code: 'tech',
46
+ name: '科技',
47
+ description: 'AI、半导体、消费电子、互联网产品、开发者生态',
48
+ focus: '关注 AI 与大模型、半导体与硬件、消费电子、互联网产品、开发者生态与开源动态。',
49
+ sources: [
50
+ '36kr',
51
+ 'ithome',
52
+ 'huxiu',
53
+ 'geekpark',
54
+ 'ifanr',
55
+ 'sspai',
56
+ 'juejin',
57
+ 'xinyu',
58
+ ],
59
+ maxItems: 8,
60
+ search: true,
61
+ },
62
+ {
63
+ code: 'sports',
64
+ name: '体育',
65
+ description: '赛事结果与赛程、运动员与俱乐部动态、体育产业',
66
+ focus: '关注国内外主要赛事结果与赛程、运动员与俱乐部动态、转会与伤病、体育产业与赛事运营;避免编造比分和未公布的阵容。',
67
+ sources: [
68
+ 'hupu',
69
+ 'weibo',
70
+ 'baidu',
71
+ 'zhihu',
72
+ 'toutiao',
73
+ 'tieba',
74
+ 'bilibili',
75
+ 'sina-news',
76
+ ],
77
+ maxItems: 8,
78
+ search: true,
79
+ },
80
+ ];
81
+ function listNewsCategoryProfiles() {
82
+ return exports.NEWS_CATEGORY_PROFILES;
83
+ }
84
+ function getNewsCategoryProfile(code) {
85
+ return exports.NEWS_CATEGORY_PROFILES.find((profile) => profile.code === code);
86
+ }
87
+ function resolveNewsCategories(codes) {
88
+ const result = [];
89
+ const seen = new Set();
90
+ for (const raw of codes) {
91
+ const code = raw.trim();
92
+ if (!code || seen.has(code))
93
+ continue;
94
+ const profile = getNewsCategoryProfile(code);
95
+ if (!profile) {
96
+ throw new Error(`Unknown news brief category "${code}". Available: ${exports.NEWS_CATEGORY_PROFILES.map((item) => item.code).join(', ')}`);
97
+ }
98
+ seen.add(code);
99
+ result.push(profile);
100
+ }
101
+ if (result.length === 0) {
102
+ throw new Error('NEWS_BRIEF_CATEGORIES must contain at least one enabled category');
103
+ }
104
+ return result;
105
+ }
106
+ //# sourceMappingURL=news-category.registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"news-category.registry.js","sourceRoot":"","sources":["../../../src/news-brief/categories/news-category.registry.ts"],"names":[],"mappings":";;;AA2FA,4DAEC;AAED,wDAIC;AAMD,sDA0BC;AAvHY,QAAA,sBAAsB,GAA0B;IAC3D;QACE,IAAI,EAAE,SAAS;QACf,IAAI,EAAE,IAAI;QACV,WAAW,EAAE,wBAAwB;QACrC,KAAK,EACH,sDAAsD;QACxD,OAAO,EAAE;YACP,KAAK;YACL,OAAO;YACP,YAAY;YACZ,OAAO;YACP,aAAa;YACb,WAAW;YACX,WAAW;YACX,QAAQ;SACT;QACD,QAAQ,EAAE,CAAC;QACX,MAAM,EAAE,IAAI;KACb;IACD;QACE,IAAI,EAAE,SAAS;QACf,IAAI,EAAE,MAAM;QACZ,WAAW,EAAE,kBAAkB;QAC/B,KAAK,EACH,4CAA4C;QAC9C,OAAO,EAAE;YACP,UAAU;YACV,SAAS;YACT,cAAc;YACd,WAAW;YACX,SAAS;YACT,OAAO;YACP,OAAO;YACP,OAAO;SACR;QACD,QAAQ,EAAE,CAAC;QACX,MAAM,EAAE,IAAI;KACb;IACD;QACE,IAAI,EAAE,MAAM;QACZ,IAAI,EAAE,IAAI;QACV,WAAW,EAAE,yBAAyB;QACtC,KAAK,EACH,0CAA0C;QAC5C,OAAO,EAAE;YACP,MAAM;YACN,QAAQ;YACR,OAAO;YACP,UAAU;YACV,OAAO;YACP,OAAO;YACP,QAAQ;YACR,OAAO;SACR;QACD,QAAQ,EAAE,CAAC;QACX,MAAM,EAAE,IAAI;KACb;IACD;QACE,IAAI,EAAE,QAAQ;QACd,IAAI,EAAE,IAAI;QACV,WAAW,EAAE,wBAAwB;QACrC,KAAK,EACH,yDAAyD;QAC3D,OAAO,EAAE;YACP,MAAM;YACN,OAAO;YACP,OAAO;YACP,OAAO;YACP,SAAS;YACT,OAAO;YACP,UAAU;YACV,WAAW;SACZ;QACD,QAAQ,EAAE,CAAC;QACX,MAAM,EAAE,IAAI;KACb;CACF,CAAC;AAEF,SAAgB,wBAAwB;IACtC,OAAO,8BAAsB,CAAC;AAChC,CAAC;AAED,SAAgB,sBAAsB,CACpC,IAAY;IAEZ,OAAO,8BAAsB,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;AACzE,CAAC;AAMD,SAAgB,qBAAqB,CAAC,KAAe;IACnD,MAAM,MAAM,GAA0B,EAAE,CAAC;IACzC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAE/B,KAAK,MAAM,GAAG,IAAI,KAAK,EAAE,CAAC;QACxB,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;QACxB,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,SAAS;QACtC,MAAM,OAAO,GAAG,sBAAsB,CAAC,IAAI,CAAC,CAAC;QAC7C,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CACb,gCAAgC,IAAI,iBAAiB,8BAAsB,CAAC,GAAG,CAC7E,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CACpB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACf,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACf,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACvB,CAAC;IAED,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CACb,kEAAkE,CACnE,CAAC;IACJ,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,45 @@
1
+ import { NewsBriefAnalysis, NewsBriefInputItem, NewsBriefSearchEvidence, NewsCategoryProfile } from '../interfaces/news-brief.interface';
2
+ import { NewsBriefConfigService } from '../news-brief.config';
3
+ export declare const PROMPT_BUDGET: {
4
+ readonly maxCandidates: 40;
5
+ readonly maxSearchEvidence: 8;
6
+ readonly maxOutputItems: 10;
7
+ readonly maxTitleChars: 160;
8
+ readonly maxDescChars: 280;
9
+ readonly maxEvidenceQueryChars: 200;
10
+ readonly maxEvidenceContentChars: 300;
11
+ readonly maxEvidenceResults: 3;
12
+ };
13
+ export declare const P0_CONTENT_MAX_CHARS = 300;
14
+ export declare const P1_CONTENT_MAX_CHARS = 600;
15
+ interface NormalizeOptions {
16
+ category: NewsCategoryProfile;
17
+ candidates: Map<string, NewsBriefInputItem>;
18
+ outputItemCount: number;
19
+ contentMaxChars: number;
20
+ }
21
+ export declare class NewsBriefAiClient {
22
+ private readonly config;
23
+ constructor(config: NewsBriefConfigService);
24
+ analyzeNewsBrief(input: {
25
+ category: NewsCategoryProfile;
26
+ briefDate: string;
27
+ items: NewsBriefInputItem[];
28
+ outputItemCount: number;
29
+ searchEvidence?: NewsBriefSearchEvidence[];
30
+ contentMaxChars?: number;
31
+ }): Promise<NewsBriefAnalysis>;
32
+ getModel(): string;
33
+ private resolveOutputItemCount;
34
+ private getSystemPrompt;
35
+ private buildUserPayload;
36
+ parseAnalysis(content: string, options: NormalizeOptions): NewsBriefAnalysis;
37
+ private normalizeItems;
38
+ private normalizeTags;
39
+ private clampImportance;
40
+ private canonicalTitle;
41
+ private firstSentence;
42
+ private toIsoString;
43
+ private truncate;
44
+ }
45
+ export {};
@@ -0,0 +1,314 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.NewsBriefAiClient = exports.P1_CONTENT_MAX_CHARS = exports.P0_CONTENT_MAX_CHARS = exports.PROMPT_BUDGET = void 0;
16
+ const common_1 = require("@nestjs/common");
17
+ const openai_1 = __importDefault(require("openai"));
18
+ const crypto_1 = require("crypto");
19
+ const zod_1 = require("zod");
20
+ const news_brief_config_1 = require("../news-brief.config");
21
+ exports.PROMPT_BUDGET = {
22
+ maxCandidates: 40,
23
+ maxSearchEvidence: 8,
24
+ maxOutputItems: 10,
25
+ maxTitleChars: 160,
26
+ maxDescChars: 280,
27
+ maxEvidenceQueryChars: 200,
28
+ maxEvidenceContentChars: 300,
29
+ maxEvidenceResults: 3,
30
+ };
31
+ exports.P0_CONTENT_MAX_CHARS = 300;
32
+ exports.P1_CONTENT_MAX_CHARS = 600;
33
+ const TITLE_MAX_CHARS = 80;
34
+ const SUMMARY_MAX_CHARS = 160;
35
+ const TAG_MAX_CHARS = 12;
36
+ const TAG_MAX_COUNT = 5;
37
+ const stringArraySchema = zod_1.z.array(zod_1.z.string()).catch([]);
38
+ const aiItemSchema = zod_1.z
39
+ .object({
40
+ sourceRef: zod_1.z.string().catch(''),
41
+ title: zod_1.z.string().catch(''),
42
+ summary: zod_1.z.string().catch(''),
43
+ content: zod_1.z.string().catch(''),
44
+ tags: stringArraySchema,
45
+ importance: zod_1.z.coerce.number().catch(50),
46
+ })
47
+ .strip();
48
+ const aiPayloadSchema = zod_1.z
49
+ .object({
50
+ title: zod_1.z.string().catch(''),
51
+ summary: zod_1.z.string().catch(''),
52
+ highlights: stringArraySchema,
53
+ items: zod_1.z.array(aiItemSchema).catch([]),
54
+ })
55
+ .strip();
56
+ let NewsBriefAiClient = class NewsBriefAiClient {
57
+ constructor(config) {
58
+ this.config = config;
59
+ }
60
+ async analyzeNewsBrief(input) {
61
+ const apiKey = this.config.openaiApiKey;
62
+ if (!apiKey) {
63
+ throw new Error('OPENAI_API_KEY is not configured');
64
+ }
65
+ const model = this.getModel();
66
+ const isDeepseek = this.config.openaiBaseUrl
67
+ ? new URL(this.config.openaiBaseUrl).hostname === 'api.deepseek.com'
68
+ : false;
69
+ const client = new openai_1.default({
70
+ apiKey,
71
+ baseURL: this.config.openaiBaseUrl,
72
+ timeout: this.config.aiTimeoutMs,
73
+ maxRetries: this.config.aiMaxRetries,
74
+ });
75
+ const outputItemCount = this.resolveOutputItemCount(input.outputItemCount);
76
+ const contentMaxChars = input.contentMaxChars ?? exports.P0_CONTENT_MAX_CHARS;
77
+ const completion = await client.chat.completions.create({
78
+ model,
79
+ temperature: 0.3,
80
+ response_format: { type: 'json_object' },
81
+ ...(isDeepseek
82
+ ? { thinking: { type: 'disabled' }, max_tokens: 8192 }
83
+ : {}),
84
+ messages: [
85
+ { role: 'system', content: this.getSystemPrompt() },
86
+ {
87
+ role: 'user',
88
+ content: JSON.stringify(this.buildUserPayload({
89
+ ...input,
90
+ outputItemCount,
91
+ contentMaxChars,
92
+ })),
93
+ },
94
+ ],
95
+ });
96
+ const content = completion.choices[0]?.message?.content;
97
+ if (!content) {
98
+ throw new Error('AI returned empty content');
99
+ }
100
+ const candidates = new Map(input.items.map((item) => [item.ref, item]));
101
+ return this.parseAnalysis(content, {
102
+ category: input.category,
103
+ candidates,
104
+ outputItemCount,
105
+ contentMaxChars,
106
+ });
107
+ }
108
+ getModel() {
109
+ return this.config.aiModel;
110
+ }
111
+ resolveOutputItemCount(requested) {
112
+ if (!Number.isFinite(requested))
113
+ return exports.PROMPT_BUDGET.maxOutputItems;
114
+ return Math.min(Math.max(1, Math.floor(requested)), exports.PROMPT_BUDGET.maxOutputItems);
115
+ }
116
+ getSystemPrompt() {
117
+ return [
118
+ '你是一名严谨的中文新闻编辑,负责把当日热点榜单整理成「板块新闻简报」。',
119
+ '你只使用给定的候选条目和检索证据中的信息;不得编造引语、数字、时间、机构表态或链接。',
120
+ '信息不足时,在 content 中明确写「细节待核实」,不要用推测填充。',
121
+ '同一事件合并为一条;按重要性从高到低排序。',
122
+ '只输出合法 JSON,不要输出 Markdown 围栏、HTML 或解释性文字。',
123
+ ].join('\n');
124
+ }
125
+ buildUserPayload(input) {
126
+ const targetItemCount = this.resolveOutputItemCount(input.outputItemCount);
127
+ const minItemCount = Math.min(3, targetItemCount);
128
+ const contentMaxChars = input.contentMaxChars ?? exports.P0_CONTENT_MAX_CHARS;
129
+ return {
130
+ instruction: {
131
+ outputLanguage: 'zh-CN',
132
+ targetItemCount,
133
+ contentTarget: `${contentMaxChars} 字以内(${contentMaxChars > exports.P0_CONTENT_MAX_CHARS ? '开启搜索增强,可结合检索证据' : 'P0 无搜索增强,基于标题与摘要整理'};信息不足时短写)`,
134
+ requiredJsonShape: {
135
+ title: 'string,简报标题',
136
+ summary: 'string,100–200 字,整份简报摘要',
137
+ highlights: ['string,3–5 条要点'],
138
+ items: [
139
+ {
140
+ sourceRef: 'string,必须取自候选条目的 ref',
141
+ title: 'string,新闻标题,≤80 字',
142
+ summary: 'string,新闻简介,20–160 字',
143
+ content: `string,新闻内容,${contentMaxChars} 字以内`,
144
+ tags: ['string,≤5 个'],
145
+ importance: 'number,0–100',
146
+ },
147
+ ],
148
+ },
149
+ rules: [
150
+ `items 数量 ${minItemCount}–${targetItemCount} 条(超出的条目会被丢弃)`,
151
+ 'sourceRef 必须是候选条目的 ref,禁止编造',
152
+ '标题不要原样复制候选标题',
153
+ '不得输出候选之外的新闻',
154
+ 'content 信息不足时短写并注明「细节待核实」',
155
+ '若提供了 searchEvidence,可用其补充背景,但不得编造证据之外的事实',
156
+ ],
157
+ },
158
+ category: {
159
+ code: input.category.code,
160
+ name: input.category.name,
161
+ description: input.category.description,
162
+ focus: input.category.focus,
163
+ },
164
+ briefDate: input.briefDate,
165
+ candidates: input.items
166
+ .slice(0, exports.PROMPT_BUDGET.maxCandidates)
167
+ .map((item) => ({
168
+ ref: item.ref,
169
+ source: item.sourceName,
170
+ title: this.truncate(item.title, exports.PROMPT_BUDGET.maxTitleChars),
171
+ desc: this.truncate(item.desc ?? '', exports.PROMPT_BUDGET.maxDescChars),
172
+ hot: item.hot ?? null,
173
+ timestamp: item.timestamp ?? null,
174
+ })),
175
+ searchEvidence: (input.searchEvidence ?? [])
176
+ .slice(0, exports.PROMPT_BUDGET.maxSearchEvidence)
177
+ .map((evidence) => ({
178
+ ref: evidence.ref,
179
+ query: this.truncate(evidence.query, exports.PROMPT_BUDGET.maxEvidenceQueryChars),
180
+ results: evidence.results
181
+ .slice(0, exports.PROMPT_BUDGET.maxEvidenceResults)
182
+ .map((result) => ({
183
+ title: this.truncate(result.title, exports.PROMPT_BUDGET.maxTitleChars),
184
+ content: this.truncate(result.content ?? '', exports.PROMPT_BUDGET.maxEvidenceContentChars),
185
+ })),
186
+ })),
187
+ };
188
+ }
189
+ parseAnalysis(content, options) {
190
+ let parsed;
191
+ try {
192
+ parsed = JSON.parse(content);
193
+ }
194
+ catch (error) {
195
+ throw new Error(`AI returned invalid JSON: ${error instanceof Error ? error.message : String(error)}`);
196
+ }
197
+ const result = aiPayloadSchema.safeParse(parsed);
198
+ if (!result.success) {
199
+ throw new Error(`AI returned invalid news brief structure: ${result.error.message}`);
200
+ }
201
+ const payload = result.data;
202
+ const items = this.normalizeItems(payload, options);
203
+ if (items.length === 0) {
204
+ throw new Error('AI returned no valid news items');
205
+ }
206
+ const highlights = payload.highlights
207
+ .map((item) => this.truncate(item.trim(), SUMMARY_MAX_CHARS))
208
+ .filter(Boolean)
209
+ .slice(0, 5);
210
+ const summary = this.truncate(payload.summary.trim(), 200) ||
211
+ this.truncate(highlights.join(';'), 200);
212
+ if (!summary) {
213
+ throw new Error('AI returned a news brief without a summary');
214
+ }
215
+ const title = this.truncate(payload.title.trim(), SUMMARY_MAX_CHARS) ||
216
+ `${options.category.name}每日简报`;
217
+ return { title, summary, highlights, items };
218
+ }
219
+ normalizeItems(payload, options) {
220
+ const seenRefs = new Set();
221
+ const seenTitles = new Set();
222
+ const items = [];
223
+ payload.items.forEach((raw, index) => {
224
+ const sourceRef = raw.sourceRef.trim();
225
+ const candidate = options.candidates.get(sourceRef);
226
+ if (!candidate || seenRefs.has(sourceRef))
227
+ return;
228
+ const title = this.truncate(raw.title.trim(), TITLE_MAX_CHARS);
229
+ const content = this.truncate(raw.content.trim(), options.contentMaxChars);
230
+ const summary = this.truncate(raw.summary.trim(), SUMMARY_MAX_CHARS) ||
231
+ this.truncate(this.firstSentence(content), SUMMARY_MAX_CHARS);
232
+ if (!title || !content || !summary)
233
+ return;
234
+ const titleKey = this.canonicalTitle(title);
235
+ if (titleKey && seenTitles.has(titleKey))
236
+ return;
237
+ seenRefs.add(sourceRef);
238
+ if (titleKey)
239
+ seenTitles.add(titleKey);
240
+ const tags = this.normalizeTags(raw.tags ?? []);
241
+ const publishedAt = this.toIsoString(candidate.timestamp);
242
+ items.push({
243
+ order: index,
244
+ item: {
245
+ id: (0, crypto_1.createHash)('sha256')
246
+ .update(`${options.category.code}|${sourceRef}`)
247
+ .digest('hex')
248
+ .slice(0, 12),
249
+ title,
250
+ summary,
251
+ content,
252
+ source: {
253
+ code: candidate.source,
254
+ name: candidate.sourceName,
255
+ url: candidate.url,
256
+ },
257
+ ...(publishedAt ? { publishedAt } : {}),
258
+ ...(tags.length > 0 ? { tags } : {}),
259
+ importance: this.clampImportance(raw.importance ?? 50),
260
+ },
261
+ });
262
+ });
263
+ return items
264
+ .sort((a, b) => (b.item.importance ?? 0) - (a.item.importance ?? 0) ||
265
+ a.order - b.order)
266
+ .slice(0, options.outputItemCount)
267
+ .map((entry) => entry.item);
268
+ }
269
+ normalizeTags(tags) {
270
+ const result = [];
271
+ const seen = new Set();
272
+ for (const raw of tags) {
273
+ const tag = this.truncate(raw.trim(), TAG_MAX_CHARS);
274
+ if (!tag || seen.has(tag))
275
+ continue;
276
+ seen.add(tag);
277
+ result.push(tag);
278
+ if (result.length >= TAG_MAX_COUNT)
279
+ break;
280
+ }
281
+ return result;
282
+ }
283
+ clampImportance(value) {
284
+ if (!Number.isFinite(value))
285
+ return 50;
286
+ return Math.min(100, Math.max(0, Math.round(value)));
287
+ }
288
+ canonicalTitle(title) {
289
+ return title.toLocaleLowerCase().replace(/[\s\p{P}\p{S}]+/gu, '');
290
+ }
291
+ firstSentence(content) {
292
+ const match = content.match(/[^。!?!?]+[。!?!?]?/u);
293
+ return match ? match[0] : '';
294
+ }
295
+ toIsoString(timestamp) {
296
+ if (typeof timestamp !== 'number' || !Number.isFinite(timestamp)) {
297
+ return undefined;
298
+ }
299
+ const millis = timestamp < 1e12 ? timestamp * 1000 : timestamp;
300
+ const date = new Date(millis);
301
+ if (Number.isNaN(date.getTime()))
302
+ return undefined;
303
+ return date.toISOString();
304
+ }
305
+ truncate(value, limit) {
306
+ return value.length > limit ? value.slice(0, limit) : value;
307
+ }
308
+ };
309
+ exports.NewsBriefAiClient = NewsBriefAiClient;
310
+ exports.NewsBriefAiClient = NewsBriefAiClient = __decorate([
311
+ (0, common_1.Injectable)(),
312
+ __metadata("design:paramtypes", [news_brief_config_1.NewsBriefConfigService])
313
+ ], NewsBriefAiClient);
314
+ //# sourceMappingURL=news-brief-ai.client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"news-brief-ai.client.js","sourceRoot":"","sources":["../../../src/news-brief/clients/news-brief-ai.client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import { NewsBriefConfigService } from '../news-brief.config';
2
+ import { SearchOutcome } from '../../daily-brief/interfaces/search-provider.interface';
3
+ import { TavilySearchClient } from '../../daily-brief/clients/tavily-search.client';
4
+ import { DeepseekSearchClient } from '../../daily-brief/clients/deepseek-search.client';
5
+ export declare class NewsBriefSearchService {
6
+ private readonly config;
7
+ private readonly logger;
8
+ private readonly providers;
9
+ constructor(config: NewsBriefConfigService, tavily: TavilySearchClient, deepseek: DeepseekSearchClient);
10
+ isConfigured(): boolean;
11
+ search(query: string): Promise<SearchOutcome>;
12
+ }
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var NewsBriefSearchService_1;
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.NewsBriefSearchService = void 0;
14
+ const common_1 = require("@nestjs/common");
15
+ const news_brief_config_1 = require("../news-brief.config");
16
+ const tavily_search_client_1 = require("../../daily-brief/clients/tavily-search.client");
17
+ const deepseek_search_client_1 = require("../../daily-brief/clients/deepseek-search.client");
18
+ let NewsBriefSearchService = NewsBriefSearchService_1 = class NewsBriefSearchService {
19
+ constructor(config, tavily, deepseek) {
20
+ this.config = config;
21
+ this.logger = new common_1.Logger(NewsBriefSearchService_1.name);
22
+ this.providers = { tavily, deepseek };
23
+ }
24
+ isConfigured() {
25
+ return this.config.searchProviders.some((name) => this.providers[name]?.isConfigured());
26
+ }
27
+ async search(query) {
28
+ for (const name of this.config.searchProviders) {
29
+ const provider = this.providers[name];
30
+ if (!provider)
31
+ continue;
32
+ if (!provider.isConfigured()) {
33
+ this.logger.debug(`Search provider ${name} is not configured`);
34
+ continue;
35
+ }
36
+ try {
37
+ const results = await provider.search(query);
38
+ if (results.length > 0)
39
+ return { provider: name, results };
40
+ this.logger.debug(`Search provider ${name} returned no results`);
41
+ }
42
+ catch (error) {
43
+ const reason = error instanceof Error ? error.message : String(error);
44
+ this.logger.warn(`Search provider ${name} failed: ${reason}`);
45
+ }
46
+ }
47
+ return { provider: null, results: [] };
48
+ }
49
+ };
50
+ exports.NewsBriefSearchService = NewsBriefSearchService;
51
+ exports.NewsBriefSearchService = NewsBriefSearchService = NewsBriefSearchService_1 = __decorate([
52
+ (0, common_1.Injectable)(),
53
+ __metadata("design:paramtypes", [news_brief_config_1.NewsBriefConfigService,
54
+ tavily_search_client_1.TavilySearchClient,
55
+ deepseek_search_client_1.DeepseekSearchClient])
56
+ ], NewsBriefSearchService);
57
+ //# sourceMappingURL=news-brief-search.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"news-brief-search.service.js","sourceRoot":"","sources":["../../../src/news-brief/clients/news-brief-search.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,2CAAoD;AACpD,4DAA8D;AAM9D,yFAAoF;AACpF,6FAAwF;AASjF,IAAM,sBAAsB,8BAA5B,MAAM,sBAAsB;IAIjC,YACmB,MAA8B,EAC/C,MAA0B,EAC1B,QAA8B;QAFb,WAAM,GAAN,MAAM,CAAwB;QAJhC,WAAM,GAAG,IAAI,eAAM,CAAC,wBAAsB,CAAC,IAAI,CAAC,CAAC;QAQhE,IAAI,CAAC,SAAS,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;IACxC,CAAC;IAGD,YAAY;QACV,OAAO,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAC/C,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,YAAY,EAAE,CACrC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,KAAa;QACxB,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC;YAC/C,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;YACtC,IAAI,CAAC,QAAQ;gBAAE,SAAS;YACxB,IAAI,CAAC,QAAQ,CAAC,YAAY,EAAE,EAAE,CAAC;gBAC7B,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,mBAAmB,IAAI,oBAAoB,CAAC,CAAC;gBAC/D,SAAS;YACX,CAAC;YAED,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBAC7C,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;oBAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;gBAC3D,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,mBAAmB,IAAI,sBAAsB,CAAC,CAAC;YACnE,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,MAAM,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBACtE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,mBAAmB,IAAI,YAAY,MAAM,EAAE,CAAC,CAAC;YAChE,CAAC;QACH,CAAC;QAED,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IACzC,CAAC;CACF,CAAA;AAxCY,wDAAsB;iCAAtB,sBAAsB;IADlC,IAAA,mBAAU,GAAE;qCAMgB,0CAAsB;QACvC,yCAAkB;QAChB,6CAAoB;GAPrB,sBAAsB,CAwClC"}
@@ -0,0 +1,7 @@
1
+ export declare class GenerateNewsBriefDto {
2
+ category?: string;
3
+ date?: string;
4
+ period?: string;
5
+ sources?: string[];
6
+ force?: boolean;
7
+ }
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.GenerateNewsBriefDto = void 0;
13
+ const class_validator_1 = require("class-validator");
14
+ class GenerateNewsBriefDto {
15
+ }
16
+ exports.GenerateNewsBriefDto = GenerateNewsBriefDto;
17
+ __decorate([
18
+ (0, class_validator_1.IsOptional)(),
19
+ (0, class_validator_1.IsString)(),
20
+ __metadata("design:type", String)
21
+ ], GenerateNewsBriefDto.prototype, "category", void 0);
22
+ __decorate([
23
+ (0, class_validator_1.IsOptional)(),
24
+ (0, class_validator_1.IsString)(),
25
+ (0, class_validator_1.Matches)(/^\d{4}-\d{2}-\d{2}$/),
26
+ __metadata("design:type", String)
27
+ ], GenerateNewsBriefDto.prototype, "date", void 0);
28
+ __decorate([
29
+ (0, class_validator_1.IsOptional)(),
30
+ (0, class_validator_1.IsString)(),
31
+ (0, class_validator_1.Matches)(/^[a-z0-9][a-z0-9-]{0,31}$/),
32
+ __metadata("design:type", String)
33
+ ], GenerateNewsBriefDto.prototype, "period", void 0);
34
+ __decorate([
35
+ (0, class_validator_1.IsOptional)(),
36
+ (0, class_validator_1.IsArray)(),
37
+ (0, class_validator_1.IsString)({ each: true }),
38
+ __metadata("design:type", Array)
39
+ ], GenerateNewsBriefDto.prototype, "sources", void 0);
40
+ __decorate([
41
+ (0, class_validator_1.IsOptional)(),
42
+ (0, class_validator_1.IsBoolean)(),
43
+ __metadata("design:type", Boolean)
44
+ ], GenerateNewsBriefDto.prototype, "force", void 0);
45
+ //# sourceMappingURL=generate-news-brief.dto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-news-brief.dto.js","sourceRoot":"","sources":["../../../src/news-brief/dto/generate-news-brief.dto.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qDAMyB;AAEzB,MAAa,oBAAoB;CAuBhC;AAvBD,oDAuBC;AApBC;IAFC,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;sDACO;AAKlB;IAHC,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;IACV,IAAA,yBAAO,EAAC,qBAAqB,CAAC;;kDACjB;AAKd;IAHC,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;IACV,IAAA,yBAAO,EAAC,2BAA2B,CAAC;;oDACrB;AAKhB;IAHC,IAAA,4BAAU,GAAE;IACZ,IAAA,yBAAO,GAAE;IACT,IAAA,0BAAQ,EAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;;qDACN;AAInB;IAFC,IAAA,4BAAU,GAAE;IACZ,IAAA,2BAAS,GAAE;;mDACI"}
@@ -0,0 +1,7 @@
1
+ export declare class ListNewsBriefsQueryDto {
2
+ page?: number;
3
+ limit?: number;
4
+ category?: string;
5
+ status?: 'generating' | 'success' | 'failed';
6
+ period?: string;
7
+ }