@xpert-ai/plugin-community-wechat 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +353 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +139 -0
- package/dist/lib/constants.d.ts +23 -0
- package/dist/lib/constants.js +23 -0
- package/dist/lib/conversation-user-key.d.ts +13 -0
- package/dist/lib/conversation-user-key.js +28 -0
- package/dist/lib/conversation.service.d.ts +215 -0
- package/dist/lib/conversation.service.js +1179 -0
- package/dist/lib/decorators.d.ts +2 -0
- package/dist/lib/decorators.js +3 -0
- package/dist/lib/entities/index.d.ts +4 -0
- package/dist/lib/entities/index.js +4 -0
- package/dist/lib/entities/wechat-personal-account.entity.d.ts +19 -0
- package/dist/lib/entities/wechat-personal-account.entity.js +83 -0
- package/dist/lib/entities/wechat-personal-conversation-binding.entity.d.ts +14 -0
- package/dist/lib/entities/wechat-personal-conversation-binding.entity.js +65 -0
- package/dist/lib/entities/wechat-personal-message-log.entity.d.ts +27 -0
- package/dist/lib/entities/wechat-personal-message-log.entity.js +108 -0
- package/dist/lib/entities/wechat-personal-trigger-binding.entity.d.ts +17 -0
- package/dist/lib/entities/wechat-personal-trigger-binding.entity.js +71 -0
- package/dist/lib/handoff/index.d.ts +4 -0
- package/dist/lib/handoff/index.js +4 -0
- package/dist/lib/handoff/wechat-personal-chat-callback.processor.d.ts +26 -0
- package/dist/lib/handoff/wechat-personal-chat-callback.processor.js +312 -0
- package/dist/lib/handoff/wechat-personal-chat-dispatch.service.d.ts +26 -0
- package/dist/lib/handoff/wechat-personal-chat-dispatch.service.js +187 -0
- package/dist/lib/handoff/wechat-personal-chat-run-state.service.d.ts +21 -0
- package/dist/lib/handoff/wechat-personal-chat-run-state.service.js +39 -0
- package/dist/lib/handoff/wechat-personal-chat.types.d.ts +69 -0
- package/dist/lib/handoff/wechat-personal-chat.types.js +2 -0
- package/dist/lib/message.d.ts +49 -0
- package/dist/lib/message.js +64 -0
- package/dist/lib/remote-components/wechat-personal-workbench/app.js +1831 -0
- package/dist/lib/tokens.d.ts +1 -0
- package/dist/lib/tokens.js +1 -0
- package/dist/lib/types.d.ts +48 -0
- package/dist/lib/types.js +365 -0
- package/dist/lib/views/wechat-personal-view.provider.d.ts +17 -0
- package/dist/lib/views/wechat-personal-view.provider.js +441 -0
- package/dist/lib/wechat-personal-channel.strategy.d.ts +33 -0
- package/dist/lib/wechat-personal-channel.strategy.js +197 -0
- package/dist/lib/wechat-personal-integration.strategy.d.ts +56 -0
- package/dist/lib/wechat-personal-integration.strategy.js +217 -0
- package/dist/lib/wechat-personal.client.d.ts +29 -0
- package/dist/lib/wechat-personal.client.js +146 -0
- package/dist/lib/wechat-personal.controller.d.ts +50 -0
- package/dist/lib/wechat-personal.controller.js +270 -0
- package/dist/lib/wechat-personal.middleware.d.ts +20 -0
- package/dist/lib/wechat-personal.middleware.js +267 -0
- package/dist/lib/wechat-personal.plugin.d.ts +2 -0
- package/dist/lib/wechat-personal.plugin.js +58 -0
- package/dist/lib/wechat-personal.templates.d.ts +2 -0
- package/dist/lib/wechat-personal.templates.js +100 -0
- package/dist/lib/workflow/index.d.ts +5 -0
- package/dist/lib/workflow/index.js +5 -0
- package/dist/lib/workflow/wechat-personal-trigger-aggregation.service.d.ts +10 -0
- package/dist/lib/workflow/wechat-personal-trigger-aggregation.service.js +39 -0
- package/dist/lib/workflow/wechat-personal-trigger-aggregation.types.d.ts +30 -0
- package/dist/lib/workflow/wechat-personal-trigger-aggregation.types.js +2 -0
- package/dist/lib/workflow/wechat-personal-trigger-flush.processor.d.ts +8 -0
- package/dist/lib/workflow/wechat-personal-trigger-flush.processor.js +39 -0
- package/dist/lib/workflow/wechat-personal-trigger.strategy.d.ts +65 -0
- package/dist/lib/workflow/wechat-personal-trigger.strategy.js +511 -0
- package/dist/lib/workflow/wechat-personal-trigger.types.d.ts +10 -0
- package/dist/lib/workflow/wechat-personal-trigger.types.js +2 -0
- package/dist/xpert-wechat-personal-admin-assistant.yaml +103 -0
- package/dist/xpert-wechat-personal-user-assistant.yaml +127 -0
- package/package.json +79 -0
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
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;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
var WechatPersonalChatCallbackProcessor_1;
|
|
11
|
+
import { Injectable, Logger } from '@nestjs/common';
|
|
12
|
+
import { HandoffProcessorStrategy } from '@xpert-ai/plugin-sdk';
|
|
13
|
+
import { ChatMessageEventTypeEnum, ChatMessageTypeEnum } from '@xpert-ai/chatkit-types';
|
|
14
|
+
import { WechatPersonalChannelStrategy } from '../wechat-personal-channel.strategy.js';
|
|
15
|
+
import { WechatPersonalConversationService } from '../conversation.service.js';
|
|
16
|
+
import { WECHAT_PERSONAL_CHAT_CALLBACK_MESSAGE_TYPE } from './wechat-personal-chat.types.js';
|
|
17
|
+
import { WechatPersonalChatRunStateService } from './wechat-personal-chat-run-state.service.js';
|
|
18
|
+
function getTextStreamId(content) {
|
|
19
|
+
if (!content || typeof content !== 'object') {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
const id = content.id;
|
|
23
|
+
return typeof id === 'string' && id ? id : undefined;
|
|
24
|
+
}
|
|
25
|
+
function appendPlainText(accumulator, incoming, joinWithoutSeparator) {
|
|
26
|
+
if (!accumulator) {
|
|
27
|
+
return incoming;
|
|
28
|
+
}
|
|
29
|
+
if (!incoming || /^\s/.test(incoming) || joinWithoutSeparator) {
|
|
30
|
+
return `${accumulator}${incoming}`;
|
|
31
|
+
}
|
|
32
|
+
if (!/[\s\n]$/.test(accumulator)) {
|
|
33
|
+
return `${accumulator}\n${incoming}`;
|
|
34
|
+
}
|
|
35
|
+
return `${accumulator}${incoming}`;
|
|
36
|
+
}
|
|
37
|
+
function filterTextItem(content) {
|
|
38
|
+
if (typeof content === 'string') {
|
|
39
|
+
return content;
|
|
40
|
+
}
|
|
41
|
+
if (content && typeof content === 'object' && content.type === 'text') {
|
|
42
|
+
const text = content.text;
|
|
43
|
+
return typeof text === 'string' ? text : '';
|
|
44
|
+
}
|
|
45
|
+
return '';
|
|
46
|
+
}
|
|
47
|
+
function filterText(content) {
|
|
48
|
+
if (typeof content === 'string') {
|
|
49
|
+
return content;
|
|
50
|
+
}
|
|
51
|
+
if (Array.isArray(content)) {
|
|
52
|
+
let result = '';
|
|
53
|
+
let previousStreamId;
|
|
54
|
+
for (const item of content) {
|
|
55
|
+
const nextText = filterTextItem(item);
|
|
56
|
+
if (!nextText) {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
const streamId = getTextStreamId(item);
|
|
60
|
+
result = appendPlainText(result, nextText, !!previousStreamId && previousStreamId === streamId);
|
|
61
|
+
previousStreamId = streamId;
|
|
62
|
+
}
|
|
63
|
+
return result || null;
|
|
64
|
+
}
|
|
65
|
+
return filterTextItem(content) || null;
|
|
66
|
+
}
|
|
67
|
+
let WechatPersonalChatCallbackProcessor = WechatPersonalChatCallbackProcessor_1 = class WechatPersonalChatCallbackProcessor {
|
|
68
|
+
constructor(wechatChannel, conversationService, runStateService) {
|
|
69
|
+
this.wechatChannel = wechatChannel;
|
|
70
|
+
this.conversationService = conversationService;
|
|
71
|
+
this.runStateService = runStateService;
|
|
72
|
+
this.logger = new Logger(WechatPersonalChatCallbackProcessor_1.name);
|
|
73
|
+
this.sourceLocks = new Map();
|
|
74
|
+
}
|
|
75
|
+
async process(message, _ctx) {
|
|
76
|
+
const payload = message.payload;
|
|
77
|
+
if (!payload?.sourceMessageId) {
|
|
78
|
+
return {
|
|
79
|
+
status: 'dead',
|
|
80
|
+
reason: 'Missing sourceMessageId in WeChat personal callback payload'
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
if (!payload?.sequence || payload.sequence <= 0) {
|
|
84
|
+
return {
|
|
85
|
+
status: 'dead',
|
|
86
|
+
reason: 'Missing sequence in WeChat personal callback payload'
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
return this.runWithSourceLock(payload.sourceMessageId, async () => {
|
|
90
|
+
let state = await this.runStateService.get(payload.sourceMessageId);
|
|
91
|
+
if (!state) {
|
|
92
|
+
if (!payload.context) {
|
|
93
|
+
return {
|
|
94
|
+
status: 'dead',
|
|
95
|
+
reason: `Run state not found for source message "${payload.sourceMessageId}"`
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
state = this.createRunState(payload.sourceMessageId, payload.context);
|
|
99
|
+
}
|
|
100
|
+
state = this.ensureDefaults(state);
|
|
101
|
+
state.firstCallbackAt ||= Date.now();
|
|
102
|
+
if (payload.sequence < state.nextSequence) {
|
|
103
|
+
return { status: 'ok' };
|
|
104
|
+
}
|
|
105
|
+
if (!state.pendingEvents[String(payload.sequence)]) {
|
|
106
|
+
state.pendingEvents[String(payload.sequence)] = payload;
|
|
107
|
+
}
|
|
108
|
+
const completed = await this.processPendingEvents(state);
|
|
109
|
+
if (completed) {
|
|
110
|
+
await this.runStateService.clear(state.sourceMessageId);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
await this.runStateService.save(state);
|
|
114
|
+
}
|
|
115
|
+
return { status: 'ok' };
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
async runWithSourceLock(sourceMessageId, task) {
|
|
119
|
+
const previous = this.sourceLocks.get(sourceMessageId) ?? Promise.resolve();
|
|
120
|
+
const current = previous.catch(() => undefined).then(task);
|
|
121
|
+
this.sourceLocks.set(sourceMessageId, current);
|
|
122
|
+
try {
|
|
123
|
+
return await current;
|
|
124
|
+
}
|
|
125
|
+
finally {
|
|
126
|
+
if (this.sourceLocks.get(sourceMessageId) === current) {
|
|
127
|
+
this.sourceLocks.delete(sourceMessageId);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
ensureDefaults(state) {
|
|
132
|
+
if (!state.pendingEvents) {
|
|
133
|
+
state.pendingEvents = {};
|
|
134
|
+
}
|
|
135
|
+
if (!state.nextSequence || state.nextSequence <= 0) {
|
|
136
|
+
state.nextSequence = 1;
|
|
137
|
+
}
|
|
138
|
+
if (typeof state.responseMessageContent !== 'string') {
|
|
139
|
+
state.responseMessageContent = '';
|
|
140
|
+
}
|
|
141
|
+
if (!state.runCreatedAt || state.runCreatedAt <= 0) {
|
|
142
|
+
state.runCreatedAt = Date.now();
|
|
143
|
+
}
|
|
144
|
+
return state;
|
|
145
|
+
}
|
|
146
|
+
createRunState(sourceMessageId, context) {
|
|
147
|
+
return {
|
|
148
|
+
sourceMessageId,
|
|
149
|
+
nextSequence: 1,
|
|
150
|
+
responseMessageContent: '',
|
|
151
|
+
finalMessageContent: undefined,
|
|
152
|
+
terminalError: undefined,
|
|
153
|
+
runCreatedAt: Date.now(),
|
|
154
|
+
context,
|
|
155
|
+
pendingEvents: {}
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
async processPendingEvents(state) {
|
|
159
|
+
while (true) {
|
|
160
|
+
if (!state.pendingEvents[String(state.nextSequence)]) {
|
|
161
|
+
const nextAvailableSequence = this.resolveNextAvailableSequence(state);
|
|
162
|
+
if (!nextAvailableSequence) {
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
this.logger.warn(`[wechat-personal-callback] missing callback sequence source=${state.sourceMessageId} expected=${state.nextSequence} next=${nextAvailableSequence}; continuing with available event`);
|
|
166
|
+
state.nextSequence = nextAvailableSequence;
|
|
167
|
+
}
|
|
168
|
+
const payload = state.pendingEvents[String(state.nextSequence)];
|
|
169
|
+
delete state.pendingEvents[String(state.nextSequence)];
|
|
170
|
+
switch (payload.kind) {
|
|
171
|
+
case 'stream': {
|
|
172
|
+
this.applyStreamEvent(state, payload.event);
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
case 'complete': {
|
|
176
|
+
await this.completeRun(state);
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
case 'error': {
|
|
180
|
+
await this.failRun(state, payload.error);
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
default: {
|
|
184
|
+
this.logger.warn(`Unprocessed WeChat personal callback kind "${payload.kind}" in source "${state.sourceMessageId}"`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
state.nextSequence += 1;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
applyStreamEvent(state, event) {
|
|
191
|
+
const eventPayload = event?.data;
|
|
192
|
+
if (!eventPayload) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
if (eventPayload.type === ChatMessageTypeEnum.MESSAGE) {
|
|
196
|
+
const text = filterText(eventPayload.data) ?? '';
|
|
197
|
+
if (text) {
|
|
198
|
+
state.responseMessageContent += text;
|
|
199
|
+
}
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
if (eventPayload.type !== ChatMessageTypeEnum.EVENT) {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
const conversationId = this.resolveConversationId(eventPayload);
|
|
206
|
+
if (conversationId) {
|
|
207
|
+
state.context.conversationId = conversationId;
|
|
208
|
+
}
|
|
209
|
+
if (eventPayload.event === ChatMessageEventTypeEnum.ON_MESSAGE_END) {
|
|
210
|
+
const finalText = this.extractFinalMessageText(eventPayload);
|
|
211
|
+
if (finalText) {
|
|
212
|
+
state.finalMessageContent = finalText;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
async completeRun(state) {
|
|
217
|
+
const context = state.context;
|
|
218
|
+
const finalText = this.normalizeFinalText(state.finalMessageContent || state.responseMessageContent);
|
|
219
|
+
if (context.conversationUserKey && context.xpertId && context.conversationId) {
|
|
220
|
+
await this.conversationService.setConversation(context.conversationUserKey, context.xpertId, context.conversationId, undefined, context);
|
|
221
|
+
}
|
|
222
|
+
if (!finalText) {
|
|
223
|
+
this.logger.debug(`[wechat-personal-callback] skip empty final text integration=${context.integrationId} contact=${context.contactId}`);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
const result = await this.wechatChannel.sendTextByIntegrationId(context.integrationId, {
|
|
227
|
+
uuid: context.uuid,
|
|
228
|
+
contactId: context.contactId,
|
|
229
|
+
content: finalText
|
|
230
|
+
});
|
|
231
|
+
await this.conversationService.logOutbound({
|
|
232
|
+
context,
|
|
233
|
+
content: finalText,
|
|
234
|
+
status: result.success ? 'sent' : 'failed',
|
|
235
|
+
messageId: result.messageId,
|
|
236
|
+
error: result.error
|
|
237
|
+
});
|
|
238
|
+
if (!result.success) {
|
|
239
|
+
throw new Error(result.error || 'Failed to send WeChat personal reply');
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
async failRun(state, error) {
|
|
243
|
+
const context = state.context;
|
|
244
|
+
const message = error instanceof Error ? error.message : typeof error === 'string' ? error : 'Agent execution failed';
|
|
245
|
+
await this.conversationService.logOutbound({
|
|
246
|
+
context,
|
|
247
|
+
content: '',
|
|
248
|
+
status: 'failed',
|
|
249
|
+
error: message
|
|
250
|
+
});
|
|
251
|
+
const fallback = this.resolveFallbackText(context.preferLanguage, message);
|
|
252
|
+
await this.wechatChannel.sendTextByIntegrationId(context.integrationId, {
|
|
253
|
+
uuid: context.uuid,
|
|
254
|
+
contactId: context.contactId,
|
|
255
|
+
content: fallback
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
resolveConversationId(eventPayload) {
|
|
259
|
+
const candidates = [
|
|
260
|
+
eventPayload?.conversationId,
|
|
261
|
+
eventPayload?.conversation_id,
|
|
262
|
+
eventPayload?.data?.conversationId,
|
|
263
|
+
eventPayload?.data?.conversation_id,
|
|
264
|
+
eventPayload?.data?.id
|
|
265
|
+
];
|
|
266
|
+
return candidates.find((value) => typeof value === 'string' && value.trim())?.trim();
|
|
267
|
+
}
|
|
268
|
+
resolveNextAvailableSequence(state) {
|
|
269
|
+
const sequences = Object.keys(state.pendingEvents)
|
|
270
|
+
.map((value) => Number(value))
|
|
271
|
+
.filter((value) => Number.isInteger(value) && value > state.nextSequence)
|
|
272
|
+
.sort((a, b) => a - b);
|
|
273
|
+
return sequences[0];
|
|
274
|
+
}
|
|
275
|
+
extractFinalMessageText(eventPayload) {
|
|
276
|
+
const candidates = [
|
|
277
|
+
eventPayload?.data?.content,
|
|
278
|
+
eventPayload?.data?.message?.content,
|
|
279
|
+
eventPayload?.data?.output,
|
|
280
|
+
eventPayload?.data?.text
|
|
281
|
+
];
|
|
282
|
+
for (const candidate of candidates) {
|
|
283
|
+
const text = filterText(candidate);
|
|
284
|
+
if (text) {
|
|
285
|
+
return text;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return null;
|
|
289
|
+
}
|
|
290
|
+
normalizeFinalText(value) {
|
|
291
|
+
return (value || '').replace(/\n{3,}/g, '\n\n').trim();
|
|
292
|
+
}
|
|
293
|
+
resolveFallbackText(language, message) {
|
|
294
|
+
if (language === 'en') {
|
|
295
|
+
return `Sorry, I failed to generate a reply this time. ${message}`;
|
|
296
|
+
}
|
|
297
|
+
return `抱歉,这次回复生成失败了。${message}`;
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
WechatPersonalChatCallbackProcessor = WechatPersonalChatCallbackProcessor_1 = __decorate([
|
|
301
|
+
Injectable(),
|
|
302
|
+
HandoffProcessorStrategy(WECHAT_PERSONAL_CHAT_CALLBACK_MESSAGE_TYPE, {
|
|
303
|
+
types: [WECHAT_PERSONAL_CHAT_CALLBACK_MESSAGE_TYPE],
|
|
304
|
+
policy: {
|
|
305
|
+
lane: 'main'
|
|
306
|
+
}
|
|
307
|
+
}),
|
|
308
|
+
__metadata("design:paramtypes", [WechatPersonalChannelStrategy,
|
|
309
|
+
WechatPersonalConversationService,
|
|
310
|
+
WechatPersonalChatRunStateService])
|
|
311
|
+
], WechatPersonalChatCallbackProcessor);
|
|
312
|
+
export { WechatPersonalChatCallbackProcessor };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { AgentChatDispatchPayload, HandoffMessage, type PluginContext } from '@xpert-ai/plugin-sdk';
|
|
2
|
+
import { WechatPersonalMessage } from '../message.js';
|
|
3
|
+
import { WechatPersonalChatRunStateService } from './wechat-personal-chat-run-state.service.js';
|
|
4
|
+
export type WechatPersonalChatDispatchInput = {
|
|
5
|
+
xpertId: string;
|
|
6
|
+
input?: string;
|
|
7
|
+
wechatMessage: WechatPersonalMessage;
|
|
8
|
+
conversationId?: string;
|
|
9
|
+
conversationUserKey?: string;
|
|
10
|
+
tenantId: string;
|
|
11
|
+
organizationId?: string;
|
|
12
|
+
executorUserId?: string;
|
|
13
|
+
endUserId?: string;
|
|
14
|
+
};
|
|
15
|
+
export declare class WechatPersonalChatDispatchService {
|
|
16
|
+
private readonly runStateService;
|
|
17
|
+
private readonly pluginContext;
|
|
18
|
+
private readonly logger;
|
|
19
|
+
private _handoffPermissionService;
|
|
20
|
+
constructor(runStateService: WechatPersonalChatRunStateService, pluginContext: PluginContext);
|
|
21
|
+
private get handoffPermissionService();
|
|
22
|
+
enqueueDispatch(input: WechatPersonalChatDispatchInput): Promise<WechatPersonalMessage>;
|
|
23
|
+
buildDispatchMessage(input: WechatPersonalChatDispatchInput): Promise<HandoffMessage<AgentChatDispatchPayload>>;
|
|
24
|
+
private buildChatRequest;
|
|
25
|
+
private normalizeString;
|
|
26
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
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;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
11
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
12
|
+
};
|
|
13
|
+
var WechatPersonalChatDispatchService_1;
|
|
14
|
+
import { randomUUID } from 'crypto';
|
|
15
|
+
import { Inject, Injectable, Logger } from '@nestjs/common';
|
|
16
|
+
import { AGENT_CHAT_DISPATCH_MESSAGE_TYPE, HANDOFF_PERMISSION_SERVICE_TOKEN, RequestContext } from '@xpert-ai/plugin-sdk';
|
|
17
|
+
import { WECHAT_PERSONAL_PLUGIN_CONTEXT } from '../tokens.js';
|
|
18
|
+
import { WECHAT_PERSONAL_CHAT_CALLBACK_MESSAGE_TYPE } from './wechat-personal-chat.types.js';
|
|
19
|
+
import { WechatPersonalChatRunStateService } from './wechat-personal-chat-run-state.service.js';
|
|
20
|
+
let WechatPersonalChatDispatchService = WechatPersonalChatDispatchService_1 = class WechatPersonalChatDispatchService {
|
|
21
|
+
constructor(runStateService, pluginContext) {
|
|
22
|
+
this.runStateService = runStateService;
|
|
23
|
+
this.pluginContext = pluginContext;
|
|
24
|
+
this.logger = new Logger(WechatPersonalChatDispatchService_1.name);
|
|
25
|
+
}
|
|
26
|
+
get handoffPermissionService() {
|
|
27
|
+
if (!this._handoffPermissionService) {
|
|
28
|
+
this._handoffPermissionService = this.pluginContext.resolve(HANDOFF_PERMISSION_SERVICE_TOKEN);
|
|
29
|
+
}
|
|
30
|
+
return this._handoffPermissionService;
|
|
31
|
+
}
|
|
32
|
+
async enqueueDispatch(input) {
|
|
33
|
+
const message = await this.buildDispatchMessage(input);
|
|
34
|
+
await this.handoffPermissionService.enqueue(message, {
|
|
35
|
+
delayMs: 0
|
|
36
|
+
});
|
|
37
|
+
return input.wechatMessage;
|
|
38
|
+
}
|
|
39
|
+
async buildDispatchMessage(input) {
|
|
40
|
+
const { xpertId, wechatMessage, input: textInput, conversationId, conversationUserKey, tenantId, organizationId, executorUserId, endUserId } = input;
|
|
41
|
+
const resolvedExecutorUserId = this.normalizeString(executorUserId) ||
|
|
42
|
+
this.normalizeString(RequestContext.currentUserId()) ||
|
|
43
|
+
`wechat-personal:${wechatMessage.integrationId}:system`;
|
|
44
|
+
const runId = `wechat-personal-chat-${randomUUID()}`;
|
|
45
|
+
const sessionKey = conversationId ||
|
|
46
|
+
conversationUserKey ||
|
|
47
|
+
`${wechatMessage.integrationId}:${wechatMessage.uuid}:${wechatMessage.contactId}:${wechatMessage.senderId || ''}`;
|
|
48
|
+
const language = (wechatMessage.language || RequestContext.getLanguageCode() || 'zh-Hans');
|
|
49
|
+
const callbackContext = {
|
|
50
|
+
tenantId,
|
|
51
|
+
organizationId,
|
|
52
|
+
userId: resolvedExecutorUserId,
|
|
53
|
+
xpertId,
|
|
54
|
+
from: 'wechat_personal',
|
|
55
|
+
channelType: 'wechat_personal',
|
|
56
|
+
wechatPersonalConversation: true,
|
|
57
|
+
wechat_personal_conversation: true,
|
|
58
|
+
channelSource: 'wechat_personal_webhook',
|
|
59
|
+
channel_source: 'wechat_personal_webhook',
|
|
60
|
+
integrationId: wechatMessage.integrationId,
|
|
61
|
+
uuid: wechatMessage.uuid,
|
|
62
|
+
ownerWxid: wechatMessage.ownerWxid,
|
|
63
|
+
contactId: wechatMessage.contactId,
|
|
64
|
+
contact_id: wechatMessage.contactId,
|
|
65
|
+
chatId: wechatMessage.contactId,
|
|
66
|
+
chat_id: wechatMessage.contactId,
|
|
67
|
+
chatType: wechatMessage.chatType,
|
|
68
|
+
chat_type: wechatMessage.chatType,
|
|
69
|
+
senderId: wechatMessage.senderId,
|
|
70
|
+
sender_id: wechatMessage.senderId,
|
|
71
|
+
responseStrategy: 'final_text',
|
|
72
|
+
preferLanguage: language,
|
|
73
|
+
conversationUserKey: conversationUserKey || undefined,
|
|
74
|
+
conversationId: conversationId || undefined,
|
|
75
|
+
message: {
|
|
76
|
+
id: wechatMessage.id || undefined,
|
|
77
|
+
messageId: wechatMessage.messageId || undefined,
|
|
78
|
+
status: wechatMessage.status,
|
|
79
|
+
language
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
await this.runStateService.save({
|
|
83
|
+
sourceMessageId: runId,
|
|
84
|
+
nextSequence: 1,
|
|
85
|
+
responseMessageContent: '',
|
|
86
|
+
runCreatedAt: Date.now(),
|
|
87
|
+
context: callbackContext,
|
|
88
|
+
pendingEvents: {}
|
|
89
|
+
});
|
|
90
|
+
this.logger.debug(`[wechat-personal-dispatch] build dispatch runId=${runId} xpertId=${xpertId} sessionKey=${sessionKey} integration=${wechatMessage.integrationId}`);
|
|
91
|
+
return {
|
|
92
|
+
id: runId,
|
|
93
|
+
type: AGENT_CHAT_DISPATCH_MESSAGE_TYPE,
|
|
94
|
+
version: 1,
|
|
95
|
+
tenantId,
|
|
96
|
+
sessionKey,
|
|
97
|
+
businessKey: sessionKey,
|
|
98
|
+
attempt: 1,
|
|
99
|
+
maxAttempts: 1,
|
|
100
|
+
enqueuedAt: Date.now(),
|
|
101
|
+
traceId: runId,
|
|
102
|
+
payload: {
|
|
103
|
+
request: this.buildChatRequest({
|
|
104
|
+
conversationId,
|
|
105
|
+
input: textInput
|
|
106
|
+
}),
|
|
107
|
+
options: {
|
|
108
|
+
xpertId,
|
|
109
|
+
from: 'wechat_personal',
|
|
110
|
+
fromEndUserId: endUserId || wechatMessage.senderId || resolvedExecutorUserId,
|
|
111
|
+
tenantId,
|
|
112
|
+
organizationId,
|
|
113
|
+
user: {
|
|
114
|
+
id: resolvedExecutorUserId,
|
|
115
|
+
tenantId
|
|
116
|
+
},
|
|
117
|
+
language,
|
|
118
|
+
channelType: 'wechat_personal',
|
|
119
|
+
wechatPersonalConversation: true,
|
|
120
|
+
wechat_personal_conversation: true,
|
|
121
|
+
channelSource: 'wechat_personal_webhook',
|
|
122
|
+
channel_source: 'wechat_personal_webhook',
|
|
123
|
+
integrationId: wechatMessage.integrationId,
|
|
124
|
+
uuid: wechatMessage.uuid,
|
|
125
|
+
ownerWxid: wechatMessage.ownerWxid,
|
|
126
|
+
contactId: wechatMessage.contactId,
|
|
127
|
+
contact_id: wechatMessage.contactId,
|
|
128
|
+
chatId: wechatMessage.contactId,
|
|
129
|
+
chat_id: wechatMessage.contactId,
|
|
130
|
+
chatType: wechatMessage.chatType,
|
|
131
|
+
chat_type: wechatMessage.chatType,
|
|
132
|
+
senderId: wechatMessage.senderId,
|
|
133
|
+
sender_id: wechatMessage.senderId,
|
|
134
|
+
channelUserId: wechatMessage.senderId
|
|
135
|
+
},
|
|
136
|
+
callback: {
|
|
137
|
+
messageType: WECHAT_PERSONAL_CHAT_CALLBACK_MESSAGE_TYPE,
|
|
138
|
+
headers: {
|
|
139
|
+
...(organizationId ? { organizationId } : {}),
|
|
140
|
+
...(resolvedExecutorUserId ? { userId: resolvedExecutorUserId } : {}),
|
|
141
|
+
...(language ? { language } : {}),
|
|
142
|
+
...(conversationId ? { conversationId } : {}),
|
|
143
|
+
source: 'api',
|
|
144
|
+
handoffQueue: 'integration',
|
|
145
|
+
requestedLane: 'main',
|
|
146
|
+
...(wechatMessage.integrationId ? { integrationId: wechatMessage.integrationId } : {})
|
|
147
|
+
},
|
|
148
|
+
context: callbackContext
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
headers: {
|
|
152
|
+
...(organizationId ? { organizationId } : {}),
|
|
153
|
+
...(resolvedExecutorUserId ? { userId: resolvedExecutorUserId } : {}),
|
|
154
|
+
...(language ? { language } : {}),
|
|
155
|
+
...(conversationId ? { conversationId } : {}),
|
|
156
|
+
source: 'api',
|
|
157
|
+
requestedLane: 'main',
|
|
158
|
+
handoffQueue: 'realtime',
|
|
159
|
+
...(wechatMessage.integrationId ? { integrationId: wechatMessage.integrationId } : {})
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
buildChatRequest(params) {
|
|
164
|
+
return {
|
|
165
|
+
action: 'send',
|
|
166
|
+
...(params.conversationId ? { conversationId: params.conversationId } : {}),
|
|
167
|
+
message: {
|
|
168
|
+
input: {
|
|
169
|
+
input: params.input ?? ''
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
normalizeString(value) {
|
|
175
|
+
if (typeof value !== 'string') {
|
|
176
|
+
return undefined;
|
|
177
|
+
}
|
|
178
|
+
const text = value.trim();
|
|
179
|
+
return text || undefined;
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
WechatPersonalChatDispatchService = WechatPersonalChatDispatchService_1 = __decorate([
|
|
183
|
+
Injectable(),
|
|
184
|
+
__param(1, Inject(WECHAT_PERSONAL_PLUGIN_CONTEXT)),
|
|
185
|
+
__metadata("design:paramtypes", [WechatPersonalChatRunStateService, Object])
|
|
186
|
+
], WechatPersonalChatDispatchService);
|
|
187
|
+
export { WechatPersonalChatDispatchService };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type Cache } from 'cache-manager';
|
|
2
|
+
import { WechatPersonalChatCallbackContext, WechatPersonalChatCallbackPayload } from './wechat-personal-chat.types.js';
|
|
3
|
+
export interface WechatPersonalChatRunState {
|
|
4
|
+
sourceMessageId: string;
|
|
5
|
+
nextSequence: number;
|
|
6
|
+
responseMessageContent: string;
|
|
7
|
+
finalMessageContent?: string;
|
|
8
|
+
terminalError?: string;
|
|
9
|
+
runCreatedAt: number;
|
|
10
|
+
firstCallbackAt?: number;
|
|
11
|
+
context: WechatPersonalChatCallbackContext;
|
|
12
|
+
pendingEvents: Record<string, WechatPersonalChatCallbackPayload>;
|
|
13
|
+
}
|
|
14
|
+
export declare class WechatPersonalChatRunStateService {
|
|
15
|
+
private readonly cacheManager;
|
|
16
|
+
constructor(cacheManager: Cache);
|
|
17
|
+
save(state: WechatPersonalChatRunState, ttlSeconds?: number): Promise<void>;
|
|
18
|
+
get(sourceMessageId: string): Promise<WechatPersonalChatRunState | null>;
|
|
19
|
+
clear(sourceMessageId: string): Promise<void>;
|
|
20
|
+
private buildKey;
|
|
21
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
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;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
11
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
12
|
+
};
|
|
13
|
+
import { CACHE_MANAGER } from '@nestjs/cache-manager';
|
|
14
|
+
import { Inject, Injectable } from '@nestjs/common';
|
|
15
|
+
const DEFAULT_RUN_STATE_TTL_SECONDS = 15 * 60;
|
|
16
|
+
let WechatPersonalChatRunStateService = class WechatPersonalChatRunStateService {
|
|
17
|
+
constructor(cacheManager) {
|
|
18
|
+
this.cacheManager = cacheManager;
|
|
19
|
+
}
|
|
20
|
+
async save(state, ttlSeconds = DEFAULT_RUN_STATE_TTL_SECONDS) {
|
|
21
|
+
await this.cacheManager.set(this.buildKey(state.sourceMessageId), state, ttlSeconds * 1000);
|
|
22
|
+
}
|
|
23
|
+
async get(sourceMessageId) {
|
|
24
|
+
const state = await this.cacheManager.get(this.buildKey(sourceMessageId));
|
|
25
|
+
return state ?? null;
|
|
26
|
+
}
|
|
27
|
+
async clear(sourceMessageId) {
|
|
28
|
+
await this.cacheManager.del(this.buildKey(sourceMessageId));
|
|
29
|
+
}
|
|
30
|
+
buildKey(sourceMessageId) {
|
|
31
|
+
return `wechat-personal:handoff:run:${sourceMessageId}`;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
WechatPersonalChatRunStateService = __decorate([
|
|
35
|
+
Injectable(),
|
|
36
|
+
__param(0, Inject(CACHE_MANAGER)),
|
|
37
|
+
__metadata("design:paramtypes", [Object])
|
|
38
|
+
], WechatPersonalChatRunStateService);
|
|
39
|
+
export { WechatPersonalChatRunStateService };
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { LanguagesEnum, TChatOptions, TChatRequest } from '@xpert-ai/contracts';
|
|
2
|
+
import { AgentChatCallbackEnvelopePayload } from '@xpert-ai/plugin-sdk';
|
|
3
|
+
export declare const WECHAT_PERSONAL_CHAT_CALLBACK_MESSAGE_TYPE: import("@xpert-ai/plugin-sdk").StructuredHandoffMessageType;
|
|
4
|
+
export interface WechatPersonalChatMessageSnapshot {
|
|
5
|
+
id?: string;
|
|
6
|
+
messageId?: string;
|
|
7
|
+
status?: string;
|
|
8
|
+
language?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface WechatPersonalChatCallbackContext extends Record<string, unknown> {
|
|
11
|
+
tenantId: string;
|
|
12
|
+
organizationId?: string;
|
|
13
|
+
userId: string;
|
|
14
|
+
xpertId: string;
|
|
15
|
+
from?: string;
|
|
16
|
+
channelType?: string;
|
|
17
|
+
wechatPersonalConversation?: boolean;
|
|
18
|
+
wechat_personal_conversation?: boolean;
|
|
19
|
+
channelSource?: string;
|
|
20
|
+
channel_source?: string;
|
|
21
|
+
integrationId: string;
|
|
22
|
+
uuid: string;
|
|
23
|
+
ownerWxid?: string;
|
|
24
|
+
contactId: string;
|
|
25
|
+
contact_id?: string;
|
|
26
|
+
chatId: string;
|
|
27
|
+
chat_id?: string;
|
|
28
|
+
chatType?: 'private' | 'group';
|
|
29
|
+
chat_type?: 'private' | 'group';
|
|
30
|
+
senderId?: string;
|
|
31
|
+
sender_id?: string;
|
|
32
|
+
responseStrategy?: 'final_text';
|
|
33
|
+
preferLanguage?: string;
|
|
34
|
+
conversationUserKey?: string;
|
|
35
|
+
conversationId?: string;
|
|
36
|
+
message: WechatPersonalChatMessageSnapshot;
|
|
37
|
+
}
|
|
38
|
+
export interface WechatPersonalChatCallbackPayload extends AgentChatCallbackEnvelopePayload {
|
|
39
|
+
context?: WechatPersonalChatCallbackContext;
|
|
40
|
+
}
|
|
41
|
+
export interface WechatPersonalChatHandoffPayload extends Record<string, unknown> {
|
|
42
|
+
request: TChatRequest;
|
|
43
|
+
options: TChatOptions & {
|
|
44
|
+
xpertId: string;
|
|
45
|
+
from: string;
|
|
46
|
+
fromEndUserId: string;
|
|
47
|
+
tenantId: string;
|
|
48
|
+
organizationId?: string;
|
|
49
|
+
user?: any;
|
|
50
|
+
language?: LanguagesEnum;
|
|
51
|
+
channelType?: string;
|
|
52
|
+
wechatPersonalConversation?: boolean;
|
|
53
|
+
wechat_personal_conversation?: boolean;
|
|
54
|
+
channelSource?: string;
|
|
55
|
+
channel_source?: string;
|
|
56
|
+
integrationId?: string;
|
|
57
|
+
uuid?: string;
|
|
58
|
+
ownerWxid?: string;
|
|
59
|
+
contactId?: string;
|
|
60
|
+
contact_id?: string;
|
|
61
|
+
chatId?: string;
|
|
62
|
+
chat_id?: string;
|
|
63
|
+
chatType?: 'private' | 'group';
|
|
64
|
+
chat_type?: 'private' | 'group';
|
|
65
|
+
senderId?: string;
|
|
66
|
+
sender_id?: string;
|
|
67
|
+
channelUserId?: string;
|
|
68
|
+
};
|
|
69
|
+
}
|