@cicctencent/agent-midway 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (160) hide show
  1. package/README.md +280 -0
  2. package/dist/adapters/express.d.ts +8 -0
  3. package/dist/adapters/express.js +91 -0
  4. package/dist/adapters/index.d.ts +5 -0
  5. package/dist/adapters/index.js +21 -0
  6. package/dist/adapters/koa.d.ts +3 -0
  7. package/dist/adapters/koa.js +75 -0
  8. package/dist/adapters/midway.d.ts +5 -0
  9. package/dist/adapters/midway.js +11 -0
  10. package/dist/adapters/next.d.ts +12 -0
  11. package/dist/adapters/next.js +89 -0
  12. package/dist/adapters/shared.d.ts +4 -0
  13. package/dist/adapters/shared.js +31 -0
  14. package/dist/channel/dingtalk.d.ts +18 -0
  15. package/dist/channel/dingtalk.js +68 -0
  16. package/dist/channel/feishu.d.ts +20 -0
  17. package/dist/channel/feishu.js +96 -0
  18. package/dist/channel/index.d.ts +46 -0
  19. package/dist/channel/index.js +311 -0
  20. package/dist/channel/types.d.ts +77 -0
  21. package/dist/channel/types.js +7 -0
  22. package/dist/channel/wecom.d.ts +22 -0
  23. package/dist/channel/wecom.js +106 -0
  24. package/dist/component.d.ts +49 -0
  25. package/dist/component.js +129 -0
  26. package/dist/connector/calendar-adapter.d.ts +19 -0
  27. package/dist/connector/calendar-adapter.js +236 -0
  28. package/dist/connector/db-adapter.d.ts +28 -0
  29. package/dist/connector/db-adapter.js +193 -0
  30. package/dist/connector/email-adapter.d.ts +23 -0
  31. package/dist/connector/email-adapter.js +192 -0
  32. package/dist/connector/fs-adapter.d.ts +15 -0
  33. package/dist/connector/fs-adapter.js +199 -0
  34. package/dist/connector/http-adapter.d.ts +29 -0
  35. package/dist/connector/http-adapter.js +181 -0
  36. package/dist/connector/index.d.ts +24 -0
  37. package/dist/connector/index.js +454 -0
  38. package/dist/connector/mcp-adapter.d.ts +27 -0
  39. package/dist/connector/mcp-adapter.js +156 -0
  40. package/dist/connector/mq-adapter.d.ts +25 -0
  41. package/dist/connector/mq-adapter.js +181 -0
  42. package/dist/connector/types.d.ts +205 -0
  43. package/dist/connector/types.js +9 -0
  44. package/dist/controller/a2a.controller.d.ts +41 -0
  45. package/dist/controller/a2a.controller.js +150 -0
  46. package/dist/controller/agent-profile.controller.d.ts +97 -0
  47. package/dist/controller/agent-profile.controller.js +200 -0
  48. package/dist/controller/agent.controller.d.ts +199 -0
  49. package/dist/controller/agent.controller.js +414 -0
  50. package/dist/controller/application.controller.d.ts +113 -0
  51. package/dist/controller/application.controller.js +217 -0
  52. package/dist/controller/automation.controller.d.ts +113 -0
  53. package/dist/controller/automation.controller.js +246 -0
  54. package/dist/controller/channel.controller.d.ts +73 -0
  55. package/dist/controller/channel.controller.js +183 -0
  56. package/dist/controller/chat.controller.d.ts +188 -0
  57. package/dist/controller/chat.controller.js +375 -0
  58. package/dist/controller/connector.controller.d.ts +134 -0
  59. package/dist/controller/connector.controller.js +257 -0
  60. package/dist/controller/knowledge-base.controller.d.ts +157 -0
  61. package/dist/controller/knowledge-base.controller.js +278 -0
  62. package/dist/controller/mcp-server.controller.d.ts +115 -0
  63. package/dist/controller/mcp-server.controller.js +236 -0
  64. package/dist/controller/model-config.controller.d.ts +139 -0
  65. package/dist/controller/model-config.controller.js +274 -0
  66. package/dist/controller/observability.controller.d.ts +124 -0
  67. package/dist/controller/observability.controller.js +142 -0
  68. package/dist/controller/security.controller.d.ts +91 -0
  69. package/dist/controller/security.controller.js +172 -0
  70. package/dist/controller/settings.controller.d.ts +83 -0
  71. package/dist/controller/settings.controller.js +280 -0
  72. package/dist/core/ai-workstation.d.ts +17 -0
  73. package/dist/core/ai-workstation.js +129 -0
  74. package/dist/core/index.d.ts +4 -0
  75. package/dist/core/index.js +20 -0
  76. package/dist/core/service-container.d.ts +12 -0
  77. package/dist/core/service-container.js +54 -0
  78. package/dist/core/sse.d.ts +6 -0
  79. package/dist/core/sse.js +56 -0
  80. package/dist/core/types.d.ts +72 -0
  81. package/dist/core/types.js +2 -0
  82. package/dist/dto/agent.dto.d.ts +21 -0
  83. package/dist/dto/agent.dto.js +79 -0
  84. package/dist/dto/ai-config.dto.d.ts +67 -0
  85. package/dist/dto/ai-config.dto.js +249 -0
  86. package/dist/dto/chat.dto.d.ts +40 -0
  87. package/dist/dto/chat.dto.js +122 -0
  88. package/dist/index.d.ts +101 -0
  89. package/dist/index.js +195 -0
  90. package/dist/memory/db-store.d.ts +33 -0
  91. package/dist/memory/db-store.js +143 -0
  92. package/dist/memory/index.d.ts +187 -0
  93. package/dist/memory/index.js +443 -0
  94. package/dist/model/ai-agent-profile.entity.d.ts +32 -0
  95. package/dist/model/ai-agent-profile.entity.js +289 -0
  96. package/dist/model/ai-application.entity.d.ts +20 -0
  97. package/dist/model/ai-application.entity.js +166 -0
  98. package/dist/model/ai-chat-memory.entity.d.ts +16 -0
  99. package/dist/model/ai-chat-memory.entity.js +123 -0
  100. package/dist/model/ai-chat-message.entity.d.ts +16 -0
  101. package/dist/model/ai-chat-message.entity.js +122 -0
  102. package/dist/model/ai-chat-skill.entity.d.ts +19 -0
  103. package/dist/model/ai-chat-skill.entity.js +155 -0
  104. package/dist/model/ai-chat-thread.entity.d.ts +15 -0
  105. package/dist/model/ai-chat-thread.entity.js +113 -0
  106. package/dist/model/ai-chat-workspace.entity.d.ts +17 -0
  107. package/dist/model/ai-chat-workspace.entity.js +136 -0
  108. package/dist/model/ai-kb-document.entity.d.ts +16 -0
  109. package/dist/model/ai-kb-document.entity.js +122 -0
  110. package/dist/model/ai-knowledge-base.entity.d.ts +22 -0
  111. package/dist/model/ai-knowledge-base.entity.js +185 -0
  112. package/dist/model/ai-mcp-server.entity.d.ts +23 -0
  113. package/dist/model/ai-mcp-server.entity.js +198 -0
  114. package/dist/model/ai-model-config.entity.d.ts +24 -0
  115. package/dist/model/ai-model-config.entity.js +200 -0
  116. package/dist/service/a2a.service.d.ts +142 -0
  117. package/dist/service/a2a.service.js +537 -0
  118. package/dist/service/agent-profile.service.d.ts +34 -0
  119. package/dist/service/agent-profile.service.js +110 -0
  120. package/dist/service/agent-server.service.d.ts +91 -0
  121. package/dist/service/agent-server.service.js +634 -0
  122. package/dist/service/agent-task-queue.service.d.ts +98 -0
  123. package/dist/service/agent-task-queue.service.js +283 -0
  124. package/dist/service/ai-chat.service.d.ts +103 -0
  125. package/dist/service/ai-chat.service.js +431 -0
  126. package/dist/service/ai-skill.service.d.ts +116 -0
  127. package/dist/service/ai-skill.service.js +457 -0
  128. package/dist/service/application.service.d.ts +42 -0
  129. package/dist/service/application.service.js +139 -0
  130. package/dist/service/automation.service.d.ts +37 -0
  131. package/dist/service/automation.service.js +196 -0
  132. package/dist/service/connector.service.d.ts +136 -0
  133. package/dist/service/connector.service.js +524 -0
  134. package/dist/service/knowledge-base.service.d.ts +138 -0
  135. package/dist/service/knowledge-base.service.js +528 -0
  136. package/dist/service/mcp-server.service.d.ts +39 -0
  137. package/dist/service/mcp-server.service.js +143 -0
  138. package/dist/service/model-config.service.d.ts +57 -0
  139. package/dist/service/model-config.service.js +168 -0
  140. package/dist/service/observability.service.d.ts +145 -0
  141. package/dist/service/observability.service.js +281 -0
  142. package/dist/service/openai.service.d.ts +88 -0
  143. package/dist/service/openai.service.js +406 -0
  144. package/dist/service/prompt-builder.service.d.ts +50 -0
  145. package/dist/service/prompt-builder.service.js +246 -0
  146. package/dist/tools/code-exec.tool.d.ts +37 -0
  147. package/dist/tools/code-exec.tool.js +162 -0
  148. package/dist/tools/datetime.tool.d.ts +21 -0
  149. package/dist/tools/datetime.tool.js +379 -0
  150. package/dist/tools/http-request.tool.d.ts +43 -0
  151. package/dist/tools/http-request.tool.js +455 -0
  152. package/dist/tools/registry.d.ts +71 -0
  153. package/dist/tools/registry.js +77 -0
  154. package/dist/tools/text-process.tool.d.ts +7 -0
  155. package/dist/tools/text-process.tool.js +366 -0
  156. package/dist/tools/web-search.tool.d.ts +28 -0
  157. package/dist/tools/web-search.tool.js +304 -0
  158. package/dist/types.d.ts +70 -0
  159. package/dist/types.js +7 -0
  160. package/package.json +69 -0
@@ -0,0 +1,431 @@
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
+ var AIChatService_1;
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.AIChatService = void 0;
17
+ const core_1 = require("@midwayjs/core");
18
+ const midwayjs_base_1 = require("@cicctencent/midwayjs-base");
19
+ const typeorm_1 = require("@midwayjs/typeorm");
20
+ const typeorm_2 = require("typeorm");
21
+ const ai_chat_workspace_entity_1 = __importDefault(require("../model/ai-chat-workspace.entity"));
22
+ const ai_chat_thread_entity_1 = __importDefault(require("../model/ai-chat-thread.entity"));
23
+ const ai_chat_message_entity_1 = __importDefault(require("../model/ai-chat-message.entity"));
24
+ let AIChatService = AIChatService_1 = class AIChatService extends midwayjs_base_1.BaseService {
25
+ get workspaceModel() {
26
+ if (this.wsRepo)
27
+ return this.wsRepo;
28
+ return (this.wsRepo = this.dataSource.getRepository(ai_chat_workspace_entity_1.default));
29
+ }
30
+ get threadModel() {
31
+ if (this.thRepo)
32
+ return this.thRepo;
33
+ return (this.thRepo =
34
+ this.dataSource.getRepository(ai_chat_thread_entity_1.default));
35
+ }
36
+ get messageModel() {
37
+ if (this.msgRepo)
38
+ return this.msgRepo;
39
+ return (this.msgRepo =
40
+ this.dataSource.getRepository(ai_chat_message_entity_1.default));
41
+ }
42
+ // ========== Workspace ==========
43
+ async getWorkspaces(userId, applicationId) {
44
+ return this.workspaceModel.find({
45
+ where: { userId, applicationId, isDeleted: 0 },
46
+ order: { createTime: 'ASC' },
47
+ });
48
+ }
49
+ async getWorkspace(id, userId, applicationId) {
50
+ return this.workspaceModel.findOne({
51
+ where: { id, userId, applicationId, isDeleted: 0 },
52
+ });
53
+ }
54
+ async createWorkspace(userId, applicationId, data) {
55
+ const ws = this.workspaceModel.create({
56
+ ...data,
57
+ userId,
58
+ applicationId,
59
+ threadCount: 0,
60
+ status: 'active',
61
+ isDeleted: 0,
62
+ });
63
+ return this.workspaceModel.save(ws);
64
+ }
65
+ async updateWorkspace(id, userId, applicationId, data) {
66
+ await this.workspaceModel.update({ id, userId, applicationId, isDeleted: 0 }, data);
67
+ return this.getWorkspace(id, userId, applicationId);
68
+ }
69
+ async deleteWorkspace(id, userId, applicationId) {
70
+ // 软删除空间下的所有线程和消息
71
+ const threads = await this.threadModel.find({
72
+ where: { workspaceId: id, userId, applicationId, isDeleted: 0 },
73
+ });
74
+ for (const th of threads) {
75
+ await this.threadModel.update({ id: th.id }, { isDeleted: 1 });
76
+ await this.messageModel.update({ threadId: th.id, userId, applicationId, isDeleted: 0 }, { isDeleted: 1 });
77
+ }
78
+ await this.workspaceModel.update({ id, userId, applicationId, isDeleted: 0 }, { isDeleted: 1 });
79
+ }
80
+ // ========== Thread ==========
81
+ async getThreads(workspaceId, userId, applicationId) {
82
+ return this.threadModel.find({
83
+ where: { workspaceId, userId, applicationId, isDeleted: 0 },
84
+ order: { createTime: 'DESC' },
85
+ });
86
+ }
87
+ async getThread(id, userId, applicationId) {
88
+ return this.threadModel.findOne({
89
+ where: { id, userId, applicationId, isDeleted: 0 },
90
+ });
91
+ }
92
+ async createThread(userId, applicationId, data) {
93
+ const th = this.threadModel.create({
94
+ userId,
95
+ applicationId,
96
+ workspaceId: data.workspaceId,
97
+ title: data.title || '新对话',
98
+ messageCount: 0,
99
+ status: 'active',
100
+ isDeleted: 0,
101
+ });
102
+ const saved = await this.threadModel.save(th);
103
+ // 更新工作空间的线程计数
104
+ await this.workspaceModel.increment({ id: data.workspaceId, userId, applicationId, isDeleted: 0 }, 'threadCount', 1);
105
+ return saved;
106
+ }
107
+ async updateThread(id, userId, applicationId, data) {
108
+ await this.threadModel.update({ id, userId, applicationId, isDeleted: 0 }, data);
109
+ return this.getThread(id, userId, applicationId);
110
+ }
111
+ async deleteThread(id, userId, applicationId) {
112
+ const thread = await this.getThread(id, userId, applicationId);
113
+ if (!thread)
114
+ return;
115
+ // 软删除该线程的所有消息
116
+ await this.messageModel.update({ threadId: id, userId, applicationId, isDeleted: 0 }, { isDeleted: 1 });
117
+ // 软删除线程
118
+ await this.threadModel.update({ id, userId, applicationId, isDeleted: 0 }, { isDeleted: 1 });
119
+ // 减少工作空间的线程计数
120
+ await this.workspaceModel.decrement({ id: thread.workspaceId, userId, applicationId, isDeleted: 0 }, 'threadCount', 1);
121
+ }
122
+ // ========== Message ==========
123
+ async getMessages(threadId, userId, applicationId, options) {
124
+ const limit = options?.limit || 50;
125
+ const offset = options?.offset || 0;
126
+ const list = await this.messageModel.find({
127
+ where: { threadId, userId, applicationId, isDeleted: 0 },
128
+ order: { createTime: 'DESC' },
129
+ take: limit,
130
+ skip: offset,
131
+ });
132
+ const total = await this.messageModel.count({
133
+ where: { threadId, userId, applicationId, isDeleted: 0 },
134
+ });
135
+ return {
136
+ items: list.reverse(),
137
+ total,
138
+ hasMore: offset + list.length < total,
139
+ };
140
+ }
141
+ async addMessage(userId, applicationId, data) {
142
+ const msg = this.messageModel.create({
143
+ userId,
144
+ applicationId,
145
+ threadId: data.threadId,
146
+ role: data.role,
147
+ content: data.content,
148
+ type: data.type || 'text',
149
+ meta: data.meta || null,
150
+ isDeleted: 0,
151
+ });
152
+ const saved = await this.messageModel.save(msg);
153
+ // 更新线程的消息计数和时间
154
+ await this.threadModel.increment({ id: data.threadId, userId, applicationId, isDeleted: 0 }, 'messageCount', 1);
155
+ await this.threadModel.update({ id: data.threadId, userId, applicationId, isDeleted: 0 }, { updater: userId });
156
+ return saved;
157
+ }
158
+ async updateMessage(id, userId, applicationId, data) {
159
+ await this.messageModel.update({ id, userId, applicationId, isDeleted: 0 }, data);
160
+ return this.messageModel.findOne({
161
+ where: { id, userId, applicationId, isDeleted: 0 },
162
+ });
163
+ }
164
+ async deleteMessage(id, userId, applicationId) {
165
+ const msg = await this.messageModel.findOne({
166
+ where: { id, userId, applicationId, isDeleted: 0 },
167
+ });
168
+ if (!msg)
169
+ return;
170
+ await this.messageModel.update({ id, userId, applicationId, isDeleted: 0 }, { isDeleted: 1 });
171
+ // 减少线程的消息计数
172
+ await this.threadModel.decrement({ id: msg.threadId, userId, applicationId, isDeleted: 0 }, 'messageCount', 1);
173
+ }
174
+ async clearThreadMessages(threadId, userId, applicationId) {
175
+ await this.messageModel.update({ threadId, userId, applicationId, isDeleted: 0 }, { isDeleted: 1 });
176
+ await this.threadModel.update({ id: threadId, userId, applicationId, isDeleted: 0 }, { messageCount: 0 });
177
+ }
178
+ /** 初始化默认工作空间(新用户首次使用时,可由业务层覆盖) */
179
+ async ensureDefaultWorkspaces(userId, applicationId) {
180
+ const count = await this.workspaceModel.count({
181
+ where: { userId, applicationId, isDeleted: 0 },
182
+ });
183
+ if (count > 0)
184
+ return;
185
+ // 创建一个通用默认工作空间,业务层可覆盖此方法创建业务特定的工作空间
186
+ await this.createWorkspace(userId, applicationId, {
187
+ name: '默认工作空间',
188
+ description: '默认工作空间',
189
+ color: '#2d9f6f',
190
+ icon: 'folder',
191
+ });
192
+ }
193
+ // ========== 搜索 ==========
194
+ /**
195
+ * 全局搜索对话消息
196
+ * @param query 搜索关键词
197
+ * @param options.workspaceId 限定工作空间
198
+ * @param options.limit 结果上限
199
+ */
200
+ async searchMessages(query, options) {
201
+ if (!query.trim())
202
+ return [];
203
+ const limit = options?.limit || 50;
204
+ // 查找匹配的消息(使用 LIKE 搜索)
205
+ const messages = await this.messageModel.find({
206
+ where: {
207
+ content: (0, typeorm_2.Like)(`%${query}%`),
208
+ isDeleted: 0,
209
+ ...(options?.workspaceId
210
+ ? {}
211
+ : {}),
212
+ },
213
+ order: { createTime: 'DESC' },
214
+ take: limit,
215
+ });
216
+ // 如果指定了 workspaceId,需要过滤
217
+ let filtered = messages;
218
+ if (options?.workspaceId) {
219
+ const threadIds = [...new Set(messages.map(m => m.threadId))];
220
+ const threads = await this.threadModel.find({
221
+ where: threadIds.map(id => ({ id, isDeleted: 0 })),
222
+ });
223
+ const validThreadIds = new Set(threads
224
+ .filter(t => t.workspaceId === options.workspaceId)
225
+ .map(t => t.id));
226
+ filtered = messages.filter(m => validThreadIds.has(m.threadId));
227
+ }
228
+ // 获取线程标题
229
+ const threadIds = [...new Set(filtered.map(m => m.threadId))];
230
+ const threads = await this.threadModel.find({
231
+ where: threadIds.map(id => ({ id, isDeleted: 0 })),
232
+ });
233
+ const threadMap = new Map(threads.map(t => [t.id, t]));
234
+ const queryLower = query.toLowerCase();
235
+ return filtered.map(msg => {
236
+ const thread = threadMap.get(msg.threadId);
237
+ const content = msg.content || '';
238
+ const idx = content.toLowerCase().indexOf(queryLower);
239
+ const snippetStart = Math.max(0, idx - 30);
240
+ const snippetEnd = Math.min(content.length, idx + query.length + 50);
241
+ const prefix = snippetStart > 0 ? '...' : '';
242
+ const suffix = snippetEnd < content.length ? '...' : '';
243
+ return {
244
+ threadId: msg.threadId,
245
+ threadTitle: thread?.title || '未命名对话',
246
+ messageId: msg.id,
247
+ role: msg.role,
248
+ content,
249
+ createTime: msg.createTime,
250
+ snippet: prefix + content.slice(snippetStart, snippetEnd) + suffix,
251
+ };
252
+ });
253
+ }
254
+ // ========== 导出 ==========
255
+ /**
256
+ * 导出对话为指定格式
257
+ * @param threadId 线程 ID
258
+ * @param format 导出格式:markdown / json / text
259
+ * @param userId 用户 ID
260
+ * @param applicationId 应用 ID
261
+ */
262
+ async exportThread(threadId, format, userId, applicationId) {
263
+ const thread = await this.getThread(threadId, userId, applicationId);
264
+ if (!thread)
265
+ throw new Error('Thread not found');
266
+ const messages = await this.messageModel.find({
267
+ where: { threadId, userId, applicationId, isDeleted: 0 },
268
+ order: { createTime: 'ASC' },
269
+ });
270
+ switch (format) {
271
+ case 'markdown':
272
+ return this.exportAsMarkdown(thread.title || '未命名对话', messages);
273
+ case 'json':
274
+ return JSON.stringify({ thread, messages }, null, 2);
275
+ case 'text':
276
+ return this.exportAsText(thread.title || '未命名对话', messages);
277
+ default:
278
+ throw new Error(`Unsupported format: ${format}`);
279
+ }
280
+ }
281
+ exportAsMarkdown(title, messages) {
282
+ const lines = [];
283
+ lines.push(`# ${title}`);
284
+ lines.push('');
285
+ lines.push(`> 导出时间: ${new Date().toLocaleString('zh-CN')}`);
286
+ lines.push(`> 消息数: ${messages.length}`);
287
+ lines.push('');
288
+ lines.push('---');
289
+ lines.push('');
290
+ for (const msg of messages) {
291
+ const time = new Date(msg.createTime).toLocaleString('zh-CN');
292
+ if (msg.role === 'user') {
293
+ lines.push(`### 用户 (${time})`);
294
+ lines.push('');
295
+ lines.push(msg.content || '');
296
+ }
297
+ else if (msg.role === 'assistant') {
298
+ lines.push(`### 助手 (${time})`);
299
+ lines.push('');
300
+ lines.push(msg.content || '');
301
+ const toolCalls = msg.meta?.toolCalls;
302
+ if (Array.isArray(toolCalls) && toolCalls.length > 0) {
303
+ lines.push('');
304
+ lines.push('<details><summary>工具调用</summary>');
305
+ lines.push('');
306
+ for (const tc of toolCalls) {
307
+ lines.push(`- **${tc.displayName || tc.toolName}**: ${tc.status} (${tc.duration || 0}ms)`);
308
+ }
309
+ lines.push('');
310
+ lines.push('</details>');
311
+ }
312
+ }
313
+ lines.push('');
314
+ lines.push('---');
315
+ lines.push('');
316
+ }
317
+ return lines.join('\n');
318
+ }
319
+ exportAsText(title, messages) {
320
+ const lines = [];
321
+ lines.push(`=== ${title} ===`);
322
+ lines.push(`导出时间: ${new Date().toLocaleString('zh-CN')}`);
323
+ lines.push('');
324
+ for (const msg of messages) {
325
+ const time = new Date(msg.createTime).toLocaleString('zh-CN');
326
+ const role = msg.role === 'user' ? '用户' : '助手';
327
+ lines.push(`[${role} ${time}]`);
328
+ lines.push(msg.content || '');
329
+ lines.push('');
330
+ }
331
+ return lines.join('\n');
332
+ }
333
+ /**
334
+ * 判断 assistant 消息是否应排除出历史(精简 token)
335
+ */
336
+ shouldSkipMessage(msg) {
337
+ const toolCalls = msg.meta?.toolCalls;
338
+ const hasToolCalls = toolCalls && toolCalls.length > 0;
339
+ const content = (msg.content || '').trim();
340
+ if (!hasToolCalls && content.length < 5)
341
+ return true;
342
+ if (hasToolCalls && toolCalls.every(tc => tc.status === 'error'))
343
+ return true;
344
+ if (msg.meta?.isError || msg.meta?.error)
345
+ return true;
346
+ return false;
347
+ }
348
+ /**
349
+ * 从已持久化的消息记录重建 LLM 格式的对话历史(用于 AgentEngine)
350
+ *
351
+ * @param threadId 线程 ID
352
+ * @param userId 用户 ID
353
+ * @param applicationId 应用 ID
354
+ * @param limit 最大 DB 消息数,默认 20
355
+ * @returns Message[] 格式的对话历史
356
+ */
357
+ async buildThreadHistory(threadId, userId, applicationId, limit = 20) {
358
+ const { items } = await this.getMessages(threadId, userId, applicationId, { limit });
359
+ if (items.length === 0)
360
+ return [];
361
+ const result = [];
362
+ for (const msg of items) {
363
+ if (msg.role === 'user') {
364
+ const content = (msg.content || '').trim();
365
+ if (content.length < 3)
366
+ continue;
367
+ result.push({ role: 'user', content: msg.content });
368
+ }
369
+ else if (msg.role === 'assistant') {
370
+ if (this.shouldSkipMessage(msg))
371
+ continue;
372
+ let content = msg.content || '';
373
+ if (msg.meta?.error && content) {
374
+ content = `${content}\n\n[执行出错: ${msg.meta.error}]`;
375
+ }
376
+ else if (msg.meta?.error) {
377
+ content = `[执行出错: ${msg.meta.error}]`;
378
+ }
379
+ const toolCalls = msg.meta?.toolCalls;
380
+ if (toolCalls && toolCalls.length > 0) {
381
+ // assistant 消息带 tool_calls
382
+ result.push({ role: 'assistant', content });
383
+ for (const tc of toolCalls) {
384
+ let output;
385
+ if (tc.output == null || tc.output === '') {
386
+ output = '';
387
+ }
388
+ else if (typeof tc.output === 'string') {
389
+ output = tc.output;
390
+ }
391
+ else {
392
+ output = JSON.stringify(tc.output);
393
+ }
394
+ if (tc.status === 'error') {
395
+ output = `[工具调用失败] ${tc.error || output || 'Unknown error'}`;
396
+ }
397
+ const truncated = output.length > AIChatService_1.MAX_TOOL_OUTPUT_LEN
398
+ ? output.slice(0, AIChatService_1.MAX_TOOL_OUTPUT_LEN) + `\n... [truncated]`
399
+ : output;
400
+ result.push({ role: 'tool', content: truncated });
401
+ }
402
+ }
403
+ else {
404
+ result.push({ role: 'assistant', content });
405
+ }
406
+ }
407
+ }
408
+ // 硬上限
409
+ if (result.length > AIChatService_1.MAX_RESULT_MESSAGES) {
410
+ const trimmed = result.slice(result.length - AIChatService_1.MAX_RESULT_MESSAGES);
411
+ while (trimmed.length > 0 && trimmed[0].role === 'tool') {
412
+ trimmed.shift();
413
+ }
414
+ return trimmed;
415
+ }
416
+ return result;
417
+ }
418
+ };
419
+ exports.AIChatService = AIChatService;
420
+ // ========== 对话历史重建 ==========
421
+ /** 单条工具输出截断长度 */
422
+ AIChatService.MAX_TOOL_OUTPUT_LEN = 4000;
423
+ /** 最终 LLM 消息数硬上限 */
424
+ AIChatService.MAX_RESULT_MESSAGES = 60;
425
+ __decorate([
426
+ (0, typeorm_1.InjectDataSource)('default'),
427
+ __metadata("design:type", typeorm_2.DataSource)
428
+ ], AIChatService.prototype, "dataSource", void 0);
429
+ exports.AIChatService = AIChatService = AIChatService_1 = __decorate([
430
+ (0, core_1.Provide)()
431
+ ], AIChatService);
@@ -0,0 +1,116 @@
1
+ import { Application } from '@midwayjs/koa';
2
+ import type { ToolDefinition } from '../types';
3
+ /**
4
+ * 从 Markdown 文件加载的 Skill 数据结构
5
+ */
6
+ export interface FileSkill {
7
+ id: number;
8
+ name: string;
9
+ description: string;
10
+ category: string;
11
+ contentType: string;
12
+ content: string;
13
+ agentTypes: string;
14
+ injectionMode: string;
15
+ sort: number;
16
+ isActive: number;
17
+ isDeleted: number;
18
+ toolSchema?: any;
19
+ /** 源文件路径(相对于 skills 目录) */
20
+ filePath: string;
21
+ }
22
+ /**
23
+ * AI Skill 管理 Service(本地文件版)
24
+ *
25
+ * 从 data/skills/ 目录递归加载 Markdown 文件作为 Skills。
26
+ * 每个 .md 文件使用 YAML frontmatter 定义元数据,正文为 Skill 内容。
27
+ *
28
+ * 支持两种注入方式:
29
+ * - prompt: 将 skill 内容拼接到 system prompt
30
+ * - tool: 将 skill 注册为 Tool,AI 主动调用获取内容
31
+ * - both: 两者兼具
32
+ *
33
+ * 缓存策略:
34
+ * - 文件加载结果缓存在内存中,TTL 5 分钟
35
+ * - 文件变更后自动刷新缓存
36
+ */
37
+ export declare class AISkillService {
38
+ app: Application;
39
+ /** skills 根目录 */
40
+ private skillsDir;
41
+ /** 加载的 Skills */
42
+ private skills;
43
+ /** 自增 ID 计数器 */
44
+ private nextId;
45
+ /** 缓存 */
46
+ private static cache;
47
+ private static readonly CACHE_TTL;
48
+ /** 上次扫描时间 */
49
+ private _lastScanTime;
50
+ get lastScanTime(): number;
51
+ init(): Promise<void>;
52
+ /** 重新扫描并加载所有 Skills */
53
+ reloadSkills(): Promise<void>;
54
+ /** 递归扫描目录获取所有 .md 文件 */
55
+ private scanDirectory;
56
+ /** 解析单个 Skill 文件 */
57
+ private parseSkillFile;
58
+ /** 解析 YAML frontmatter */
59
+ private parseFrontmatter;
60
+ private clearCache;
61
+ private getFromCache;
62
+ private setCache;
63
+ /** 查询所有 Skills */
64
+ getSkills(options?: {
65
+ category?: string;
66
+ agentType?: string;
67
+ activeOnly?: boolean;
68
+ }): Promise<FileSkill[]>;
69
+ /** 获取单个 Skill */
70
+ getSkill(id: number): Promise<FileSkill | null>;
71
+ /** 根据 name 获取 Skill */
72
+ getSkillByName(name: string): Promise<FileSkill | null>;
73
+ /** 创建 Skill(写入新文件) */
74
+ createSkill(data: Partial<FileSkill>): Promise<FileSkill>;
75
+ /** 更新 Skill(覆写文件) */
76
+ updateSkill(id: number, data: Partial<FileSkill>): Promise<FileSkill>;
77
+ /** 删除 Skill(删除文件) */
78
+ deleteSkill(id: number): Promise<void>;
79
+ /** 生成 Skill 文件内容 */
80
+ private buildSkillFileContent;
81
+ /** 确保 Skills 已加载 */
82
+ private ensureLoaded;
83
+ /**
84
+ * 获取指定 Agent 类型的 Skills
85
+ */
86
+ getSkillsForAgent(agentType: string, injectionMode?: 'prompt' | 'tool' | 'both'): Promise<FileSkill[]>;
87
+ /**
88
+ * 构建 Skill Context(用于注入到 system prompt)
89
+ */
90
+ buildSkillContext(agentType: string): Promise<string>;
91
+ /**
92
+ * 将 Skills 转换为 ToolDefinition(供 Agent 自动调用)
93
+ */
94
+ getSkillsAsTools(agentType: string): Promise<ToolDefinition[]>;
95
+ /**
96
+ * 预览指定 Agent 的 Skills Context
97
+ */
98
+ previewSkillContext(agentType: string): Promise<{
99
+ agentType: string;
100
+ total: number;
101
+ promptSkills: number;
102
+ toolSkills: number;
103
+ context: string;
104
+ tools: Array<{
105
+ id: string;
106
+ name: string;
107
+ description: string;
108
+ }>;
109
+ }>;
110
+ /** 判断 skill 是否匹配指定 agent 类型 */
111
+ private matchesAgentType;
112
+ /** 按 category 分组 */
113
+ private groupByCategory;
114
+ /** 将 Skill 转换为 ToolDefinition */
115
+ private skillToTool;
116
+ }