@hedgehog-finance/hedgehog-plugin 1.0.20 → 1.0.21

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.
@@ -0,0 +1,192 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { getDB } from "../../core/database.js";
3
+ import {
4
+ ArticleAiAnalysis,
5
+ GetArticleAiAnalysisParamsSchema,
6
+ GetStockAiAnalysisParamsSchema,
7
+ QueryStockAiAnalysisHistoryParamsSchema,
8
+ SaveArticleAiAnalysisParamsSchema,
9
+ SaveStockAiAnalysisParamsSchema,
10
+ StockAiAnalysis
11
+ } from "./schema.js";
12
+
13
+ interface RuntimeTool {
14
+ name: string;
15
+ description: string;
16
+ parameters: unknown;
17
+ registerTool?: boolean;
18
+ execute(params: unknown, ctx: { userId: string }): Promise<string>;
19
+ }
20
+
21
+ export function normalizeStockCode(stockCode: string): string {
22
+ return stockCode.trim().toUpperCase().replace(/\.SS$/i, ".SH");
23
+ }
24
+
25
+ function selectLatestStockAnalysis(
26
+ db: ReturnType<typeof getDB>,
27
+ userId: string,
28
+ stockCode: string
29
+ ): StockAiAnalysis | undefined {
30
+ return db.prepare(`
31
+ SELECT id, stockCode, stockName, market, content, createdAt, updatedAt
32
+ FROM stock_ai_analysis
33
+ WHERE userId = ? AND stockCode = ?
34
+ ORDER BY updatedAt DESC, createdAt DESC
35
+ LIMIT 1
36
+ `).get(userId, stockCode) as StockAiAnalysis | undefined;
37
+ }
38
+
39
+ export function saveStockAiAnalysisRecord(
40
+ db: ReturnType<typeof getDB>,
41
+ userId: string,
42
+ args: {
43
+ stockCode: string;
44
+ stockName: string;
45
+ market: string;
46
+ content: string;
47
+ }
48
+ ): StockAiAnalysis {
49
+ const stockCode = normalizeStockCode(args.stockCode);
50
+ const id = randomUUID();
51
+
52
+ db.prepare(`
53
+ INSERT INTO stock_ai_analysis (id, userId, stockCode, stockName, market, content)
54
+ VALUES (?, ?, ?, ?, ?, ?)
55
+ `).run(id, userId, stockCode, args.stockName, args.market, args.content);
56
+
57
+ return db.prepare(`
58
+ SELECT id, stockCode, stockName, market, content, createdAt, updatedAt
59
+ FROM stock_ai_analysis
60
+ WHERE userId = ? AND id = ?
61
+ `).get(userId, id) as StockAiAnalysis;
62
+ }
63
+
64
+ function selectLatestArticleAnalysis(
65
+ db: ReturnType<typeof getDB>,
66
+ userId: string,
67
+ sourceId: string,
68
+ analysisType: ArticleAiAnalysis["analysisType"],
69
+ market: string
70
+ ): ArticleAiAnalysis | undefined {
71
+ return db.prepare(`
72
+ SELECT id, sourceId, analysisType, market, content, createdAt, updatedAt
73
+ FROM article_ai_analysis
74
+ WHERE userId = ? AND sourceId = ? AND analysisType = ? AND market = ?
75
+ ORDER BY updatedAt DESC, createdAt DESC
76
+ LIMIT 1
77
+ `).get(userId, sourceId, analysisType, market) as ArticleAiAnalysis | undefined;
78
+ }
79
+
80
+ function saveArticleAiAnalysisRecord(
81
+ db: ReturnType<typeof getDB>,
82
+ userId: string,
83
+ args: {
84
+ sourceId: string;
85
+ analysisType: ArticleAiAnalysis["analysisType"];
86
+ market: string;
87
+ content: string;
88
+ }
89
+ ): ArticleAiAnalysis {
90
+ const id = randomUUID();
91
+
92
+ db.prepare(`
93
+ INSERT INTO article_ai_analysis (id, sourceId, userId, analysisType, market, content)
94
+ VALUES (?, ?, ?, ?, ?, ?)
95
+ ON CONFLICT(sourceId, userId, analysisType, market) DO UPDATE SET
96
+ content = excluded.content,
97
+ updatedAt = STRFTIME('%Y-%m-%dT%H:%M:%fZ', 'NOW')
98
+ `).run(id, args.sourceId, userId, args.analysisType, args.market, args.content);
99
+
100
+ return db.prepare(`
101
+ SELECT id, sourceId, analysisType, market, content, createdAt, updatedAt
102
+ FROM article_ai_analysis
103
+ WHERE userId = ? AND sourceId = ? AND analysisType = ? AND market = ?
104
+ `).get(userId, args.sourceId, args.analysisType, args.market) as ArticleAiAnalysis;
105
+ }
106
+
107
+ export const stockAnalysisTools: Record<string, RuntimeTool> = {
108
+ get_stock_ai_analysis: {
109
+ name: "get_stock_ai_analysis",
110
+ description: "读取股票 AI 分析的最新一条历史记录;不触发模型分析。",
111
+ parameters: GetStockAiAnalysisParamsSchema,
112
+ registerTool: false,
113
+ async execute(params, ctx) {
114
+ const args = GetStockAiAnalysisParamsSchema.parse(params);
115
+ const db = getDB();
116
+ const data = selectLatestStockAnalysis(db, ctx.userId, normalizeStockCode(args.stockCode));
117
+ return JSON.stringify({ success: true, data: data || null });
118
+ }
119
+ },
120
+ query_stock_ai_analysis_history: {
121
+ name: "query_stock_ai_analysis_history",
122
+ description: "分页读取股票 AI 分析历史记录;不触发模型分析。",
123
+ parameters: QueryStockAiAnalysisHistoryParamsSchema,
124
+ registerTool: false,
125
+ async execute(params, ctx) {
126
+ const args = QueryStockAiAnalysisHistoryParamsSchema.parse(params);
127
+ const db = getDB();
128
+ const stockCode = normalizeStockCode(args.stockCode);
129
+ const offset = (args.page - 1) * args.pageSize;
130
+ const rows = db.prepare(`
131
+ SELECT id, stockCode, stockName, market, content, createdAt, updatedAt
132
+ FROM stock_ai_analysis
133
+ WHERE userId = ? AND stockCode = ? AND market = ?
134
+ ORDER BY updatedAt DESC, createdAt DESC
135
+ LIMIT ? OFFSET ?
136
+ `).all(ctx.userId, stockCode, args.market, args.pageSize, offset) as StockAiAnalysis[];
137
+ const countRow = db.prepare(`
138
+ SELECT COUNT(*) AS total
139
+ FROM stock_ai_analysis
140
+ WHERE userId = ? AND stockCode = ? AND market = ?
141
+ `).get(ctx.userId, stockCode, args.market) as { total: number };
142
+ const total = countRow.total || 0;
143
+
144
+ return JSON.stringify({
145
+ success: true,
146
+ data: rows,
147
+ pagination: {
148
+ page: args.page,
149
+ pageSize: args.pageSize,
150
+ total,
151
+ totalPages: Math.ceil(total / args.pageSize)
152
+ }
153
+ });
154
+ }
155
+ },
156
+ save_stock_ai_analysis: {
157
+ name: "save_stock_ai_analysis",
158
+ description: "追加保存一条股票 AI 分析历史记录。",
159
+ parameters: SaveStockAiAnalysisParamsSchema,
160
+ registerTool: false,
161
+ async execute(params, ctx) {
162
+ const args = SaveStockAiAnalysisParamsSchema.parse(params);
163
+ const db = getDB();
164
+ const data = saveStockAiAnalysisRecord(db, ctx.userId, args);
165
+ return JSON.stringify({ success: true, data });
166
+ }
167
+ },
168
+ get_article_ai_analysis: {
169
+ name: "get_article_ai_analysis",
170
+ description: "读取文章 AI 分析结果;支持信息求证与深度推演,不触发模型分析。",
171
+ parameters: GetArticleAiAnalysisParamsSchema,
172
+ registerTool: false,
173
+ async execute(params, ctx) {
174
+ const args = GetArticleAiAnalysisParamsSchema.parse(params);
175
+ const db = getDB();
176
+ const data = selectLatestArticleAnalysis(db, ctx.userId, args.id, args.analysisType, args.market);
177
+ return JSON.stringify({ success: true, data: data || null });
178
+ }
179
+ },
180
+ save_article_ai_analysis: {
181
+ name: "save_article_ai_analysis",
182
+ description: "保存文章 AI 分析结果;支持信息求证与深度推演。",
183
+ parameters: SaveArticleAiAnalysisParamsSchema,
184
+ registerTool: false,
185
+ async execute(params, ctx) {
186
+ const args = SaveArticleAiAnalysisParamsSchema.parse(params);
187
+ const db = getDB();
188
+ const data = saveArticleAiAnalysisRecord(db, ctx.userId, { ...args, sourceId: args.id });
189
+ return JSON.stringify({ success: true, data });
190
+ }
191
+ }
192
+ };